Files
martial-admin-mini/doc/API接口设计.md
宅房 7bd197f4ac Mock版本完成 - UI冻结版本
完成内容:
- 5个完整的UI页面(登录、评分列表、评分详情、多场地列表、修改评分)
- 完整的Mock数据展示
- 完整的业务逻辑实现
- 文档体系建立(2000+行文档)

文档包含:
- 项目概述.md
- 页面功能说明.md
- API接口设计.md (17个接口)
- 数据结构设计.md (17个接口定义)
- 功能模块划分.md
- 后端实现对比报告.md
- 数据可行性分析报告.md (95分评估)
- 保护Mock版本的实施方案.md (4层保护机制)
- API对接完成度检查报告.md

此版本为Mock原型版本,所有UI功能完整,数据为硬编码Mock数据。
2025-12-11 13:22:19 +08:00

1108 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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']
```