Files
martial-admin-mini/doc/数据可行性分析报告.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

34 KiB
Raw Blame History

数据可行性分析报告

报告说明

本报告基于前端Mock数据业务需求,深入分析后端数据库和API能否提供这些业务数据。目的是在不改动前端UI代码的前提下,验证后端数据支持能力。


一、分析方法

1.1 分析维度

前端页面Mock数据需求
    ↓
后端Entity字段能力
    ↓
后端Controller API能力
    ↓
业务逻辑计算能力
    ↓
数据可行性结论

1.2 评估标准

评级 说明 标记
完全支持 后端已有字段和接口,可直接提供
🔄 需要计算 后端有原始数据,需通过计算/聚合得出 🔄
⚠️ 需要扩展 后端缺少字段,需要添加字段或接口 ⚠️
无法支持 后端缺少数据源,需要重新设计

二、登录页数据分析

2.1 前端Mock数据需求

页面: pages/login/login.vue

// 用户输入
{
  matchCode: '',      // 比赛编码
  inviteCode: ''      // 邀请码 (pub/admin)
}

// 验证逻辑
- 判断 inviteCode  'pub' 还是 'admin'
- 根据角色跳转不同页面

2.2 后端数据能力

Entity支持度

// 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支持度

现有接口:

// 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 需要补充的接口

⚠️ 缺少小程序登录专用接口

// 建议新增
@PostMapping("/api/mini/login")
public R<LoginVO> 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

