library-picturebook-activity/docs/CONTEST_MANAGEMENT_PLAN.md
2025-11-23 14:04:20 +08:00

23 KiB
Raw Permalink Blame History

赛事管理模块产品方案与实现计划

📋 目录

  1. 产品交互方案
  2. 数据库设计分析
  3. 功能模块划分
  4. 实现计划
  5. 技术实现要点

产品交互方案

1. 赛事创建

1.1 功能概述

管理员创建赛事,填写赛事基本信息、时间安排、参赛范围等。

1.2 交互流程

管理员进入"赛事管理" → 点击"创建赛事" → 填写表单 → 保存草稿/提交审核

1.3 表单字段(基于 t_contest 表)

  • 基本信息

    • 赛事名称(必填,唯一性校验)
    • 赛事类型字典individual/team
    • 赛事状态默认unpublished
    • 封面图(上传)
    • 海报图(上传)
    • 赛事详情(富文本编辑器)
  • 时间安排

    • 赛事开始时间
    • 赛事结束时间
    • 报名开始时间
    • 报名结束时间
    • 作品提交开始时间
    • 作品提交结束时间
    • 评审开始时间
    • 评审结束时间
    • 结果发布时间(可选)
  • 参赛范围

    • 授权租户(多选,支持租户列表选择)
    • 提交规则once/resubmit
  • 联系信息

    • 联系人姓名
    • 联系电话
    • 联系人二维码(上传)
  • 组织信息

    • 主办单位(数组)
    • 协办单位(数组)
    • 赞助单位(数组)
  • 线下信息

    • 线下地址(可选)
  • 评审规则

    • 评审规则ID关联评审规则配置

1.4 业务规则

  • 时间顺序校验:报名开始 < 报名结束 < 提交开始 < 提交结束 < 评审开始 < 评审结束 < 结果发布
  • 赛事名称在系统内唯一
  • 创建后状态为 unpublished,需要发布后才能被租户看到
  • 支持保存草稿(可多次编辑)

2. 赛事发布

2.1 功能概述

管理员将已创建的赛事发布,使其对授权租户可见。

2.2 交互流程

赛事列表 → 选择赛事 → 点击"发布" → 确认发布 → 更新状态为 published

2.3 业务规则

  • 只有状态为 unpublished 的赛事可以发布
  • 发布前校验必填字段完整性
  • 发布后,授权租户可以看到该赛事
  • 已发布的赛事可以撤回(状态改回 unpublished),但需要检查是否有报名记录

3. 赛事公告

3.1 功能概述

管理员发布赛事相关公告,通知参赛者重要信息。

3.2 交互流程

赛事详情页 → "公告管理" → 创建公告 → 编辑内容 → 发布公告

3.3 功能设计

注意:当前 SQL 中没有公告表,需要新增:

  • t_contest_notice
    • id, contest_id, title, content, notice_type, priority, publish_time, creator, create_time, modify_time, valid_state

3.4 公告类型

  • 系统公告(系统自动生成)
  • 人工公告(管理员发布)
  • 紧急通知(高优先级)

4. 赛事报名

4.1 功能概述

授权租户的用户(学生/老师)报名参加赛事,支持个人赛和团队赛。

4.2 交互流程

个人赛报名:

租户用户登录 → 浏览已发布赛事 → 选择赛事 → 点击"立即报名" → 填写信息 → 提交报名

团队赛报名:

队长创建团队 → 邀请成员 → 成员确认加入 → 队长提交团队报名 → 等待审核

4.3 报名流程详细设计

4.3.1 个人赛报名

  1. 用户选择赛事
  2. 检查报名时间是否在有效期内
  3. 检查用户是否已报名(防止重复报名)
  4. 填写报名信息(账号信息自动填充)
  5. 提交报名状态pending
  6. 管理员审核(可选,根据赛事配置)
  7. 审核通过状态passed或拒绝状态rejected

4.3.2 团队赛报名

  1. 队长创建团队
    • 填写团队名称(租户内唯一)
    • 设置最大成员数
    • 邀请成员(通过账号搜索)
  2. 成员确认加入
    • 收到邀请通知
    • 确认/拒绝加入
  3. 队长提交团队报名
    • 检查团队成员数量是否符合要求
    • 提交报名所有成员状态pending
  4. 管理员审核团队报名
    • 审核通过:所有成员状态改为 passed
    • 审核拒绝:所有成员状态改为 rejected

