Files
martial-master/docs/tasks/02-比赛日流程功能.md
n72595987@gmail.com 21c133f9c9
All checks were successful
continuous-integration/drone/push Build is passing
feat: 实现成绩计算引擎、比赛日流程和导出打印功能
本次提交完成了武术比赛系统的核心功能模块,包括:

## 1. 成绩计算引擎 (Tasks 1.1-1.8) 
- 实现多裁判评分平均分计算(去最高/最低分)
- 支持难度系数应用
- 自动排名算法(支持并列)
- 奖牌自动分配(金银铜)
- 成绩复核机制
- 成绩发布/撤销审批流程

## 2. 比赛日流程功能 (Tasks 2.1-2.6) 
- 运动员签到/检录系统
- 评分有效性验证(范围检查0-10分)
- 异常分数警告机制(偏差>2.0)
- 异常情况记录和处理
- 检录长角色权限管理
- 比赛状态流转管理

## 3. 导出打印功能 (Tasks 3.1-3.4) 
- 成绩单Excel导出(EasyExcel)
- 运动员名单Excel导出
- 赛程表Excel导出
- 证书生成(HTML模板+数据接口)

## 4. 单元测试 
- MartialResultServiceTest: 10个测试用例
- MartialScoreServiceTest: 10个测试用例
- MartialAthleteServiceTest: 14个测试用例
- 测试通过率: 100% (34/34)

## 技术实现
- 使用BigDecimal进行精度计算(保留3位小数)
- EasyExcel实现Excel导出
- HTML证书模板(支持浏览器打印为PDF)
- JUnit 5 + Mockito单元测试框架

## 新增文件
- 3个新控制器:MartialExportController, MartialExceptionEventController, MartialJudgeProjectController
- 3个Excel VO类:ResultExportExcel, AthleteExportExcel, ScheduleExportExcel
- CertificateVO证书数据对象
- 证书HTML模板
- 3个测试类(676行测试代码)
- 任务文档(docs/tasks/)
- 数据库迁移脚本

## 项目进度
已完成: 64% (18/28 任务)
-  成绩计算引擎: 100%
-  比赛日流程: 100%
-  导出打印功能: 80%

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 17:11:12 +08:00

6.0 KiB
Raw Blame History

比赛日流程功能 - 详细任务清单

优先级: P1重要 预计工时: 4天 负责人: 待分配 创建时间: 2025-11-30


📋 任务概述

比赛日流程功能包括运动员签到检录、评分验证、异常处理等关键环节。


任务列表

任务 2.1:运动员签到/检录系统 🔴

状态: 未开始 工时: 1.5天

需求描述

  • 运动员签到功能
  • 更新比赛状态(待出场 → 进行中 → 已完成)
  • 检录员角色权限管理

实现要点

// MartialAthleteServiceImpl.java
public void checkIn(Long athleteId, Long scheduleId) {
    MartialAthlete athlete = this.getById(athleteId);

    // 更新运动员状态:待出场 → 进行中
    athlete.setCompetitionStatus(1);  // 进行中
    this.updateById(athlete);

    // 更新赛程运动员关联状态
    MartialScheduleAthlete scheduleAthlete = scheduleAthleteService.getOne(
        new QueryWrapper<MartialScheduleAthlete>()
            .eq("schedule_id", scheduleId)
            .eq("athlete_id", athleteId)
    );
    scheduleAthlete.setIsCompleted(0);  // 未完成
    scheduleAthleteService.updateById(scheduleAthlete);
}

public void completePerformance(Long athleteId) {
    MartialAthlete athlete = this.getById(athleteId);
    athlete.setCompetitionStatus(2);  // 已完成
    this.updateById(athlete);
}

API接口

  • POST /martial/athlete/checkin - 签到
  • POST /martial/athlete/complete - 完成比赛

任务 2.2:评分有效性验证 🔴

状态: 未开始 工时: 0.5天

需求描述

  • 分数范围检查5.000 - 10.000
  • 评分提交前验证
  • 异常分数提示

实现要点

// MartialScoreServiceImpl.java
public boolean validateScore(BigDecimal score) {
    BigDecimal MIN_SCORE = new BigDecimal("5.000");
    BigDecimal MAX_SCORE = new BigDecimal("10.000");

    return score.compareTo(MIN_SCORE) >= 0
        && score.compareTo(MAX_SCORE) <= 0;
}