// 页面展示数据
{
  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 (选手信息):

 playerName          name
 idCard             idCard
 organization/teamName  team
 playerNo           number
 competitionStatus   比赛状态
 totalScore         totalScore
 缺少 myScore       需要关联 MartialScore 表查询
 缺少 scored        需要计算判断是否有评分记录

MartialScore (评分记录):

 judgeId            识别"我的评分"
 athleteId          关联选手
 score              评分值
 scoreTime          评分时间

MartialCompetition (比赛信息):

 competitionName    matchInfo.title
 startTime          matchInfo.time

MartialVenue (场地信息):

 venueName          venue

MartialProject (项目信息):

 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 需要补充的接口

⚠️ 需要新增带评分状态的选手列表接口

@GetMapping("/api/mini/athletes")
public R<List<AthleteWithScoreVO>> 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查询示例:

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

{
  // 选手信息
  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:

 playerName, idCard, teamName, playerNo  // 选手信息完整

MartialProject:

 minScore (可配置)     // 但当前Entity未找到
 maxScore (可配置)     // 但当前Entity未找到
 scoreStep            // 当前Entity未找到

MartialDeductionItem (扣分项):

 itemName              text
 deductionPoint        扣分值
 category              分类
 applicableProjects    适用项目(JSON)
 sortOrder             排序

MartialScore (提交数据):

 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 中添加字段

// MartialProject.java
private BigDecimal minScore = new BigDecimal("5.000");
private BigDecimal maxScore = new BigDecimal("10.000");
private BigDecimal scoreStep = new BigDecimal("0.001");

方案2: 系统配置表

// 全局配置
system.score.min = 5.000
system.score.max = 10.000
system.score.step = 0.001

方案3: 前端硬编码(推荐)

// 武术评分标准固定为 5.0-10.0
minScore: 5.0
maxScore: 10.0

结论: 数据基本支持,评分范围可通过配置或硬编码解决


五、多场地列表页数据分析(裁判长)

5.1 前端Mock数据需求

页面: pages/score-list-multi/score-list-multi.vue

{
  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 (场地):

 id                    venues[].id
 venueName             venues[].name
 competitionId         关联比赛
 status                场地状态

MartialProject (项目):

 id                    项目ID
 projectName           projects[]
 competitionId         关联比赛
 sortOrder             排序

MartialAthlete (选手):

 playerName, idCard, teamName, playerNo  // 基本信息
 totalScore            totalScore
🔄 canModify             需要计算判断是否所有评委已评分

MartialScore (评分记录):

// 用于判断是否可修改
 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 需要补充的接口

⚠️ 需要新增裁判长选手列表接口

@GetMapping("/api/mini/athletes/admin")
public R<List<AthleteAdminVO>> 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查询示例:

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

{
  // 选手信息
  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:

 playerName, idCard, teamName, playerNo, totalScore

MartialScore (评委评分):

 judgeId               评委ID
 judgeName             评委姓名已有字段
 athleteId             关联选手
 score                 评分值
 scoreTime             评分时间
 deductionItems        扣分项可选展示
 note                  评委备注

MartialScore (修改记录):

 originalScore         原始分数已有字段
 modifyReason          修改原因已有字段
 modifyTime            修改时间已有字段

6.3 数据可行性评估

前端需求 后端能力 评估结果 实现方案
选手信息 MartialAthlete 完全支持 直接查询
原始总分 totalScore 完全支持 已有字段
评委评分列表 MartialScore 完全支持 SELECT * FROM martial_score WHERE athleteId=?
评委姓名 judgeName 完全支持 已有字段(重要发现!)
评委分数 score 完全支持 已有字段
修改后分数 - 完全支持 前端控制,提交时更新 totalScore
修改原因 modifyReason 完全支持 已有字段
修改时间 modifyTime 完全支持 已有字段
原始分数 originalScore 完全支持 已有字段(用于审计)

6.4 需要补充的接口

⚠️ 需要新增评分详情查询和修改接口

// 1. 查询评分详情
@GetMapping("/api/mini/score/detail/{athleteId}")
public R<AthleteScoreDetailVO> 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查询示例:

-- 查询评委评分明细
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推荐

@RestController
@RequestMapping("/api/mini")
public class MartialMiniController {

    // 登录
    @PostMapping("/login")
    public R<LoginVO> login(@RequestBody LoginDTO dto) { }

    // 普通评委:选手列表
    @GetMapping("/athletes")
    public R<List<AthleteWithScoreVO>> getMyAthletes(...) { }

    // 裁判长:选手列表
    @GetMapping("/athletes/admin")
    public R<List<AthleteAdminVO>> getAthletesForAdmin(...) { }

    // 评分详情
    @GetMapping("/score/detail/{athleteId}")
    public R<AthleteScoreDetailVO> getScoreDetail(...) { }

    // 修改评分
    @PutMapping("/score/modify")
    public R modifyScore(@RequestBody ScoreModifyDTO dto) { }

    // 提交评分(复用现有的 MartialScoreController.submit
    // 或者在这里做一层转发
}

方案B: 扩展现有Controller

// 在现有 Controller 中添加小程序专用方法
// 优点:复用现有逻辑
// 缺点:代码耦合,不好维护

推荐: 方案A - 创建独立的 MartialMiniController

8.3 VO类设计

// 登录响应
@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<String> 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<JudgeScoreVO> judgeScores;
    ScoreModificationVO modification;  // 修改记录(如果有)
}

@Data
class JudgeScoreVO {
    Long judgeId;
    String judgeName;
    BigDecimal score;
    LocalDateTime scoreTime;
    String note;
}

8.4 Service层实现要点

@Service
public class MartialMiniService {

    @Autowired
    private MartialAthleteMapper athleteMapper;

    @Autowired
    private MartialScoreMapper scoreMapper;

    /**
     * 获取带评分状态的选手列表
     */
    public List<AthleteWithScoreVO> getAthletesWithScore(
        Long judgeId, Long venueId, Long projectId
    ) {
        // 方案1: 自定义SQL推荐
        return athleteMapper.selectAthletesWithScore(judgeId, venueId, projectId);

        // 方案2: 分步查询(性能较差)
        // List<Athlete> athletes = athleteMapper.selectList(...);
        // for (Athlete athlete : athletes) {
        //     Score score = scoreMapper.selectOne(athleteId, judgeId);
        //     ...
        // }
    }
}

Mapper XML:

<select id="selectAthletesWithScore" resultType="AthleteWithScoreVO">
    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.is_deleted = 0
    ORDER BY a.order_num ASC
</select>

九、前端对接改造方案

9.1 保护Mock版本策略

方案A: Git分支隔离推荐

# 当前分支保留Mock版本
main (或 mock-version)

# 创建API对接分支
git checkout -b feature/api-integration

# 后续可以随时切换
git checkout main        # 回到Mock版本
git checkout feature/api-integration  # 使用API版本

方案B: 配置开关

// 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备份)

推荐: 方案AGit分支+ 方案B配置开关

9.2 最小改动对接方案

// 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 数据格式适配

后端响应格式:

{
  "code": 200,
  "success": true,
  "msg": "操作成功",
  "data": { }
}

前端期望格式:

{
  "code": 200,
  "message": "成功",
  "data": { }
}

适配方案:

// 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
    • 编写自定义SQLLEFT 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 数据库优化建议

-- 添加索引(提升查询性能)
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天

  • 创建 MartialMiniController1个类
  • 创建 5 个接口方法
  • 创建 5-6 个VO类
  • 编写 3-4 个自定义SQL
  • 添加数据库索引

前端预计2-3天

  • 创建 utils/request.js1个文件
  • 创建 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% 支持