4.4 数据表关系

  • t_contest_registration:报名记录表
    • 个人赛registration_type = 'individual'team_id = null
    • 团队赛registration_type = 'team'team_id 关联 t_contest_team
  • t_contest_team:团队表
  • t_contest_team_member:团队成员表

4.5 业务规则

  • 报名时间限制:必须在 register_start_timeregister_end_time 之间
  • 报名状态流转pending → passed/rejected/withdrawn
  • 已通过的报名可以撤回withdrawn但需要检查是否已提交作品
  • 团队名称在同一赛事、同一租户内唯一
  • 团队成员角色leader队长、member队员、mentor指导教师

5. 赛事作品提交

5.1 功能概述

已报名的用户提交参赛作品,支持单次提交和多次提交(根据赛事配置)。

5.2 交互流程

已报名用户 → 进入"我的赛事" → 选择赛事 → 点击"提交作品" → 上传作品文件 → 填写作品信息 → 提交

5.3 作品提交表单

  • 作品标题(必填)
  • 作品说明(可选)
  • 作品文件(支持多文件上传)
    • 图片、视频、3D模型等
    • 文件类型和大小限制
  • 作品预览URL可选用于3D/视频预览)
  • AI建模元数据可选JSON格式

5.4 提交规则

单次提交submit_rule = 'once'

  • 只能提交一次作品
  • 提交后状态为 submitted,不可修改

多次提交submit_rule = 'resubmit'

  • 可以多次提交作品
  • 每次提交创建新版本version 递增)
  • 旧版本 is_latest = 0,新版本 is_latest = 1
  • 只有最新版本参与评审

5.5 数据表关系

  • t_contest_work:作品主表
    • entry_id 关联 t_contest_registration.id
    • files 字段存储简易文件列表JSON
  • t_contest_work_attachment:作品附件表(详细文件信息)

5.6 业务规则

  • 提交时间限制:必须在 submit_start_timesubmit_end_time 之间
  • 必须已通过报名审核registration_state = 'passed'
  • 作品编号work_no自动生成格式CONTEST-{contest_id}-{序号}
  • 作品状态流转:
    • submitted已提交
    • locked已锁定不可修改
    • reviewing评审中
    • rejected已拒绝
    • accepted已接受

6. 赛事作品评审

6.1 功能概述

评委对提交的作品进行评分,支持多维度评分和评语。

6.2 交互流程

评委登录 → 进入"评审管理" → 选择赛事 → 查看分配的作品列表 → 点击作品 → 查看作品详情 → 评分 → 提交评分

6.3 评审流程设计

6.3.1 作品分配

  • 需要新增表:t_contest_work_judge_assignment(作品分配表)
    • id, contest_id, work_id, judge_id, assignment_time, status
  • 管理员或系统自动分配作品给评委
  • 支持手动分配和自动分配(轮询、随机等)

6.3.2 评分界面

  • 显示作品信息(标题、说明、文件、预览等)
  • 显示评审规则review_rule_id 关联的评审规则)
  • 多维度评分表单
    • 根据评审规则动态生成评分维度
    • 每个维度设置分数范围
    • 总分自动计算(根据评审规则配置的权重)
  • 评语输入框
  • 提交评分按钮

6.3.3 评分数据

  • t_contest_work_score:评分表
    • dimension_scoresJSON格式存储各维度分数
    • total_score总分根据规则计算
    • comments评语

6.4 评审规则设计

需要新增表:t_contest_review_rule(评审规则表)

  • id, contest_id, rule_name, dimensionsJSON存储评分维度配置
  • 示例维度配置:
    {
      "dimension1": {
        "name": "创意性",
        "weight": 0.3,
        "maxScore": 100
      },
      "dimension2": {
        "name": "技术性",
        "weight": 0.4,
        "maxScore": 100
      },
      "dimension3": {
        "name": "完成度",
        "weight": 0.3,
        "maxScore": 100
      }
    }
    

6.5 业务规则

  • 评审时间限制:必须在 review_start_timereview_end_time 之间
  • 作品状态更新:评审开始时,作品状态改为 reviewing
  • 每个作品可以被多个评委评审
  • 最终得分计算:取所有评委的平均分,或根据评审规则计算
  • 评审完成后,作品状态改为 acceptedrejected

7. 赛事结果公布

7.1 功能概述

管理员公布赛事评审结果,包括获奖名单、排名等。

7.2 交互流程

管理员 → 进入"赛事管理" → 选择赛事 → 点击"公布结果" → 确认公布 → 结果发布

7.3 结果公布内容

  • 获奖名单(按奖项分类)
  • 作品排名(按总分排序)
  • 各维度平均分统计
  • 评审统计信息(参与评审人数、作品数量等)

