完成内容: - 5个完整的UI页面(登录、评分列表、评分详情、多场地列表、修改评分) - 完整的Mock数据展示 - 完整的业务逻辑实现 - 文档体系建立(2000+行文档) 文档包含: - 项目概述.md - 页面功能说明.md - API接口设计.md (17个接口) - 数据结构设计.md (17个接口定义) - 功能模块划分.md - 后端实现对比报告.md - 数据可行性分析报告.md (95分评估) - 保护Mock版本的实施方案.md (4层保护机制) - API对接完成度检查报告.md 此版本为Mock原型版本,所有UI功能完整,数据为硬编码Mock数据。
1137 lines
26 KiB
Markdown
1137 lines
26 KiB
Markdown
# 数据结构设计文档
|
|
|
|
## 文档说明
|
|
|
|
本文档定义了武术评分系统中所有核心数据结构和数据模型。这些数据结构用于前端状态管理、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<T> {
|
|
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'
|
|
```
|