diff --git a/.claude/settings.local.json b/.claude/settings.local.json index b9a1f5d..0cfaeac 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -2,7 +2,8 @@ "permissions": { "allow": [ "Bash(tree:*)", - "Bash(find:*)" + "Bash(find:*)", + "Bash(git add:*)" ], "deny": [], "ask": [] diff --git a/doc/API对接完成度检查报告.md b/doc/API对接完成度检查报告.md new file mode 100644 index 0000000..7fa421e --- /dev/null +++ b/doc/API对接完成度检查报告.md @@ -0,0 +1,375 @@ +# API对接完成度检查报告 + +**检查时间**: 2025-12-11 +**项目路径**: `D:\workspace\31.比赛项目\project\martial-admin-mini` + +--- + +## 📊 完成度总览 + +``` +总体进度: ██░░░░░░░░ 20% + +✅ 已完成: + - 数据可行性分析 (100%) + - 保护方案设计 (100%) + - 后端能力评估 (100%) + - 文档编写 (100%) + +❌ 未开始: + - 前端API对接代码 (0%) + - 后端小程序接口 (0%) + - 联调测试 (0%) +``` + +--- + +## ✅ 已完成的工作 + +### 1. 文档编写(100%) + +| 文档 | 状态 | 内容 | +|------|------|------| +| 数据可行性分析报告.md | ✅ 完成 | 600+行,详细分析前后端数据能力 | +| 保护Mock版本的实施方案.md | ✅ 完成 | 700+行,4层保护机制 + 完整代码 | +| 后端实现对比报告.md | ✅ 完成 | 后端API和Entity对比 | +| API接口设计.md | ✅ 完成 | 17个API接口定义 | +| 数据结构设计.md | ✅ 完成 | 17个TypeScript接口 | + +### 2. 分析评估(100%) + +- ✅ 5个页面的Mock数据需求分析 +- ✅ 后端Entity字段能力评估 +- ✅ 后端Controller接口能力评估 +- ✅ 数据可行性评分:95/100 +- ✅ 缺失接口清单(5个) +- ✅ SQL查询示例编写 +- ✅ VO类设计 + +### 3. 方案设计(100%) + +- ✅ Git分支隔离方案 +- ✅ 配置开关控制方案 +- ✅ 数据适配器架构设计 +- ✅ Mock数据整理方案 +- ✅ API目录结构设计 +- ✅ 实施步骤规划 + +--- + +## ❌ 未完成的工作 + +### 1. 前端代码实现(0%) + +#### 缺失文件清单 + +``` +martial-admin-mini/ +├── ❌ config/ +│ ├── ❌ env.config.js # 环境配置 +│ └── ❌ api.config.js # API配置 +│ +├── ❌ mock/ # Mock数据目录 +│ ├── ❌ index.js # Mock入口 +│ ├── ❌ login.js # 登录Mock +│ ├── ❌ athlete.js # 选手Mock +│ └── ❌ score.js # 评分Mock +│ +├── ❌ api/ # API调用目录 +│ ├── ❌ index.js # API入口 +│ ├── ❌ auth.js # 认证API +│ ├── ❌ athlete.js # 选手API +│ └── ❌ score.js # 评分API +│ +└── utils/ + ├── ❌ request.js # 网络请求封装 + └── ❌ dataAdapter.js # 数据适配器 +``` + +#### 需要修改的页面 + +``` +pages/ +├── login/ +│ └── ⚠️ login.vue # 需要接入dataAdapter +├── score-list/ +│ └── ⚠️ score-list.vue # 需要接入dataAdapter +├── score-detail/ +│ └── ⚠️ score-detail.vue # 需要接入dataAdapter +├── score-list-multi/ +│ └── ⚠️ score-list-multi.vue # 需要接入dataAdapter +└── modify-score/ + └── ⚠️ modify-score.vue # 需要接入dataAdapter +``` + +### 2. 后端代码实现(0%) + +#### 缺失的后端文件 + +``` +martial-master/src/main/java/org/springblade/modules/martial/ +├── controller/ +│ └── ❌ MartialMiniController.java # 小程序专用Controller +│ +├── service/ +│ ├── ❌ IMartialMiniService.java # 接口 +│ └── impl/ +│ └── ❌ MartialMiniServiceImpl.java # 实现类 +│ +├── mapper/ +│ └── ❌ (需要添加自定义SQL方法) +│ +└── pojo/ + └── vo/ + ├── ❌ LoginVO.java # 登录响应VO + ├── ❌ AthleteWithScoreVO.java # 带评分的选手VO + ├── ❌ AthleteAdminVO.java # 裁判长选手VO + ├── ❌ AthleteScoreDetailVO.java # 评分详情VO + └── ❌ JudgeScoreVO.java # 评委评分VO +``` + +#### 缺失的接口(5个) + +```java +❌ POST /api/mini/login # 登录验证 +❌ GET /api/mini/athletes # 普通评委选手列表 +❌ GET /api/mini/athletes/admin # 裁判长选手列表 +❌ GET /api/mini/score/detail/{athleteId} # 评分详情 +❌ PUT /api/mini/score/modify # 修改评分 +``` + +### 3. 保护机制实施(0%) + +``` +❌ Git分支创建 + - 未创建 v1.0-mock 标签 + - 未创建 feature/api-integration 分支 + +❌ Mock版本备份 + - 未创建代码压缩包 + - 未导出Git存档 + +❌ 配置文件部署 + - 未部署 env.config.js + - 未设置 dataMode 开关 +``` + +### 4. 测试验证(0%) + +``` +❌ 单元测试 +❌ 接口测试 +❌ 联调测试 +❌ UI功能测试 +❌ 数据格式验证 +❌ Token验证测试 +❌ 权限验证测试 +``` + +--- + +## 📋 待办事项清单 + +### 第一阶段:保护现有代码(1小时) + +- [ ] 提交当前代码到Git +- [ ] 创建 v1.0-mock 标签 +- [ ] 创建 feature/api-integration 分支 +- [ ] 创建代码备份压缩包 + +### 第二阶段:前端基础架构(2-3小时) + +- [ ] 创建 `config/env.config.js` +- [ ] 创建 `utils/request.js` +- [ ] 创建 `utils/dataAdapter.js` +- [ ] 创建 `mock/` 目录结构 +- [ ] 创建 `api/` 目录结构 + +### 第三阶段:Mock数据整理(2小时) + +- [ ] 从页面中提取Mock数据到 `mock/login.js` +- [ ] 提取到 `mock/athlete.js` +- [ ] 提取到 `mock/score.js` +- [ ] 编写 `mock/index.js` 入口 + +### 第四阶段:API接口定义(1-2小时) + +- [ ] 编写 `api/auth.js` +- [ ] 编写 `api/athlete.js` +- [ ] 编写 `api/score.js` +- [ ] 编写 `api/index.js` 入口 + +### 第五阶段:页面改造(2-3小时) + +- [ ] 修改 `login.vue` 使用 dataAdapter +- [ ] 修改 `score-list.vue` 使用 dataAdapter +- [ ] 修改 `score-detail.vue` 使用 dataAdapter +- [ ] 修改 `score-list-multi.vue` 使用 dataAdapter +- [ ] 修改 `modify-score.vue` 使用 dataAdapter + +### 第六阶段:后端开发(3-5天) + +- [ ] 创建 `MartialMiniController.java` +- [ ] 实现登录接口 +- [ ] 实现选手列表接口(普通评委) +- [ ] 实现选手列表接口(裁判长) +- [ ] 实现评分详情接口 +- [ ] 实现修改评分接口 +- [ ] 编写自定义SQL +- [ ] 创建VO类 + +### 第七阶段:联调测试(2-3天) + +- [ ] Mock模式测试 +- [ ] API模式测试 +- [ ] 模式切换测试 +- [ ] 完整业务流程测试 +- [ ] 错误处理测试 +- [ ] 性能测试 + +--- + +## 🎯 当前状态总结 + +### ✅ 优势 + +1. **分析充分**: 数据可行性已全面评估(95分) +2. **方案完整**: 保护机制、架构设计全部完成 +3. **文档齐全**: 600+700行的详细实施文档 +4. **可行性高**: 后端数据能力完全支持前端需求 + +### ⚠️ 差距 + +1. **无实际代码**: 所有API对接代码均未创建 +2. **无保护措施**: Git分支、标签、备份均未实施 +3. **无后端接口**: 5个小程序专用接口需要开发 +4. **页面未改造**: 5个页面仍使用硬编码Mock数据 + +### 📊 完成度评估 + +``` +理论准备: ████████████████████ 100% +实际开发: ████░░░░░░░░░░░░░░░░ 20% + ↑ + 仅有文档 +``` + +--- + +## 🚀 下一步建议 + +### 方案A:立即开始前端API对接(推荐) + +**优势**: +- 快速验证方案可行性 +- 前端可独立开发(Mock模式) +- 为后端开发提供明确的接口规范 + +**步骤**: +1. 保护当前代码(Git分支 + 标签) +2. 创建前端基础架构文件 +3. 整理Mock数据 +4. 改造5个页面 +5. 测试Mock模式 + +**预计时间**: 1-2天 + +### 方案B:先开发后端接口 + +**优势**: +- 先有真实数据 +- 前端直接对接真实API + +**步骤**: +1. 创建 `MartialMiniController` +2. 实现5个接口 +3. 测试接口 +4. 前端对接 + +**预计时间**: 3-5天 + +### 方案C:前后端并行开发 + +**优势**: +- 最快完成 +- 前端Mock模式开发,后端实现接口 +- 最后联调 + +**步骤**: +1. 前端:创建架构 + Mock数据 +2. 后端:实现5个接口 +3. 联调:切换到API模式测试 + +**预计时间**: 1周 + +--- + +## ⚡ 快速启动建议 + +**如果要立即开始,我建议按以下顺序进行**: + +### Step 1: 保护现有代码(10分钟) +```bash +git add . +git commit -m "✅ Mock版本完成" +git tag -a v1.0-mock -m "Mock原型版本" +git checkout -b feature/api-integration +``` + +### Step 2: 创建基础文件(30分钟) + +我可以帮您创建: +- ✅ `config/env.config.js` +- ✅ `utils/request.js` +- ✅ `utils/dataAdapter.js` +- ✅ `mock/index.js` +- ✅ `api/index.js` + +### Step 3: 改造一个页面(1小时) + +先改造登录页作为示例: +- ✅ 提取Mock数据 +- ✅ 接入dataAdapter +- ✅ 测试Mock模式 + +### Step 4: 完成其他页面(2-3小时) + +按照登录页的模式改造其他4个页面 + +--- + +## 🤔 需要您的决策 + +**请告诉我您希望如何进行**: + +1. ❓ **是否立即开始前端API对接代码开发?** + - 如果是,我将开始创建所有必要的文件 + +2. ❓ **是否需要先保护Mock版本?** + - 创建Git分支和标签 + +3. ❓ **是否需要先开发后端接口?** + - 创建 `MartialMiniController` 和相关代码 + +4. ❓ **选择哪种方案?** + - 方案A: 前端先行 + - 方案B: 后端先行 + - 方案C: 前后端并行 + +--- + +## 📞 总结 + +**当前状态**: +- ✅ 完成了所有的分析、设计和文档工作 +- ❌ 尚未开始实际的代码开发工作 + +**完成度**: 20%(仅理论准备) + +**下一步**: 等待您的决策,选择实施方案后开始开发 + +--- + +**报告生成时间**: 2025-12-11 +**检查范围**: 前端代码 + 文档 +**检查结果**: ❌ API对接尚未开始 diff --git a/doc/API接口设计.md b/doc/API接口设计.md new file mode 100644 index 0000000..d019d2c --- /dev/null +++ b/doc/API接口设计.md @@ -0,0 +1,1107 @@ +# API接口设计文档 + +## 接口概述 + +本文档定义了武术评分系统需要对接的所有后端API接口。当前项目为前端原型,所有数据均为Mock数据,需要按照本文档定义的接口规范进行后端开发和对接。 + +## 接口规范 + +### 基础信息 + +- **协议**: HTTPS +- **请求格式**: JSON +- **响应格式**: JSON +- **字符编码**: UTF-8 +- **认证方式**: JWT Token + +### 通用请求头 + +``` +Content-Type: application/json +Authorization: Bearer {token} +``` + +### 通用响应格式 + +```json +{ + "code": 200, // 状态码 + "message": "成功", // 提示信息 + "data": {} // 业务数据 +} +``` + +### 状态码说明 + +| 状态码 | 说明 | +|-------|------| +| 200 | 请求成功 | +| 400 | 请求参数错误 | +| 401 | 未授权/Token失效 | +| 403 | 权限不足 | +| 404 | 资源不存在 | +| 500 | 服务器错误 | + +--- + +## 一、认证模块 + +### 1.1 用户登录 + +**接口地址**: `POST /api/auth/login` + +**功能描述**: 通过比赛编码和邀请码进行登录认证 + +**请求参数**: + +```json +{ + "matchCode": "string", // 比赛编码,必填 + "inviteCode": "string" // 邀请码 (pub/admin),必填 +} +``` + +**响应数据**: + +```json +{ + "code": 200, + "message": "登录成功", + "data": { + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", // JWT Token + "userRole": "pub", // 用户角色: pub-普通评委, admin-裁判长 + "matchId": "match_001", // 比赛ID + "matchName": "2025年全国武术散打锦标赛", // 比赛名称 + "matchTime": "2025-06-25T09:00:00", // 比赛时间 + "judgeId": "judge_001", // 评委ID + "judgeName": "张三", // 评委姓名 + "venueId": "venue_001", // 场地ID (普通评委) + "venueName": "第一场地" // 场地名称 (普通评委) + } +} +``` + +**错误响应**: + +```json +{ + "code": 401, + "message": "邀请码错误或已失效", + "data": null +} +``` + +### 1.2 退出登录 + +**接口地址**: `POST /api/auth/logout` + +**功能描述**: 用户退出登录,清除Token + +**请求参数**: 无 + +**响应数据**: + +```json +{ + "code": 200, + "message": "退出成功", + "data": null +} +``` + +### 1.3 Token验证 + +**接口地址**: `GET /api/auth/verify` + +**功能描述**: 验证Token是否有效 + +**请求参数**: 无(通过Header传递Token) + +**响应数据**: + +```json +{ + "code": 200, + "message": "Token有效", + "data": { + "valid": true, + "expiresIn": 3600 // 剩余有效时间(秒) + } +} +``` + +--- + +## 二、比赛信息模块 + +### 2.1 获取比赛详情 + +**接口地址**: `GET /api/matches/{matchId}` + +**功能描述**: 获取指定比赛的详细信息 + +**路径参数**: +- `matchId`: 比赛ID + +**响应数据**: + +```json +{ + "code": 200, + "message": "成功", + "data": { + "matchId": "match_001", + "matchName": "2025年全国武术散打锦标赛暨第十七届世界武术锦标赛选拔赛", + "matchTime": "2025-06-25T09:00:00", + "matchEndTime": "2025-06-25T18:00:00", + "location": "北京体育馆", + "status": "ongoing", // ongoing-进行中, finished-已结束, upcoming-未开始 + "description": "比赛说明", + "venueCount": 5, // 场地数量 + "projectCount": 8, // 项目数量 + "athleteCount": 150 // 参赛选手数量 + } +} +``` + +--- + +## 三、场地管理模块 + +### 3.1 获取场地列表 + +**接口地址**: `GET /api/venues` + +**功能描述**: 获取比赛的所有场地列表(裁判长专用) + +**请求参数**: +``` +matchId: string // 比赛ID,必填 +``` + +**响应数据**: + +```json +{ + "code": 200, + "message": "成功", + "data": [ + { + "venueId": "venue_001", + "venueName": "第一场地", + "order": 1, // 排序顺序 + "athleteCount": 30, // 选手数量 + "scoredCount": 25, // 已评分数量 + "status": "active" // active-进行中, completed-已完成 + }, + { + "venueId": "venue_002", + "venueName": "第二场地", + "order": 2, + "athleteCount": 30, + "scoredCount": 28, + "status": "active" + } + ] +} +``` + +--- + +## 四、项目管理模块 + +### 4.1 获取项目列表 + +**接口地址**: `GET /api/projects` + +**功能描述**: 获取比赛的所有项目列表 + +**请求参数**: +``` +matchId: string // 比赛ID,必填 +venueId: string // 场地ID,选填(裁判长查询所有场地时不传) +``` + +**响应数据**: + +```json +{ + "code": 200, + "message": "成功", + "data": [ + { + "projectId": "project_001", + "projectName": "女子组长拳", + "category": "套路", // 项目类别 + "order": 1, // 排序顺序 + "athleteCount": 15, // 参赛选手数 + "minScore": 5.0, // 最低分 + "maxScore": 10.0, // 最高分 + "status": "ongoing" // ongoing-进行中, completed-已完成 + }, + { + "projectId": "project_002", + "projectName": "男子组陈氏太极拳", + "category": "太极拳", + "order": 2, + "athleteCount": 20, + "minScore": 5.0, + "maxScore": 10.0, + "status": "ongoing" + } + ] +} +``` + +### 4.2 获取项目详情 + +**接口地址**: `GET /api/projects/{projectId}` + +**功能描述**: 获取指定项目的详细信息 + +**路径参数**: +- `projectId`: 项目ID + +**响应数据**: + +```json +{ + "code": 200, + "message": "成功", + "data": { + "projectId": "project_001", + "projectName": "女子组长拳", + "category": "套路", + "description": "项目说明", + "rules": "评分规则", + "minScore": 5.0, + "maxScore": 10.0, + "scoreStep": 0.001, // 评分步进值 + "athleteCount": 15, + "deductions": [ // 扣分项列表 + { + "deductionId": "deduction_001", + "text": "起势不稳", + "score": -0.1 + } + ] + } +} +``` + +--- + +## 五、选手管理模块 + +### 5.1 获取选手列表(普通评委) + +**接口地址**: `GET /api/athletes` + +**功能描述**: 普通评委获取分配给自己的选手列表 + +**请求参数**: +``` +matchId: string // 比赛ID,必填 +venueId: string // 场地ID,必填 +projectId: string // 项目ID,必填 +judgeId: string // 评委ID,必填 +``` + +**响应数据**: + +```json +{ + "code": 200, + "message": "成功", + "data": { + "total": 30, // 总选手数 + "scored": 25, // 已评分数 + "unscored": 5, // 未评分数 + "athletes": [ + { + "athleteId": "athlete_001", + "name": "张三", + "gender": "male", // male-男, female-女 + "idCard": "123456789000000000", + "team": "少林寺武术大学院", + "number": "123-4567898275", // 比赛编号 + "order": 1, // 出场顺序 + "myScore": 8.906, // 我的评分(已评分时有值) + "totalScore": 8.907, // 总分(已评分时有值) + "scored": true, // 是否已评分 + "scoreTime": "2025-06-25T09:15:00", // 评分时间 + "status": "finished" // waiting-等待, performing-表演中, finished-完成 + }, + { + "athleteId": "athlete_002", + "name": "李四", + "gender": "male", + "idCard": "123456789000000001", + "team": "武当山武术学院", + "number": "123-4567898276", + "order": 2, + "myScore": null, + "totalScore": null, + "scored": false, + "scoreTime": null, + "status": "waiting" + } + ] + } +} +``` + +### 5.2 获取选手列表(裁判长) + +**接口地址**: `GET /api/athletes/all` + +**功能描述**: 裁判长获取所有场地和项目的选手列表 + +**请求参数**: +``` +matchId: string // 比赛ID,必填 +venueId: string // 场地ID,选填 +projectId: string // 项目ID,选填 +``` + +**响应数据**: + +```json +{ + "code": 200, + "message": "成功", + "data": { + "total": 150, + "scored": 120, + "athletes": [ + { + "athleteId": "athlete_001", + "name": "张三", + "gender": "male", + "idCard": "123456789000000000", + "team": "少林寺武术大学院", + "number": "123-4567898275", + "order": 1, + "venueId": "venue_001", + "venueName": "第一场地", + "projectId": "project_001", + "projectName": "女子组长拳", + "totalScore": 8.907, // 总分 + "judgeCount": 6, // 评分评委数 + "totalJudges": 6, // 总评委数 + "canModify": true, // 是否可修改 + "status": "finished" + } + ] + } +} +``` + +### 5.3 获取选手详情 + +**接口地址**: `GET /api/athletes/{athleteId}` + +**功能描述**: 获取选手详细信息 + +**路径参数**: +- `athleteId`: 选手ID + +**响应数据**: + +```json +{ + "code": 200, + "message": "成功", + "data": { + "athleteId": "athlete_001", + "name": "张三", + "gender": "male", + "age": 25, + "idCard": "123456789000000000", + "team": "少林寺武术大学院", + "number": "123-4567898275", + "order": 1, + "projectId": "project_001", + "projectName": "女子组长拳", + "venueId": "venue_001", + "venueName": "第一场地", + "photo": "https://example.com/photo.jpg", // 选手照片URL + "defaultScore": 8.907, // 默认起评分 + "status": "finished" + } +} +``` + +--- + +## 六、评分管理模块 + +### 6.1 提交评分 + +**接口地址**: `POST /api/scores` + +**功能描述**: 普通评委提交对选手的评分 + +**请求参数**: + +```json +{ + "matchId": "match_001", // 比赛ID,必填 + "athleteId": "athlete_001", // 选手ID,必填 + "judgeId": "judge_001", // 评委ID,必填 + "projectId": "project_001", // 项目ID,必填 + "venueId": "venue_001", // 场地ID,必填 + "score": 8.907, // 评分,必填 + "deductions": [ // 扣分项ID列表,选填 + "deduction_001", + "deduction_003" + ], + "note": "整体表现良好" // 备注,选填 +} +``` + +**响应数据**: + +```json +{ + "code": 200, + "message": "评分提交成功", + "data": { + "scoreId": "score_001", + "athleteId": "athlete_001", + "score": 8.907, + "createTime": "2025-06-25T09:15:00", + "totalScore": 8.905, // 当前总分(如果有多个评委) + "judgeCount": 3 // 已评分评委数 + } +} +``` + +**错误响应**: + +```json +{ + "code": 400, + "message": "该选手已评分,不可重复提交", + "data": null +} +``` + +### 6.2 获取评分详情 + +**接口地址**: `GET /api/scores/{athleteId}` + +**功能描述**: 获取选手的所有评分详情(裁判长使用) + +**路径参数**: +- `athleteId`: 选手ID + +**请求参数**: +``` +judgeId: string // 评委ID,选填(普通评委只能看自己的评分) +``` + +**响应数据**: + +```json +{ + "code": 200, + "message": "成功", + "data": { + "athleteId": "athlete_001", + "athleteName": "张三", + "team": "少林寺武术大学院", + "number": "123-4567898275", + "projectName": "女子组长拳", + "totalScore": 8.907, + "judgeCount": 6, + "totalJudges": 6, + "judgeScores": [ + { + "scoreId": "score_001", + "judgeId": "judge_001", + "judgeName": "欧阳丽娜", + "score": 8.907, + "deductions": [ + { + "deductionId": "deduction_001", + "text": "起势不稳", + "score": -0.1 + } + ], + "note": "整体表现良好", + "scoreTime": "2025-06-25T09:15:00" + }, + { + "scoreId": "score_002", + "judgeId": "judge_002", + "judgeName": "张三", + "score": 8.901, + "deductions": [], + "note": "", + "scoreTime": "2025-06-25T09:16:00" + } + ], + "modifications": [ + { + "modifyId": "modify_001", + "modifierId": "admin_001", + "modifierName": "总裁判长", + "originalScore": 8.907, + "modifiedScore": 8.910, + "note": "技术动作优秀,加分", + "modifyTime": "2025-06-25T10:00:00" + } + ] + } +} +``` + +### 6.3 修改评分(裁判长) + +**接口地址**: `PUT /api/scores/{athleteId}/modify` + +**功能描述**: 裁判长修改选手的总分 + +**路径参数**: +- `athleteId`: 选手ID + +**请求参数**: + +```json +{ + "modifierId": "admin_001", // 裁判长ID,必填 + "originalScore": 8.907, // 原始分数,必填 + "modifiedScore": 8.910, // 修改后的分数,必填 + "note": "技术动作优秀,加分" // 修改原因,必填 +} +``` + +**响应数据**: + +```json +{ + "code": 200, + "message": "修改成功", + "data": { + "modifyId": "modify_001", + "athleteId": "athlete_001", + "originalScore": 8.907, + "modifiedScore": 8.910, + "modifyTime": "2025-06-25T10:00:00" + } +} +``` + +**错误响应**: + +```json +{ + "code": 403, + "message": "只有裁判长可以修改评分", + "data": null +} +``` + +### 6.4 获取我的评分记录 + +**接口地址**: `GET /api/scores/my-records` + +**功能描述**: 获取当前评委的所有评分记录 + +**请求参数**: +``` +judgeId: string // 评委ID,必填 +matchId: string // 比赛ID,必填 +page: number // 页码,选填,默认1 +pageSize: number // 每页数量,选填,默认20 +``` + +**响应数据**: + +```json +{ + "code": 200, + "message": "成功", + "data": { + "total": 25, + "page": 1, + "pageSize": 20, + "records": [ + { + "scoreId": "score_001", + "athleteId": "athlete_001", + "athleteName": "张三", + "team": "少林寺武术大学院", + "projectName": "女子组长拳", + "score": 8.907, + "scoreTime": "2025-06-25T09:15:00" + } + ] + } +} +``` + +--- + +## 七、扣分项管理模块 + +### 7.1 获取扣分项列表 + +**接口地址**: `GET /api/deductions` + +**功能描述**: 获取指定项目的扣分项列表 + +**请求参数**: +``` +projectId: string // 项目ID,必填 +``` + +**响应数据**: + +```json +{ + "code": 200, + "message": "成功", + "data": [ + { + "deductionId": "deduction_001", + "text": "起势不稳", + "score": -0.1, + "category": "基础动作", + "order": 1 + }, + { + "deductionId": "deduction_002", + "text": "节奏不准", + "score": -0.05, + "category": "节奏控制", + "order": 2 + }, + { + "deductionId": "deduction_003", + "text": "力度不足", + "score": -0.1, + "category": "技术要求", + "order": 3 + } + ] +} +``` + +--- + +## 八、统计分析模块 + +### 8.1 获取比赛统计 + +**接口地址**: `GET /api/statistics/match/{matchId}` + +**功能描述**: 获取比赛的整体统计数据(裁判长专用) + +**路径参数**: +- `matchId`: 比赛ID + +**响应数据**: + +```json +{ + "code": 200, + "message": "成功", + "data": { + "matchId": "match_001", + "totalAthletes": 150, // 总选手数 + "finishedAthletes": 120, // 已完成评分选手数 + "totalJudges": 30, // 总评委数 + "activeJudges": 28, // 活跃评委数 + "totalScores": 720, // 总评分数 + "averageScore": 8.756, // 平均分 + "highestScore": 9.850, // 最高分 + "lowestScore": 7.200, // 最低分 + "venueStats": [ // 各场地统计 + { + "venueId": "venue_001", + "venueName": "第一场地", + "athleteCount": 30, + "finishedCount": 28, + "progress": 93.3 + } + ], + "projectStats": [ // 各项目统计 + { + "projectId": "project_001", + "projectName": "女子组长拳", + "athleteCount": 15, + "finishedCount": 15, + "averageScore": 8.650, + "progress": 100 + } + ] + } +} +``` + +### 8.2 获取评委统计 + +**接口地址**: `GET /api/statistics/judges` + +**功能描述**: 获取所有评委的评分统计(裁判长专用) + +**请求参数**: +``` +matchId: string // 比赛ID,必填 +``` + +**响应数据**: + +```json +{ + "code": 200, + "message": "成功", + "data": [ + { + "judgeId": "judge_001", + "judgeName": "欧阳丽娜", + "role": "pub", + "venueId": "venue_001", + "venueName": "第一场地", + "totalScores": 25, // 总评分数 + "averageScore": 8.765, // 平均给分 + "highestScore": 9.500, // 最高给分 + "lowestScore": 7.800, // 最低给分 + "lastScoreTime": "2025-06-25T15:30:00" // 最后评分时间 + } + ] +} +``` + +--- + +## 九、实时推送模块(可选) + +### 9.1 WebSocket连接 + +**连接地址**: `wss://api.example.com/ws` + +**认证方式**: URL参数传递Token +``` +wss://api.example.com/ws?token={jwt_token} +``` + +**功能描述**: 建立WebSocket连接,接收实时评分更新 + +### 9.2 推送消息格式 + +#### 新评分推送 + +```json +{ + "type": "new_score", + "data": { + "athleteId": "athlete_001", + "athleteName": "张三", + "judgeId": "judge_001", + "judgeName": "欧阳丽娜", + "score": 8.907, + "totalScore": 8.905, + "judgeCount": 3, + "timestamp": "2025-06-25T09:15:00" + } +} +``` + +#### 评分修改推送 + +```json +{ + "type": "score_modified", + "data": { + "athleteId": "athlete_001", + "athleteName": "张三", + "modifierId": "admin_001", + "modifierName": "总裁判长", + "originalScore": 8.907, + "modifiedScore": 8.910, + "note": "技术动作优秀,加分", + "timestamp": "2025-06-25T10:00:00" + } +} +``` + +#### 选手状态更新 + +```json +{ + "type": "athlete_status", + "data": { + "athleteId": "athlete_001", + "athleteName": "张三", + "status": "performing", // waiting-等待, performing-表演中, finished-完成 + "timestamp": "2025-06-25T09:10:00" + } +} +``` + +--- + +## 十、文件上传模块(扩展) + +### 10.1 上传选手照片 + +**接口地址**: `POST /api/upload/photo` + +**功能描述**: 上传选手照片 + +**请求格式**: `multipart/form-data` + +**请求参数**: +``` +file: File // 图片文件,必填 +athleteId: string // 选手ID,必填 +``` + +**响应数据**: + +```json +{ + "code": 200, + "message": "上传成功", + "data": { + "fileId": "file_001", + "fileName": "athlete_photo.jpg", + "fileUrl": "https://example.com/photos/athlete_photo.jpg", + "fileSize": 102400, + "uploadTime": "2025-06-25T08:00:00" + } +} +``` + +--- + +## 接口调用示例 + +### JavaScript调用示例 + +```javascript +// 1. 登录 +const login = async (matchCode, inviteCode) => { + try { + const response = await uni.request({ + url: 'https://api.example.com/api/auth/login', + method: 'POST', + data: { + matchCode, + inviteCode + } + }) + + if (response.data.code === 200) { + // 保存Token + uni.setStorageSync('token', response.data.data.token) + uni.setStorageSync('userInfo', response.data.data) + return response.data.data + } + } catch (error) { + console.error('登录失败:', error) + throw error + } +} + +// 2. 获取选手列表 +const getAthletes = async (matchId, venueId, projectId, judgeId) => { + const token = uni.getStorageSync('token') + + try { + const response = await uni.request({ + url: 'https://api.example.com/api/athletes', + method: 'GET', + header: { + 'Authorization': `Bearer ${token}` + }, + data: { + matchId, + venueId, + projectId, + judgeId + } + }) + + return response.data.data + } catch (error) { + console.error('获取选手列表失败:', error) + throw error + } +} + +// 3. 提交评分 +const submitScore = async (scoreData) => { + const token = uni.getStorageSync('token') + + try { + const response = await uni.request({ + url: 'https://api.example.com/api/scores', + method: 'POST', + header: { + 'Authorization': `Bearer ${token}` + }, + data: scoreData + }) + + if (response.data.code === 200) { + uni.showToast({ + title: '提交成功', + icon: 'success' + }) + return response.data.data + } + } catch (error) { + console.error('提交评分失败:', error) + uni.showToast({ + title: '提交失败', + icon: 'none' + }) + throw error + } +} + +// 4. 修改评分(裁判长) +const modifyScore = async (athleteId, modifyData) => { + const token = uni.getStorageSync('token') + + try { + const response = await uni.request({ + url: `https://api.example.com/api/scores/${athleteId}/modify`, + method: 'PUT', + header: { + 'Authorization': `Bearer ${token}` + }, + data: modifyData + }) + + if (response.data.code === 200) { + uni.showToast({ + title: '修改成功', + icon: 'success' + }) + return response.data.data + } + } catch (error) { + console.error('修改评分失败:', error) + uni.showToast({ + title: '修改失败', + icon: 'none' + }) + throw error + } +} +``` + +--- + +## 接口安全建议 + +### 1. 认证安全 +- 使用HTTPS协议 +- JWT Token设置合理过期时间(建议8小时) +- Token应包含用户角色信息 +- 实现Token刷新机制 + +### 2. 权限控制 +- 普通评委只能访问自己的数据 +- 裁判长可以访问所有数据 +- 每个接口都需要验证用户权限 + +### 3. 数据验证 +- 后端必须验证所有输入参数 +- 评分范围必须在5.0-10.0之间 +- 评分精度必须为0.001 +- 防止重复提交评分 + +### 4. 日志记录 +- 记录所有评分操作 +- 记录所有修改操作 +- 记录登录和登出 +- 保留操作时间戳 + +### 5. 错误处理 +- 统一错误响应格式 +- 不暴露敏感信息 +- 提供友好的错误提示 + +--- + +## 接口性能优化建议 + +### 1. 缓存策略 +- 比赛信息、场地列表、项目列表可缓存 +- 扣分项列表可缓存 +- 选手列表需要实时更新 + +### 2. 分页查询 +- 选手列表支持分页 +- 评分记录支持分页 +- 统计数据可按需加载 + +### 3. 数据压缩 +- 使用Gzip压缩响应数据 +- 图片使用CDN加速 + +### 4. 并发控制 +- 限制单个评委的评分频率 +- 防止恶意刷接口 +- 实现请求限流 + +--- + +## 接口测试建议 + +### 1. 单元测试 +- 测试所有接口的正常流程 +- 测试各种异常情况 +- 测试权限验证 + +### 2. 压力测试 +- 模拟多评委同时评分 +- 测试并发修改评分 +- 测试WebSocket连接数 + +### 3. 集成测试 +- 测试完整的评分流程 +- 测试跨模块的数据一致性 + +--- + +## 附录:环境配置 + +### 开发环境 +``` +BASE_URL: https://dev-api.example.com +``` + +### 测试环境 +``` +BASE_URL: https://test-api.example.com +``` + +### 生产环境 +``` +BASE_URL: https://api.example.com +``` + +建议在项目中创建环境配置文件: + +```javascript +// config/env.js +const env = { + development: { + baseUrl: 'https://dev-api.example.com', + timeout: 10000 + }, + production: { + baseUrl: 'https://api.example.com', + timeout: 5000 + } +} + +export default env[process.env.NODE_ENV || 'development'] +``` diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..bd4a941 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,375 @@ +# 武术评分系统 - 项目文档 + +## 📚 文档目录 + +本目录包含武术评分系统的完整项目文档,帮助开发者快速了解项目结构、功能设计和API接口。 + +--- + +## 📖 文档列表 + +### 1. [项目概述](./项目概述.md) + +**内容概要**: +- 项目基本信息 +- 核心功能介绍 +- 技术架构 +- 项目结构 +- 页面流程 +- 色彩系统 +- 运行和部署说明 +- 后续扩展建议 + +**适合人群**: 项目管理者、新加入的开发者、产品经理 + +--- + +### 2. [页面功能说明](./页面功能说明.md) + +**内容概要**: +- 所有页面总览 +- 登录页详细说明 +- 评分列表页详细说明 +- 评分详情页详细说明 +- 多场地列表页详细说明 +- 修改评分页详细说明 +- 页面间跳转关系 +- 页面共同特性 + +**适合人群**: 前端开发者、UI设计师、测试工程师 + +**关键信息**: +- 5个页面的完整功能说明 +- 每个页面需要对接的API接口 +- 页面数据结构 +- 交互逻辑说明 + +--- + +### 3. [API接口设计](./API接口设计.md) + +**内容概要**: +- 接口规范和通用格式 +- 9大功能模块的完整API接口 + - 认证模块(3个接口) + - 比赛信息模块(1个接口) + - 场地管理模块(1个接口) + - 项目管理模块(2个接口) + - 选手管理模块(3个接口) + - 评分管理模块(4个接口) + - 扣分项管理模块(1个接口) + - 统计分析模块(2个接口) + - 实时推送模块(WebSocket) +- 接口调用示例(JavaScript) +- 接口安全建议 +- 性能优化建议 +- 环境配置 + +**适合人群**: 后端开发者、前端开发者、接口对接人员 + +**关键信息**: +- 共计17个REST API接口 +- 完整的请求参数和响应格式 +- 错误处理规范 +- WebSocket推送协议 + +--- + +### 4. [数据结构设计](./数据结构设计.md) + +**内容概要**: +- 17个核心数据结构定义(TypeScript) +- 用户角色相关(3个) +- 比赛相关(1个) +- 场地相关(2个) +- 项目相关(2个) +- 选手相关(3个) +- 评分相关(4个) +- 评分修改相关(2个) +- 扣分项相关(2个) +- 统计分析相关(3个) +- 前端页面数据结构(5个) +- 全局数据结构(1个) +- WebSocket推送数据结构(3个) +- 分页数据结构(2个) +- 数据验证规则 +- 枚举类型汇总 +- 数据关系图 +- 数据存储建议 + +**适合人群**: 前端开发者、后端开发者、数据库设计人员 + +**关键信息**: +- 完整的TypeScript类型定义 +- 数据关系说明 +- 数据验证规则 +- 前后端数据格式统一 + +--- + +### 5. [功能模块划分](./功能模块划分.md) + +**内容概要**: +- 10大功能模块详细说明 + 1. 用户认证模块 + 2. 比赛信息模块 + 3. 场地管理模块 + 4. 项目管理模块 + 5. 选手管理模块 + 6. 评分管理模块(普通评委) + 7. 评分修改模块(裁判长) + 8. 扣分项管理模块 + 9. 统计分析模块 + 10. 实时推送模块 +- 每个模块的核心功能 +- 涉及的页面和API接口 +- 数据结构 +- 业务规则 +- 权限说明 +- 模块依赖关系 +- 开发优先级建议 +- 模块测试要点 + +**适合人群**: 项目经理、开发团队、测试团队 + +**关键信息**: +- 模块化的功能设计 +- 开发优先级(3个阶段) +- 模块依赖关系图 +- 完整的测试要点 + +--- + +## 🎯 快速导航 + +### 我是项目管理者 + +**推荐阅读顺序**: +1. [项目概述](./项目概述.md) - 了解项目整体情况 +2. [功能模块划分](./功能模块划分.md) - 了解功能模块和开发计划 +3. [页面功能说明](./页面功能说明.md) - 了解页面功能需求 + +### 我是前端开发者 + +**推荐阅读顺序**: +1. [项目概述](./项目概述.md) - 了解技术栈 +2. [页面功能说明](./页面功能说明.md) - 了解页面需求 +3. [数据结构设计](./数据结构设计.md) - 了解数据格式 +4. [API接口设计](./API接口设计.md) - 了解如何对接API + +### 我是后端开发者 + +**推荐阅读顺序**: +1. [项目概述](./项目概述.md) - 了解项目背景 +2. [功能模块划分](./功能模块划分.md) - 了解业务逻辑 +3. [API接口设计](./API接口设计.md) - 实现API接口 +4. [数据结构设计](./数据结构设计.md) - 设计数据库 + +### 我是UI/UX设计师 + +**推荐阅读顺序**: +1. [项目概述](./项目概述.md) - 了解色彩系统和设计规范 +2. [页面功能说明](./页面功能说明.md) - 了解页面需求和交互 + +### 我是测试工程师 + +**推荐阅读顺序**: +1. [页面功能说明](./页面功能说明.md) - 了解功能需求 +2. [功能模块划分](./功能模块划分.md) - 了解测试要点 +3. [API接口设计](./API接口设计.md) - 了解接口规范 + +--- + +## 📊 项目数据统计 + +### 页面统计 +- **总页面数**: 5个 +- **普通评委页面**: 3个(登录页、评分列表页、评分详情页) +- **裁判长页面**: 3个(登录页、多场地列表页、修改评分页) + +### 功能模块统计 +- **功能模块数**: 10个 +- **已完成模块**: 8个 +- **待开发模块**: 2个(统计分析、实时推送) + +### API接口统计 +- **REST API接口数**: 17个 +- **WebSocket接口**: 1个 +- **认证相关**: 3个 +- **业务相关**: 14个 + +### 数据结构统计 +- **核心数据结构**: 17个 +- **页面数据结构**: 5个 +- **推送数据结构**: 3个 +- **枚举类型**: 6个 + +### 代码统计 +- **总代码行数**: 约3000行 +- **Vue页面**: 5个 +- **配置文件**: 7个 +- **文档文件**: 5个 + +--- + +## 🔧 技术栈 + +### 前端技术 +- **框架**: UniApp + Vue.js 2.x +- **样式**: CSS3 + SCSS +- **UI**: UniApp原生组件 +- **状态管理**: getApp().globalData +- **路由**: UniApp内置路由 + +### 支持平台 +- 微信小程序 +- H5 + +### 开发工具 +- HBuilderX(推荐) +- VS Code + uni-cli + +--- + +## 📝 版本历史 + +### v1.0.0 (2025-06-25) +- ✅ 完成5个页面的UI开发 +- ✅ 完成角色区分(pub/admin) +- ✅ 完成评分功能(精度0.001) +- ✅ 完成扣分项多选 +- ✅ 完成场地和项目切换 +- ✅ 完成评分修改功能 +- ✅ 完成项目文档 + +### 待开发功能 +- ❌ 后端API对接 +- ❌ 真实数据存储 +- ❌ 用户身份持久化 +- ❌ 实时数据同步 +- ❌ 离线支持 +- ❌ 数据统计和导出 + +--- + +## 👥 团队协作 + +### 前端开发 + +**工作内容**: +1. 阅读[页面功能说明](./页面功能说明.md) +2. 阅读[数据结构设计](./数据结构设计.md) +3. 根据现有页面进行功能完善 +4. 对接后端API接口 + +**需要对接**: +- 与后端确认API接口格式 +- 与UI设计师确认页面样式 + +### 后端开发 + +**工作内容**: +1. 阅读[API接口设计](./API接口设计.md) +2. 阅读[数据结构设计](./数据结构设计.md) +3. 设计数据库表结构 +4. 实现API接口 + +**需要对接**: +- 与前端确认数据格式 +- 与测试确认接口规范 + +### 测试工程师 + +**工作内容**: +1. 阅读[功能模块划分](./功能模块划分.md)中的测试要点 +2. 编写测试用例 +3. 进行功能测试和接口测试 + +**需要对接**: +- 与开发确认测试环境 +- 与产品确认验收标准 + +--- + +## 🚀 开发建议 + +### 第一阶段(核心功能) - 2-3周 + +**任务**: +1. 搭建后端框架 +2. 实现用户认证API +3. 实现评分相关API +4. 前端对接API +5. 完成基础功能测试 + +**产出**: +- 可用的登录功能 +- 可用的评分功能 +- 可用的评分修改功能 + +### 第二阶段(管理功能) - 1-2周 + +**任务**: +1. 实现场地和项目管理API +2. 实现选手管理API +3. 完善权限控制 +4. 前端对接API +5. 完成功能测试 + +**产出**: +- 完整的裁判长功能 +- 完整的权限控制 + +### 第三阶段(增强功能) - 2-3周 + +**任务**: +1. 实现统计分析功能 +2. 实现WebSocket实时推送 +3. 实现数据导出功能 +4. 性能优化 +5. 完成全面测试 + +**产出**: +- 完整的统计分析 +- 实时数据更新 +- 数据导出功能 + +--- + +## 📞 联系方式 + +如有疑问,请联系项目负责人。 + +--- + +## 📄 文档维护 + +**文档版本**: v1.0.0 +**最后更新**: 2025-06-25 +**维护人员**: 开发团队 + +**更新记录**: +- 2025-06-25: 创建初始文档 + +--- + +## ⚠️ 注意事项 + +1. 所有文档中的示例数据均为Mock数据 +2. 实际开发时请根据后端返回的真实数据进行调整 +3. API接口地址需要根据实际环境配置 +4. 所有涉及安全的功能(登录、Token等)需要严格测试 +5. 评分精度必须保证为0.001,不可有误差 + +--- + +## 📚 相关资源 + +- [UniApp官方文档](https://uniapp.dcloud.io/) +- [Vue.js官方文档](https://cn.vuejs.org/) +- [项目GitHub地址]() - 待添加 +- [设计原型](../image/) - 查看设计图 + +--- + +**祝开发顺利!** 🎉 diff --git a/doc/保护Mock版本的实施方案.md b/doc/保护Mock版本的实施方案.md new file mode 100644 index 0000000..3a2d0e3 --- /dev/null +++ b/doc/保护Mock版本的实施方案.md @@ -0,0 +1,1040 @@ +# 保护Mock版本的完整实施方案 + +## 方案目标 + +在对接后端API的同时,**永久保护当前的Mock版本UI代码**,确保: + +1. ✅ Mock版本UI代码不被修改 +2. ✅ 可随时切换Mock模式和API模式 +3. ✅ 支持独立演示Mock版本 +4. ✅ 便于团队协作开发 + +--- + +## 一、保护策略总览 + +### 1.1 多层保护机制 + +``` +┌─────────────────────────────────────────┐ +│ 第一层:Git分支隔离 │ +│ - main分支:永久保存Mock版本 │ +│ - feature/api分支:API对接开发 │ +└─────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────┐ +│ 第二层:配置开关控制 │ +│ - dataMode: 'mock' | 'api' │ +│ - 运行时动态切换数据源 │ +└─────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────┐ +│ 第三层:代码架构分离 │ +│ - Mock数据独立目录 │ +│ - API调用独立目录 │ +│ - 业务逻辑层统一适配 │ +└─────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────┐ +│ 第四层:文档备份 │ +│ - 完整的Mock版本文档存档 │ +│ - 代码快照保存 │ +└─────────────────────────────────────────┘ +``` + +--- + +## 二、Git分支管理方案(第一层保护) + +### 2.1 分支结构设计 + +``` +main (Mock版本,永久保护) + │ + ├─ feature/api-integration (API对接开发) + │ │ + │ ├─ feature/api-login (登录接口) + │ ├─ feature/api-score (评分接口) + │ └─ feature/api-admin (裁判长功能) + │ + ├─ release/v1.0-mock (Mock版本发布) + └─ release/v2.0-api (API版本发布) +``` + +### 2.2 实施步骤 + +#### 步骤1:保存当前Mock版本 + +```bash +# 1. 确保所有更改已提交 +git add . +git commit -m "✅ Mock版本完成 - UI冻结版本" + +# 2. 打标签标记Mock版本 +git tag -a v1.0-mock -m "Mock原型版本 - 完整UI展示" + +# 3. 推送到远程 +git push origin main +git push origin v1.0-mock +``` + +#### 步骤2:创建API对接分支 + +```bash +# 1. 基于main创建API分支 +git checkout -b feature/api-integration + +# 2. 推送到远程 +git push -u origin feature/api-integration +``` + +#### 步骤3:开发过程中的保护 + +```bash +# ❌ 禁止:直接在main分支修改 +git checkout main +# ... 修改代码 (错误操作!) + +# ✅ 正确:始终在feature分支工作 +git checkout feature/api-integration +# ... 修改代码 +git add . +git commit -m "feat: 添加登录API对接" +git push +``` + +#### 步骤4:快速切换版本 + +```bash +# 切换到Mock版本(演示UI) +git checkout main + +# 切换到API版本(开发/测试) +git checkout feature/api-integration + +# 切换到特定标签 +git checkout v1.0-mock +``` + +### 2.3 .gitignore 配置 + +```bash +# .gitignore +node_modules/ +dist/ +unpackage/ +.DS_Store +*.log + +# 开发环境配置(不提交) +.env.development.local + +# 构建产物 +*.zip +``` + +--- + +## 三、配置开关控制方案(第二层保护) + +### 3.1 环境配置文件 + +#### 创建 `config/env.config.js` + +```javascript +/** + * 环境配置 + * 控制应用的数据源模式 + */ + +const ENV_CONFIG = { + // 开发环境 + development: { + // 数据模式: 'mock' | 'api' + dataMode: 'mock', + + // API基础路径 + apiBaseURL: 'http://localhost:8080', + + // 是否开启调试 + debug: true, + + // 请求超时时间(毫秒) + timeout: 30000 + }, + + // 测试环境 + test: { + dataMode: 'api', + apiBaseURL: 'http://test-api.yourdomain.com', + debug: true, + timeout: 30000 + }, + + // 生产环境 + production: { + dataMode: 'api', + apiBaseURL: 'https://api.yourdomain.com', + debug: false, + timeout: 30000 + } +} + +// 获取当前环境 +const env = process.env.NODE_ENV || 'development' + +// 导出配置 +export default { + ...ENV_CONFIG[env], + env +} + +/** + * 使用说明: + * + * 1. Mock模式(UI演示): + * 设置 dataMode: 'mock' + * + * 2. API模式(真实对接): + * 设置 dataMode: 'api' + * + * 3. 动态切换: + * import config from '@/config/env.config.js' + * if (config.dataMode === 'mock') { ... } + */ +``` + +### 3.2 数据源适配器 + +#### 创建 `utils/dataAdapter.js` + +```javascript +/** + * 数据源适配器 + * 根据配置动态选择Mock数据或API数据 + */ + +import config from '@/config/env.config.js' +import mockData from '@/mock/index.js' +import apiService from '@/api/index.js' + +class DataAdapter { + constructor() { + this.mode = config.dataMode + this.debug = config.debug + } + + /** + * 统一数据获取接口 + * @param {String} resource - 资源名称 + * @param {Object} params - 请求参数 + * @returns {Promise} + */ + async getData(resource, params = {}) { + if (this.mode === 'mock') { + return this._getMockData(resource, params) + } else { + return this._getApiData(resource, params) + } + } + + /** + * 获取Mock数据 + */ + async _getMockData(resource, params) { + if (this.debug) { + console.log(`[Mock数据] 请求: ${resource}`, params) + } + + // 模拟网络延迟 + await this._delay(300) + + const data = mockData[resource](params) + + if (this.debug) { + console.log(`[Mock数据] 响应: ${resource}`, data) + } + + return { + code: 200, + message: '成功', + data + } + } + + /** + * 获取API数据 + */ + async _getApiData(resource, params) { + if (this.debug) { + console.log(`[API请求] 请求: ${resource}`, params) + } + + const response = await apiService[resource](params) + + if (this.debug) { + console.log(`[API请求] 响应: ${resource}`, response) + } + + return response + } + + /** + * 延迟函数(模拟网络请求) + */ + _delay(ms) { + return new Promise(resolve => setTimeout(resolve, ms)) + } + + /** + * 切换数据模式 + */ + switchMode(mode) { + if (mode === 'mock' || mode === 'api') { + this.mode = mode + console.log(`数据模式已切换为: ${mode}`) + } else { + console.error('无效的数据模式,只能是 "mock" 或 "api"') + } + } + + /** + * 获取当前模式 + */ + getMode() { + return this.mode + } +} + +// 导出单例 +export default new DataAdapter() +``` + +### 3.3 页面使用示例 + +#### 修改 `pages/login/login.vue` + +```vue + +``` + +--- + +## 四、代码架构分离(第三层保护) + +### 4.1 目录结构设计 + +``` +martial-admin-mini/ +├── config/ +│ ├── env.config.js # 环境配置 +│ └── api.config.js # API配置 +│ +├── mock/ # 🔥 Mock数据目录(独立) +│ ├── index.js # Mock数据入口 +│ ├── login.js # 登录Mock数据 +│ ├── athlete.js # 选手Mock数据 +│ ├── score.js # 评分Mock数据 +│ └── README.md # Mock数据说明 +│ +├── api/ # 🔥 API调用目录(独立) +│ ├── index.js # API入口 +│ ├── auth.js # 认证API +│ ├── athlete.js # 选手API +│ ├── score.js # 评分API +│ └── README.md # API说明 +│ +├── utils/ +│ ├── request.js # 网络请求封装 +│ ├── dataAdapter.js # 🔥 数据适配器(核心) +│ └── storage.js # 本地存储工具 +│ +├── pages/ +│ ├── login/ +│ │ └── login.vue # ✅ UI代码不变 +│ ├── score-list/ +│ │ └── score-list.vue # ✅ UI代码不变 +│ └── ... +│ +└── doc/ + ├── Mock版本快照.md # Mock版本文档备份 + └── 数据可行性分析报告.md +``` + +### 4.2 Mock数据模块 + +#### 创建 `mock/index.js` + +```javascript +/** + * Mock数据中心 + * 所有Mock数据的统一入口 + */ + +import loginMock from './login.js' +import athleteMock from './athlete.js' +import scoreMock from './score.js' + +export default { + // 登录 + login: loginMock.login, + + // 选手列表 + getMyAthletes: athleteMock.getMyAthletes, + getAthletesForAdmin: athleteMock.getAthletesForAdmin, + + // 评分 + submitScore: scoreMock.submitScore, + getScoreDetail: scoreMock.getScoreDetail, + modifyScore: scoreMock.modifyScore, + + // 扣分项 + getDeductions: scoreMock.getDeductions +} +``` + +#### 创建 `mock/login.js` + +```javascript +/** + * 登录相关Mock数据 + */ + +export default { + /** + * 登录 + */ + login(params) { + const { matchCode, inviteCode } = params + const role = inviteCode.toLowerCase() + + // 模拟验证 + if (role !== 'pub' && role !== 'admin') { + throw new Error('邀请码错误') + } + + // 返回Mock数据 + return { + token: 'mock_token_' + Date.now(), + userRole: role, + matchId: '123', + matchName: '2025年全国武术散打锦标赛暨第十七届世界武术锦标赛选拔赛', + matchTime: '2025年6月25日 9:00', + judgeId: '456', + judgeName: '欧阳丽娜', + venueId: role === 'pub' ? '1' : null, + venueName: role === 'pub' ? '第一场地' : null, + projects: ['女子组长拳', '男子组陈氏太极拳'] + } + } +} +``` + +#### 创建 `mock/athlete.js` + +```javascript +/** + * 选手相关Mock数据 + */ + +export default { + /** + * 获取我的选手列表(普通评委) + */ + getMyAthletes(params) { + return [ + { + athleteId: '1', + name: '张三', + idCard: '123456789000000000', + team: '少林寺武术大学院', + number: '123-4567898275', + myScore: 8.906, + totalScore: 8.907, + scored: true, + scoreTime: '2025-06-25 09:15:00' + }, + { + athleteId: '2', + name: '李四', + idCard: '123456789000000001', + team: '武当山武术学院', + number: '123-4567898276', + myScore: null, + totalScore: null, + scored: false, + scoreTime: null + }, + // ... 更多Mock数据 + ] + }, + + /** + * 获取选手列表(裁判长) + */ + getAthletesForAdmin(params) { + return [ + { + athleteId: '1', + name: '张三', + idCard: '123456789000000000', + team: '少林寺武术大学院', + number: '123-4567898275', + totalScore: 8.907, + judgeCount: 6, + totalJudges: 6, + canModify: true + }, + // ... 更多Mock数据 + ] + } +} +``` + +### 4.3 API调用模块 + +#### 创建 `api/index.js` + +```javascript +/** + * API调用中心 + * 所有API调用的统一入口 + */ + +import authApi from './auth.js' +import athleteApi from './athlete.js' +import scoreApi from './score.js' + +export default { + // 登录 + login: authApi.login, + + // 选手列表 + getMyAthletes: athleteApi.getMyAthletes, + getAthletesForAdmin: athleteApi.getAthletesForAdmin, + + // 评分 + submitScore: scoreApi.submitScore, + getScoreDetail: scoreApi.getScoreDetail, + modifyScore: scoreApi.modifyScore, + + // 扣分项 + getDeductions: scoreApi.getDeductions +} +``` + +#### 创建 `api/auth.js` + +```javascript +/** + * 认证相关API + */ + +import request from '@/utils/request.js' + +export default { + /** + * 登录 + */ + login(data) { + return request({ + url: '/mini/login', + method: 'POST', + data + }) + }, + + /** + * 退出登录 + */ + logout() { + return request({ + url: '/mini/logout', + method: 'POST' + }) + }, + + /** + * Token验证 + */ + verifyToken() { + return request({ + url: '/mini/verify', + method: 'GET' + }) + } +} +``` + +--- + +## 五、文档备份(第四层保护) + +### 5.1 Mock版本快照文档 + +#### 创建 `doc/Mock版本快照.md` + +```markdown +# Mock版本快照文档 + +## 版本信息 + +- **版本号**: v1.0-mock +- **创建时间**: 2025-12-11 +- **Git标签**: v1.0-mock +- **Git提交**: c2f3313 +- **分支**: main + +## 页面列表 + +### 1. 登录页 (`pages/login/login.vue`) +- **功能**: 比赛编码 + 邀请码登录 +- **角色**: pub(普通评委), admin(裁判长) +- **数据**: 完全Mock,无API依赖 + +### 2. 评分列表页 (`pages/score-list/score-list.vue`) +- **功能**: 选手列表展示,评分状态显示 +- **数据**: 3个Mock选手数据 + +### 3. 评分详情页 (`pages/score-detail/score-detail.vue`) +- **功能**: 评分操作(0.001精度),扣分项选择 +- **数据**: 8个扣分项Mock数据 + +### 4. 多场地列表页 (`pages/score-list-multi/score-list-multi.vue`) +- **功能**: 5个场地 + 8个项目切换 +- **数据**: 场地和项目Mock数据 + +### 5. 修改评分页 (`pages/modify-score/modify-score.vue`) +- **功能**: 查看评委评分,修改总分 +- **数据**: 6位评委评分Mock数据 + +## Mock数据说明 + +所有Mock数据硬编码在组件的 `data()` 中,无外部依赖。 + +## 恢复Mock版本 + +```bash +git checkout v1.0-mock +``` + +或 + +```bash +git checkout main +``` + +## 运行Mock版本 + +```bash +# 确保在main分支 +git checkout main + +# 运行 +npm run dev:mp-weixin +# 或 +npm run dev:h5 +``` +``` + +### 5.2 代码备份策略 + +```bash +# 1. 创建压缩包备份 +cd D:\workspace\31.比赛项目\project\ +tar -czf martial-admin-mini-mock-v1.0-$(date +%Y%m%d).tar.gz martial-admin-mini/ + +# 2. 创建Git存档 +cd martial-admin-mini +git archive --format=zip --output=../martial-admin-mini-mock-v1.0.zip v1.0-mock + +# 3. 导出完整提交历史 +git log --pretty=format:"%h - %an, %ar : %s" > ../git-history.txt +``` + +--- + +## 六、实施操作指南 + +### 6.1 初始化保护(首次执行) + +```bash +# ========== 步骤1:提交当前代码 ========== +cd D:\workspace\31.比赛项目\project\martial-admin-mini + +git add . +git commit -m "✅ Mock版本完成 - 冻结UI代码" + +# ========== 步骤2:打标签 ========== +git tag -a v1.0-mock -m "Mock原型版本 - 完整UI展示" + +# ========== 步骤3:创建API分支 ========== +git checkout -b feature/api-integration +git push -u origin feature/api-integration + +# ========== 步骤4:创建目录结构 ========== +mkdir -p config mock api + +# ========== 步骤5:创建配置文件 ========== +# 创建 config/env.config.js +# 创建 utils/dataAdapter.js +# 创建 mock/index.js +# ... (根据上面的代码创建) + +# ========== 步骤6:测试Mock模式 ========== +# 设置 config/env.config.js 中 dataMode: 'mock' +npm run dev:mp-weixin + +# ========== 步骤7:提交保护机制 ========== +git add . +git commit -m "feat: 添加Mock版本保护机制" +git push +``` + +### 6.2 日常开发流程 + +```bash +# ========== API开发时 ========== +git checkout feature/api-integration + +# 修改代码... +git add . +git commit -m "feat: 添加登录API对接" +git push + +# ========== 演示Mock版本时 ========== +git checkout main +npm run dev:mp-weixin + +# ========== 测试API版本时 ========== +git checkout feature/api-integration +# 修改 config/env.config.js: dataMode: 'api' +npm run dev:mp-weixin +``` + +### 6.3 紧急恢复流程 + +```bash +# ========== 如果API分支出错,立即恢复Mock版本 ========== +git checkout main + +# ========== 如果main分支被误修改 ========== +git reset --hard v1.0-mock + +# ========== 如果本地代码全部丢失 ========== +git clone +git checkout v1.0-mock +``` + +--- + +## 七、团队协作规范 + +### 7.1 分支保护规则 + +**main分支保护**: +- ❌ 禁止直接push +- ❌ 禁止force push +- ✅ 只允许通过PR合并 +- ✅ 需要代码审查 + +**feature分支规范**: +- ✅ 可以自由提交 +- ✅ 定期合并main分支 +- ✅ 完成后提PR到main + +### 7.2 提交信息规范 + +```bash +# Mock版本相关 +git commit -m "✅ Mock版本: 修复样式问题" +git commit -m "✅ Mock版本: 更新文档" + +# API对接相关 +git commit -m "feat: 添加登录API对接" +git commit -m "feat: 实现评分提交接口" +git commit -m "fix: 修复Token过期问题" + +# 配置相关 +git commit -m "config: 切换到Mock模式" +git commit -m "config: 更新API地址" +``` + +### 7.3 代码审查清单 + +**合并到main分支前检查**: +- [ ] UI代码是否被修改?(应该没有) +- [ ] Mock数据是否完整? +- [ ] 配置文件是否正确? +- [ ] 文档是否更新? +- [ ] 版本号是否更新? + +--- + +## 八、配置开关使用指南 + +### 8.1 切换到Mock模式 + +```javascript +// config/env.config.js +const ENV_CONFIG = { + development: { + dataMode: 'mock', // ✅ 修改这里 + apiBaseURL: 'http://localhost:8080', + debug: true, + timeout: 30000 + } +} +``` + +**场景**: +- UI演示 +- 前端独立开发 +- 离线开发 +- 快速原型展示 + +### 8.2 切换到API模式 + +```javascript +// config/env.config.js +const ENV_CONFIG = { + development: { + dataMode: 'api', // ✅ 修改这里 + apiBaseURL: 'http://localhost:8080', + debug: true, + timeout: 30000 + } +} +``` + +**场景**: +- 后端联调 +- 真实数据测试 +- 生产环境 +- 集成测试 + +### 8.3 动态切换(高级) + +```javascript +// 在代码中动态切换 +import dataAdapter from '@/utils/dataAdapter.js' + +// 切换到Mock模式 +dataAdapter.switchMode('mock') + +// 切换到API模式 +dataAdapter.switchMode('api') + +// 查看当前模式 +console.log(dataAdapter.getMode()) +``` + +--- + +## 九、验证清单 + +### 9.1 Mock版本验证 + +- [ ] 切换到main分支 +- [ ] 设置 `dataMode: 'mock'` +- [ ] 运行项目 +- [ ] 登录功能正常 +- [ ] 所有页面正常显示 +- [ ] Mock数据正常加载 +- [ ] UI样式无变化 + +### 9.2 API版本验证 + +- [ ] 切换到feature分支 +- [ ] 设置 `dataMode: 'api'` +- [ ] 确认后端服务已启动 +- [ ] 运行项目 +- [ ] API登录成功 +- [ ] 数据正常加载 +- [ ] Token正常传递 + +### 9.3 切换验证 + +- [ ] Mock → API 切换成功 +- [ ] API → Mock 切换成功 +- [ ] 配置文件生效 +- [ ] 数据适配器正常工作 +- [ ] 无报错 + +--- + +## 十、常见问题 + +### Q1: 如果不小心在main分支修改了代码怎么办? + +```bash +# 方案1:撤销未提交的修改 +git checkout -- . + +# 方案2:回退到上一次提交 +git reset --hard HEAD + +# 方案3:回退到Mock版本标签 +git reset --hard v1.0-mock +``` + +### Q2: 如何查看当前使用的数据模式? + +```javascript +// 方法1:查看配置文件 +import config from '@/config/env.config.js' +console.log(config.dataMode) + +# 方法2:查看数据适配器 +import dataAdapter from '@/utils/dataAdapter.js' +console.log(dataAdapter.getMode()) +``` + +### Q3: Mock数据和API数据格式不一致怎么办? + +**方案**: 在数据适配器中统一格式 + +```javascript +// utils/dataAdapter.js +async _getApiData(resource, params) { + const response = await apiService[resource](params) + + // 🔥 统一数据格式 + return { + code: response.code, + message: response.msg || response.message, + data: this._transformData(response.data) + } +} + +_transformData(data) { + // 根据需要转换数据格式 + // 例如: Long → String, BigDecimal → Number + return data +} +``` + +### Q4: 如何给其他人演示Mock版本? + +```bash +# 方案1:发送压缩包 +git archive --format=zip --output=martial-admin-mini-mock.zip main + +# 方案2:提供Git地址和分支 +git clone +git checkout main +# 确保 config/env.config.js 中 dataMode: 'mock' +npm install +npm run dev:mp-weixin +``` + +--- + +## 十一、总结 + +### ✅ 保护机制优势 + +1. **安全性**: 4层保护机制,Mock版本不会被破坏 +2. **灵活性**: 随时切换Mock/API模式 +3. **可维护性**: 代码结构清晰,易于维护 +4. **可扩展性**: 便于添加新的数据源 +5. **团队协作**: 分支隔离,互不干扰 + +### 📊 实施成本 + +| 项目 | 预计时间 | 难度 | +|------|---------|------| +| Git分支设置 | 10分钟 | 简单 | +| 创建配置文件 | 30分钟 | 简单 | +| 创建数据适配器 | 1小时 | 中等 | +| Mock数据整理 | 2小时 | 简单 | +| 文档编写 | 1小时 | 简单 | +| **总计** | **约5小时** | **中等** | + +### 🎯 最终效果 + +``` +✅ Mock版本:永久保护,随时演示 +✅ API版本:独立开发,不影响Mock +✅ 一键切换:配置文件控制 +✅ 零UI改动:完全兼容现有代码 +``` + +--- + +**文档版本**: v1.0 +**创建时间**: 2025-12-11 +**适用项目**: martial-admin-mini +**维护人员**: 开发团队 diff --git a/doc/功能模块划分.md b/doc/功能模块划分.md new file mode 100644 index 0000000..7c179a8 --- /dev/null +++ b/doc/功能模块划分.md @@ -0,0 +1,1102 @@ +# 功能模块划分文档 + +## 文档说明 + +本文档按照功能模块对武术评分系统进行清晰的划分,便于开发和维护。每个模块包含功能说明、涉及页面、API接口、数据结构等信息。 + +--- + +## 模块总览 + +``` +武术评分系统 +├── 1. 用户认证模块 +├── 2. 比赛信息模块 +├── 3. 场地管理模块 +├── 4. 项目管理模块 +├── 5. 选手管理模块 +├── 6. 评分管理模块 +├── 7. 评分修改模块(裁判长) +├── 8. 扣分项管理模块 +├── 9. 统计分析模块 +└── 10. 实时推送模块 +``` + +--- + +## 一、用户认证模块 + +### 1.1 模块概述 + +负责用户登录、权限验证、Token管理等功能。 + +### 1.2 涉及页面 + +- **登录页** (`pages/login/login.vue`) + +### 1.3 核心功能 + +#### 1.3.1 登录认证 + +**功能描述**: 用户通过比赛编码和邀请码进行登录 + +**涉及字段**: +- `matchCode`: 比赛编码 +- `inviteCode`: 邀请码 (pub/admin) + +**业务流程**: +``` +用户输入 → 验证非空 → 调用登录API → 保存Token和用户信息 → 根据角色跳转 +``` + +**角色跳转规则**: +- `pub` (普通评委) → 评分列表页 +- `admin` (裁判长) → 多场地列表页 + +#### 1.3.2 Token管理 + +**功能描述**: 管理JWT Token的存储、验证、刷新 + +**存储方式**: +```javascript +// 存储Token +uni.setStorageSync('auth_token', token) + +// 获取Token +const token = uni.getStorageSync('auth_token') + +// 清除Token +uni.removeStorageSync('auth_token') +``` + +#### 1.3.3 权限验证 + +**功能描述**: 验证用户角色和访问权限 + +**权限控制**: +```javascript +// 获取用户角色 +const userRole = getApp().globalData.userRole + +// 权限检查 +if (userRole !== 'admin') { + uni.showToast({ title: '权限不足', icon: 'none' }) + uni.navigateBack() +} +``` + +### 1.4 API接口 + +- `POST /api/auth/login` - 用户登录 +- `POST /api/auth/logout` - 退出登录 +- `GET /api/auth/verify` - Token验证 + +### 1.5 数据结构 + +```typescript +// 登录凭证 +interface LoginCredentials { + matchCode: string + inviteCode: string +} + +// 认证响应 +interface AuthResponse { + token: string + userRole: 'pub' | 'admin' + matchId: string + matchName: string + judgeId: string + judgeName: string + // ... +} + +// 全局数据 +getApp().globalData = { + userRole: 'pub' | 'admin', + matchCode: string, + token: string +} +``` + +### 1.6 待开发功能 + +- [ ] 找回密码 +- [ ] 修改密码 +- [ ] Token自动刷新 +- [ ] 登录日志记录 + +--- + +## 二、比赛信息模块 + +### 2.1 模块概述 + +管理比赛的基本信息、时间、地点等。 + +### 2.2 涉及页面 + +- **评分列表页** (`pages/score-list/score-list.vue`) +- **多场地列表页** (`pages/score-list-multi/score-list-multi.vue`) + +### 2.3 核心功能 + +#### 2.3.1 比赛信息展示 + +**显示内容**: +- 比赛名称 +- 比赛时间 +- 比赛地点 +- 场地数量 +- 项目数量 + +**示例**: +``` +2025年全国武术散打锦标赛暨第十七届世界武术锦标赛选拔赛 +比赛时间:2025年6月25日 9:00 +``` + +#### 2.3.2 比赛状态管理 + +**状态类型**: +- `upcoming`: 未开始 +- `ongoing`: 进行中 +- `finished`: 已结束 + +### 2.4 API接口 + +- `GET /api/matches/{matchId}` - 获取比赛详情 + +### 2.5 数据结构 + +```typescript +interface Match { + matchId: string + matchName: string + matchTime: string + matchEndTime: string + location: string + status: 'upcoming' | 'ongoing' | 'finished' + venueCount: number + projectCount: number + athleteCount: number +} +``` + +### 2.6 待开发功能 + +- [ ] 比赛日程管理 +- [ ] 比赛公告 +- [ ] 比赛规则说明 + +--- + +## 三、场地管理模块 + +### 3.1 模块概述 + +管理比赛的各个场地信息,支持多场地切换(裁判长专用)。 + +### 3.2 涉及页面 + +- **多场地列表页** (`pages/score-list-multi/score-list-multi.vue`) + +### 3.3 核心功能 + +#### 3.3.1 场地列表展示 + +**功能描述**: 显示所有场地,支持横向滚动切换 + +**场地数量**: 5个场地 +- 第一场地 +- 第二场地 +- 第三场地 +- 第四场地 +- 第五场地 + +**展示信息**: +- 场地名称 +- 选手数量 +- 已评分数量 +- 评分进度 + +#### 3.3.2 场地切换 + +**交互方式**: 横向滚动选择 + +**实现方式**: +```vue + + + {{ venue.name }} + + +``` + +**切换效果**: +- 当前场地高亮显示 +- 底部绿色下划线 +- 自动加载对应选手列表 + +### 3.4 API接口 + +- `GET /api/venues` - 获取场地列表 + +### 3.5 数据结构 + +```typescript +interface Venue { + venueId: string + venueName: string + order: number + athleteCount: number + scoredCount: number + status: 'active' | 'completed' | 'paused' +} + +// 前端数据 +data() { + return { + currentVenue: 1, + venues: [ + { id: 1, name: '第一场地' }, + { id: 2, name: '第二场地' }, + { id: 3, name: '第三场地' }, + { id: 4, name: '第四场地' }, + { id: 5, name: '第五场地' } + ] + } +} +``` + +### 3.6 权限说明 + +- **普通评委**: 只能查看分配的场地 +- **裁判长**: 可以查看所有场地 + +--- + +## 四、项目管理模块 + +### 4.1 模块概述 + +管理比赛的各个项目类型,支持多项目切换。 + +### 4.2 涉及页面 + +- **评分列表页** (`pages/score-list/score-list.vue`) +- **多场地列表页** (`pages/score-list-multi/score-list-multi.vue`) + +### 4.3 核心功能 + +#### 4.3.1 项目列表展示 + +**功能描述**: 显示所有项目,支持横向滚动切换 + +**项目数量**: 8个项目 +1. 女子组长拳 +2. 男子组陈氏太极拳 +3. 女子组双剑(含长穗双剑) +4. 男子组杨氏太极拳 +5. 女子组刀术 +6. 男子组棍术 +7. 女子组枪术 +8. 男子组剑术 + +**展示信息**: +- 项目名称 +- 参赛选手数 +- 评分范围 + +#### 4.3.2 项目切换 + +**交互方式**: 横向滚动选择 + +**实现方式**: +```vue + + + {{ project }} + + +``` + +**切换效果**: +- 当前项目背景变绿 +- 文字变白 +- 自动加载对应选手列表 + +### 4.4 API接口 + +- `GET /api/projects` - 获取项目列表 +- `GET /api/projects/{projectId}` - 获取项目详情 + +### 4.5 数据结构 + +```typescript +interface Project { + projectId: string + projectName: string + category: string + order: number + athleteCount: number + minScore: 5.0 + maxScore: 10.0 + scoreStep: 0.001 +} + +// 前端数据 +data() { + return { + currentProject: 0, + projects: [ + '女子组长拳', + '男子组陈氏太极拳', + // ... + ] + } +} +``` + +--- + +## 五、选手管理模块 + +### 5.1 模块概述 + +管理参赛选手信息,展示选手列表,支持查询和筛选。 + +### 5.2 涉及页面 + +- **评分列表页** (`pages/score-list/score-list.vue`) +- **评分详情页** (`pages/score-detail/score-detail.vue`) +- **多场地列表页** (`pages/score-list-multi/score-list-multi.vue`) +- **修改评分页** (`pages/modify-score/modify-score.vue`) + +### 5.3 核心功能 + +#### 5.3.1 选手列表展示 + +**普通评委视图**: +- 选手姓名 +- 身份证号 +- 队伍名称 +- 比赛编号 +- 我的评分(已评分) +- 总分(已评分) +- 评分按钮(未评分) + +**裁判长视图**: +- 选手姓名 +- 身份证号 +- 队伍名称 +- 比赛编号 +- 总分 +- 修改按钮 + +#### 5.3.2 选手详情展示 + +**显示内容**: +``` +姓名:张三 +身份证:123456789000000000 +队伍:少林寺武术大学院 +编号:123-4567898275 +``` + +#### 5.3.3 选手状态管理 + +**状态类型**: +- `waiting`: 等待表演 +- `performing`: 正在表演 +- `finished`: 已完成 + +### 5.4 API接口 + +- `GET /api/athletes` - 获取选手列表(普通评委) +- `GET /api/athletes/all` - 获取选手列表(裁判长) +- `GET /api/athletes/{athleteId}` - 获取选手详情 + +### 5.5 数据结构 + +```typescript +// 普通评委���图 +interface AthleteListItem { + athleteId: string + name: string + idCard: string + team: string + number: string + myScore?: number + totalScore?: number + scored: boolean +} + +// 裁判长视图 +interface AthleteListItemAdmin { + athleteId: string + name: string + idCard: string + team: string + number: string + totalScore?: number + judgeCount: number + totalJudges: number + canModify: boolean +} +``` + +### 5.6 待开发功能 + +- [ ] 选手照片展示 +- [ ] 选手历史成绩 +- [ ] 选手搜索功能 +- [ ] 选手分组管理 + +--- + +## 六、评分管理模块(普通评委) + +### 6.1 模块概述 + +普通评委对选手进行评分的核心功能模块。 + +### 6.2 涉及页面 + +- **评分列表页** (`pages/score-list/score-list.vue`) +- **评分详情页** (`pages/score-detail/score-detail.vue`) + +### 6.3 核心功能 + +#### 6.3.1 评分界面 + +**评分控制**: +- 当前分数显示(精度0.001,3位小数) +- 减分按钮(-0.001) +- 加分按钮(+0.001) +- 分数范围:5.000 - 10.000 + +**实现方式**: +```javascript +decreaseScore() { + if (this.currentScore > this.minScore) { + this.currentScore = parseFloat((this.currentScore - 0.001).toFixed(3)) + } +} + +increaseScore() { + if (this.currentScore < this.maxScore) { + this.currentScore = parseFloat((this.currentScore + 0.001).toFixed(3)) + } +} +``` + +#### 6.3.2 扣分项选择 + +**功能描述**: 多选扣分项 + +**扣分项数量**: 8项 + +**交互方式**: +```javascript +toggleDeduction(index) { + this.deductions[index].checked = !this.deductions[index].checked +} +``` + +**显示方式**: +- 2列网格布局 +- 圆形复选框 +- 选中变绿 + +#### 6.3.3 备注填写 + +**功能描述**: 填写评分备注(选填) + +**限制**: +- 最大200字 +- 多行文本输入 + +#### 6.3.4 提交评分 + +**提交流程**: +``` +验证分数范围 → 收集扣分项 → 调用API → 显示成功提示 → 返回列表页 +``` + +**验证规则**: +- 分数必须在5.000-10.000之间 +- 精度必须为0.001 +- 不可重复提交 + +### 6.4 API接口 + +- `POST /api/scores` - 提交评分 +- `GET /api/scores/my-records` - 获取我的评分记录 + +### 6.5 数据结构 + +```typescript +// 评分提交 +interface ScoreSubmit { + matchId: string + athleteId: string + judgeId: string + projectId: string + venueId: string + score: number + deductions: string[] + note?: string +} + +// 页面数据 +data() { + return { + currentScore: 8.907, + note: '', + minScore: 5.0, + maxScore: 10.0, + deductions: [ + { text: '扣分项描述', checked: false }, + // ... + ] + } +} +``` + +### 6.6 业务规则 + +1. **评分范围**: 5.000 - 10.000 +2. **评分精度**: 0.001(3位小数) +3. **重复提交**: 不允许重复提交 +4. **备注长度**: 最多200字 +5. **扣分项**: 支持多选 + +--- + +## 七、评分修改模块(裁判长) + +### 7.1 模块概述 + +裁判长查看所有评分并进行修改的功能模块。 + +### 7.2 涉及页面 + +- **多场地列表页** (`pages/score-list-multi/score-list-multi.vue`) +- **修改评分页** (`pages/modify-score/modify-score.vue`) + +### 7.3 核心功能 + +#### 7.3.1 评分详情查看 + +**显示内容**: +- 选手基本信息 +- 当前总分 +- 各评委评分明细 + +**评委评分明细**: +``` +共有6位评委完成评分 + +欧阳丽娜:8.907 +张三:8.901 +裁判姓名:8.902 +裁判姓名:8.907 +裁判姓名:8.905 +裁判姓名:8.904 +``` + +#### 7.3.2 修改总分 + +**修改控制**: +- 原始总分显示 +- 修改后分数(可调整) +- "可不改"提示 +- 分数范围:5.000 - 10.000 + +**实现方式**: +```javascript +decreaseScore() { + if (this.currentScore > this.minScore) { + this.currentScore = parseFloat((this.currentScore - 0.001).toFixed(3)) + } +} + +increaseScore() { + if (this.currentScore < this.maxScore) { + this.currentScore = parseFloat((this.currentScore + 0.001).toFixed(3)) + } +} +``` + +#### 7.3.3 修改备注 + +**功能描述**: 填写修改原因(可选填) + +**限制**: +- 最大200字 +- 多行文本输入 +- "可不填"提示 + +#### 7.3.4 提交修改 + +**提交流程**: +``` +验证权限 → 验证分数 → 调用API → 记录修改日志 → 返回列表页 +``` + +### 7.4 API接口 + +- `GET /api/scores/{athleteId}` - 获取评分详情 +- `PUT /api/scores/{athleteId}/modify` - 修改评分 + +### 7.5 数据结构 + +```typescript +// 修改提交 +interface ScoreModifySubmit { + athleteId: string + modifierId: string + originalScore: number + modifiedScore: number + note: string +} + +// 评委评分 +interface JudgeScore { + scoreId: string + judgeId: string + judgeName: string + score: number + deductions: Deduction[] + note?: string + scoreTime: string +} + +// 修改记录 +interface ScoreModification { + modifyId: string + modifierId: string + modifierName: string + originalScore: number + modifiedScore: number + note: string + modifyTime: string +} +``` + +### 7.6 权限控制 + +- 只有裁判长可以修改评分 +- 修改前验证用户角色 +- 记录修改人和修改时间 + +### 7.7 业务规则 + +1. **权限**: 只有admin角色可以修改 +2. **修改范围**: 5.000 - 10.000 +3. **修改精度**: 0.001 +4. **修改记录**: 保留完整的修改历史 +5. **备注**: 建议填写修改原因 + +--- + +## 八、扣分项管理模块 + +### 8.1 模块概述 + +管理各项目的扣分项,用于评分时选择。 + +### 8.2 涉及页面 + +- **评分详情页** (`pages/score-detail/score-detail.vue`) + +### 8.3 核心功能 + +#### 8.3.1 扣分项展示 + +**显示方式**: +- 2列网格布局 +- 圆形复选框 +- 扣分项描述 + +**扣分项数量**: 8项(当前为Mock数据) + +#### 8.3.2 扣分项选择 + +**功能描述**: 支持多选 + +**交互方式**: +- 点击切换选中状态 +- 选中后复选框变绿 +- 显示白色对勾 + +### 8.4 API接口 + +- `GET /api/deductions` - 获取扣分项列表 + +### 8.5 数据结构 + +```typescript +// 扣分项 +interface Deduction { + deductionId: string + projectId: string + text: string + score: number // 扣分值(负数) + category?: string + order: number +} + +// 前端选择项 +interface DeductionItem { + deductionId?: string + text: string + checked: boolean +} + +// 前端数据 +data() { + return { + deductions: [ + { text: '扣分项描述', checked: false }, + { text: '扣分项描述', checked: false }, + { text: '扣分项描述', checked: true }, + // ... 共8项 + ] + } +} +``` + +### 8.6 待开发功能 + +- [ ] 动态加载扣分项 +- [ ] 扣分项分类 +- [ ] 扣分值显示 +- [ ] 扣分项说明 + +--- + +## 九、统计分析模块 + +### 9.1 模块概述 + +提供比赛的统计数据和分析报表(裁判长专用)。 + +### 9.2 当前状态 + +**状态**: 待开发 + +### 9.3 规划功能 + +#### 9.3.1 比赛总览 + +**统计内容**: +- 总选手数 +- 已完成评分数 +- 评分进度 +- 平均分 +- 最高分/最低分 + +#### 9.3.2 场地统计 + +**统计内容**: +- 各场地选手数 +- 各场地完成度 +- 各场地平均分 + +#### 9.3.3 项目统计 + +**统计内容**: +- 各项目选手数 +- 各项目完成度 +- 各项目平均分 +- 各项目最高分/最低分 + +#### 9.3.4 评委统计 + +**统计内容**: +- 评委评分数量 +- 评委平均给分 +- 评委评分分布 +- 评委评分时间统计 + +#### 9.3.5 数据导出 + +**导出功能**: +- 导出Excel报表 +- 导出PDF报告 +- 导出选手成绩单 + +### 9.4 API接口 + +- `GET /api/statistics/match/{matchId}` - 比赛统计 +- `GET /api/statistics/judges` - 评委统计 +- `POST /api/export/excel` - 导出Excel +- `POST /api/export/pdf` - 导出PDF + +### 9.5 数据结构 + +```typescript +// 比赛统计 +interface MatchStatistics { + matchId: string + totalAthletes: number + finishedAthletes: number + totalJudges: number + activeJudges: number + totalScores: number + averageScore: number + highestScore: number + lowestScore: number + venueStats: VenueStatistics[] + projectStats: ProjectStatistics[] +} +``` + +--- + +## 十、实时推送模块 + +### 10.1 模块概述 + +通过WebSocket实现评分数据的实时推送和更新。 + +### 10.2 当前状态 + +**状态**: 待开发 + +### 10.3 规划功能 + +#### 10.3.1 评分实时更新 + +**功能描述**: 其他评委评分后实时推送 + +**推送内容**: +- 选手ID +- 评委姓名 +- 评分 +- 当前总分 + +#### 10.3.2 评分修改通知 + +**功能描述**: 裁判长修改评分后推送通知 + +**推送内容**: +- 选手ID +- 原始分数 +- 修改后分数 +- 修改原因 + +#### 10.3.3 选手状态更新 + +**功能描述**: 选手表演状态实时更新 + +**推送内容**: +- 选手ID +- 状态变化(等待→表演中→完成) + +### 10.4 WebSocket连接 + +```javascript +// 连接WebSocket +const connectWebSocket = () => { + const token = uni.getStorageSync('auth_token') + + uni.connectSocket({ + url: `wss://api.example.com/ws?token=${token}`, + success: () => { + console.log('WebSocket连接成功') + } + }) + + // 监听消息 + uni.onSocketMessage((res) => { + const message = JSON.parse(res.data) + handleWebSocketMessage(message) + }) +} + +// 处理推送消息 +const handleWebSocketMessage = (message) => { + switch (message.type) { + case 'new_score': + // 处理新评分 + break + case 'score_modified': + // 处理评分修改 + break + case 'athlete_status': + // 处理选手状态更新 + break + } +} +``` + +### 10.5 数据结构 + +```typescript +// 新评分推送 +interface NewScoreMessage { + type: 'new_score' + data: { + athleteId: string + athleteName: string + judgeId: string + judgeName: string + score: number + totalScore: number + judgeCount: number + timestamp: string + } +} + +// 评分修改推送 +interface ScoreModifiedMessage { + type: 'score_modified' + data: { + athleteId: string + athleteName: string + modifierId: string + modifierName: string + originalScore: number + modifiedScore: number + note: string + timestamp: string + } +} + +// 选手状态推送 +interface AthleteStatusMessage { + type: 'athlete_status' + data: { + athleteId: string + athleteName: string + status: 'waiting' | 'performing' | 'finished' + timestamp: string + } +} +``` + +--- + +## 模块依赖关系 + +``` +用户认证模块 + ↓ + ├─→ 比赛信息模块 + │ ↓ + │ ├─→ 场地管理模块 + │ │ ↓ + │ │ └─→ 选手管理模块 + │ │ ↓ + │ │ ├─→ 评分管理模块 + │ │ │ ↓ + │ │ │ └─→ 扣分项管理模块 + │ │ │ + │ │ └─→ 评分修改模块 + │ │ + │ └─→ 项目管理模块 + │ ↓ + │ └─→ 选手管理模块 + │ + └─→ 统计分析模块 + ↓ + └─→ 实时推送模块 +``` + +--- + +## 开发优先级建议 + +### 第一阶段(核心功能) + +1. **用户认证模块** - 必须先完成 +2. **比赛信息模块** - 基础数据 +3. **选手管理模块** - 核心业务 +4. **评分管理模块** - 核心业务 +5. **扣分项管理模块** - 配合评分 + +**预计时间**: 2-3周 + +### 第二阶段(管理功能) + +1. **场地管理模块** - 裁判长功能 +2. **项目管理模块** - 裁判长功能 +3. **评分修改模块** - 裁判长功能 + +**预计时间**: 1-2周 + +### 第三阶段(增强功能) + +1. **统计分析模块** - 数据分析 +2. **实时推送模块** - 实时更新 + +**预计时间**: 2-3周 + +--- + +## 模块测试要点 + +### 用户认证模块 + +- [ ] 登录成功/失败 +- [ ] Token存储和获取 +- [ ] 角色判断和跳转 +- [ ] Token过期处理 + +### 评分管理模块 + +- [ ] 评分范围验证 +- [ ] 评分精度验证 +- [ ] 扣分项多选 +- [ ] 重复提交防止 +- [ ] 备注长度限制 + +### 评分修改模块 + +- [ ] 权限验证 +- [ ] 修改记录保存 +- [ ] 修改通知推送 +- [ ] 修改历史查询 + +### 选手管理模块 + +- [ ] 列表分页加载 +- [ ] 选手详情展示 +- [ ] 状态实时更新 +- [ ] 搜索和筛选 + +--- + +## 附录:模块接口对应表 + +| 模块 | 主要接口 | 涉及页面 | +|------|---------|---------| +| 用户认证 | POST /api/auth/login | login.vue | +| 比赛信息 | GET /api/matches/{matchId} | score-list.vue, score-list-multi.vue | +| 场地管理 | GET /api/venues | score-list-multi.vue | +| 项目管理 | GET /api/projects | score-list.vue, score-list-multi.vue | +| 选手管理 | GET /api/athletes | score-list.vue | +| 评分管理 | POST /api/scores | score-detail.vue | +| 评分修改 | PUT /api/scores/{athleteId}/modify | modify-score.vue | +| 扣分项管理 | GET /api/deductions | score-detail.vue | +| 统计分析 | GET /api/statistics/match/{matchId} | 待开发 | +| 实时推送 | WebSocket连接 | 所有页面 | + +--- + +## 总结 + +本文档按功能模块清晰地划分了武术评分系统的所有功能,每个模块都包含了详细的功能说明、涉及页面、API接口和数据结构。建议按照开发优先级进行分阶段开发,确保核心功能先完成,再逐步添加增强功能。 diff --git a/功能说明.md b/doc/功能说明.md similarity index 100% rename from 功能说明.md rename to doc/功能说明.md diff --git a/doc/后端实现对比报告.md b/doc/后端实现对比报告.md new file mode 100644 index 0000000..5caea0c --- /dev/null +++ b/doc/后端实现对比报告.md @@ -0,0 +1,1073 @@ +# 后端实现与前端文档对比报告 + +## 项目概述 + +### 项目结构 + +``` +D:\workspace\31.比赛项目\project\ +├── martial-admin-mini # 前端原型项目(纯Mock,无API对接) +├── martial-mini # 另一个小程序项目(已对接后端API) +├── martial-master # 后端项目(Spring Boot + MyBatis Plus) +└── martial-web # Web前端项目 +``` + +### 关键发现 + +1. **martial-admin-mini**(当前项目)是纯前端原型,没有对接任何后端API +2. **martial-mini** 项目已经实现了后端API对接 +3. **martial-master** 后端项目已经实现了大部分API接口 +4. 后端使用 **BladeX** 框架(基于Spring Boot) + +--- + +## 一、后端技术栈 + +### 技术框架 + +| 技术 | 版本/说明 | +|------|----------| +| **基础框架** | Spring Boot | +| **ORM框架** | MyBatis Plus | +| **架构框架** | BladeX 4.0.1.RELEASE | +| **Java版本** | JDK 17 | +| **API文档** | Swagger 3 (OpenAPI) | +| **数据库** | MySQL (推测) | +| **认证方式** | Blade-Auth Token | + +### 项目结构 + +``` +martial-master/ +├── src/main/java/org/springblade/ +│ ├── common/ # 通用工具类 +│ ├── modules/ +│ │ ├── auth/ # 认证模块 +│ │ └── martial/ # 武术评分核心模块 +│ │ ├── controller/ # 控制器层 +│ │ ├── service/ # 服务层 +│ │ ├── mapper/ # 数据访问层 +│ │ └── pojo/ # 数据对象 +│ │ ├── entity/ # 实体类 +│ │ ├── vo/ # 视图对象 +│ │ └── dto/ # 数据传输对象 +│ └── job/ # 定时任务模块 +└── database/ # 数据库脚本 +``` + +--- + +## 二、API接口对比 + +### 2.1 已实现的Controller + +| Controller | 路径 | 功能 | 文档要求 | 实现状态 | +|-----------|------|------|---------|---------| +| **MartialJudgeInviteController** | `/martial/judgeInvite` | 裁判邀请码管理 | ✅ | ✅ 已实现 | +| **MartialScoreController** | `/martial/score` | 评分记录管理 | ✅ | ✅ 已实现 | +| **MartialAthleteController** | `/martial/athlete` | 参赛选手管理 | ✅ | ✅ 已实现 | +| **MartialVenueController** | `/martial/venue` | 场地信息管理 | ✅ | ✅ 已实现 | +| **MartialProjectController** | `/martial/project` | 比赛项目管理 | ✅ | ✅ 已实现 | +| **MartialDeductionItemController** | `/martial/deductionItem` | 扣分项配置 | ✅ | ✅ 已实现 | +| **MartialJudgeController** | `/martial/judge` | 评委管理 | ✅ | ✅ 已实现 | +| **MartialJudgeProjectController** | `/martial/judgeProject` | 评委项目关系 | ✅ | ✅ 已实现 | +| **MartialCompetitionController** | `/martial/competition` | 比赛管理 | ✅ | ✅ 已实现 | +| **MartialScheduleController** | `/martial/schedule` | 赛程管理 | - | ✅ 已实现(额外功能) | + +### 2.2 接口对比详情 + +#### 认证模块 + +**文档定义**: +``` +POST /api/auth/login # 用户登录 +POST /api/auth/logout # 退出登录 +GET /api/auth/verify # Token验证 +``` + +**后端实现**: +```java +// MartialJudgeInviteController.java +@RequestMapping("/martial/judgeInvite") +- GET /detail # 获取邀请码详情 +- GET /list # 邀请码列表 +- POST /submit # 创建/更新邀请码 +- POST /remove # 删除邀请码 +``` + +**对比结果**: ⚠️ **接口路径不匹配** +- 文档要求: `/api/auth/login` +- 后端实现: `/martial/judgeInvite` (管理接口) +- **缺少**: 小程序专用的登录验证接口 + +--- + +#### 评分管理模块 + +**文档定义**: +``` +POST /api/scores # 提交评分 +GET /api/scores/{athleteId} # 获取评分详情 +PUT /api/scores/{athleteId}/modify # 修改评分(裁判长) +GET /api/scores/my-records # 获取我的评分记录 +``` + +**后端实现**: +```java +// MartialScoreController.java +@RequestMapping("/martial/score") +- GET /detail # 评分详情 +- GET /list # 评分列表(分页) +- POST /submit # 新增或修改评分 +- POST /remove # 删除评分 +- GET /anomalies # 获取异常评分列表 +- POST /validate # 批量验证评分 +``` + +**对比结果**: ⚠️ **接口设计不同** +- **共同点**: 都有评分提交、查询、修改功能 +- **差异**: + - 文档使用 RESTful 风格 (POST/GET/PUT) + - 后端使用统一的 `/submit` 接口 + - 后端多了异常评分和验证功能 +- **缺少**: + - 专门的 `/my-records` 接口 + - 裁判长修改评分的专用接口 + +--- + +#### 选手管理模块 + +**文档定义**: +``` +GET /api/athletes # 获取选手列表(普通评委) +GET /api/athletes/all # 获取选手列表(裁判长) +GET /api/athletes/{athleteId} # 获取选手详情 +``` + +**后端实现**: +```java +// MartialAthleteController.java +@RequestMapping("/martial/athlete") +- GET /detail # 选手详情 +- GET /list # 选手列表(分页,含VO) +- POST /submit # 新增或修改选手 +- POST /remove # 删除选手 +- POST /checkin # 选手签到 +- POST /complete # 完成比赛 +- POST /status # 更新比赛状态 +``` + +**对比结果**: ✅ **基本匹配,功能更丰富** +- 文档中的查询接口可以通过 `/list` 实现(通过参数筛选) +- 后端额外提供了签到、完成、状态更新等功能 + +--- + +#### 场地管理模块 + +**文档定义**: +``` +GET /api/venues # 获取场地列表 +``` + +**后端实现**: +```java +// MartialVenueController.java +@RequestMapping("/martial/venue") +- GET /detail # 场地详情 +- GET /list # 场地列表(分页) +- POST /submit # 新增或修改场地 +- POST /remove # 删除场地 +``` + +**对比结果**: ✅ **完全覆盖** + +--- + +#### 项目管理模块 + +**文档定义**: +``` +GET /api/projects # 获取项目列表 +GET /api/projects/{projectId} # 获取项目详情 +``` + +**后端实现**: +```java +// MartialProjectController.java +@RequestMapping("/martial/project") +- GET /detail # 项目详情 +- GET /list # 项目列表(分页) +- POST /submit # 新增或修改项目 +- POST /remove # 删除项目 +``` + +**对比结果**: ✅ **完全覆盖** + +--- + +#### 扣分项管理模块 + +**文档定义**: +``` +GET /api/deductions # 获取扣分项列表 +``` + +**后端实现**: +```java +// MartialDeductionItemController.java +@RequestMapping("/martial/deductionItem") +- GET /detail # 扣分项详情 +- GET /list # 扣分项列表 +- POST /submit # 新增或修改扣分项 +- POST /remove # 删除扣分项 +``` + +**对比结果**: ✅ **完全覆盖** + +--- + +#### 统计分析模块 + +**文档定义**: +``` +GET /api/statistics/match/{matchId} # 比赛统计 +GET /api/statistics/judges # 评委统计 +``` + +**后端实现**: ❌ **未找到对应Controller** + +**对比结果**: ❌ **缺少统计接口** + +--- + +## 三、数据结构对比 + +### 3.1 评分记录 (Score) + +**文档定义 (TypeScript)**: +```typescript +interface Score { + scoreId: string + matchId: string + athleteId: string + judgeId: string + projectId: string + venueId: string + score: number // 5.000-10.000 + deductions: Deduction[] // 扣分项数组 + note?: string + scoreTime: string +} +``` + +**后端实现 (Java)**: +```java +@TableName("martial_score") +public class MartialScore extends TenantEntity { + private Long competitionId; // ✅ 对应 matchId + private Long athleteId; // ✅ + private Long projectId; // ✅ + private Long scheduleId; // ⚠️ 文档中没有 + private Long venueId; // ✅ + private Long judgeId; // ✅ + private String judgeName; // ⚠️ 文档中没有 + private BigDecimal score; // ✅ + private BigDecimal originalScore; // ⚠️ 用于记录修改前的分数 + private String deductionItems; // ✅ JSON数组格式 + private String note; // ✅ + private String modifyReason; // ⚠️ 修改原因 + private LocalDateTime scoreTime;// ✅ + private LocalDateTime modifyTime; // ⚠️ 修改时间 + private String ipAddress; // ⚠️ IP地址记录 +} +``` + +**对比结果**: ✅ **匹配度: 95%** +- **类型映射**: `string` → `Long`, `number` → `BigDecimal` +- **额外字段**: 后端多了 `scheduleId`, `judgeName`, `originalScore`, `modifyReason`, `modifyTime`, `ipAddress` +- **优点**: 后端设计更完善,支持审计和追溯 + +--- + +### 3.2 选手信息 (Athlete) + +**文档定义**: +```typescript +interface Athlete { + athleteId: string + name: string + gender: 'male' | 'female' + age?: number + idCard: string + team: string + number: string + order: number + projectId: string + venueId: string + matchId: string + photo?: string + defaultScore?: number + status: 'waiting' | 'performing' | 'finished' +} +``` + +**后端实现**: +```java +@TableName("martial_athlete") +public class MartialAthlete extends TenantEntity { + private Long orderId; // ⚠️ 订单ID(报名相关) + private Long competitionId; // ✅ matchId + private Long projectId; // ✅ + private String playerName; // ✅ name + private String playerNo; // ✅ number + private Integer gender; // ✅ (1-男, 2-女) + private Integer age; // ✅ + private String idCard; // ✅ + private Integer idCardType; // ⚠️ 证件类型 + private LocalDate birthDate; // ⚠️ 出生日期 + private String nation; // ⚠️ 民族 + private String contactPhone; // ⚠️ 联系电话 + private String organization; // ✅ team (单位) + private Integer organizationType; // ⚠️ 单位类别 + private String teamName; // ✅ team + private String category; // ⚠️ 组别 + private Integer orderNum; // ✅ order + private String introduction; // ⚠️ 简介 + private String attachments; // ⚠️ 附件 + private String photoUrl; // ✅ photo + private Integer registrationStatus; // ⚠️ 报名状态 + private Integer competitionStatus; // ✅ status (0-待出场,1-进行中,2-已完成) + private BigDecimal totalScore; // ⚠️ 总分 + private Integer ranking; // ⚠️ 排名 + private String remark; // ⚠️ 备注 +} +``` + +**对比结果**: ✅ **匹配度: 90%** +- 核心字段完全匹配 +- 后端包含了更多的报名和管理字段 +- 状态值映射: `'waiting'` → `0`, `'performing'` → `1`, `'finished'` → `2` + +--- + +### 3.3 场地信息 (Venue) + +**文档定义**: +```typescript +interface Venue { + venueId: string + venueName: string + matchId: string + order: number + athleteCount: number + scoredCount: number + status: 'active' | 'completed' | 'paused' +} +``` + +**后端实现**: +```java +@TableName("martial_venue") +public class MartialVenue extends TenantEntity { + private Long competitionId; // ✅ matchId + private String venueName; // ✅ + private String venueCode; // ⚠️ 场地编码 + private Integer capacity; // ⚠️ 容纳人数 + private String location; // ⚠️ 位置 + private String facilities; // ⚠️ 场地设施 + private Integer status; // ✅ (0-禁用, 1-启用) +} +``` + +**对比结果**: ⚠️ **匹配度: 60%** +- **缺少**: `order`, `athleteCount`, `scoredCount` (需要通过查询计算) +- **状态不同**: 文档定义3种状态,后端只有启用/禁用 + +--- + +### 3.4 项目信息 (Project) + +**文档定义**: +```typescript +interface Project { + projectId: string + projectName: string + matchId: string + category: string + order: number + athleteCount: number + minScore: 5.0 + maxScore: 10.0 + scoreStep: 0.001 + status: 'upcoming' | 'ongoing' | 'completed' +} +``` + +**后端实现**: +```java +@TableName("martial_project") +public class MartialProject extends TenantEntity { + private Long competitionId; // ✅ matchId + private String projectName; // ✅ + private String projectCode; // ⚠️ 项目编码 + private String category; // ✅ 组别 + private Integer type; // ⚠️ 类型(个人/双人/集体) + private Integer minParticipants; // ⚠️ 最少参赛人数 + private Integer maxParticipants; // ⚠️ 最多参赛人数 + private Integer minAge; // ⚠️ 最小年龄 + private Integer maxAge; // ⚠️ 最大年龄 + private Integer genderLimit; // ⚠️ 性别限制 + private Integer estimatedDuration; // ⚠️ 预估时长 + private BigDecimal price; // ⚠️ 报名费用 + private BigDecimal difficultyCoefficient; // ⚠️ 难度系数 + private LocalDateTime registrationDeadline; // ⚠️ 报名截止时间 + private String description; // ⚠️ 项目描述 + private Integer sortOrder; // ✅ order +} +``` + +**对比结果**: ⚠️ **匹配度: 65%** +- **缺少**: `minScore`, `maxScore`, `scoreStep`, `athleteCount`, `status` +- 后端侧重于项目的报名和管理配置 +- 评分范围等信息可能在其他地方配置 + +--- + +### 3.5 裁判邀请码 (JudgeInvite) + +**文档定义**: 无明确定义,但登录需要邀请码 + +**后端实现**: +```java +@TableName("martial_judge_invite") +public class MartialJudgeInvite extends TenantEntity { + private Long competitionId; // 赛事ID + private Long judgeId; // 裁判ID + private String inviteCode; // 邀请码 + private String role; // 角色 (judge/chief_judge) + private Long venueId; // 分配场地ID + private String projects; // 分配项目(JSON数组) + private LocalDateTime expireTime; // 过期时间 + private Integer isUsed; // 是否已使用 + private LocalDateTime useTime; // 使用时间 + private String deviceInfo; // 设备信息 + private String loginIp; // 登录IP + private String accessToken; // 访问令牌 + private LocalDateTime tokenExpireTime; // token过期时间 +} +``` + +**对比结果**: ✅ **设计完善** +- 支持邀请码验证 +- **角色映射**: `'judge'` 对应文档的 `'pub'`, `'chief_judge'` 对应 `'admin'` +- 包含了Token管理和安全审计功能 + +--- + +### 3.6 扣分项 (Deduction) + +**文档定义**: +```typescript +interface Deduction { + deductionId: string + projectId: string + text: string + score: number // 负数 + category?: string + order: number +} +``` + +**后端实现**: +```java +@TableName("martial_deduction_item") +public class MartialDeductionItem extends TenantEntity { + private String itemName; // ✅ text + private String itemCode; // ⚠️ 扣分项编码 + private BigDecimal deductionPoint; // ✅ score + private String category; // ✅ + private String applicableProjects; // ✅ projectId (JSON数组) + private String description; // ⚠️ 描述 + private Integer sortOrder; // ✅ order +} +``` + +**对比结果**: ✅ **匹配度: 95%** + +--- + +## 四、认证机制对比 + +### 4.1 文档设计 + +**认证方式**: JWT Token + +**Token格式**: +``` +Authorization: Bearer {token} +``` + +**全局数据存储**: +```javascript +getApp().globalData = { + userRole: 'pub' | 'admin', + matchCode: string, + token: string +} +``` + +### 4.2 后端实现 + +**认证方式**: Blade-Auth Token + +**Token格式**: +``` +Blade-Auth: Bearer {token} +``` + +**实现代码** (martial-mini项目): +```javascript +// utils/request.js +getHeaders(customHeader = {}) { + const token = uni.getStorageSync('token') || '' + return { + 'Content-Type': 'application/json', + 'Blade-Auth': token ? `Bearer ${token}` : '', + ...customHeader + } +} +``` + +**对比结果**: ⚠️ **Token头名称不同** +- 文档: `Authorization` +- 后端: `Blade-Auth` +- **建议**: 前端需要改为 `Blade-Auth` + +--- + +## 五、API路径规范对比 + +### 5.1 文档定义 + +``` +/api/auth/login +/api/matches/{matchId} +/api/venues +/api/projects +/api/athletes +/api/scores +/api/deductions +/api/statistics/match/{matchId} +``` + +### 5.2 后端实现 + +``` +/martial/judgeInvite +/martial/competition +/martial/venue +/martial/project +/martial/athlete +/martial/score +/martial/deductionItem +(统计接口未实现) +``` + +**对比结果**: ❌ **路径前缀不同** +- 文档: `/api/` +- 后端: `/martial/` +- **影响**: 前端需要修改所有API路径 + +--- + +## 六、响应格式对比 + +### 6.1 文档定义 + +```json +{ + "code": 200, + "message": "成功", + "data": {} +} +``` + +### 6.2 后端实现(BladeX框架) + +**成功响应**: +```json +{ + "code": 200, + "success": true, + "data": {}, + "msg": "操作成功" +} +``` + +**分页响应**: +```json +{ + "code": 200, + "success": true, + "data": { + "records": [], // 数据列表 + "total": 100, // 总记录数 + "size": 20, // 每页大小 + "current": 1, // 当前页 + "pages": 5 // 总页数 + } +} +``` + +**对比结果**: ⚠️ **格式略有不同** +- 多了 `success` 字段 +- 字段名 `message` vs `msg` +- 分页格式不同(MyBatis Plus标准格式) + +--- + +## 七、数据类型映射 + +| 前端文档 (TypeScript) | 后端实现 (Java) | 说明 | +|---------------------|----------------|------| +| `string` | `Long` | ID类型 | +| `string` | `String` | 普通字符串 | +| `number` | `BigDecimal` | 分数(精确小数) | +| `number` | `Integer` | 整数 | +| `boolean` | `Integer` | 0/1表示 | +| `string` (ISO 8601) | `LocalDateTime` | 时间类型 | +| `Array` | `String` (JSON) | 数组存储为JSON字符串 | +| `'male' | 'female'` | `Integer` (1/2) | 枚举映射 | + +--- + +## 八、缺失的接口 + +### 8.1 小程序登录专用接口 + +**需要**: +``` +POST /api/auth/login +请求: { matchCode, inviteCode } +响应: { token, userRole, matchId, ... } +``` + +**建议实现**: 创建新的Controller `MartialMiniAuthController` + +### 8.2 选手列表(带评分状态) + +**需要**: +``` +GET /api/athletes +- 返回当前评委的选手列表 +- 包含 myScore, totalScore, scored 字段 +``` + +**建议**: 在 `MartialAthleteController` 中添加新方法 + +### 8.3 评分修改(裁判长专用) + +**需要**: +``` +PUT /api/scores/{athleteId}/modify +- 只有裁判长可以调用 +- 记录修改历史 +``` + +**建议**: 在 `MartialScoreController` 中添加专用方法 + +### 8.4 统计分析接口 + +**需要**: +``` +GET /api/statistics/match/{matchId} +GET /api/statistics/judges +``` + +**建议**: 创建新的 `MartialStatisticsController` + +--- + +## 九、对接建议 + +### 9.1 短期方案(1-2周) + +#### 1. 创建小程序专用Controller + +```java +@RestController +@RequestMapping("/api/mini") +public class MartialMiniController { + + /** + * 小程序登录 + */ + @PostMapping("/login") + public R login(@RequestBody LoginDTO dto) { + // 验证邀请码 + // 生成Token + // 返回用户信息和权限 + } + + /** + * 获取评委的选手列表(带评分状态) + */ + @GetMapping("/athletes") + public R> getMyAthletes( + @RequestParam Long judgeId, + @RequestParam Long venueId, + @RequestParam Long projectId + ) { + // 查询选手列表 + // 关联评分状态 + // 返回 myScore, totalScore 等 + } + + /** + * 提交评分 + */ + @PostMapping("/score/submit") + public R submitScore(@RequestBody ScoreDTO dto) { + // 验证评分范围 + // 保存评分 + // 计算总分 + } + + /** + * 修改评分(裁判长) + */ + @PutMapping("/score/modify") + public R modifyScore(@RequestBody ModifyScoreDTO dto) { + // 验证权限 + // 修改评分 + // 记录日志 + } +} +``` + +#### 2. 前端适配 + +**修改 martial-admin-mini 项目**: + +1. **创建 `utils/request.js`**(参考 martial-mini 项目) + +2. **创建 `config/api.config.js`**: + ```javascript + export default { + baseURL: '/martial', // 或配置代理 + timeout: 30000 + } + ``` + +3. **创建 `api/` 目录**: + ``` + api/ + ├── auth.js # 登录相关 + ├── athlete.js # 选手相关 + ├── score.js # 评分相关 + ├── venue.js # 场地相关 + ├── project.js # 项目相关 + └── deduction.js # 扣分项相关 + ``` + +4. **修改页面代码**,替换Mock数据为API调用 + +#### 3. Token处理 + +**统一改为 `Blade-Auth`**: +```javascript +header: { + 'Blade-Auth': `Bearer ${token}` +} +``` + +### 9.2 中期方案(2-4周) + +1. **完善数据结构** + - 在 VO 类中添加文档要求的字段 + - 创建专门的小程序VO类 + +2. **实现统计分析功能** + - 创建 `MartialStatisticsController` + - 实现比赛统计、评委统计等接口 + +3. **WebSocket实时推送** + - 集成 Spring WebSocket + - 实现评分实时更新推送 + +4. **完善权限控制** + - 基于角色的访问控制 + - 接口级别的权限验证 + +### 9.3 长期方案(1-2月) + +1. **API版本管理** + - `/api/v1/...` 路径规范 + - 向后兼容处理 + +2. **性能优化** + - Redis缓存 + - 数据库查询优化 + - 分页优化 + +3. **监控和日志** + - 接口调用监控 + - 异常日志记录 + - 性能分析 + +--- + +## 十、数据库表设计 + +### 已有的表 + +根据Entity类推断的表结构: + +```sql +-- 评分记录表 +CREATE TABLE martial_score ( + id BIGINT PRIMARY KEY, + competition_id BIGINT, -- 赛事ID + athlete_id BIGINT, -- 选手ID + project_id BIGINT, -- 项目ID + schedule_id BIGINT, -- 赛程ID + venue_id BIGINT, -- 场地ID + judge_id BIGINT, -- 裁判ID + judge_name VARCHAR(50), -- 裁判姓名 + score DECIMAL(5,3), -- 评分 + original_score DECIMAL(5,3),-- 原始评分 + deduction_items TEXT, -- 扣分项(JSON) + note VARCHAR(500), -- 备注 + modify_reason VARCHAR(500), -- 修改原因 + score_time DATETIME, -- 评分时间 + modify_time DATETIME, -- 修改时间 + ip_address VARCHAR(50), -- IP地址 + -- 其他审计字段 + created_by BIGINT, + create_time DATETIME, + updated_by BIGINT, + update_time DATETIME, + is_deleted INT DEFAULT 0 +); + +-- 选手表 +CREATE TABLE martial_athlete ( + id BIGINT PRIMARY KEY, + order_id BIGINT, -- 订单ID + competition_id BIGINT, -- 赛事ID + project_id BIGINT, -- 项目ID + player_name VARCHAR(50), -- 选手姓名 + player_no VARCHAR(50), -- 参赛编号 + gender INT, -- 性别 + age INT, -- 年龄 + id_card VARCHAR(20), -- 身份证号 + id_card_type INT, -- 证件类型 + birth_date DATE, -- 出生日期 + nation VARCHAR(20), -- 民族 + contact_phone VARCHAR(20), -- 联系电话 + organization VARCHAR(100), -- 所属单位 + organization_type INT, -- 单位类别 + team_name VARCHAR(100), -- 队伍名称 + category VARCHAR(50), -- 组别 + order_num INT, -- 出场顺序 + introduction TEXT, -- 选手简介 + attachments TEXT, -- 附件(JSON) + photo_url VARCHAR(500), -- 照片URL + registration_status INT, -- 报名状态 + competition_status INT, -- 比赛状态 + total_score DECIMAL(5,3), -- 总分 + ranking INT, -- 排名 + remark VARCHAR(500), -- 备注 + -- 审计字段... +); + +-- 场地表 +CREATE TABLE martial_venue ( + id BIGINT PRIMARY KEY, + competition_id BIGINT, -- 赛事ID + venue_name VARCHAR(50), -- 场地名称 + venue_code VARCHAR(50), -- 场地编码 + capacity INT, -- 容纳人数 + location VARCHAR(200), -- 位置 + facilities TEXT, -- 场地设施 + status INT, -- 状态 + -- 审计字段... +); + +-- 项目表 +CREATE TABLE martial_project ( + id BIGINT PRIMARY KEY, + competition_id BIGINT, -- 赛事ID + project_name VARCHAR(100), -- 项目名称 + project_code VARCHAR(50), -- 项目编码 + category VARCHAR(50), -- 组别 + type INT, -- 类型 + min_participants INT, -- 最少参赛人数 + max_participants INT, -- 最多参赛人数 + min_age INT, -- 最小年龄 + max_age INT, -- 最大年龄 + gender_limit INT, -- 性别限制 + estimated_duration INT, -- 预估时长 + price DECIMAL(10,2), -- 报名费用 + difficulty_coefficient DECIMAL(4,2), -- 难度系数 + registration_deadline DATETIME, -- 报名截止时间 + description TEXT, -- 项目描述 + sort_order INT, -- 排序 + -- 审计字段... +); + +-- 裁判邀请码表 +CREATE TABLE martial_judge_invite ( + id BIGINT PRIMARY KEY, + competition_id BIGINT, -- 赛事ID + judge_id BIGINT, -- 裁判ID + invite_code VARCHAR(50), -- 邀请码 + role VARCHAR(20), -- 角色 + venue_id BIGINT, -- 分配场地ID + projects TEXT, -- 分配项目(JSON) + expire_time DATETIME, -- 过期时间 + is_used INT, -- 是否已使用 + use_time DATETIME, -- 使用时间 + device_info VARCHAR(500), -- 设备信息 + login_ip VARCHAR(50), -- 登录IP + access_token VARCHAR(500), -- 访问令牌 + token_expire_time DATETIME, -- token过期时间 + -- 审计字段... +); + +-- 扣分项表 +CREATE TABLE martial_deduction_item ( + id BIGINT PRIMARY KEY, + item_name VARCHAR(100), -- 扣分项名称 + item_code VARCHAR(50), -- 扣分项编码 + deduction_point DECIMAL(4,3), -- 扣分值 + category VARCHAR(50), -- 分类 + applicable_projects TEXT, -- 适用项目(JSON) + description VARCHAR(500), -- 描述 + sort_order INT, -- 排序 + -- 审计字段... +); +``` + +--- + +## 十一、总结 + +### ✅ 优点 + +1. **后端架构完整**:基于成熟的BladeX框架 +2. **数据库设计合理**:字段完善,支持审计 +3. **代码规范**:使用Swagger文档,代码结构清晰 +4. **功能丰富**:比文档要求的功能更多 + +### ⚠️ 问题 + +1. **API路径不匹配**:`/martial/` vs `/api/` +2. **接口设计差异**:后端使用统一的 `/submit` 而不是 RESTful 风格 +3. **缺少小程序专用接口**:登录、评分等需要定制 +4. **Token头名称不同**:`Blade-Auth` vs `Authorization` +5. **响应格式略有差异**:多了 `success` 字段 + +### 📋 需要开发的内容 + +#### 后端(1-2周工作量) + +- [ ] 创建 `MartialMiniController` 或 `MartialMiniAuthController` +- [ ] 实现小程序登录接口 +- [ ] 实现获取评委选手列表接口(带评分状态) +- [ ] 实现评分提交接口(简化版) +- [ ] 实现评分修改接口(裁判长专用) +- [ ] 创建小程序专用的VO类 +- [ ] (可选)创建统计分析Controller + +#### 前端(2-3周工作量) + +- [ ] 创建 `utils/request.js` 请求封装 +- [ ] 创建 `api/` 目录和所有API文件 +- [ ] 修改所有页面,替换Mock数据为API调用 +- [ ] 实现Token存储和管理 +- [ ] 实现登录流程 +- [ ] 实现评分提交流程 +- [ ] 实现评分修改流程 +- [ ] 实现错误处理和loading状态 +- [ ] (可选)实现WebSocket实时更新 + +### 🎯 推荐方案 + +**最快对接方案**(1周): + +1. **后端**:在现有Controller基础上添加小程序专用方法 +2. **前端**:直接复用 martial-mini 项目的 `utils/request.js` 和 `api/` 文件 +3. **适配**:调整API路径和数据格式映射 + +**完整方案**(3-4周): + +1. 按照上面的对接建议,分阶段实现 +2. 编写接口文档和测试用例 +3. 进行完整的联调测试 + +--- + +## 十二、martial-mini 项目参考 + +`martial-mini` 项目已经成功对接了后端,可以作为参考: + +**文件结构**: +``` +martial-mini/ +├── api/ # API接口定义 +│ ├── athlete.js +│ ├── competition.js +│ ├── info.js +│ ├── registration.js +│ ├── result.js +│ └── user.js +├── config/ +│ └── api.config.js # API配置 +└── utils/ + └── request.js # 请求封装 +``` + +**建议**: 直接复制 `martial-mini` 项目的这些文件到 `martial-admin-mini`,然后根据需要调整。 + +--- + +## 附录:快速对接检查清单 + +### 后端检查 + +- [ ] 确认后端服务已启动 +- [ ] 确认数据库已初始化 +- [ ] 确认Swagger文档可访问(通常在 `/doc.html`) +- [ ] 创建测试用的邀请码数据 +- [ ] 测试各个接口是否正常 + +### 前端检查 + +- [ ] 复制 `request.js` 文件 +- [ ] 复制 `api.config.js` 文件 +- [ ] 创建各个API文件 +- [ ] 修改登录页面对接API +- [ ] 修改评分列表页面对接API +- [ ] 修改评分详情页面对接API +- [ ] 测试完整流程 + +### 联调测试 + +- [ ] 登录功能测试 +- [ ] 获取选手列表测试 +- [ ] 评分提交测试 +- [ ] 评分修改测试(裁判长) +- [ ] 场地切换测试 +- [ ] 项目切换测试 +- [ ] 扣分项加载测试 +- [ ] Token过期处理测试 + +--- + +**报告生成时间**: 2025-12-11 +**文档版本**: v1.0 +**后端项目**: martial-master (BladeX 4.0.1) +**前端项目**: martial-admin-mini (UniApp) diff --git a/如何运行.md b/doc/如何运行.md similarity index 100% rename from 如何运行.md rename to doc/如何运行.md diff --git a/doc/数据可行性分析报告.md b/doc/数据可行性分析报告.md new file mode 100644 index 0000000..c4b081c --- /dev/null +++ b/doc/数据可行性分析报告.md @@ -0,0 +1,1290 @@ +# 数据可行性分析报告 + +## 报告说明 + +本报告基于**前端Mock数据业务需求**,深入分析**后端数据库和API能否提供**这些业务数据。目的是在**不改动前端UI代码**的前提下,验证后端数据支持能力。 + +--- + +## 一、分析方法 + +### 1.1 分析维度 + +``` +前端页面Mock数据需求 + ↓ +后端Entity字段能力 + ↓ +后端Controller API能力 + ↓ +业务逻辑计算能力 + ↓ +数据可行性结论 +``` + +### 1.2 评估标准 + +| 评级 | 说明 | 标记 | +|------|------|------| +| **✅ 完全支持** | 后端已有字段和接口,可直接提供 | ✅ | +| **🔄 需要计算** | 后端有原始数据,需通过计算/聚合得出 | 🔄 | +| **⚠️ 需要扩展** | 后端缺少字段,需要添加字段或接口 | ⚠️ | +| **❌ 无法支持** | 后端缺少数据源,需要重新设计 | ❌ | + +--- + +## 二、登录页数据分析 + +### 2.1 前端Mock数据需求 + +**页面**: `pages/login/login.vue` + +```javascript +// 用户输入 +{ + matchCode: '', // 比赛编码 + inviteCode: '' // 邀请码 (pub/admin) +} + +// 验证逻辑 +- 判断 inviteCode 是 'pub' 还是 'admin' +- 根据角色跳转不同页面 +``` + +### 2.2 后端数据能力 + +#### Entity支持度 + +```java +// MartialJudgeInvite.java +@TableName("martial_judge_invite") +public class MartialJudgeInvite { + private Long competitionId; // ✅ 对应 matchCode + private Long judgeId; // ✅ 裁判ID + private String inviteCode; // ✅ 邀请码 + private String role; // ✅ 角色 (judge/chief_judge) + private Long venueId; // ✅ 分配场地 + private String projects; // ✅ 分配项目(JSON) + private LocalDateTime expireTime; // ✅ 过期时间 + private Integer isUsed; // ✅ 是否已使用 + private String accessToken; // ✅ Token + private LocalDateTime tokenExpireTime; // ✅ Token过期时间 +} +``` + +#### API支持度 + +**现有接口**: +```java +// MartialJudgeInviteController +GET /martial/judgeInvite/detail?id=xxx // 获取邀请码详情 +GET /martial/judgeInvite/list // 邀请码列表 +POST /martial/judgeInvite/submit // 创建/更新邀请码 +``` + +### 2.3 数据可行性评估 + +| 前端需求 | 后端能力 | 评估结果 | 说明 | +|---------|---------|---------|------| +| 比赛编码验证 | competitionId | ✅ 完全支持 | 可通过 competitionId 关联比赛 | +| 邀请码验证 | inviteCode | ✅ 完全支持 | 直接匹配 inviteCode 字段 | +| 角色识别 | role | ✅ 完全支持 | 'judge' → 'pub', 'chief_judge' → 'admin' | +| Token生成 | accessToken | ✅ 完全支持 | 已有 Token 管理机制 | +| 场地分配 | venueId | ✅ 完全支持 | 普通评委有固定场地 | +| 项目分配 | projects (JSON) | ✅ 完全支持 | JSON数组存储分配的项目 | +| 过期校验 | expireTime, isUsed | ✅ 完全支持 | 支持过期时间和使用状态 | + +### 2.4 需要补充的接口 + +⚠️ **缺少小程序登录专用接口** + +```java +// 建议新增 +@PostMapping("/api/mini/login") +public R login(@RequestBody LoginDTO dto) { + // dto.matchCode + dto.inviteCode + // 1. 验证邀请码是否存在且未过期 + // 2. 验证是否已使用 + // 3. 生成Token + // 4. 返回用户信息、角色、分配的场地和项目 +} + +// 响应数据 +{ + "token": "xxx", + "userRole": "pub", // 或 "admin" + "matchId": "123", + "matchName": "2025年全国武术散打锦标赛...", + "judgeId": "456", + "judgeName": "欧阳丽娜", + "venueId": "1", // 普通评委有 + "venueName": "第一场地", + "projects": ["项目1", "项目2"] +} +``` + +**结论**: ✅ **数据完全支持,需要新增1个登录接口** + +--- + +## 三、评分列表页数据分析(普通评委) + +### 3.1 前端Mock数据需求 + +**页面**: `pages/score-list/score-list.vue` + +```javascript +// 页面展示数据 +{ + matchInfo: { + title: "2025年全国武术散打锦标赛暨第十七届世界武术锦标赛选拔赛", + time: "2025年6月25日 9:00" + }, + venue: "第一场地", + project: "男子组陈氏太极拳", + scoreStats: { + scored: 2, // 已评分数量 + total: 30 // 总选手数 + }, + players: [ + { + name: "张三", + idCard: "123456789000000000", + team: "少林寺武术大学院", + number: "123-4567898275", + myScore: 8.906, // 我的评分(已评分) + totalScore: 8.907, // 总分(已评分) + scored: true // 是否已评分 + } + ] +} +``` + +### 3.2 后端数据能力 + +#### Entity支持度 + +**MartialAthlete** (选手信息): +```java +✅ playerName → name +✅ idCard → idCard +✅ organization/teamName → team +✅ playerNo → number +✅ competitionStatus → 比赛状态 +✅ totalScore → totalScore +❌ 缺少 myScore → 需要关联 MartialScore 表查询 +❌ 缺少 scored → 需要计算(判断是否有评分记录) +``` + +**MartialScore** (评分记录): +```java +✅ judgeId → 识别"我的评分" +✅ athleteId → 关联选手 +✅ score → 评分值 +✅ scoreTime → 评分时间 +``` + +**MartialCompetition** (比赛信息): +```java +✅ competitionName → matchInfo.title +✅ startTime → matchInfo.time +``` + +**MartialVenue** (场地信息): +```java +✅ venueName → venue +``` + +**MartialProject** (项目信息): +```java +✅ projectName → project +``` + +### 3.3 数据可行性评估 + +| 前端需求 | 后端能力 | 评估结果 | 实现方案 | +|---------|---------|---------|---------| +| 比赛标题 | MartialCompetition.competitionName | ✅ 完全支持 | 直接查询 | +| 比赛时间 | MartialCompetition.startTime | ✅ 完全支持 | 格式化后返回 | +| 场地名称 | MartialVenue.venueName | ✅ 完全支持 | 直接查询 | +| 项目名称 | MartialProject.projectName | ✅ 完全支持 | 直接查询 | +| 选手基本信息 | MartialAthlete.* | ✅ 完全支持 | 直接查询 | +| 我的评分 | MartialScore.score | 🔄 需要计算 | `SELECT score FROM martial_score WHERE athleteId=? AND judgeId=?` | +| 总分 | MartialAthlete.totalScore | ✅ 完全支持 | 已有字段 | +| 是否已评分 | - | 🔄 需要计算 | `COUNT(*) FROM martial_score WHERE athleteId=? AND judgeId=? > 0` | +| 已评分数量 | - | 🔄 需要计算 | `COUNT(DISTINCT athleteId) FROM martial_score WHERE judgeId=?` | +| 总选手数 | - | 🔄 需要计算 | `COUNT(*) FROM martial_athlete WHERE projectId=? AND venueId=?` | + +### 3.4 需要补充的接口 + +⚠️ **需要新增带评分状态的选手列表接口** + +```java +@GetMapping("/api/mini/athletes") +public R> getMyAthletes( + @RequestParam Long judgeId, + @RequestParam Long venueId, + @RequestParam Long projectId +) { + // 1. 查询选手列表 + // 2. LEFT JOIN 查询当前评委的评分 + // 3. 计算 scored 状态 + // 4. 组装 VO +} + +// VO 数据结构 +class AthleteWithScoreVO { + Long athleteId; + String name; + String idCard; + String team; + String number; + BigDecimal myScore; // 我的评分 + BigDecimal totalScore; // 总分 + Boolean scored; // 是否已评分 + LocalDateTime scoreTime; // 评分时间 +} +``` + +**SQL查询示例**: +```sql +SELECT + a.id AS athleteId, + a.player_name AS name, + a.id_card AS idCard, + a.team_name AS team, + a.player_no AS number, + a.total_score AS totalScore, + s.score AS myScore, + CASE WHEN s.id IS NOT NULL THEN 1 ELSE 0 END AS scored, + s.score_time AS scoreTime +FROM martial_athlete a +LEFT JOIN martial_score s + ON a.id = s.athlete_id + AND s.judge_id = #{judgeId} +WHERE a.venue_id = #{venueId} + AND a.project_id = #{projectId} + AND a.competition_status IN (1, 2) -- 进行中或已完成 +ORDER BY a.order_num ASC +``` + +**结论**: ✅ **数据完全支持,需要新增1个查询接口和SQL关联查询** + +--- + +## 四、评分详情页数据分析(普通评委) + +### 4.1 前端Mock数据需求 + +**页面**: `pages/score-detail/score-detail.vue` + +```javascript +{ + // 选手信息 + athleteInfo: { + name: "张三", + idCard: "123456789000000000", + team: "少林寺武术大学院", + number: "123-4567898275" + }, + + // 评分控制 + currentScore: 8.907, // 当前分数 + minScore: 5.0, // 最低分 + maxScore: 10.0, // 最高分 + + // 扣分项(8项) + deductions: [ + { text: "扣分项描述", checked: false }, + { text: "扣分项描述", checked: false }, + // ... 共8项 + ], + + // 备注 + note: "" +} +``` + +### 4.2 后端数据能力 + +#### Entity支持度 + +**MartialAthlete**: +```java +✅ playerName, idCard, teamName, playerNo // 选手信息完整 +``` + +**MartialProject**: +```java +✅ minScore (可配置) // 但当前Entity未找到 +✅ maxScore (可配置) // 但当前Entity未找到 +❌ scoreStep // 当前Entity未找到 +``` + +**MartialDeductionItem** (扣分项): +```java +✅ itemName → text +✅ deductionPoint → 扣分值 +✅ category → 分类 +✅ applicableProjects → 适用项目(JSON) +✅ sortOrder → 排序 +``` + +**MartialScore** (提交数据): +```java +✅ athleteId → 选手ID +✅ judgeId → 评委ID +✅ projectId → 项目ID +✅ venueId → 场地ID +✅ score → 评分 +✅ deductionItems → 扣分项(JSON) +✅ note → 备注 +✅ scoreTime → 评分时间 +``` + +### 4.3 数据可行性评估 + +| 前端需求 | 后端能力 | 评估结果 | 实现方案 | +|---------|---------|---------|---------| +| 选手信息 | MartialAthlete.* | ✅ 完全支持 | 直接查询 | +| 默认评分 | MartialAthlete.defaultScore | ⚠️ 需要扩展 | Entity无此字段,建议添加或在Project中配置 | +| 最低分/最高分 | - | ⚠️ 需要扩展 | 可硬编码 5.0-10.0 或在 MartialProject 中添加 | +| 扣分项列表 | MartialDeductionItem | ✅ 完全支持 | 根据 projectId 查询 | +| 扣分项文本 | itemName | ✅ 完全支持 | 直接使用 | +| 评分提交 | MartialScore.submit | ✅ 完全支持 | 已有提交接口 | +| 扣分项JSON | deductionItems | ✅ 完全支持 | JSON格式存储 | +| 备注 | note | ✅ 完全支持 | 已有字段 | + +### 4.4 需要补充的数据 + +⚠️ **评分范围配置** + +**方案1**: 在 MartialProject 中添加字段 +```java +// MartialProject.java +private BigDecimal minScore = new BigDecimal("5.000"); +private BigDecimal maxScore = new BigDecimal("10.000"); +private BigDecimal scoreStep = new BigDecimal("0.001"); +``` + +**方案2**: 系统配置表 +```java +// 全局配置 +system.score.min = 5.000 +system.score.max = 10.000 +system.score.step = 0.001 +``` + +**方案3**: 前端硬编码(推荐) +```javascript +// 武术评分标准固定为 5.0-10.0 +minScore: 5.0 +maxScore: 10.0 +``` + +**结论**: ✅ **数据基本支持,评分范围可通过配置或硬编码解决** + +--- + +## 五、多场地列表页数据分析(裁判长) + +### 5.1 前端Mock数据需求 + +**页面**: `pages/score-list-multi/score-list-multi.vue` + +```javascript +{ + matchInfo: { + title: "2025年全国武术散打锦标赛...", + time: "2025年6月25日 9:00" + }, + + // 场地列表(5个) + venues: [ + { id: 1, name: "第一场地" }, + { id: 2, name: "第二场地" }, + { id: 3, name: "第三场地" }, + { id: 4, name: "第四场地" }, + { id: 5, name: "第五场地" } + ], + + // 项目列表(8个) + projects: [ + "女子组长拳", + "男子组陈氏太极拳", + "女子组双剑(含长穗双剑)", + "男子组杨氏太极拳", + "女子组刀术", + "男子组棍术", + "女子组枪术", + "男子组剑术" + ], + + currentVenue: 1, // 当前场地 + currentProject: 0, // 当前项目索引 + + scoreStats: { + scored: 2, // 已评分 + total: 30 // 总数 + }, + + // 选手列表(裁判长视角) + players: [ + { + name: "张三", + idCard: "123456789000000000", + team: "少林寺武术大学院", + number: "123-4567898275", + totalScore: 8.907, // 总分 + canModify: true // 是否可修改(总分出来后才能修改) + } + ] +} +``` + +### 5.2 后端数据能力 + +#### Entity支持度 + +**MartialVenue** (场地): +```java +✅ id → venues[].id +✅ venueName → venues[].name +✅ competitionId → 关联比赛 +✅ status → 场地状态 +``` + +**MartialProject** (项目): +```java +✅ id → 项目ID +✅ projectName → projects[] +✅ competitionId → 关联比赛 +✅ sortOrder → 排序 +``` + +**MartialAthlete** (选手): +```java +✅ playerName, idCard, teamName, playerNo // 基本信息 +✅ totalScore → totalScore +🔄 canModify → 需要计算(判断是否所有评委已评分) +``` + +**MartialScore** (评分记录): +```java +// 用于判断是否可修改 +✅ athleteId, judgeId, score +``` + +### 5.3 数据可行性评估 + +| 前端需求 | 后端能力 | 评估结果 | 实现方案 | +|---------|---------|---------|---------| +| 比赛信息 | MartialCompetition | ✅ 完全支持 | 直接查询 | +| 场地列表 | MartialVenue | ✅ 完全支持 | `SELECT * FROM martial_venue WHERE competitionId=?` | +| 项目列表 | MartialProject | ✅ 完全支持 | `SELECT * FROM martial_project WHERE competitionId=? ORDER BY sortOrder` | +| 当前场地/项目 | - | ✅ 完全支持 | 前端状态管理 | +| 选手基本信息 | MartialAthlete | ✅ 完全支持 | 直接查询 | +| 总分 | totalScore | ✅ 完全支持 | 已有字段 | +| 是否可修改 | - | 🔄 需要计算 | 判断逻辑:`judgeCount >= totalJudges AND totalScore IS NOT NULL` | +| 已评分统计 | - | 🔄 需要计算 | `COUNT(DISTINCT athleteId) WHERE totalScore IS NOT NULL` | + +### 5.4 需要补充的接口 + +⚠️ **需要新增裁判长选手列表接口** + +```java +@GetMapping("/api/mini/athletes/admin") +public R> getAthletesForAdmin( + @RequestParam Long competitionId, + @RequestParam Long venueId, + @RequestParam Long projectId +) { + // 1. 查询选手列表 + // 2. 统计每个选手的评分数量 + // 3. 判断是否可修改(所有评委已评分) + // 4. 组装 VO +} + +// VO 数据结构 +class AthleteAdminVO { + Long athleteId; + String name; + String idCard; + String team; + String number; + BigDecimal totalScore; + Integer judgeCount; // 已评分评委数 + Integer totalJudges; // 总评委数 + Boolean canModify; // 是否可修改 +} +``` + +**SQL查询示例**: +```sql +SELECT + a.id AS athleteId, + a.player_name AS name, + a.id_card AS idCard, + a.team_name AS team, + a.player_no AS number, + a.total_score AS totalScore, + COUNT(s.id) AS judgeCount, + (SELECT COUNT(*) FROM martial_judge WHERE venueId = #{venueId}) AS totalJudges, + CASE + WHEN COUNT(s.id) >= (SELECT COUNT(*) FROM martial_judge WHERE venueId = #{venueId}) + AND a.total_score IS NOT NULL + THEN 1 + ELSE 0 + END AS canModify +FROM martial_athlete a +LEFT JOIN martial_score s ON a.id = s.athlete_id +WHERE a.venue_id = #{venueId} + AND a.project_id = #{projectId} +GROUP BY a.id +ORDER BY a.order_num ASC +``` + +**结论**: ✅ **数据完全支持,需要新增1个查询接口** + +--- + +## 六、修改评分页数据分析(裁判长) + +### 6.1 前端Mock数据需求 + +**页面**: `pages/modify-score/modify-score.vue` + +```javascript +{ + // 选手信息 + athleteInfo: { + name: "张三", + idCard: "123456789000000000", + team: "少林寺武术大学院", + number: "123-4567898275", + totalScore: 8.907 // 原始总分 + }, + + // 评委评分明细(6位评委) + judgeScores: [ + { name: "欧阳丽娜", score: 8.907 }, + { name: "张三", score: 8.901 }, + { name: "裁判姓名", score: 8.902 }, + { name: "裁判姓名", score: 8.907 }, + { name: "裁判姓名", score: 8.905 }, + { name: "裁判姓名", score: 8.904 } + ], + + // 修改控制 + currentScore: 8.907, // 修改后的分数 + note: "", // 修改原因 + minScore: 5.0, + maxScore: 10.0 +} +``` + +### 6.2 后端数据能力 + +#### Entity支持度 + +**MartialAthlete**: +```java +✅ playerName, idCard, teamName, playerNo, totalScore +``` + +**MartialScore** (评委评分): +```java +✅ judgeId → 评委ID +✅ judgeName → 评委姓名(已有字段!) +✅ athleteId → 关联选手 +✅ score → 评分值 +✅ scoreTime → 评分时间 +✅ deductionItems → 扣分项(可选展示) +✅ note → 评委备注 +``` + +**MartialScore** (修改记录): +```java +✅ originalScore → 原始分数(已有字段!) +✅ modifyReason → 修改原因(已有字段!) +✅ modifyTime → 修改时间(已有字段!) +``` + +### 6.3 数据可行性评估 + +| 前端需求 | 后端能力 | 评估结果 | 实现方案 | +|---------|---------|---------|---------| +| 选手信息 | MartialAthlete | ✅ 完全支持 | 直接查询 | +| 原始总分 | totalScore | ✅ 完全支持 | 已有字段 | +| 评委评分列表 | MartialScore | ✅ 完全支持 | `SELECT * FROM martial_score WHERE athleteId=?` | +| 评委姓名 | judgeName | ✅ 完全支持 | **已有字段**(重要发现!) | +| 评委分数 | score | ✅ 完全支持 | 已有字段 | +| 修改后分数 | - | ✅ 完全支持 | 前端控制,提交时更新 totalScore | +| 修改原因 | modifyReason | ✅ 完全支持 | **已有字段** | +| 修改时间 | modifyTime | ✅ 完全支持 | **已有字段** | +| 原始分数 | originalScore | ✅ 完全支持 | **已有字段**(用于审计) | + +### 6.4 需要补充的接口 + +⚠️ **需要新增评分详情查询和修改接口** + +```java +// 1. 查询评分详情 +@GetMapping("/api/mini/score/detail/{athleteId}") +public R getScoreDetail(@PathVariable Long athleteId) { + // 1. 查询选手信息 + // 2. 查询所有评委评分 + // 3. 查询修改记录(如果有) + // 4. 组装 VO +} + +// 2. 修改评分 +@PutMapping("/api/mini/score/modify") +public R modifyScore(@RequestBody ScoreModifyDTO dto) { + // dto: { athleteId, modifierId, modifiedScore, note } + // 1. 验证权限(只有裁判长) + // 2. 保存 originalScore(如果是第一次修改) + // 3. 更新 totalScore + // 4. 记录 modifyReason 和 modifyTime + // 5. 可选:记录到修改历史表 +} +``` + +**SQL查询示例**: +```sql +-- 查询评委评分明细 +SELECT + s.judge_id AS judgeId, + s.judge_name AS judgeName, + s.score, + s.score_time AS scoreTime, + s.deduction_items AS deductionItems, + s.note +FROM martial_score s +WHERE s.athlete_id = #{athleteId} +ORDER BY s.score_time ASC +``` + +**结论**: ✅ **数据完全支持!后端已有完善的修改审计字段** + +--- + +## 七、综合数据可行性总结 + +### 7.1 按页面汇总 + +| 页面 | 数据支持度 | 需要新增接口 | 需要扩展字段 | 优先级 | +|------|-----------|------------|------------|--------| +| **登录页** | 95% | ✅ 1个登录接口 | ❌ 无 | 🔥 高 | +| **评分列表页** | 90% | ✅ 1个查询接口 | ❌ 无 | 🔥 高 | +| **评分详情页** | 95% | ❌ 无(复用submit) | ⚠️ 评分范围配置 | 🔥 高 | +| **多场地列表页** | 90% | ✅ 1个查询接口 | ❌ 无 | ⚠️ 中 | +| **修改评分页** | 100% | ✅ 2个接口 | ❌ 无 | ⚠️ 中 | + +### 7.2 后端数据能力评分 + +``` +✅ 完全支持: 75% +🔄 需要计算: 20% +⚠️ 需要扩展: 5% +❌ 无法支持: 0% + +综合评分: 95分 / 100分 +``` + +### 7.3 关键发现 + +#### ✅ 优势 + +1. **Entity设计完善**: 后端Entity字段非常完整,尤其是审计字段 + - `originalScore`: 修改前的原始分数 + - `modifyReason`: 修改原因 + - `modifyTime`: 修改时间 + - `judgeName`: 评委姓名(冗余字段,提升查询性能) + - `ipAddress`: IP地址(安全审计) + +2. **数据关联合理**: 所有Entity通过 competitionId, venueId, projectId, athleteId 关联 + +3. **JSON存储灵活**: 扣分项、附件等使用JSON存储,支持动态数据 + +4. **精度支持**: BigDecimal 类型支持 0.001 精度 + +#### ⚠️ 需要补充 + +1. **小程序专用接口**: 需要新增5个接口 + - `POST /api/mini/login` - 登录验证 + - `GET /api/mini/athletes` - 普通评委选手列表 + - `GET /api/mini/athletes/admin` - 裁判长选手列表 + - `GET /api/mini/score/detail/{athleteId}` - 评分详情 + - `PUT /api/mini/score/modify` - 修改评分 + +2. **计算字段**: 需要在接口层实现 + - `myScore`: 通过 LEFT JOIN 查询 + - `scored`: 判断是否存在评分记录 + - `judgeCount`: 统计已评分评委数 + - `canModify`: 判断是否所有评委已评分 + +3. **评分范围**: 建议方案 + - **方案1(推荐)**: 前端硬编码 5.0-10.0(武术标准固定) + - **方案2**: 在 MartialProject 中添加 minScore/maxScore 字段 + - **方案3**: 系统配置表 + +--- + +## 八、数据对接方案 + +### 8.1 核心对接策略 + +``` +保持前端Mock数据格式不变 + ↓ +后端提供完全匹配的VO + ↓ +前端替换数据源(Mock → API) + ↓ +UI代码零修改 +``` + +### 8.2 需要新增的Controller + +#### 方案A: 创建小程序专用Controller(推荐) + +```java +@RestController +@RequestMapping("/api/mini") +public class MartialMiniController { + + // 登录 + @PostMapping("/login") + public R login(@RequestBody LoginDTO dto) { } + + // 普通评委:选手列表 + @GetMapping("/athletes") + public R> getMyAthletes(...) { } + + // 裁判长:选手列表 + @GetMapping("/athletes/admin") + public R> getAthletesForAdmin(...) { } + + // 评分详情 + @GetMapping("/score/detail/{athleteId}") + public R getScoreDetail(...) { } + + // 修改评分 + @PutMapping("/score/modify") + public R modifyScore(@RequestBody ScoreModifyDTO dto) { } + + // 提交评分(复用现有的 MartialScoreController.submit) + // 或者在这里做一层转发 +} +``` + +#### 方案B: 扩展现有Controller + +```java +// 在现有 Controller 中添加小程序专用方法 +// 优点:复用现有逻辑 +// 缺点:代码耦合,不好维护 +``` + +**推荐**: ✅ **方案A - 创建独立的 MartialMiniController** + +### 8.3 VO类设计 + +```java +// 登录响应 +@Data +class LoginVO { + String token; + String userRole; // "pub" or "admin" + Long matchId; + String matchName; + String matchTime; + Long judgeId; + String judgeName; + Long venueId; // 普通评委有 + String venueName; + List projects; // 分配的项目 +} + +// 选手列表(普通评委) +@Data +class AthleteWithScoreVO { + Long athleteId; + String name; + String idCard; + String team; + String number; + BigDecimal myScore; // 我的评分 + BigDecimal totalScore; // 总分 + Boolean scored; // 是否已评分 + LocalDateTime scoreTime; +} + +// 选手列表(裁判长) +@Data +class AthleteAdminVO { + Long athleteId; + String name; + String idCard; + String team; + String number; + BigDecimal totalScore; + Integer judgeCount; + Integer totalJudges; + Boolean canModify; +} + +// 评分详情 +@Data +class AthleteScoreDetailVO { + Long athleteId; + String name; + String idCard; + String team; + String number; + BigDecimal totalScore; + List judgeScores; + ScoreModificationVO modification; // 修改记录(如果有) +} + +@Data +class JudgeScoreVO { + Long judgeId; + String judgeName; + BigDecimal score; + LocalDateTime scoreTime; + String note; +} +``` + +### 8.4 Service层实现要点 + +```java +@Service +public class MartialMiniService { + + @Autowired + private MartialAthleteMapper athleteMapper; + + @Autowired + private MartialScoreMapper scoreMapper; + + /** + * 获取带评分状态的选手列表 + */ + public List getAthletesWithScore( + Long judgeId, Long venueId, Long projectId + ) { + // 方案1: 自定义SQL(推荐) + return athleteMapper.selectAthletesWithScore(judgeId, venueId, projectId); + + // 方案2: 分步查询(性能较差) + // List athletes = athleteMapper.selectList(...); + // for (Athlete athlete : athletes) { + // Score score = scoreMapper.selectOne(athleteId, judgeId); + // ... + // } + } +} +``` + +**Mapper XML**: +```xml + +``` + +--- + +## 九、前端对接改造方案 + +### 9.1 保护Mock版本策略 + +#### 方案A: Git分支隔离(推荐) + +```bash +# 当前分支(保留Mock版本) +main (或 mock-version) + +# 创建API对接分支 +git checkout -b feature/api-integration + +# 后续可以随时切换 +git checkout main # 回到Mock版本 +git checkout feature/api-integration # 使用API版本 +``` + +#### 方案B: 配置开关 + +```javascript +// config/env.config.js +export default { + // 数据源模式: 'mock' | 'api' + dataMode: 'mock', + + // API基础路径 + baseURL: process.env.NODE_ENV === 'production' + ? 'https://api.yourdomain.com' + : '/api' +} + +// 使用示例 +import config from '@/config/env.config.js' + +if (config.dataMode === 'mock') { + // 使用Mock数据 + this.players = getMockPlayers() +} else { + // 调用API + const res = await getAthletes(...) + this.players = res.data +} +``` + +#### 方案C: 文件备份 + +``` +pages/ +├── login/ +│ ├── login.vue (API版本) +│ └── login.vue.mock (Mock备份) +├── score-list/ +│ ├── score-list.vue (API版本) +│ └── score-list.vue.mock (Mock备份) +``` + +**推荐**: ✅ **方案A(Git分支)+ 方案B(配置开关)** + +### 9.2 最小改动对接方案 + +```javascript +// 1. 创建 utils/request.js(从 martial-mini 复制) +import config from '@/config/api.config.js' + +const request = (options) => { + const token = uni.getStorageSync('token') || '' + + return new Promise((resolve, reject) => { + uni.request({ + url: config.baseURL + options.url, + method: options.method || 'GET', + data: options.data || {}, + header: { + 'Content-Type': 'application/json', + 'Blade-Auth': token ? `Bearer ${token}` : '', + ...options.header + }, + success: (res) => { + if (res.data.code === 200) { + resolve(res.data) + } else { + uni.showToast({ + title: res.data.msg || '请求失败', + icon: 'none' + }) + reject(res.data) + } + }, + fail: (err) => { + uni.showToast({ + title: '网络错误', + icon: 'none' + }) + reject(err) + } + }) + }) +} + +export default request + +// 2. 创建 api/auth.js +import request from '@/utils/request.js' + +export const login = (data) => { + return request({ + url: '/mini/login', + method: 'POST', + data + }) +} + +// 3. 创建 api/athlete.js +export const getMyAthletes = (params) => { + return request({ + url: '/mini/athletes', + method: 'GET', + data: params + }) +} + +// 4. 页面中使用 +// pages/login/login.vue +import { login } from '@/api/auth.js' + +async handleSubmit() { + try { + const res = await login({ + matchCode: this.matchCode, + inviteCode: this.inviteCode + }) + + // 保存Token + uni.setStorageSync('token', res.data.token) + + // 保存用户信息 + getApp().globalData = { + userRole: res.data.userRole, + matchCode: this.matchCode, + judgeId: res.data.judgeId, + judgeName: res.data.judgeName, + venueId: res.data.venueId, + projectId: res.data.projects[0] // 第一个项目 + } + + // 跳转 + if (res.data.userRole === 'admin') { + uni.navigateTo({ url: '/pages/score-list-multi/score-list-multi' }) + } else { + uni.navigateTo({ url: '/pages/score-list/score-list' }) + } + } catch (err) { + console.error(err) + } +} +``` + +### 9.3 数据格式适配 + +**后端响应格式**: +```json +{ + "code": 200, + "success": true, + "msg": "操作成功", + "data": { } +} +``` + +**前端期望格式**: +```json +{ + "code": 200, + "message": "成功", + "data": { } +} +``` + +**适配方案**: +```javascript +// utils/request.js +success: (res) => { + // 统一响应格式 + const response = { + code: res.data.code, + message: res.data.msg || res.data.message, + data: res.data.data, + success: res.data.success + } + + if (response.code === 200) { + resolve(response) + } else { + reject(response) + } +} +``` + +--- + +## 十、实施计划 + +### 10.1 开发优先级 + +#### 第一阶段:核心功能(1周) + +1. **后端开发** (3天) + - [ ] 创建 `MartialMiniController` + - [ ] 实现登录接口 `POST /api/mini/login` + - [ ] 实现选手列表接口 `GET /api/mini/athletes` + - [ ] 编写自定义SQL(LEFT JOIN) + - [ ] 创建VO类 + +2. **前端开发** (2天) + - [ ] 创建 `utils/request.js` + - [ ] 创建 `api/auth.js`, `api/athlete.js` + - [ ] 登录页API对接 + - [ ] 评分列表页API对接 + - [ ] 评分详情页API对接(提交评分) + +3. **联调测试** (2天) + - [ ] 登录流程测试 + - [ ] 评分流程测试 + - [ ] Token过期处理测试 + +#### 第二阶段:裁判长功能(3天) + +1. **后端开发** (2天) + - [ ] 实现裁判长选手列表 `GET /api/mini/athletes/admin` + - [ ] 实现评分详情 `GET /api/mini/score/detail/{athleteId}` + - [ ] 实现修改评分 `PUT /api/mini/score/modify` + +2. **前端开发** (1天) + - [ ] 多场地列表页API对接 + - [ ] 修改评分页API对接 + +#### 第三阶段:优化和完善(2天) + +1. **功能完善** + - [ ] 错误处理优化 + - [ ] Loading状态 + - [ ] 数据缓存 + - [ ] 离线支持(可选) + +2. **测试** + - [ ] 功能测试 + - [ ] 性能测试 + - [ ] 兼容性测试 + +### 10.2 风险评估 + +| 风险 | 等级 | 应对措施 | +|------|------|---------| +| SQL性能问题 | ⚠️ 中 | 添加数据库索引,使用EXPLAIN分析 | +| 数据精度丢失 | 🔥 高 | 前后端统一使用字符串传输分数 | +| Token过期 | ⚠️ 中 | 实现Token刷新机制 | +| 网络异常 | ⚠️ 中 | 实现重试机制和离线缓存 | +| UI不匹配 | 低 | VO严格按照Mock数据结构设计 | + +### 10.3 数据库优化建议 + +```sql +-- 添加索引(提升查询性能) +CREATE INDEX idx_athlete_venue_project +ON martial_athlete(venue_id, project_id, competition_status); + +CREATE INDEX idx_score_athlete_judge +ON martial_score(athlete_id, judge_id); + +CREATE INDEX idx_score_judge +ON martial_score(judge_id, score_time); + +CREATE INDEX idx_invite_code +ON martial_judge_invite(invite_code, competition_id, is_used); +``` + +--- + +## 十一、最终结论 + +### ✅ 数据可行性结论 + +**综合评估**: ⭐⭐⭐⭐⭐ **95分 / 100分** + +1. **后端数据能力**: ✅ **完全支持** + - Entity设计完善,包含所有必要字段 + - 审计字段齐全(originalScore, modifyReason, modifyTime) + - 支持 BigDecimal 精度 + - JSON存储灵活 + +2. **数据对接难度**: ✅ **较低** + - 需要新增 5 个接口 + - 需要编写 3-4 个自定义SQL + - 不需要修改现有数据库结构 + - VO可以完全匹配前端Mock数据 + +3. **UI保护**: ✅ **零影响** + - 通过VO适配,前端UI代码无需修改 + - Git分支隔离,Mock版本永久保留 + - 配置开关可随时切换Mock/API模式 + +### 📋 需要开发的内容 + +#### 后端(预计3-5天) + +- [ ] 创建 `MartialMiniController`(1个类) +- [ ] 创建 5 个接口方法 +- [ ] 创建 5-6 个VO类 +- [ ] 编写 3-4 个自定义SQL +- [ ] 添加数据库索引 + +#### 前端(预计2-3天) + +- [ ] 创建 `utils/request.js`(1个文件) +- [ ] 创建 `api/` 目录(5个文件) +- [ ] 修改 5 个页面的数据获取逻辑 +- [ ] 添加Loading和错误处理 + +### 🎯 推荐方案 + +✅ **采用方案2(完整实现)+ Git分支隔离** + +1. **保护Mock版本**: 当前代码提交到 `mock-version` 分支 +2. **创建API分支**: 新建 `feature/api-integration` 分支进行开发 +3. **后端开发**: 创建独立的 `MartialMiniController` +4. **前端改造**: 最小化改动,仅替换数据源 +5. **分阶段上线**: 先上线核心功能,再完善裁判长功能 + +### 📊 对接后的优势 + +1. **真实数据**: 所有数据来自数据库,支持多用户协作 +2. **数据持久化**: 评分数据永久保存 +3. **审计能力**: 完整的修改历史记录 +4. **性能优化**: 数据库索引 + 查询优化 +5. **扩展性**: 可轻松添加新功能(统计、导出等) + +--- + +## 附录:快速验证清单 + +### 后端验证 + +- [ ] 数据库已初始化,表结构完整 +- [ ] 测试数据已准备(比赛、场地、项目、选手、裁判) +- [ ] 邀请码已生成 +- [ ] 后端服务已启动 +- [ ] Swagger文档可访问 + +### 前端验证 + +- [ ] request.js 配置正确 +- [ ] API地址配置正确 +- [ ] Token存储和传递正确 +- [ ] 登录流程通过 +- [ ] 数据格式匹配 + +### 联调验证 + +- [ ] 登录成功,返回正确的角色和权限 +- [ ] 普通评委能看到自己的选手列表 +- [ ] 能够提交评分 +- [ ] 裁判长能看到所有场地和项目 +- [ ] 裁判长能修改评分 +- [ ] 分数精度保持0.001 + +--- + +**报告生成时间**: 2025-12-11 +**分析版本**: v1.0 +**后端项目**: martial-master (BladeX 4.0.1) +**前端项目**: martial-admin-mini (UniApp) +**数据可行性**: ✅ 95% 支持 diff --git a/doc/数据结构设计.md b/doc/数据结构设计.md new file mode 100644 index 0000000..f1c6721 --- /dev/null +++ b/doc/数据结构设计.md @@ -0,0 +1,1136 @@ +# 数据结构设计文档 + +## 文档说明 + +本文档定义了武术评分系统中所有核心数据结构和数据模型。这些数据结构用于前端状态管理、API请求响应、以及数据库设计参考。 + +--- + +## 一、用户角色相关 + +### 1.1 用户信息 (UserInfo) + +```typescript +interface UserInfo { + userId: string // 用户ID + username: string // 用户名 + realName: string // 真实姓名 + role: 'pub' | 'admin' // 角色: pub-普通评委, admin-裁判长 + phone?: string // 手机号 + email?: string // 邮箱 + avatar?: string // 头像URL + status: 'active' | 'inactive' // 状态 + createdAt: string // 创建时间 (ISO 8601) + updatedAt: string // 更新时间 (ISO 8601) +} +``` + +**示例**: +```json +{ + "userId": "user_001", + "username": "judge001", + "realName": "欧阳丽娜", + "role": "pub", + "phone": "13800138000", + "email": "judge001@example.com", + "avatar": "https://example.com/avatar.jpg", + "status": "active", + "createdAt": "2025-01-01T00:00:00Z", + "updatedAt": "2025-06-25T08:00:00Z" +} +``` + +### 1.2 登录凭证 (LoginCredentials) + +```typescript +interface LoginCredentials { + matchCode: string // 比赛编码 + inviteCode: string // 邀请码 +} +``` + +### 1.3 认证响应 (AuthResponse) + +```typescript +interface AuthResponse { + token: string // JWT Token + userRole: 'pub' | 'admin' // 用户角色 + matchId: string // 比赛ID + matchName: string // 比赛名称 + matchTime: string // 比赛时间 + judgeId: string // 评委ID + judgeName: string // 评委姓名 + venueId?: string // 场地ID (普通评委) + venueName?: string // 场地名称 (普通评委) +} +``` + +--- + +## 二、比赛相关 + +### 2.1 比赛信息 (Match) + +```typescript +interface Match { + matchId: string // 比赛ID + matchName: string // 比赛名称 + matchTime: string // 比赛开始时间 (ISO 8601) + matchEndTime: string // 比赛结束时间 (ISO 8601) + location: string // 比赛地点 + status: MatchStatus // 比赛状态 + description?: string // 比赛说明 + venueCount: number // 场地数量 + projectCount: number // 项目数量 + athleteCount: number // 选手总数 + judgeCount: number // 评委总数 + createdAt: string // 创建时间 + updatedAt: string // 更新时间 +} + +type MatchStatus = 'upcoming' | 'ongoing' | 'finished' +``` + +**示例**: +```json +{ + "matchId": "match_001", + "matchName": "2025年全国武术散打锦标赛暨第十七届世界武术锦标赛选拔赛", + "matchTime": "2025-06-25T09:00:00Z", + "matchEndTime": "2025-06-25T18:00:00Z", + "location": "北京体育馆", + "status": "ongoing", + "description": "全国性武术比赛", + "venueCount": 5, + "projectCount": 8, + "athleteCount": 150, + "judgeCount": 30, + "createdAt": "2025-01-01T00:00:00Z", + "updatedAt": "2025-06-25T08:00:00Z" +} +``` + +--- + +## 三、场地相关 + +### 3.1 场地信息 (Venue) + +```typescript +interface Venue { + venueId: string // 场地ID + venueName: string // 场地名称 + matchId: string // 所属比赛ID + order: number // 排序顺序 + athleteCount: number // 选手数量 + scoredCount: number // 已评分数量 + status: VenueStatus // 场地状态 + description?: string // 场地描述 + createdAt: string // 创建时间 +} + +type VenueStatus = 'active' | 'completed' | 'paused' +``` + +**示例**: +```json +{ + "venueId": "venue_001", + "venueName": "第一场地", + "matchId": "match_001", + "order": 1, + "athleteCount": 30, + "scoredCount": 25, + "status": "active", + "description": "主场地", + "createdAt": "2025-01-01T00:00:00Z" +} +``` + +### 3.2 场地列表 (VenueList) + +```typescript +type VenueList = Venue[] +``` + +--- + +## 四、项目相关 + +### 4.1 项目信息 (Project) + +```typescript +interface Project { + projectId: string // 项目ID + projectName: string // 项目名称 + matchId: string // 所属比赛ID + category: string // 项目类别 (如: 套路、太极拳) + order: number // 排序顺序 + athleteCount: number // 参赛选手数 + minScore: number // 最低分 + maxScore: number // 最高分 + scoreStep: number // 评分步进值 + status: ProjectStatus // 项目状态 + description?: string // 项目说明 + rules?: string // 评分规则 + createdAt: string // 创建时间 +} + +type ProjectStatus = 'upcoming' | 'ongoing' | 'completed' +``` + +**示例**: +```json +{ + "projectId": "project_001", + "projectName": "女子组长拳", + "matchId": "match_001", + "category": "套路", + "order": 1, + "athleteCount": 15, + "minScore": 5.0, + "maxScore": 10.0, + "scoreStep": 0.001, + "status": "ongoing", + "description": "女子组长拳套路比赛", + "rules": "按照国家标准执行", + "createdAt": "2025-01-01T00:00:00Z" +} +``` + +### 4.2 项目列表 + +```typescript +type ProjectList = Project[] +``` + +**前端Mock数据**: +```javascript +const projectList = [ + '女子组长拳', + '男子组陈氏太极拳', + '女子组双剑(含长穗双剑)', + '男子组杨氏太极拳', + '女子组刀术', + '男子组棍术', + '女子组枪术', + '男子组剑术' +] +``` + +--- + +## 五、选手相关 + +### 5.1 选手信息 (Athlete) + +```typescript +interface Athlete { + athleteId: string // 选手ID + name: string // 姓名 + gender: 'male' | 'female' // 性别 + age?: number // 年龄 + idCard: string // 身份证号 + team: string // 队伍/单位 + number: string // 比赛编号 + order: number // 出场顺序 + projectId: string // 参赛项目ID + projectName: string // 参赛项目名称 + venueId: string // 比赛场地ID + venueName: string // 比赛场地名称 + matchId: string // 比赛ID + photo?: string // 照片URL + defaultScore?: number // 默认起评分 + status: AthleteStatus // 选手状态 + createdAt: string // 创建时间 +} + +type AthleteStatus = 'waiting' | 'performing' | 'finished' +``` + +**示例**: +```json +{ + "athleteId": "athlete_001", + "name": "张三", + "gender": "male", + "age": 25, + "idCard": "123456789000000000", + "team": "少林寺武术大学院", + "number": "123-4567898275", + "order": 1, + "projectId": "project_001", + "projectName": "女子组长拳", + "venueId": "venue_001", + "venueName": "第一场地", + "matchId": "match_001", + "photo": "https://example.com/athlete.jpg", + "defaultScore": 8.907, + "status": "finished", + "createdAt": "2025-01-01T00:00:00Z" +} +``` + +### 5.2 选手列表项(普通评委视图) + +```typescript +interface AthleteListItem { + athleteId: string // 选手ID + name: string // 姓名 + gender: 'male' | 'female' // 性别 + idCard: string // 身份证号 + team: string // 队伍 + number: string // 比赛编号 + order: number // 出场顺序 + myScore?: number // 我的评分 + totalScore?: number // 总分 + scored: boolean // 是否已评分 + scoreTime?: string // 评分时间 + status: AthleteStatus // 状态 +} +``` + +**示例**: +```json +{ + "athleteId": "athlete_001", + "name": "张三", + "gender": "male", + "idCard": "123456789000000000", + "team": "少林寺武术大学院", + "number": "123-4567898275", + "order": 1, + "myScore": 8.906, + "totalScore": 8.907, + "scored": true, + "scoreTime": "2025-06-25T09:15:00Z", + "status": "finished" +} +``` + +### 5.3 选手列表项(裁判长视图) + +```typescript +interface AthleteListItemAdmin { + athleteId: string // 选手ID + name: string // 姓名 + gender: 'male' | 'female' // 性别 + idCard: string // 身份证号 + team: string // 队伍 + number: string // 比赛编号 + order: number // 出场顺序 + venueId: string // 场地ID + venueName: string // 场地名称 + projectId: string // 项目ID + projectName: string // 项目名称 + totalScore?: number // 总分 + judgeCount: number // 已评分评委数 + totalJudges: number // 总评委数 + canModify: boolean // 是否可修改 + status: AthleteStatus // 状态 +} +``` + +**示例**: +```json +{ + "athleteId": "athlete_001", + "name": "张三", + "gender": "male", + "idCard": "123456789000000000", + "team": "少林寺武术大学院", + "number": "123-4567898275", + "order": 1, + "venueId": "venue_001", + "venueName": "第一场地", + "projectId": "project_001", + "projectName": "女子组长拳", + "totalScore": 8.907, + "judgeCount": 6, + "totalJudges": 6, + "canModify": true, + "status": "finished" +} +``` + +--- + +## 六、评分相关 + +### 6.1 评分提交数据 (ScoreSubmit) + +```typescript +interface ScoreSubmit { + matchId: string // 比赛ID + athleteId: string // 选手ID + judgeId: string // 评委ID + projectId: string // 项目ID + venueId: string // 场地ID + score: number // 评分 (5.000 - 10.000) + deductions: string[] // 扣分项ID列表 + note?: string // 备注 +} +``` + +**示例**: +```json +{ + "matchId": "match_001", + "athleteId": "athlete_001", + "judgeId": "judge_001", + "projectId": "project_001", + "venueId": "venue_001", + "score": 8.907, + "deductions": ["deduction_001", "deduction_003"], + "note": "整体表现良好" +} +``` + +### 6.2 评分记录 (Score) + +```typescript +interface Score { + scoreId: string // 评分ID + matchId: string // 比赛ID + athleteId: string // 选手ID + judgeId: string // 评委ID + projectId: string // 项目ID + venueId: string // 场地ID + score: number // 评分 + deductions: Deduction[] // 扣分项详情 + note?: string // 备注 + scoreTime: string // 评分时间 (ISO 8601) + createdAt: string // 创建时间 + updatedAt: string // 更新时间 +} +``` + +**示例**: +```json +{ + "scoreId": "score_001", + "matchId": "match_001", + "athleteId": "athlete_001", + "judgeId": "judge_001", + "projectId": "project_001", + "venueId": "venue_001", + "score": 8.907, + "deductions": [ + { + "deductionId": "deduction_001", + "text": "起势不稳", + "score": -0.1 + } + ], + "note": "整体表现良好", + "scoreTime": "2025-06-25T09:15:00Z", + "createdAt": "2025-06-25T09:15:00Z", + "updatedAt": "2025-06-25T09:15:00Z" +} +``` + +### 6.3 评委评分项(显示用) + +```typescript +interface JudgeScore { + scoreId: string // 评分ID + judgeId: string // 评委ID + judgeName: string // 评委姓名 + score: number // 评分 + deductions: Deduction[] // 扣分项 + note?: string // 备注 + scoreTime: string // 评分时间 +} +``` + +**示例**: +```json +{ + "scoreId": "score_001", + "judgeId": "judge_001", + "judgeName": "欧阳丽娜", + "score": 8.907, + "deductions": [ + { + "deductionId": "deduction_001", + "text": "起势不稳", + "score": -0.1 + } + ], + "note": "整体表现良好", + "scoreTime": "2025-06-25T09:15:00Z" +} +``` + +### 6.4 选手评分详情 (AthleteScoreDetail) + +```typescript +interface AthleteScoreDetail { + athleteId: string // 选手ID + athleteName: string // 选手姓名 + team: string // 队伍 + number: string // 比赛编号 + projectName: string // 项目名称 + totalScore: number // 总分 + judgeCount: number // 已评分评委数 + totalJudges: number // 总评委数 + judgeScores: JudgeScore[] // 各评委评分 + modifications: ScoreModification[] // 修改记录 +} +``` + +**示例**: +```json +{ + "athleteId": "athlete_001", + "athleteName": "张三", + "team": "少林寺武术大学院", + "number": "123-4567898275", + "projectName": "女子组长拳", + "totalScore": 8.907, + "judgeCount": 6, + "totalJudges": 6, + "judgeScores": [ + { + "scoreId": "score_001", + "judgeId": "judge_001", + "judgeName": "欧阳丽娜", + "score": 8.907, + "deductions": [], + "note": "表现良好", + "scoreTime": "2025-06-25T09:15:00Z" + } + ], + "modifications": [] +} +``` + +--- + +## 七、评分修改相关 + +### 7.1 评分修改提交 (ScoreModifySubmit) + +```typescript +interface ScoreModifySubmit { + athleteId: string // 选手ID + modifierId: string // 修改人ID (裁判长) + originalScore: number // 原始分数 + modifiedScore: number // 修改后的分数 + note: string // 修改原因 +} +``` + +**示例**: +```json +{ + "athleteId": "athlete_001", + "modifierId": "admin_001", + "originalScore": 8.907, + "modifiedScore": 8.910, + "note": "技术动作优秀,加分" +} +``` + +### 7.2 评分修改记录 (ScoreModification) + +```typescript +interface ScoreModification { + modifyId: string // 修改记录ID + athleteId: string // 选手ID + modifierId: string // 修改人ID + modifierName: string // 修改人姓名 + originalScore: number // 原始分数 + modifiedScore: number // 修改后的分数 + note: string // 修改原因 + modifyTime: string // 修改时间 (ISO 8601) + createdAt: string // 创建时间 +} +``` + +**示例**: +```json +{ + "modifyId": "modify_001", + "athleteId": "athlete_001", + "modifierId": "admin_001", + "modifierName": "总裁判长", + "originalScore": 8.907, + "modifiedScore": 8.910, + "note": "技术动作优秀,加分", + "modifyTime": "2025-06-25T10:00:00Z", + "createdAt": "2025-06-25T10:00:00Z" +} +``` + +--- + +## 八、扣分项相关 + +### 8.1 扣分项 (Deduction) + +```typescript +interface Deduction { + deductionId: string // 扣分项ID + projectId: string // 所属项目ID + text: string // 扣分项描述 + score: number // 扣分值 (负数) + category?: string // 分类 + order: number // 排序 + createdAt: string // 创建时间 +} +``` + +**示例**: +```json +{ + "deductionId": "deduction_001", + "projectId": "project_001", + "text": "起势不稳", + "score": -0.1, + "category": "基础动作", + "order": 1, + "createdAt": "2025-01-01T00:00:00Z" +} +``` + +### 8.2 扣分项选择(前端用) + +```typescript +interface DeductionItem { + deductionId?: string // 扣分项ID (对接后端后使用) + text: string // 扣分项描述 + checked: boolean // 是否选中 +} +``` + +**前端Mock数据**: +```javascript +const deductions = [ + { text: '扣分项描述', checked: false }, + { text: '扣分项描述', checked: false }, + { text: '扣分项描述', checked: true }, + { text: '扣分项描述', checked: false }, + { text: '扣分项描述', checked: false }, + { text: '扣分项描述', checked: true }, + { text: '扣分项描述', checked: true }, + { text: '扣分项描述', checked: false } +] +``` + +--- + +## 九、统计分析相关 + +### 9.1 比赛统计 (MatchStatistics) + +```typescript +interface MatchStatistics { + matchId: string // 比赛ID + totalAthletes: number // 总选手数 + finishedAthletes: number // 已完成评分选手数 + totalJudges: number // 总评委数 + activeJudges: number // 活跃评委数 + totalScores: number // 总评分数 + averageScore: number // 平均分 + highestScore: number // 最高分 + lowestScore: number // 最低分 + venueStats: VenueStatistics[] // 各场地统计 + projectStats: ProjectStatistics[] // 各项目统计 +} +``` + +### 9.2 场地统计 (VenueStatistics) + +```typescript +interface VenueStatistics { + venueId: string // 场地ID + venueName: string // 场地名称 + athleteCount: number // 选手数 + finishedCount: number // 已完成数 + progress: number // 进度百分比 +} +``` + +**示例**: +```json +{ + "venueId": "venue_001", + "venueName": "第一场地", + "athleteCount": 30, + "finishedCount": 28, + "progress": 93.3 +} +``` + +### 9.3 项目统计 (ProjectStatistics) + +```typescript +interface ProjectStatistics { + projectId: string // 项目ID + projectName: string // 项目名称 + athleteCount: number // 选手数 + finishedCount: number // 已完成数 + averageScore: number // 平均分 + progress: number // 进度百分比 +} +``` + +**示例**: +```json +{ + "projectId": "project_001", + "projectName": "女子组长拳", + "athleteCount": 15, + "finishedCount": 15, + "averageScore": 8.650, + "progress": 100 +} +``` + +### 9.4 评委统计 (JudgeStatistics) + +```typescript +interface JudgeStatistics { + judgeId: string // 评委ID + judgeName: string // 评委姓名 + role: 'pub' | 'admin' // 角色 + venueId?: string // 场地ID + venueName?: string // 场地名称 + totalScores: number // 总评分数 + averageScore: number // 平均给分 + highestScore: number // 最高给分 + lowestScore: number // 最低给分 + lastScoreTime: string // 最后评分时间 +} +``` + +**示例**: +```json +{ + "judgeId": "judge_001", + "judgeName": "欧阳丽娜", + "role": "pub", + "venueId": "venue_001", + "venueName": "第一场地", + "totalScores": 25, + "averageScore": 8.765, + "highestScore": 9.500, + "lowestScore": 7.800, + "lastScoreTime": "2025-06-25T15:30:00Z" +} +``` + +--- + +## 十、前端页面数据结构 + +### 10.1 登录页数据 (LoginPageData) + +```typescript +interface LoginPageData { + matchCode: string // 比赛编码 + inviteCode: string // 邀请码 +} +``` + +### 10.2 评分列表页数据 (ScoreListPageData) + +```typescript +interface ScoreListPageData { + matchInfo: { + title: string // 比赛标题 + time: string // 比赛时间 + } + venue: string // 场地名称 + project: string // 项目名称 + scoreStats: { + scored: number // 已评分数 + total: number // 总数 + } + players: AthleteListItem[] // 选手列表 +} +``` + +### 10.3 评分详情页数据 (ScoreDetailPageData) + +```typescript +interface ScoreDetailPageData { + athleteId: string // 选手ID + athleteInfo: { + name: string // 姓名 + idCard: string // 身份证号 + team: string // 队伍 + number: string // 编号 + } + currentScore: number // 当前评分 + note: string // 备注 + minScore: number // 最低分 + maxScore: number // 最高分 + deductions: DeductionItem[] // 扣分项 +} +``` + +### 10.4 多场地列表页数据 (ScoreListMultiPageData) + +```typescript +interface ScoreListMultiPageData { + currentVenue: number // 当前场地ID + currentProject: number // 当前项目索引 + venues: Venue[] // 场地列表 + projects: string[] // 项目列表 + scoreStats: { + scored: number // 已评分数 + total: number // 总数 + } + players: AthleteListItemAdmin[] // 选手列表 +} +``` + +### 10.5 修改评分页数据 (ModifyScorePageData) + +```typescript +interface ModifyScorePageData { + athleteId: string // 选手ID + athleteInfo: { + name: string // 姓名 + idCard: string // 身份证号 + team: string // 队伍 + number: string // 编号 + } + originalScore: number // 原始总分 + currentScore: number // 修改后的分数 + note: string // 修改备注 + minScore: number // 最低分 + maxScore: number // 最高分 + judgeScores: JudgeScore[] // 评委评分列表 +} +``` + +--- + +## 十一、全局数据结构 + +### 11.1 全局数据 (GlobalData) + +```typescript +interface GlobalData { + userRole: 'pub' | 'admin' // 用户角色 + matchCode: string // 比赛编码 + token?: string // JWT Token + userInfo?: UserInfo // 用户信息 + matchInfo?: Match // 比赛信息 +} +``` + +**使用方式**: +```javascript +// 设置全局数据 +getApp().globalData = { + userRole: 'pub', + matchCode: 'MATCH2025001' +} + +// 获取全局数据 +const globalData = getApp().globalData +const userRole = globalData.userRole +``` + +--- + +## 十二、WebSocket推送数据结构 + +### 12.1 新评分推送 + +```typescript +interface NewScoreMessage { + type: 'new_score' + data: { + athleteId: string + athleteName: string + judgeId: string + judgeName: string + score: number + totalScore: number + judgeCount: number + timestamp: string + } +} +``` + +### 12.2 评分修改推送 + +```typescript +interface ScoreModifiedMessage { + type: 'score_modified' + data: { + athleteId: string + athleteName: string + modifierId: string + modifierName: string + originalScore: number + modifiedScore: number + note: string + timestamp: string + } +} +``` + +### 12.3 选手状态更新推送 + +```typescript +interface AthleteStatusMessage { + type: 'athlete_status' + data: { + athleteId: string + athleteName: string + status: AthleteStatus + timestamp: string + } +} +``` + +--- + +## 十三、分页数据结构 + +### 13.1 分页请求参数 (PageRequest) + +```typescript +interface PageRequest { + page: number // 页码(从1开始) + pageSize: number // 每页数量 + sortBy?: string // 排序字段 + sortOrder?: 'asc' | 'desc' // 排序方向 +} +``` + +### 13.2 分页响应数据 (PageResponse) + +```typescript +interface PageResponse { + total: number // 总记录数 + page: number // 当前页码 + pageSize: number // 每页数量 + totalPages: number // 总页数 + records: T[] // 数据列表 +} +``` + +**示例**: +```json +{ + "total": 150, + "page": 1, + "pageSize": 20, + "totalPages": 8, + "records": [ + { + "athleteId": "athlete_001", + "name": "张三" + } + ] +} +``` + +--- + +## 十四、数据验证规则 + +### 14.1 评分验证 + +```typescript +interface ScoreValidation { + min: 5.000 // 最低分 + max: 10.000 // 最高分 + step: 0.001 // 步进值 + precision: 3 // 小数位数 +} +``` + +**验证函数**: +```javascript +function validateScore(score) { + return score >= 5.000 && + score <= 10.000 && + /^\d+\.\d{3}$/.test(score.toFixed(3)) +} +``` + +### 14.2 必填字段验证 + +```typescript +// 登录验证 +const loginRequired = ['matchCode', 'inviteCode'] + +// 评分提交验证 +const scoreSubmitRequired = [ + 'matchId', + 'athleteId', + 'judgeId', + 'projectId', + 'venueId', + 'score' +] + +// 评分修改验证 +const scoreModifyRequired = [ + 'athleteId', + 'modifierId', + 'originalScore', + 'modifiedScore', + 'note' +] +``` + +--- + +## 十五、枚举类型汇总 + +```typescript +// 用户角色 +enum UserRole { + PUB = 'pub', // 普通评委 + ADMIN = 'admin' // 裁判长 +} + +// 比赛状态 +enum MatchStatus { + UPCOMING = 'upcoming', // 未开始 + ONGOING = 'ongoing', // 进行中 + FINISHED = 'finished' // 已结束 +} + +// 场地状态 +enum VenueStatus { + ACTIVE = 'active', // 进行中 + COMPLETED = 'completed', // 已完成 + PAUSED = 'paused' // 暂停 +} + +// 项目状态 +enum ProjectStatus { + UPCOMING = 'upcoming', // 未开始 + ONGOING = 'ongoing', // 进行中 + COMPLETED = 'completed' // 已完成 +} + +// 选手状态 +enum AthleteStatus { + WAITING = 'waiting', // 等待中 + PERFORMING = 'performing', // 表演中 + FINISHED = 'finished' // 已完成 +} + +// 用户状态 +enum UserStatus { + ACTIVE = 'active', // 激活 + INACTIVE = 'inactive' // 停用 +} + +// 性别 +enum Gender { + MALE = 'male', // 男 + FEMALE = 'female' // 女 +} +``` + +--- + +## 十六、数据关系图 + +``` +Match (比赛) + │ + ├─→ Venue (场地) [1:N] + │ │ + │ └─→ Athlete (选手) [1:N] + │ + ├─→ Project (项目) [1:N] + │ │ + │ ├─→ Athlete (选手) [1:N] + │ │ + │ └─→ Deduction (扣分项) [1:N] + │ + ├─→ Judge (评委) [1:N] + │ │ + │ └─→ Score (评分) [1:N] + │ + └─→ Athlete (选手) [1:N] + │ + ├─→ Score (评分) [1:N] + │ │ + │ └─→ ScoreModification (修改记录) [1:N] + │ + └─→ ScoreModification (修改记录) [1:N] +``` + +--- + +## 十七、数据存储建议 + +### 17.1 前端本地存储 + +```javascript +// 使用 uni.setStorageSync 存储 +const storageKeys = { + TOKEN: 'auth_token', + USER_INFO: 'user_info', + MATCH_INFO: 'match_info', + CACHE_VENUES: 'cache_venues', + CACHE_PROJECTS: 'cache_projects' +} + +// 存储Token +uni.setStorageSync(storageKeys.TOKEN, token) + +// 获取Token +const token = uni.getStorageSync(storageKeys.TOKEN) + +// 清除所有缓存 +uni.clearStorageSync() +``` + +### 17.2 数据缓存策略 + +```typescript +interface CacheStrategy { + key: string // 缓存键 + ttl: number // 过期时间(秒) + refresh: boolean // 是否需要刷新 +} + +const cacheConfig = { + matchInfo: { key: 'match', ttl: 3600, refresh: false }, + venues: { key: 'venues', ttl: 1800, refresh: false }, + projects: { key: 'projects', ttl: 1800, refresh: false }, + athletes: { key: 'athletes', ttl: 60, refresh: true } +} +``` + +--- + +## 附录:TypeScript类型定义文件 + +建议创建 `types/index.d.ts` 文件,集中管理所有类型定义: + +```typescript +// types/index.d.ts + +// 导出所有接口 +export * from './user' +export * from './match' +export * from './venue' +export * from './project' +export * from './athlete' +export * from './score' +export * from './deduction' +export * from './statistics' +``` + +这样可以在项目中统一导入使用: + +```typescript +import { UserInfo, Match, Athlete, Score } from '@/types' +``` diff --git a/doc/页面功能说明.md b/doc/页面功能说明.md new file mode 100644 index 0000000..a6c0c59 --- /dev/null +++ b/doc/页面功能说明.md @@ -0,0 +1,751 @@ +# 页面功能说明 + +## 页面总览 + +| 页面 | 路径 | 权限 | 功能描述 | +|------|------|------|----------| +| 登录页 | pages/login/login.vue | 所有用户 | 用户登录认证 | +| 评分列表页 | pages/score-list/score-list.vue | 普通评委(pub) | 查看选手列表并进行评分 | +| 评分详情页 | pages/score-detail/score-detail.vue | 普通评委(pub) | 对选手进行详细评分 | +| 多场地列表页 | pages/score-list-multi/score-list-multi.vue | 裁判长(admin) | 查看所有场地和项目 | +| 修改评分页 | pages/modify-score/modify-score.vue | 裁判长(admin) | 修改已有评分 | + +--- + +## 1. 登录页 (login.vue) + +### 页面路径 +`pages/login/login.vue` + +### 访问权限 +所有用户 + +### 页面功能 + +#### 核心功能 +- 用户通过比赛编码和邀请码进行登录认证 +- 根据邀请码类型(pub/admin)自动跳转到对应页面 + +#### 页面元素 + +1. **自定义导航栏** + - 标题: "评分系统" + - 右侧图标: 菜单按钮、关闭按钮 + +2. **输入区域** + - 比赛编码输入框 + - 评委邀请码输入框 + +3. **操作按钮** + - "立即评分" 提交按钮 + +### 数据结构 + +```javascript +data() { + return { + matchCode: '', // 比赛编码 + inviteCode: '' // 邀请码 (pub/admin) + } +} +``` + +### 业务逻辑 + +#### 提交流程 +1. 验证比赛编码不为空 +2. 验证邀请码不为空 +3. 判断邀请码类型(pub 或 admin) +4. 保存用户信息到全局数据 +5. 根据角色跳转页面 + +```javascript +// 全局数据存储 +getApp().globalData = { + userRole: 'pub' | 'admin', // 用户角色 + matchCode: string // 比赛编码 +} +``` + +#### 角色跳转规则 +- **admin (裁判长)**: 跳转到多场地列表页 (score-list-multi) +- **pub (普通评委)**: 跳转到评分列表页 (score-list) + +### 需要对接的API + +#### 1. 登录认证接口 + +**接口**: `POST /api/auth/login` + +**请求参数**: +```json +{ + "matchCode": "string", // 比赛编码 + "inviteCode": "string" // 邀请码 +} +``` + +**返回数据**: +```json +{ + "code": 200, + "message": "登录成功", + "data": { + "token": "string", // JWT Token + "userRole": "pub|admin", // 用户角色 + "matchId": "string", // 比赛ID + "matchName": "string", // 比赛名称 + "judgeName": "string", // 评委姓名 + "judgeId": "string" // 评委ID + } +} +``` + +### 样式特点 +- 使用渐变背景(深绿到浅绿) +- 输入框带阴影和圆角 +- 提交按钮带渐变和悬停效果 + +--- + +## 2. 评分列表页 (score-list.vue) + +### 页面路径 +`pages/score-list/score-list.vue` + +### 访问权限 +普通评委 (pub) + +### 页面功能 + +#### 核心功能 +- 显示当前比赛信息 +- 显示选手列表 +- 查看已评分/未评分状态 +- 跳转到评分详情页进行评分 + +#### 页面元素 + +1. **自定义导航栏** + - 标题: "评分系统" + +2. **比赛信息区域** + - 比赛标题 + - 比赛时间 + +3. **场地和项目区域** + - 当前场地显示(第一场地) + - 当前项目显示(男子组陈氏太极拳) + +4. **评分统计** + - 已评分数量/总数 (如: 2/30) + +5. **选手列表** + - 选手姓名 + - 身份证号 + - 队伍名称 + - 编号 + - 我的评分(已评分时显示) + - 总分(已评分时显示) + - "评分"按钮(未评分时显示) + +### 数据结构 + +```javascript +// 当前为静态Mock数据,需要从API获取 +data() { + return { + matchInfo: { + title: '2025年全国武术散打锦标赛暨第十七届世界武术锦标赛选拔赛', + time: '2025年6月25日 9:00' + }, + venue: '第一场地', + project: '男子组陈氏太极拳', + scoreStats: { + scored: 2, // 已评分数 + total: 30 // 总数 + }, + players: [] // 选手列表 + } +} +``` + +### 需要对接的API + +#### 1. 获取比赛信息 + +**接口**: `GET /api/match/{matchId}` + +**返回数据**: +```json +{ + "code": 200, + "data": { + "matchId": "string", + "matchName": "string", + "matchTime": "2025-06-25T09:00:00", + "venue": "第一场地", + "project": "男子组陈氏太极拳" + } +} +``` + +#### 2. 获取选手列表 + +**接口**: `GET /api/athletes` + +**请求参数**: +``` +matchId: string +venueId: string +projectId: string +judgeId: string +``` + +**返回数据**: +```json +{ + "code": 200, + "data": { + "total": 30, + "scored": 2, + "athletes": [ + { + "athleteId": "string", + "name": "张三", + "idCard": "123456789000000000", + "team": "少林寺武术大学院", + "number": "123-4567898275", + "myScore": 8.906, // 我的评分(已评分) + "totalScore": 8.907, // 总分(已评分) + "scored": true // 是否已评分 + } + ] + } +} +``` + +### 交互逻辑 + +1. 点击"评分"按钮跳转到评分详情页 +2. 页面显示时自动刷新选手列表 +3. 已评分的选手显示分数,未评分显示评分按钮 + +--- + +## 3. 评分详情页 (score-detail.vue) + +### 页面路径 +`pages/score-detail/score-detail.vue` + +### 访问权限 +普通评委 (pub) + +### 页面功能 + +#### 核心功能 +- 对选手进行精确评分(精度0.001) +- 选择扣分项(多选) +- 填写评分备注 +- 提交评分 + +#### 页面元素 + +1. **自定义导航栏** + - 返回按钮 + - 标题: "评分详情" + +2. **选手信息区域** + - 选手姓名 + - 身份证号 + - 队伍名称 + - 编号 + +3. **评分提示** + - "点击分数填写或拖动滑块打分(5-10分)" + +4. **分数控制区域** + - 减少按钮(-0.001) + - 当前分数显示(3位小数) + - 增加按钮(+0.001) + +5. **扣分项区域** + - 8个扣分项(多选) + - 圆形复选框 + +6. **备注区域** + - 多行文本输入框 + - 最大200字 + +7. **提交按钮** + +### 数据结构 + +```javascript +data() { + return { + athleteId: '', // 选手ID(从路由参数获取) + currentScore: 8.907, // 当前评分 + note: '', // 备注 + minScore: 5.0, // 最低分 + maxScore: 10.0, // 最高分 + deductions: [ // 扣分项列表 + { id: 1, text: '扣分项描述', checked: false }, + { id: 2, text: '扣分项描述', checked: false }, + // ... 共8项 + ] + } +} +``` + +### 业务逻辑 + +#### 评分控制 +- 每次点击增加/减少0.001分 +- 分数范围: 5.000 - 10.000 +- 超出范围时按钮置灰(需实现) + +#### 扣分项选择 +- 支持多选 +- 点击切换选中状态 + +#### 提交评分 +1. 验证分数范围 +2. 收集扣分项 +3. 提交数据到后端 +4. 显示成功提示 +5. 返回列表页 + +### 需要对接的API + +#### 1. 获取选手详情 + +**接口**: `GET /api/athletes/{athleteId}` + +**返回数据**: +```json +{ + "code": 200, + "data": { + "athleteId": "string", + "name": "张三", + "idCard": "123456789000000000", + "team": "少林寺武术大学院", + "number": "123-4567898275", + "defaultScore": 8.907 // 默认分数 + } +} +``` + +#### 2. 获取扣分项列表 + +**接口**: `GET /api/deductions` + +**请求参数**: +``` +projectId: string +``` + +**返回数据**: +```json +{ + "code": 200, + "data": [ + { + "deductionId": "string", + "text": "扣分项描述", + "score": -0.1 // 扣分值 + } + ] +} +``` + +#### 3. 提交评分 + +**接口**: `POST /api/scores` + +**请求参数**: +```json +{ + "matchId": "string", + "athleteId": "string", + "judgeId": "string", + "score": 8.907, + "deductions": ["deductionId1", "deductionId2"], + "note": "string" +} +``` + +**返回数据**: +```json +{ + "code": 200, + "message": "评分提交成功", + "data": { + "scoreId": "string", + "createTime": "2025-06-25T09:15:00" + } +} +``` + +--- + +## 4. 多场地列表页 (score-list-multi.vue) + +### 页面路径 +`pages/score-list-multi/score-list-multi.vue` + +### 访问权限 +裁判长 (admin) + +### 页面功能 + +#### 核心功能 +- 查看所有场地和项目 +- 切换场地(横向滚动) +- 切换项目(横向滚动) +- 查看各场地选手列表 +- 修改已有评分 + +#### 页面元素 + +1. **自定义导航栏** + - 标题: "评分系统" + +2. **比赛信息区域** + - 比赛标题 + - 比赛时间 + +3. **场地切换区域** + - 5个场地横向滚动切换 + - 当前场地高亮显示 + +4. **项目切换区域** + - 8个项目横向滚动切换 + - 当前项目高亮显示 + +5. **评分统计** + - 当前场地已评分数/总数 + +6. **选手列表** + - 选手信息 + - 总分显示 + - "修改"按钮 + +### 数据结构 + +```javascript +data() { + return { + currentVenue: 1, // 当前场地ID + currentProject: 0, // 当前项目索引 + venues: [ // 场地列表 + { id: 1, name: '第一场地' }, + { id: 2, name: '第二场地' }, + { id: 3, name: '第三场地' }, + { id: 4, name: '第四场地' }, + { id: 5, name: '第五场地' } + ], + projects: [ // 项目列表 + '女子组长拳', + '男子组陈氏太极拳', + '女子组双剑(含长穗双剑)', + '男子组杨氏太极拳', + '女子组刀术', + '男子组棍术', + '女子组枪术', + '男子组剑术' + ], + players: [] // 选手列表 + } +} +``` + +### 需要对接的API + +#### 1. 获取场地列表 + +**接口**: `GET /api/venues` + +**请求参数**: +``` +matchId: string +``` + +**返回数据**: +```json +{ + "code": 200, + "data": [ + { + "venueId": "string", + "venueName": "第一场地", + "order": 1 + } + ] +} +``` + +#### 2. 获取项目列表 + +**接口**: `GET /api/projects` + +**请求参数**: +``` +matchId: string +``` + +**返回数据**: +```json +{ + "code": 200, + "data": [ + { + "projectId": "string", + "projectName": "女子组长拳", + "order": 1 + } + ] +} +``` + +#### 3. 获取选手列表(带总分) + +**接口**: `GET /api/athletes/scored` + +**请求参数**: +``` +matchId: string +venueId: string +projectId: string +``` + +**返回数据**: +```json +{ + "code": 200, + "data": { + "total": 30, + "scored": 25, + "athletes": [ + { + "athleteId": "string", + "name": "张三", + "idCard": "123456789000000000", + "team": "少林寺武术大学院", + "number": "123-4567898275", + "totalScore": 8.907, // 总分 + "judgeCount": 6, // 评委数量 + "canModify": true // 是否可修改 + } + ] + } +} +``` + +### 交互逻辑 + +1. 横向滚动切换场地 +2. 横向滚动切换项目 +3. 切换场地/项目时自动加载对应选手列表 +4. 点击"修改"按钮跳转到修改评分页 + +--- + +## 5. 修改评分页 (modify-score.vue) + +### 页面路径 +`pages/modify-score/modify-score.vue` + +### 访问权限 +裁判长 (admin) + +### 页面功能 + +#### 核心功能 +- 查看选手信息和当前总分 +- 查看所有评委的评分详情 +- 修改总分(精度0.001) +- 填写修改备注 +- 提交修改 + +#### 页面元素 + +1. **自定义导航栏** + - 返回按钮 + - 标题: "修改评分" + +2. **选手信息区域** + - 选手姓名 + - 当前总分 + - 身份证号 + - 队伍名称 + - 编号 + +3. **评委评分统计区域** + - 标题: "共有X位评委完成评分" + - 各评委的评分列表 + +4. **修改总分区域** + - 说明: "修改总分(+-0.005分)" + - 减少按钮(-0.001) + - 当前分数显示 + - "可不改"提示 + - 增加按钮(+0.001) + +5. **备注区域** + - 多行文本输入框 + - "可不填"提示 + - 最大200字 + +6. **修改按钮** + +### 数据结构 + +```javascript +data() { + return { + athleteId: '', // 选手ID + originalScore: 8.907, // 原始总分 + currentScore: 8.907, // 修改后的分数 + note: '', // 修改备注 + minScore: 5.0, // 最低分 + maxScore: 10.0, // 最高分 + judgeScores: [] // 评委评分列表 + } +} +``` + +### 需要对接的API + +#### 1. 获取选手评分详情 + +**接口**: `GET /api/scores/{athleteId}/detail` + +**返回数据**: +```json +{ + "code": 200, + "data": { + "athleteId": "string", + "name": "张三", + "idCard": "123456789000000000", + "team": "少林寺武术大学院", + "number": "123-4567898275", + "totalScore": 8.907, + "judgeCount": 6, + "judgeScores": [ + { + "judgeId": "string", + "judgeName": "欧阳丽娜", + "score": 8.907, + "deductions": ["扣分项1", "扣分项2"], + "note": "备注内容", + "scoreTime": "2025-06-25T09:15:00" + } + ], + "modifications": [ + { + "modifyId": "string", + "modifier": "裁判长姓名", + "originalScore": 8.907, + "modifiedScore": 8.910, + "note": "修改原因", + "modifyTime": "2025-06-25T10:00:00" + } + ] + } +} +``` + +#### 2. 修改评分 + +**接口**: `PUT /api/scores/{athleteId}/modify` + +**请求参数**: +```json +{ + "athleteId": "string", + "originalScore": 8.907, + "modifiedScore": 8.910, + "note": "修改原因", + "modifier": "裁判长ID" +} +``` + +**返回数据**: +```json +{ + "code": 200, + "message": "修改成功", + "data": { + "modifyId": "string", + "modifiedScore": 8.910, + "modifyTime": "2025-06-25T10:00:00" + } +} +``` + +### 业务逻辑 + +#### 修改总分 +- 每次点击增加/减少0.001分 +- 分数范围: 5.000 - 10.000 +- "可不改"表示可以不修改分数 + +#### 提交修改 +1. 验证分数范围 +2. 检查是否有实际修改 +3. 提交数据到后端 +4. 显示成功提示 +5. 返回多场地列表页 + +### 交互逻辑 + +1. 显示所有评委的评分明细 +2. 裁判长可以调整总分 +3. 必须填写修改备注(可选) +4. 提交后返回列表页 + +--- + +## 页面间跳转关系 + +``` +登录页 (login.vue) + ├─→ [pub角色] → 评分列表页 (score-list.vue) + │ └─→ 评分详情页 (score-detail.vue) + │ └─→ [提交后返回] → 评分列表页 + │ + └─→ [admin角色] → 多场地列表页 (score-list-multi.vue) + └─→ 修改评分页 (modify-score.vue) + └─→ [修改后返回] → 多场地列表页 +``` + +## 页面共同特性 + +### 1. 自定义导航栏 +所有页面都使用自定义导航栏(`navigationStyle: "custom"`) + +- 高度: 90rpx +- 背景: 渐变绿色 (#1B7C5E → #2A9D7E) +- 标题居中 +- 右侧固定菜单和关闭按钮 + +### 2. 响应式设计 +使用rpx单位实现跨设备适配 + +### 3. 色彩系统统一 +- 主色: #1B7C5E(深绿) +- 次色: #2A9D7E(浅绿) +- 强调色: #FF4D6A(红色) + +### 4. 交互反馈 +- 按钮点击有透明度变化 +- 表单验证有toast提示 +- 加载状态需要添加loading(待实现) + +### 5. 错误处理 +- 网络请求失败提示(待实现) +- 表单验证提示 +- 权限验证提示 diff --git a/doc/项目概述.md b/doc/项目概述.md new file mode 100644 index 0000000..07d0bd2 --- /dev/null +++ b/doc/项目概述.md @@ -0,0 +1,206 @@ +# 武术评分系统 - 项目概述 + +## 项目基本信息 + +- **项目名称**: 武术评分系统 (martial-admin-mini) +- **项目类型**: UniApp 跨端小程序 +- **技术框架**: Vue.js 2.x + UniApp +- **支持平台**: 微信小程序 + H5 +- **版本**: v1.0.0 + +## 项目简介 + +武术评分系统是一个专为武术比赛评委打分设计的移动端应用。系统支持多角色权限管理,区分普通评委和裁判长两种角色,提供精确的评分功能(精度到0.001分)。 + +## 核心功能 + +### 1. 用户角色 + +- **普通评委 (pub)**: 对选手进行评分 +- **裁判长 (admin)**: 查看所有场地和项目,修改评分 + +### 2. 主要功能模块 + +1. **登录认证**: 通过比赛编码和邀请码登录 +2. **评分管理**: 精确评分(5.0-10.0分,精度0.001) +3. **场地管理**: 支持多场地切换(5个场地) +4. **项目管理**: 支持多项目切换(8个项目类型) +5. **扣分项管理**: 支持多选扣分项 +6. **评分修改**: 裁判长可修改总分 + +## 技术架构 + +### 技术栈 + +| 技术 | 版本/说明 | +|------|----------| +| 框架 | UniApp | +| 前端框架 | Vue.js 2.x | +| 样式 | CSS3 + SCSS | +| UI组件 | UniApp原生组件 | +| 状态管理 | getApp().globalData | +| 路由 | UniApp内置路由 | + +### 项目特点 + +1. **跨平台**: 一套代码,同时支持微信小程序和H5 +2. **无三方依赖**: 完全使用UniApp原生API和组件 +3. **自定义导航栏**: 所有页面使用自定义导航栏设计 +4. **响应式设计**: 使用rpx单位适配不同屏幕 +5. **精确控制**: 评分精度达0.001分 + +## 项目结构 + +``` +martial-admin-mini/ +├── pages/ # 页面文件 +│ ├── login/ # 登录页 +│ ├── score-list/ # 评分列表页(普通评委) +│ ├── score-detail/ # 评分详情页 +│ ├── score-list-multi/ # 多场地列表页(裁判长) +│ └── modify-score/ # 修改评分页(裁判长) +├── common/ # 公共资源 +│ └── common.css # 全局样式 +├── static/ # 静态资源 +├── doc/ # 项目文档 +├── image/ # 设计图 +├── pages.json # 页面配置 +├── manifest.json # 应用配置 +├── App.vue # 根组件 +├── main.js # 入口文件 +└── uni.scss # 全局样式变量 +``` + +## 页面流程 + +``` +┌─────────────────┐ +│ 登录页面 │ (login.vue) +│ 输入比赛编码 │ +│ 输入邀请码 │ +└────────┬────────┘ + │ + 验证邀请码 + │ + ┌────┴────┐ + │ │ + pub admin + │ │ + ▼ ▼ +┌────────┐ ┌────────────────┐ +│评分列表│ │多场地列表页 │ +│(普通评委)│ │(裁判长) │ +└───┬────┘ └────┬───────────┘ + │ │ + ▼ ▼ +┌────────┐ ┌──────────┐ +│评分详情│ │修改评分 │ +│页面 │ │页面 │ +└────────┘ └──────────┘ +``` + +## 色彩系统 + +| 颜色类型 | 色值 | 说明 | +|---------|------|------| +| 主色调 | #1B7C5E | 深绿色(导航栏、按钮) | +| 次要色 | #2A9D7E | 浅绿色(渐变) | +| 强调色 | #FF4D6A | 红色(提示、警告) | +| 文字主色 | #333333 | 深灰色 | +| 文字次色 | #666666 | 中灰色 | +| 占位符 | #CCCCCC | 浅灰色 | +| 页面背景 | #F5F5F5 | 浅灰色背景 | + +## 当前开发状态 + +### 已完成功能 + +- ✅ 登录权限验证 +- ✅ 角色区分(pub/admin) +- ✅ 评分界面(精度0.001) +- ✅ 扣分项多选 +- ✅ 场地切换(5个场地) +- ✅ 项目切换(8个项目) +- ✅ 评分修改功能 +- ✅ 自定义导航栏 +- ✅ 响应式布局 + +### 待开发功能 + +- ❌ 后端API对接 +- ❌ 真实数据存储 +- ❌ 用户身份持久化 +- ❌ 实时数据同步 +- ❌ 离线支持 +- ❌ 数据统计和导出 + +## 运行说明 + +### 使用 HBuilderX(推荐) + +1. 打开 HBuilderX +2. 文件 → 打开目录 → 选择项目根目录 +3. 右键点击项目名称 → 运行 → 运行到浏览器 → Chrome +4. 首次编译需要1-2分钟 + +### 使用命令行 + +```bash +# H5开发 +npm run dev:h5 + +# 微信小程序开发 +npm run dev:mp-weixin + +# 构建H5生产环境 +npm run build:h5 + +# 构建微信小程序 +npm run build:mp-weixin +``` + +## 部署说明 + +### H5部署 + +1. 运行 `npm run build:h5` +2. 将 `dist/build/h5` 目录部署到服务器 +3. 配置nginx静态资源服务 + +### 微信小程序部署 + +1. 运行 `npm run build:mp-weixin` +2. 使用微信开发者工具打开 `dist/build/mp-weixin` 目录 +3. 配置 `manifest.json` 中的 `appid` +4. 上传审核 + +## 后续扩展建议 + +### 短期优化(1-2周) + +1. 对接后端API +2. 实现JWT认证 +3. 添加loading状态 +4. 完善错误处理 + +### 中期功能(1-2月) + +1. WebSocket实时同步 +2. 评分历史记录 +3. 数据统计分析 +4. 导出Excel报表 +5. 离线缓存支持 + +### 长期架构(2-3月) + +1. 引入Vuex/Pinia状态管理 +2. 组件化拆分 +3. 单元测试覆盖 +4. CI/CD自动化部署 +5. 性能优化 + +## 联系方式 + +- 项目仓库: [项目地址] +- 技术文档: [文档地址] +- 问题反馈: [Issues地址]