7.4 业务规则

  • 只有评审已完成的赛事可以公布结果
  • 公布后,result_publish_time 设置为当前时间
  • 结果公布后,所有用户可以看到排名和获奖信息
  • 支持导出结果Excel/PDF

数据库设计分析

现有表结构

1. t_contest赛事表

优点:

  • 字段设计完整,覆盖赛事全生命周期
  • 支持多租户contest_tenant 字段)
  • 时间字段齐全

⚠️ 注意事项:

  • contest_tenant 使用 text 类型存储租户列表,建议考虑 JSON 类型或关联表
  • organizersco_organizerssponsors 使用 text 存储数组,建议使用 JSON

2. t_contest_attachment赛事附件表

设计合理,支持多种文件类型

3. t_contest_work作品表

优点:

  • 支持版本控制version、is_latest
  • 支持多种提交来源teacher/student/team_leader
  • 支持 AI 建模元数据

⚠️ 问题:

  • 索引 idx_submit_filter 引用了不存在的字段 review_status,应删除或修正

4. t_contest_work_attachment作品附件表

⚠️ 问题:

  • 第102行末尾有多余的逗号需要删除

5. t_contest_work_score评分表

⚠️ 问题:

  • 表定义重复104-123行和125-144行需要删除重复定义

6. t_contest_registration报名表

设计合理,支持个人和团队报名

7. t_contest_team团队表

⚠️ 问题:

  • 唯一索引 uk_team_name 引用了不存在的字段 name,应改为 team_name

8. t_contest_team_member团队成员表

设计合理

缺失的表结构

1. t_contest_notice赛事公告表

