完成内容: - 5个完整的UI页面(登录、评分列表、评分详情、多场地列表、修改评分) - 完整的Mock数据展示 - 完整的业务逻辑实现 - 文档体系建立(2000+行文档) 文档包含: - 项目概述.md - 页面功能说明.md - API接口设计.md (17个接口) - 数据结构设计.md (17个接口定义) - 功能模块划分.md - 后端实现对比报告.md - 数据可行性分析报告.md (95分评估) - 保护Mock版本的实施方案.md (4层保护机制) - API对接完成度检查报告.md 此版本为Mock原型版本,所有UI功能完整,数据为硬编码Mock数据。
26 KiB
26 KiB
数据结构设计文档
文档说明
本文档定义了武术评分系统中所有核心数据结构和数据模型。这些数据结构用于前端状态管理、API请求响应、以及数据库设计参考。
一、用户角色相关
1.1 用户信息 (UserInfo)
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)
}
示例:
{
"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)
interface LoginCredentials {
matchCode: string // 比赛编码
inviteCode: string // 邀请码
}
1.3 认证响应 (AuthResponse)
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)
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'
示例:
{
"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)
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'
示例:
{
"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)
type VenueList = Venue[]
四、项目相关
4.1 项目信息 (Project)
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'
示例:
{
"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 项目列表
type ProjectList = Project[]
前端Mock数据:
const projectList = [
'女子组长拳',
'男子组陈氏太极拳',
'女子组双剑(含长穗双剑)',
'男子组杨氏太极拳',
'女子组刀术',
'男子组棍术',
'女子组枪术',
'男子组剑术'
]
五、选手相关
5.1 选手信息 (Athlete)
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'
示例:
{
"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 选手列表项(普通评委视图)
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 // 状态
}
示例:
{
"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 选手列表项(裁判长视图)
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 // 状态
}
示例:
{
"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)
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 // 备注
}
示例:
{
"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)
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 // 更新时间
}
示例:
{
"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 评委评分项(显示用)
interface JudgeScore {
scoreId: string // 评分ID
judgeId: string // 评委ID
judgeName: string // 评委姓名
score: number // 评分
deductions: Deduction[] // 扣分项
note?: string // 备注
scoreTime: string // 评分时间
}
示例:
{
"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)
interface AthleteScoreDetail {
athleteId: string // 选手ID
athleteName: string // 选手姓名
team: string // 队伍
number: string // 比赛编号
projectName: string // 项目名称
totalScore: number // 总分
judgeCount: number // 已评分评委数
totalJudges: number // 总评委数
judgeScores: JudgeScore[] // 各评委评分
modifications: ScoreModification[] // 修改记录
}
示例:
{
"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)
interface ScoreModifySubmit {
athleteId: string // 选手ID
modifierId: string // 修改人ID (裁判长)
originalScore: number // 原始分数
modifiedScore: number // 修改后的分数
note: string // 修改原因
}
示例:
{
"athleteId": "athlete_001",
"modifierId": "admin_001",
"originalScore": 8.907,
"modifiedScore": 8.910,
"note": "技术动作优秀,加分"
}
7.2 评分修改记录 (ScoreModification)
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 // 创建时间
}
示例:
{
"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)
interface Deduction {
deductionId: string // 扣分项ID
projectId: string // 所属项目ID
text: string // 扣分项描述
score: number // 扣分值 (负数)
category?: string // 分类
order: number // 排序
createdAt: string // 创建时间
}
示例:
{
"deductionId": "deduction_001",
"projectId": "project_001",
"text": "起势不稳",
"score": -0.1,
"category": "基础动作",
"order": 1,
"createdAt": "2025-01-01T00:00:00Z"
}
8.2 扣分项选择(前端用)
interface DeductionItem {
deductionId?: string // 扣分项ID (对接后端后使用)
text: string // 扣分项描述
checked: boolean // 是否选中
}
前端Mock数据:
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)
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)
interface VenueStatistics {
venueId: string // 场地ID
venueName: string // 场地名称
athleteCount: number // 选手数
finishedCount: number // 已完成数
progress: number // 进度百分比
}
示例:
{
"venueId": "venue_001",
"venueName": "第一场地",
"athleteCount": 30,
"finishedCount": 28,
"progress": 93.3
}
9.3 项目统计 (ProjectStatistics)
interface ProjectStatistics {
projectId: string // 项目ID
projectName: string // 项目名称
athleteCount: number // 选手数
finishedCount: number // 已完成数
averageScore: number // 平均分
progress: number // 进度百分比
}
示例:
{
"projectId": "project_001",
"projectName": "女子组长拳",
"athleteCount": 15,
"finishedCount": 15,
"averageScore": 8.650,
"progress": 100
}
9.4 评委统计 (JudgeStatistics)
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 // 最后评分时间
}
示例:
{
"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)
interface LoginPageData {
matchCode: string // 比赛编码
inviteCode: string // 邀请码
}
10.2 评分列表页数据 (ScoreListPageData)
interface ScoreListPageData {
matchInfo: {
title: string // 比赛标题
time: string // 比赛时间
}
venue: string // 场地名称
project: string // 项目名称
scoreStats: {
scored: number // 已评分数
total: number // 总数
}
players: AthleteListItem[] // 选手列表
}
10.3 评分详情页数据 (ScoreDetailPageData)
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)
interface ScoreListMultiPageData {
currentVenue: number // 当前场地ID
currentProject: number // 当前项目索引
venues: Venue[] // 场地列表
projects: string[] // 项目列表
scoreStats: {
scored: number // 已评分数
total: number // 总数
}
players: AthleteListItemAdmin[] // 选手列表
}
10.5 修改评分页数据 (ModifyScorePageData)
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)
interface GlobalData {
userRole: 'pub' | 'admin' // 用户角色
matchCode: string // 比赛编码
token?: string // JWT Token
userInfo?: UserInfo // 用户信息
matchInfo?: Match // 比赛信息
}
使用方式:
// 设置全局数据
getApp().globalData = {
userRole: 'pub',
matchCode: 'MATCH2025001'
}
// 获取全局数据
const globalData = getApp().globalData
const userRole = globalData.userRole
十二、WebSocket推送数据结构
12.1 新评分推送
interface NewScoreMessage {
type: 'new_score'
data: {
athleteId: string
athleteName: string
judgeId: string
judgeName: string
score: number
totalScore: number
judgeCount: number
timestamp: string
}
}
12.2 评分修改推送
interface ScoreModifiedMessage {
type: 'score_modified'
data: {
athleteId: string
athleteName: string
modifierId: string
modifierName: string
originalScore: number
modifiedScore: number
note: string
timestamp: string
}
}
12.3 选手状态更新推送
interface AthleteStatusMessage {
type: 'athlete_status'
data: {
athleteId: string
athleteName: string
status: AthleteStatus
timestamp: string
}
}
十三、分页数据结构
13.1 分页请求参数 (PageRequest)
interface PageRequest {
page: number // 页码(从1开始)
pageSize: number // 每页数量
sortBy?: string // 排序字段
sortOrder?: 'asc' | 'desc' // 排序方向
}
13.2 分页响应数据 (PageResponse)
interface PageResponse<T> {
total: number // 总记录数
page: number // 当前页码
pageSize: number // 每页数量
totalPages: number // 总页数
records: T[] // 数据列表
}
示例:
{
"total": 150,
"page": 1,
"pageSize": 20,
"totalPages": 8,
"records": [
{
"athleteId": "athlete_001",
"name": "张三"
}
]
}
十四、数据验证规则
14.1 评分验证
interface ScoreValidation {
min: 5.000 // 最低分
max: 10.000 // 最高分
step: 0.001 // 步进值
precision: 3 // 小数位数
}
验证函数:
function validateScore(score) {
return score >= 5.000 &&
score <= 10.000 &&
/^\d+\.\d{3}$/.test(score.toFixed(3))
}
14.2 必填字段验证
// 登录验证
const loginRequired = ['matchCode', 'inviteCode']
// 评分提交验证
const scoreSubmitRequired = [
'matchId',
'athleteId',
'judgeId',
'projectId',
'venueId',
'score'
]
// 评分修改验证
const scoreModifyRequired = [
'athleteId',
'modifierId',
'originalScore',
'modifiedScore',
'note'
]
十五、枚举类型汇总
// 用户角色
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 前端本地存储
// 使用 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 数据缓存策略
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 文件,集中管理所有类型定义:
// 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'
这样可以在项目中统一导入使用:
import { UserInfo, Match, Athlete, Score } from '@/types'