@Override
public boolean save(MartialScore score) {
    // 验证分数范围
    if (!validateScore(score.getScore())) {
        throw new ServiceException("分数必须在5.000-10.000之间");
    }

    return super.save(score);
}

任务 2.3:异常分数警告机制 🔴

状态: 未开始 工时: 1天

需求描述

  • 检测离群值(与其他裁判差距过大)
  • 生成警告提示
  • 记录异常日志

实现要点

public void checkAnomalyScore(MartialScore newScore) {
    // 获取同一运动员的其他裁判评分
    List<MartialScore> scores = this.list(
        new QueryWrapper<MartialScore>()
            .eq("athlete_id", newScore.getAthleteId())
            .eq("project_id", newScore.getProjectId())
            .ne("judge_id", newScore.getJudgeId())
    );

    if (scores.size() < 2) {
        return;  // 评分数量不足,无法判断
    }

    // 计算其他裁判的平均分
    BigDecimal avgScore = scores.stream()
        .map(MartialScore::getScore)
        .reduce(BigDecimal.ZERO, BigDecimal::add)
        .divide(new BigDecimal(scores.size()), 3, RoundingMode.HALF_UP);

    // 判断偏差
    BigDecimal diff = newScore.getScore().subtract(avgScore).abs();
    if (diff.compareTo(new BigDecimal("1.000")) > 0) {
        // 偏差超过1.0分,记录警告
        log.warn("异常评分:裁判{}给运动员{}打分{},偏离平均分{}超过1.0",
            newScore.getJudgeName(),
            newScore.getAthleteId(),
            newScore.getScore(),
            avgScore
        );
    }
}

任务 2.4:异常情况记录和处理 🔴

状态: 未开始 工时: 0.5天

需求描述

  • 新建异常事件表
  • 记录异常类型、处理结果
  • 支持查询统计

数据库表设计

CREATE TABLE martial_exception_event (
    id BIGINT PRIMARY KEY,
    competition_id BIGINT NOT NULL COMMENT '赛事ID',
    schedule_id BIGINT COMMENT '赛程ID',
    athlete_id BIGINT COMMENT '运动员ID',
    event_type INT COMMENT '事件类型 1-器械故障 2-受伤 3-评分争议 4-其他',
    event_description VARCHAR(500) COMMENT '事件描述',
    handler_name VARCHAR(50) COMMENT '处理人',
    handle_result VARCHAR(500) COMMENT '处理结果',
    handle_time DATETIME COMMENT '处理时间',
    status INT DEFAULT 0 COMMENT '状态 0-待处理 1-已处理',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    is_deleted INT DEFAULT 0
) COMMENT '异常事件表';

任务 2.5:检录长角色权限管理 🔴

状态: 未开始 工时: 0.5天

需求描述

  • 定义检录长角色
  • 赋予特殊权限(处理异常、调整赛程)
  • 集成现有权限系统

实现要点

  • 利用 BladeX 框架的角色权限系统
  • 新增角色:ROLE_REFEREE_CHIEF
  • 权限:异常处理、成绩复核申请

任务 2.6:比赛状态流转管理 🔴

状态: 未开始 工时: 0.5天

需求描述

  • 状态机管理运动员比赛状态
  • 防止非法状态转换
  • 记录状态变更日志

状态流转图

待出场(0) → 进行中(1) → 已完成(2)
     ↓          ↓
   已取消    暂停/异常

🎯 Controller 层接口

@RestController
@RequestMapping("/martial/athlete")
public class MartialAthleteController {

    @PostMapping("/checkin")
    @Operation(summary = "运动员签到")
    public R checkIn(@RequestParam Long athleteId, @RequestParam Long scheduleId) {
        athleteService.checkIn(athleteId, scheduleId);
        return R.success("签到成功");
    }

    @PostMapping("/complete")
    @Operation(summary = "完成比赛")
    public R complete(@RequestParam Long athleteId) {
        athleteService.completePerformance(athleteId);
        return R.success("已标记为完成");
    }
}

验收标准

  • 签到功能正常,状态更新准确
  • 评分验证有效拦截非法分数
  • 异常分数警告机制生效
  • 异常事件可记录和查询
  • 权限控制符合设计