CREATE TABLE `t_contest_notice` (
  `id` varchar(63) NOT NULL COMMENT '主键id',
  `contest_id` varchar(63) NOT NULL COMMENT '赛事id',
  `title` varchar(255) NOT NULL COMMENT '公告标题',
  `content` text NOT NULL COMMENT '公告内容',
  `notice_type` varchar(31) NOT NULL DEFAULT 'manual' COMMENT '公告类型system/manual/urgent',
  `priority` int DEFAULT 0 COMMENT '优先级(数字越大优先级越高)',
  `publish_time` datetime DEFAULT NULL COMMENT '发布时间',
  `creator` varchar(63) NOT NULL DEFAULT '' COMMENT '创建人',
  `modifier` varchar(63) NOT NULL DEFAULT '' COMMENT '修改人',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `modify_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `valid_state` varchar(1) NOT NULL DEFAULT '1' COMMENT '有效状态1-有效2-失效)',
  PRIMARY KEY (`id`),
  KEY `idx_contest` (`contest_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='赛事公告表';

2. t_contest_review_rule评审规则表

CREATE TABLE `t_contest_review_rule` (
  `id` varchar(63) NOT NULL COMMENT '主键id',
  `contest_id` varchar(63) NOT NULL COMMENT '赛事id',
  `rule_name` varchar(127) NOT NULL COMMENT '规则名称',
  `dimensions` json NOT NULL COMMENT '评分维度配置JSON',
  `calculation_rule` varchar(31) DEFAULT 'average' COMMENT '计算规则average/max/min/weighted',
  `creator` varchar(63) NOT NULL DEFAULT '' COMMENT '创建人',
  `modifier` varchar(63) NOT NULL DEFAULT '' COMMENT '修改人',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `modify_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `valid_state` varchar(1) NOT NULL DEFAULT '1' COMMENT '有效状态1-有效2-失效)',
  PRIMARY KEY (`id`),
  KEY `idx_contest` (`contest_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评审规则表';

3. t_contest_work_judge_assignment作品分配表

CREATE TABLE `t_contest_work_judge_assignment` (
  `id` varchar(63) NOT NULL COMMENT '主键id',
  `contest_id` varchar(63) NOT NULL COMMENT '赛事id',
  `work_id` varchar(63) NOT NULL COMMENT '作品id',
  `judge_id` varchar(63) NOT NULL COMMENT '评委用户id',
  `assignment_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '分配时间',
  `status` varchar(31) NOT NULL DEFAULT 'assigned' COMMENT '分配状态assigned/reviewing/completed',
  `creator` varchar(63) NOT NULL DEFAULT '' COMMENT '创建人',
  `modifier` varchar(63) NOT NULL DEFAULT '' COMMENT '修改人',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `modify_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_work_judge` (`work_id`, `judge_id`),
  KEY `idx_contest_judge` (`contest_id`, `judge_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='作品分配表';

功能模块划分

后端模块结构

backend/src/contests/
├── contests.module.ts          # 赛事主模块
├── contests.controller.ts       # 赛事控制器
├── contests.service.ts         # 赛事服务
├── dto/
│   ├── create-contest.dto.ts
│   ├── update-contest.dto.ts
│   ├── query-contest.dto.ts
│   └── publish-contest.dto.ts
├── works/
│   ├── works.module.ts
│   ├── works.controller.ts
│   ├── works.service.ts
│   └── dto/
│       ├── create-work.dto.ts
│       ├── update-work.dto.ts
│       └── submit-work.dto.ts
├── registrations/
│   ├── registrations.module.ts
│   ├── registrations.controller.ts
│   ├── registrations.service.ts
│   └── dto/
│       ├── create-registration.dto.ts
│       ├── review-registration.dto.ts
│       └── create-team.dto.ts
├── teams/
│   ├── teams.module.ts
│   ├── teams.controller.ts
│   ├── teams.service.ts
│   └── dto/
│       ├── create-team.dto.ts
│       └── invite-member.dto.ts
├── reviews/
│   ├── reviews.module.ts
│   ├── reviews.controller.ts
│   ├── reviews.service.ts
│   └── dto/
│       ├── create-score.dto.ts
│       ├── assign-work.dto.ts
│       └── create-review-rule.dto.ts
└── notices/
    ├── notices.module.ts
    ├── notices.controller.ts
    ├── notices.service.ts
    └── dto/
        ├── create-notice.dto.ts
        └── update-notice.dto.ts

前端模块结构

frontend/src/views/contests/
├── Index.vue                    # 赛事列表页
├── Create.vue                   # 创建赛事页
├── Detail.vue                   # 赛事详情页
├── Edit.vue                     # 编辑赛事页
├── works/
│   ├── Index.vue                # 作品列表页
│   ├── Submit.vue               # 提交作品页
│   └── Detail.vue               # 作品详情页
├── registrations/
│   ├── Index.vue                # 报名列表页
│   ├── Register.vue            # 报名页
│   └── Review.vue              # 审核报名页
├── teams/
│   ├── Index.vue                # 团队列表页
│   ├── Create.vue               # 创建团队页
│   └── Detail.vue               # 团队详情页
├── reviews/
│   ├── Index.vue                # 评审列表页
│   ├── Score.vue                # 评分页
│   └── Results.vue              # 结果公布页
└── notices/
    ├── Index.vue                # 公告列表页
    └── Create.vue                # 创建公告页

实现计划

阶段一基础功能2-3周

1.1 数据库迁移

  • 修复 SQL 文件中的错误
    • 删除 t_contest_work_score 重复定义
    • 修复 t_contest_work_attachment 的逗号错误
    • 修复 t_contest_team 索引字段名错误
    • 修复 t_contest_work 索引字段错误
  • 创建缺失的表(公告表、评审规则表、作品分配表)
  • 更新 Prisma schema
  • 执行数据库迁移

1.2 后端基础模块

  • 创建 contests 模块Controller、Service、DTO
  • 实现赛事 CRUD 接口
  • 实现赛事发布/撤回接口
  • 实现租户权限控制
  • 编写单元测试

1.3 前端基础页面

  • 创建赛事列表页
  • 创建赛事创建/编辑页
  • 创建赛事详情页
  • 集成权限控制
  • 实现路由配置

阶段二报名功能2周

2.1 后端实现

  • 创建 registrations 模块
  • 实现个人报名接口
  • 实现团队报名接口
  • 实现报名审核接口
  • 实现报名状态流转逻辑

2.2 前端实现

  • 创建报名页面
  • 创建团队管理页面
  • 创建报名审核页面
  • 实现报名状态展示

阶段三作品提交功能2周

3.1 后端实现

  • 创建 works 模块
  • 实现作品提交接口
  • 实现文件上传功能
  • 实现作品版本控制逻辑
  • 实现作品状态管理

3.2 前端实现

  • 创建作品提交页面
  • 实现文件上传组件
  • 创建作品列表页
  • 创建作品详情页

阶段四评审功能2-3周

4.1 后端实现

  • 创建 reviews 模块
  • 实现评审规则管理接口
  • 实现作品分配接口
  • 实现评分接口
  • 实现评分计算逻辑

4.2 前端实现

  • 创建评审规则配置页
  • 创建作品分配页
  • 创建评分页面
  • 实现多维度评分表单

阶段五结果公布与公告1周

5.1 后端实现

  • 创建 notices 模块
  • 实现公告 CRUD 接口
  • 实现结果公布接口
  • 实现结果统计接口

5.2 前端实现

  • 创建公告管理页面
  • 创建结果公布页面
  • 实现结果展示页面

阶段六优化与测试1-2周

6.1 功能优化

  • 性能优化(数据库查询优化、缓存)
  • 用户体验优化
  • 错误处理完善

6.2 测试

  • 单元测试
  • 集成测试
  • 端到端测试
  • 压力测试

技术实现要点

1. 权限设计

1.1 权限编码规划

contest:create      # 创建赛事
contest:read        # 查看赛事
contest:update      # 更新赛事
contest:delete      # 删除赛事
contest:publish     # 发布赛事
contest:register    # 报名赛事
work:submit         # 提交作品
work:read           # 查看作品
work:update         # 更新作品
review:assign       # 分配作品
review:score        # 评分
review:read         # 查看评审
result:publish      # 公布结果
notice:create       # 创建公告
notice:read         # 查看公告

1.2 角色规划

  • 超级管理员:所有权限
  • 赛事管理员contest:、notice:、result:publish
  • 评委review:assign、review:score、review:read、work:read
  • 参赛者contest:read、contest:register、work:submit、work:read

2. 租户隔离

2.1 数据隔离策略

  • 赛事创建:超级租户创建,通过 contest_tenant 字段控制可见范围
  • 报名数据:通过 tenant_key 字段隔离
  • 作品数据:通过 tenant_key 字段隔离
  • 评审数据:通过 tenant_key 字段隔离

2.2 接口权限控制

  • 使用 @TenantId() 装饰器获取租户信息
  • Service 层自动过滤租户数据
  • Controller 层验证租户权限

3. 时间状态管理

3.1 赛事状态机

unpublished → published → (可撤回) → unpublished

3.2 报名状态机

pending → passed/rejected/withdrawn

3.3 作品状态机

submitted → locked → reviewing → accepted/rejected

3.4 定时任务

  • 自动更新报名状态(根据时间)
  • 自动更新作品提交状态
  • 自动更新评审状态
  • 自动发送通知(可选)

4. 文件上传

4.1 文件存储

  • 使用对象存储OSS/S3或本地存储
  • 文件类型限制图片、视频、3D模型、文档等
  • 文件大小限制:根据文件类型设置

4.2 文件管理

  • 文件上传接口
  • 文件删除接口
  • 文件预览接口
  • 文件下载接口

5. 通知系统

5.1 通知类型

  • 报名成功通知
  • 报名审核结果通知
  • 作品提交成功通知
  • 评审结果通知
  • 结果公布通知

5.2 通知方式(可选)

  • 站内消息
  • 邮件通知
  • 短信通知(可选)

6. 数据统计

6.1 赛事统计

  • 报名人数统计
  • 作品提交数量统计
  • 评审进度统计
  • 结果统计

6.2 报表导出

  • 报名名单导出Excel
  • 作品列表导出Excel
  • 评审结果导出Excel
  • 结果报告导出PDF

注意事项

1. SQL 文件问题修复

在开始开发前,必须先修复 SQL 文件中的错误:

  • 删除重复的 t_contest_work_score 表定义
  • 修复 t_contest_work_attachment 表的语法错误
  • 修复 t_contest_team 表的索引错误
  • 修复 t_contest_work 表的索引错误

2. 数据一致性

  • 报名和作品的关系:一个报名可以对应多个作品版本
  • 团队和报名的关系:一个团队对应多个报名记录(每个成员一条)
  • 作品和评分的关系:一个作品可以有多条评分记录(多个评委)

3. 性能考虑

  • 赛事列表查询需要分页
  • 作品列表查询需要分页和筛选
  • 评分计算需要缓存
  • 文件上传需要异步处理

4. 安全性

  • 文件上传需要验证文件类型和大小
  • 接口需要权限验证
  • 敏感操作需要日志记录
  • 防止 SQL 注入和 XSS 攻击

总结

本方案基于现有的多租户 RBAC 系统架构,设计了完整的赛事管理功能模块。主要特点:

  1. 完整的生命周期管理:从赛事创建到结果公布的全流程
  2. 灵活的参赛方式:支持个人赛和团队赛
  3. 强大的评审系统:支持多维度评分和自定义评审规则
  4. 良好的扩展性:模块化设计,易于扩展新功能

建议按照阶段逐步实现,每个阶段完成后进行测试和优化,确保系统稳定可靠。