import request from "@/utils/request"; import type { PaginationParams, PaginationResponse } from "@/types/api"; // ==================== 比赛相关类型 ==================== export interface Contest { id: number; contestName: string; contestType: "individual" | "team"; contestState: "unpublished" | "published"; status: "ongoing" | "finished"; // 赛事进度状态 startTime: string; endTime: string; address?: string; content?: string; contestTenants?: number[]; coverUrl?: string; posterUrl?: string; contactName?: string; contactPhone?: string; contactQrcode?: string; organizers?: string; coOrganizers?: string; sponsors?: string; // 报名配置 registerStartTime: string; registerEndTime: string; registerState?: string; requireAudit: boolean; // 是否需要审核 allowedGrades?: number[]; // 允许报名的年级 allowedClasses?: number[]; // 允许报名的班级 teamMinMembers?: number; // 团队最少人数 teamMaxMembers?: number; // 团队最多人数 // 作品配置 submitRule: "once" | "resubmit"; submitStartTime: string; submitEndTime: string; workType?: "image" | "video" | "document" | "code" | "other"; // 作品类型 workRequirement?: string; // 作品要求 // 评审配置 reviewRuleId?: number; reviewStartTime: string; reviewEndTime: string; // 赛果配置 resultState: "unpublished" | "published"; // 赛果发布状态 resultPublishTime?: string; creator?: number; modifier?: number; createTime?: string; modifyTime?: string; validState?: number; attachments?: ContestAttachment[]; reviewRule?: ContestReviewRule; _count?: { registrations: number; works: number; teams: number; judges: number; }; } export interface CreateContestForm { id?: number; // 编辑模式时使用 contestName: string; contestType: "individual" | "team"; startTime: string; endTime: string; address?: string; content?: string; contestTenants?: number[]; coverUrl?: string; posterUrl?: string; contactName?: string; contactPhone?: string; contactQrcode?: string; organizers?: string; coOrganizers?: string; sponsors?: string; // 报名配置 registerStartTime: string; registerEndTime: string; requireAudit?: boolean; allowedGrades?: number[]; allowedClasses?: number[]; teamMinMembers?: number; teamMaxMembers?: number; // 作品配置 submitRule?: "once" | "resubmit"; submitStartTime: string; submitEndTime: string; workType?: "image" | "video" | "document" | "code" | "other"; workRequirement?: string; // 评审配置 reviewStartTime: string; reviewEndTime: string; resultPublishTime?: string; } export interface UpdateContestForm extends Partial { contestState?: "unpublished" | "published"; } export interface QueryContestParams extends PaginationParams { contestName?: string; contestState?: "unpublished" | "published"; status?: "ongoing" | "finished"; contestType?: string; role?: "student" | "teacher" | "judge"; } // ==================== 附件相关类型 ==================== export interface ContestAttachment { id: number; contestId: number; fileName: string; fileUrl: string; format?: string; fileType?: string; size?: string; creator?: number; modifier?: number; createTime?: string; modifyTime?: string; validState?: number; } export interface CreateAttachmentForm { contestId: number; fileName: string; fileUrl: string; format?: string; fileType?: string; size?: string; } // ==================== 评审规则相关类型 ==================== export interface ReviewDimension { name: string; percentage: number; description?: string; } export interface ContestReviewRule { id: number; tenantId: number; ruleName: string; ruleDescription?: string; judgeCount: number; dimensions: ReviewDimension[]; calculationRule: "average" | "remove_max_min" | "remove_min"; creator?: number; modifier?: number; createTime?: string; modifyTime?: string; validState?: number; contests?: Array<{ id: number; contestName: string; }>; } export interface CreateReviewRuleForm { ruleName: string; ruleDescription?: string; judgeCount: number; dimensions: ReviewDimension[]; calculationRule?: "average" | "remove_max_min" | "remove_min"; } // ==================== 报名相关类型 ==================== export interface ContestRegistration { id: number; contestId: number; tenantId: number; registrationType?: "individual" | "team"; teamId?: number; teamName?: string; userId: number; accountNo: string; accountName: string; role?: string; registrationState: "pending" | "passed" | "rejected" | "withdrawn"; registrant?: number; registrationTime: string; reason?: string; operator?: number; operationDate?: string; creator?: number; modifier?: number; createTime?: string; modifyTime?: string; contest?: Contest; user?: { id: number; username: string; nickname: string; gender?: string; phone?: string; student?: { id: number; gender?: number; phone?: string; class?: { id: number; name: string; grade?: { id: number; name: string; }; }; }; }; registrant?: number; teachers?: Array<{ id: number; userId: number; isDefault: boolean; user: { id: number; username: string; nickname: string; }; }>; team?: ContestTeam; _count?: { works: number; }; } export interface CreateRegistrationForm { contestId: number; registrationType: "individual" | "team"; teamId?: number; userId: number; } export interface ReviewRegistrationForm { registrationState: "pending" | "passed" | "rejected" | "withdrawn"; reason?: string; } export interface QueryRegistrationParams extends PaginationParams { contestId?: number; registrationState?: "pending" | "passed" | "rejected" | "withdrawn"; registrationType?: string; userId?: number; } // ==================== 团队相关类型 ==================== export interface ContestTeam { id: number; tenantId: number; contestId: number; teamName: string; leaderUserId: number; maxMembers?: number; creator?: number; modifier?: number; createTime?: string; modifyTime?: string; validState?: number; leader?: { id: number; username: string; nickname: string; }; members?: ContestTeamMember[]; contest?: Contest; _count?: { members: number; registrations: number; }; } export interface ContestTeamMember { id: number; tenantId: number; teamId: number; userId: number; role: "leader" | "member" | "mentor"; creator?: number; modifier?: number; createTime?: string; modifyTime?: string; user?: { id: number; username: string; nickname: string; }; } export interface CreateTeamForm { contestId: number; teamName: string; leaderId: number; memberIds?: number[]; teacherIds?: number[]; maxMembers?: number; } export interface InviteMemberForm { userId: number; role?: "leader" | "member" | "mentor"; } // ==================== 作品相关类型 ==================== export interface ContestWork { id: number; tenantId: number; contestId: number; registrationId: number; workNo?: string; title: string; description?: string; files?: string[]; version: number; isLatest: boolean; status: "submitted" | "locked" | "reviewing" | "rejected" | "accepted"; submitTime: string; submitterUserId?: number; submitterAccountNo?: string; submitSource: string; previewUrl?: string; previewUrls?: string[]; aiModelMeta?: any; creator?: number; modifier?: number; createTime?: string; modifyTime?: string; validState?: number; contest?: Contest; registration?: ContestRegistration; attachments?: ContestWorkAttachment[]; assignments?: Array<{ id: number; judgeId: number; status: string; judge?: { id: number; username: string; nickname: string; }; }>; _count?: { scores: number; assignments: number; }; // 评审统计字段(由后端计算返回) reviewedCount?: number; totalJudgesCount?: number; averageScore?: number | null; } export interface ContestWorkAttachment { id: number; tenantId: number; contestId: number; workId: number; fileName: string; fileUrl: string; format?: string; fileType?: string; size?: string; creator?: number; modifier?: number; createTime?: string; modifyTime?: string; } export interface SubmitWorkForm { registrationId: number; title: string; description?: string; files?: string[]; previewUrl?: string; previewUrls?: string[]; aiModelMeta?: any; } export interface QueryWorkParams extends PaginationParams { contestId?: number; registrationId?: number; status?: "submitted" | "locked" | "reviewing" | "rejected" | "accepted"; title?: string; workNo?: string; username?: string; } // ==================== 评审相关类型 ==================== export interface ContestWorkJudgeAssignment { id: number; contestId: number; workId: number; judgeId: number; assignmentTime: string; status: "assigned" | "reviewing" | "completed"; creator?: number; modifier?: number; createTime?: string; modifyTime?: string; work?: ContestWork; judge?: { id: number; username: string; nickname: string; }; scores?: ContestWorkScore[]; } export interface ContestWorkScore { id: number; tenantId: number; contestId: number; workId: number; assignmentId: number; judgeId: number; judgeName: string; dimensionScores: any; totalScore: number; comments?: string; scoreTime: string; creator?: number; modifier?: number; createTime?: string; modifyTime?: string; validState?: number; work?: ContestWork; judge?: { id: number; username: string; nickname: string; }; } export interface AssignWorkForm { workId: number; judgeIds: number[]; } export interface CreateScoreForm { workId: number; assignmentId: number; dimensionScores: any; totalScore: number; comments?: string; } // ==================== 公告相关类型 ==================== export interface ContestNotice { id: number; contestId: number; title: string; content: string; noticeType: "system" | "manual" | "urgent"; priority: number; publishTime?: string; creator?: number; modifier?: number; createTime?: string; modifyTime?: string; validState?: number; contest?: Contest; } export interface CreateNoticeForm { contestId: number; title: string; content: string; noticeType?: "system" | "manual" | "urgent"; priority?: number; } // ==================== 评委相关类型 ==================== export interface ContestJudge { id: number; contestId: number; judgeId: number; specialty?: string; weight?: number; description?: string; creator?: number; modifier?: number; createTime?: string; modifyTime?: string; validState?: number; contest?: Contest; judge?: { id: number; username: string; nickname: string; email?: string; phone?: string; gender?: 'male' | 'female'; status?: 'enabled' | 'disabled'; tenantId?: number; tenant?: { id: number; name: string; }; contestJudges?: Array<{ contest: { id: number; contestName: string; status: string; }; }>; }; _count?: { assignedContestWorks: number; scoredContestWorks: number; }; } export interface CreateJudgeForm { contestId: number; judgeId: number; specialty?: string; weight?: number; description?: string; } // ==================== API 函数 ==================== // 比赛管理 export const contestsApi = { // 获取比赛列表 getList: async ( params: QueryContestParams ): Promise> => { const response = await request.get>( "/contests", { params } ); return response; }, // 获取我参与的赛事列表 getMyContests: async ( params: QueryContestParams ): Promise> => { const response = await request.get>( "/contests/my-contests", { params } ); return response; }, // 获取比赛详情 getDetail: async (id: number): Promise => { const response = await request.get(`/contests/${id}`); return response; }, // 创建比赛 create: async (data: CreateContestForm): Promise => { const response = await request.post("/contests", data); return response; }, // 更新比赛 update: async (id: number, data: UpdateContestForm): Promise => { const response = await request.patch(`/contests/${id}`, data); return response; }, // 发布/撤回比赛 publish: async ( id: number, contestState: "unpublished" | "published" ): Promise => { const response = await request.patch( `/contests/${id}/publish`, { contestState } ); return response; }, // 删除比赛 delete: async (id: number): Promise => { return await request.delete(`/contests/${id}`); }, // 标记比赛完结 finish: async (id: number): Promise => { const response = await request.patch(`/contests/${id}/finish`); return response; }, // 重新开启已完结的比赛 reopen: async (id: number): Promise => { const response = await request.patch(`/contests/${id}/reopen`); return response; }, }; // 附件管理 export const attachmentsApi = { // 获取比赛附件列表 getList: async (contestId: number): Promise => { const response = await request.get( `/contests/attachments/contest/${contestId}` ); return response; }, // 创建附件 create: async (data: CreateAttachmentForm): Promise => { const response = await request.post( "/contests/attachments", data ); return response; }, // 删除附件 delete: async (id: number): Promise => { return await request.delete(`/contests/attachments/${id}`); }, }; // 评审规则 export interface ReviewRule { id: number; tenantId: number; ruleName: string; ruleDescription?: string; judgeCount: number; dimensions: ReviewDimension[]; calculationRule: "average" | "remove_max_min" | "remove_min"; validState: number; contests?: Array<{ id: number; contestName: string; }>; createTime?: string; modifyTime?: string; } // 用于选择器的简化评审规则 export interface ReviewRuleForSelect { id: number; ruleName: string; ruleDescription?: string; judgeCount: number; calculationRule: string; } export interface QueryReviewRuleParams { ruleName?: string; page?: number; pageSize?: number; } export const reviewRulesApi = { // 获取评审规则列表 getList: async (params?: QueryReviewRuleParams): Promise<{ list: ReviewRule[]; total: number; page: number; pageSize: number; }> => { const response = await request.get("/contests/review-rules", { params }); return response; }, // 获取所有可用的评审规则(用于赛事创建时选择) getForSelect: async (): Promise => { const response = await request.get( "/contests/review-rules/select" ); return response; }, // 获取评审规则详情 getDetail: async (id: number): Promise => { const response = await request.get( `/contests/review-rules/${id}` ); return response; }, // 创建评审规则 create: async (data: CreateReviewRuleForm): Promise => { const response = await request.post( "/contests/review-rules", data ); return response; }, // 更新评审规则 update: async ( id: number, data: Partial ): Promise => { const response = await request.patch( `/contests/review-rules/${id}`, data ); return response; }, // 删除评审规则 delete: async (id: number): Promise => { await request.delete(`/contests/review-rules/${id}`); }, }; // 报名管理 export const registrationsApi = { // 获取报名列表 getList: async ( params: QueryRegistrationParams ): Promise> => { const response = await request.get< any, PaginationResponse >("/contests/registrations", { params }); return response; }, // 获取报名详情 getDetail: async (id: number): Promise => { const response = await request.get( `/contests/registrations/${id}` ); return response; }, // 创建报名 create: async ( data: CreateRegistrationForm ): Promise => { const response = await request.post( "/contests/registrations", data ); return response; }, // 添加指导老师 addTeacher: async ( registrationId: number, teacherUserId: number ): Promise => { const response = await request.post( `/contests/registrations/${registrationId}/teachers`, { teacherUserId } ); return response; }, // 移除指导老师 removeTeacher: async ( registrationId: number, teacherUserId: number ): Promise => { await request.delete( `/contests/registrations/${registrationId}/teachers/${teacherUserId}` ); }, // 审核报名 review: async ( id: number, data: ReviewRegistrationForm ): Promise => { const response = await request.patch( `/contests/registrations/${id}/review`, data ); return response; }, // 删除报名 delete: async (id: number): Promise => { return await request.delete(`/contests/registrations/${id}`); }, }; // 团队管理 export const teamsApi = { // 获取团队列表 getList: async (contestId: number): Promise => { const response = await request.get( `/contests/teams/contest/${contestId}` ); return response; }, // 获取团队详情 getDetail: async (id: number): Promise => { const response = await request.get( `/contests/teams/${id}` ); return response; }, // 创建团队 create: async (data: CreateTeamForm): Promise => { const response = await request.post( "/contests/teams", data ); return response; }, // 更新团队 update: async ( id: number, data: Partial ): Promise => { const response = await request.patch( `/contests/teams/${id}`, data ); return response; }, // 邀请成员 inviteMember: async ( teamId: number, data: InviteMemberForm ): Promise => { const response = await request.post( `/contests/teams/${teamId}/members`, data ); return response; }, // 移除成员 removeMember: async (teamId: number, userId: number): Promise => { return await request.delete( `/contests/teams/${teamId}/members/${userId}` ); }, // 删除团队 delete: async (id: number): Promise => { return await request.delete(`/contests/teams/${id}`); }, }; // 教师指导作品查询参数 export interface QueryGuidedWorkParams extends PaginationParams { contestId?: number; workNo?: string; playerName?: string; accountNo?: string; } // 教师指导的作品(含评审进度) export interface GuidedWork extends ContestWork { reviewProgress: string; } // 作品管理 export const worksApi = { // 获取作品列表 getList: async ( params: QueryWorkParams ): Promise> => { const response = await request.get>( "/contests/works", { params } ); return response; }, // 获取教师指导的作品列表 getGuidedWorks: async ( params: QueryGuidedWorkParams ): Promise> => { const response = await request.get>( "/contests/works/guided", { params } ); return response; }, // 获取作品详情 getDetail: async (id: number): Promise => { const response = await request.get( `/contests/works/${id}` ); return response; }, // 提交作品 submit: async (data: SubmitWorkForm): Promise => { const response = await request.post( "/contests/works/submit", data ); return response; }, // 获取作品版本列表 getVersions: async (registrationId: number): Promise => { const response = await request.get( `/contests/works/registration/${registrationId}/versions` ); return response; }, // 删除作品 delete: async (id: number): Promise => { return await request.delete(`/contests/works/${id}`); }, }; // ==================== 评审进度相关类型 ==================== export interface ReviewProgress { contest: { id: number; contestName: string; reviewStartTime: string; reviewEndTime: string; reviewRule: ContestReviewRule | null; }; summary: { totalWorks: number; assignedWorksCount: number; scoredWorksCount: number; unassignedWorksCount: number; totalJudges: number; totalAssignments: number; totalScores: number; pendingScoresCount: number; }; progress: { assignmentProgress: number; scoringProgress: number; overallProgress: number; }; judgeProgress: JudgeProgressItem[]; unassignedWorks: UnassignedWork[]; pendingAssignments: PendingAssignment[]; } export interface JudgeProgressItem { judgeId: number; judgeName: string; specialty: string | null; weight: number | null; assignedCount: number; scoredCount: number; pendingCount: number; progress: number; } export interface UnassignedWork { id: number; workNo: string | null; title: string; createTime: string; registration: { user: { id: number; nickname: string; username: string } | null; team: { id: number; teamName: string } | null; } | null; } export interface PendingAssignment { id: number; workId: number; workNo: string | null; workTitle: string; judgeId: number; judgeName: string; status: string; assignmentTime: string; } export interface WorkStatusStats { submitted: number; reviewing: number; reviewed: number; awarded: number; total: number; } export interface BatchAssignForm { workIds: number[]; judgeIds: number[]; } export interface BatchAssignResult { created: number; skipped: number; assignments: ContestWorkJudgeAssignment[]; } export interface AutoAssignResult { message: string; worksCount?: number; created: number; judgesPerWork?: number; } // 评审管理 export const reviewsApi = { // 分配作品给评委 assignWork: async ( contestId: number, data: AssignWorkForm ): Promise => { const response = await request.post( `/contests/reviews/assign?contestId=${contestId}`, data ); return response; }, // 批量分配作品给评委 batchAssignWorks: async ( contestId: number, data: BatchAssignForm ): Promise => { const response = await request.post( `/contests/reviews/batch-assign?contestId=${contestId}`, data ); return response; }, // 自动分配作品给评委 autoAssignWorks: async (contestId: number): Promise => { const response = await request.post( `/contests/reviews/auto-assign?contestId=${contestId}` ); return response; }, // 评分 score: async (data: CreateScoreForm): Promise => { const response = await request.post( "/contests/reviews/score", data ); return response; }, // 更新评分 updateScore: async ( scoreId: number, data: Partial ): Promise => { const response = await request.patch( `/contests/reviews/score/${scoreId}`, data ); return response; }, // 获取分配给当前评委的作品 getAssignedWorks: async ( contestId: number ): Promise => { const response = await request.get( `/contests/reviews/assigned?contestId=${contestId}` ); return response; }, // 获取评审进度统计 getReviewProgress: async (contestId: number): Promise => { const response = await request.get( `/contests/reviews/progress/${contestId}` ); return response; }, // 获取作品状态统计 getWorkStatusStats: async (contestId: number): Promise => { const response = await request.get( `/contests/reviews/work-status/${contestId}` ); return response; }, // 获取作品评分列表 getWorkScores: async (workId: number): Promise => { const response = await request.get( `/contests/reviews/work/${workId}/scores` ); return response; }, // 计算最终得分 calculateFinalScore: async ( workId: number ): Promise<{ finalScore: number; scoreCount: number; calculationRule: string; }> => { const response = await request.get< any, { finalScore: number; scoreCount: number; calculationRule: string } >(`/contests/reviews/work/${workId}/final-score`); return response; }, // 替换评委 replaceJudge: async ( assignmentId: number, newJudgeId: number ): Promise => { await request.post( `/contests/reviews/replace-judge`, { assignmentId, newJudgeId } ); }, }; // 公告管理 export const noticesApi = { // 获取公告列表(按赛事) getList: async (contestId: number): Promise => { const response = await request.get( `/contests/notices/contest/${contestId}` ); return response; }, // 获取所有公告(支持搜索和分页) getAll: async (params?: { title?: string; publishDate?: string; page?: number; pageSize?: number; }): Promise> => { const response = await request.get>( '/contests/notices', { params } ); return response; }, // 获取公告详情 getDetail: async (id: number): Promise => { const response = await request.get( `/contests/notices/${id}` ); return response; }, // 创建公告 create: async (data: CreateNoticeForm): Promise => { const response = await request.post( "/contests/notices", data ); return response; }, // 更新公告 update: async ( id: number, data: Partial ): Promise => { const response = await request.patch( `/contests/notices/${id}`, data ); return response; }, // 删除公告 delete: async (id: number): Promise => { return await request.delete(`/contests/notices/${id}`); }, }; // ==================== 赛果相关类型 ==================== export interface ContestResult { id: number; workNo: string | null; title: string; finalScore: number | null; rank: number | null; awardLevel: string | null; awardName: string | null; certificateUrl: string | null; registration: { user: { id: number; username: string; nickname: string } | null; team: { id: number; teamName: string } | null; } | null; } export interface ResultsResponse { contest: { id: number; contestName: string; resultState: string; resultPublishTime: string | null; reviewRule: ContestReviewRule | null; }; list: ContestResult[]; total: number; page: number; pageSize: number; awardDistribution: Record; } export interface ResultsSummary { contest: { id: number; contestName: string; resultState: string; resultPublishTime: string | null; }; summary: { totalWorks: number; scoredWorks: number; rankedWorks: number; awardedWorks: number; unscoredWorks: number; }; awardDistribution: Record; scoreStats: { avgScore: string | null; maxScore: string | null; minScore: string | null; }; } export interface SetAwardForm { awardLevel: 'first' | 'second' | 'third' | 'excellent' | 'none'; awardName?: string; certificateUrl?: string; } export interface BatchSetAwardsForm { awards: Array<{ workId: number; awardLevel: 'first' | 'second' | 'third' | 'excellent' | 'none'; awardName?: string; }>; } export interface AutoSetAwardsForm { first?: number; second?: number; third?: number; excellent?: number; } // 赛果管理 export const resultsApi = { // 计算所有作品的最终得分 calculateScores: async (contestId: number): Promise<{ message: string; calculatedCount: number; calculationRule: string }> => { const response = await request.post( `/contests/results/${contestId}/calculate-scores` ); return response; }, // 计算排名 calculateRankings: async (contestId: number): Promise<{ message: string; rankedCount: number }> => { const response = await request.post( `/contests/results/${contestId}/calculate-rankings` ); return response; }, // 设置单个作品奖项 setAward: async (workId: number, data: SetAwardForm): Promise => { const response = await request.patch( `/contests/results/work/${workId}/award`, data ); return response; }, // 批量设置奖项 batchSetAwards: async (contestId: number, data: BatchSetAwardsForm): Promise => { const response = await request.post( `/contests/results/${contestId}/batch-set-awards`, data ); return response; }, // 根据排名自动设置奖项 autoSetAwards: async (contestId: number, data: AutoSetAwardsForm): Promise => { const response = await request.post( `/contests/results/${contestId}/auto-set-awards`, data ); return response; }, // 发布赛果 publish: async (contestId: number): Promise => { const response = await request.post( `/contests/results/${contestId}/publish` ); return response; }, // 撤回发布 unpublish: async (contestId: number): Promise => { const response = await request.post( `/contests/results/${contestId}/unpublish` ); return response; }, // 获取比赛结果列表(作品列表) getResults: async ( contestId: number, params: { page?: number; pageSize?: number; workNo?: string; accountNo?: string; } = {} ): Promise => { const response = await request.get( `/contests/results/${contestId}`, { params: { page: params.page || 1, pageSize: params.pageSize || 10, ...params } } ); return response; }, // 获取比赛结果统计摘要 getSummary: async (contestId: number): Promise => { const response = await request.get( `/contests/results/${contestId}/summary` ); return response; }, }; // 评委管理 export const judgesApi = { // 获取评委列表 getList: async (contestId: number): Promise => { const response = await request.get( `/contests/judges/contest/${contestId}` ); return response; }, // 获取评委详情 getDetail: async (id: number): Promise => { const response = await request.get( `/contests/judges/${id}` ); return response; }, // 添加评委 create: async (data: CreateJudgeForm): Promise => { const response = await request.post( "/contests/judges", data ); return response; }, // 更新评委 update: async ( id: number, data: Partial ): Promise => { const response = await request.patch( `/contests/judges/${id}`, data ); return response; }, // 删除评委 delete: async (id: number): Promise => { return await request.delete(`/contests/judges/${id}`); }, };