# 后端开发快速上手指南 > 30分钟了解需要做什么,然后开始开发 --- ## 📋 你需要做什么 ### 创建1个Controller,实现5个接口 ```java @RestController @RequestMapping("/api/mini") public class MartialMiniController { // 5个接口方法 } ``` **预计工作量**: 6人天(约1周) --- ## 🚀 第一步:创建Controller(30分钟) ### 1. 创建文件 ``` src/main/java/org/springblade/modules/martial/controller/ └── MartialMiniController.java ``` ### 2. 基础代码 ```java package org.springblade.modules.martial.controller; import org.springblade.core.tool.api.R; import org.springframework.web.bind.annotation.*; import lombok.AllArgsConstructor; /** * 武术评分系统 - 小程序专用接口 * * @author 你的名字 */ @RestController @RequestMapping("/api/mini") @AllArgsConstructor public class MartialMiniController { // 注入需要的Service // private IMartialJudgeInviteService judgeInviteService; // private IMartialAthleteService athleteService; // private IMartialScoreService scoreService; /** * 小程序登录 */ @PostMapping("/login") public R login(@RequestBody LoginDTO dto) { // TODO: 实现登录逻辑 return R.success(null); } /** * 获取评委的选手列表(普通评委) */ @GetMapping("/athletes") public R> getMyAthletes( @RequestParam Long judgeId, @RequestParam Long venueId, @RequestParam Long projectId ) { // TODO: 实现获取选手列表逻辑 return R.success(null); } /** * 获取选手列表(裁判长) */ @GetMapping("/athletes/admin") public R> getAthletesForAdmin( @RequestParam Long competitionId, @RequestParam Long venueId, @RequestParam Long projectId ) { // TODO: 实现裁判长选手列表逻辑 return R.success(null); } /** * 获取评分详情(裁判长查看) */ @GetMapping("/score/detail/{athleteId}") public R getScoreDetail(@PathVariable Long athleteId) { // TODO: 实现评分详情逻辑 return R.success(null); } /** * 修改评分(裁判长) */ @PutMapping("/score/modify") public R modifyScore(@RequestBody ModifyScoreDTO dto) { // TODO: 实现修改评分逻辑 return R.success(null); } } ``` --- ## 🔴 第二步:实现登录接口(2天) ### 接口规范 ``` POST /api/mini/login 请求: { "matchCode": "123", "inviteCode": "pub" } 响应: { "code": 200, "success": true, "msg": "登录成功", "data": { "token": "xxx", "userRole": "pub", "matchId": "123", "matchName": "2025年全国武术散打锦标赛...", "matchTime": "2025年6月25日 9:00", "judgeId": "456", "judgeName": "欧阳丽娜", "venueId": "1", "venueName": "第一场地", "projects": ["女子组长拳", "男子组陈氏太极拳"] } } ``` ### 实现逻辑 ```java @PostMapping("/login") public R login(@RequestBody LoginDTO dto) { // 1. 验证比赛编码 MartialCompetition competition = competitionService.getOne( Wrappers.lambdaQuery() .eq(MartialCompetition::getCompetitionCode, dto.getMatchCode()) .eq(MartialCompetition::getIsDeleted, 0) ); if (competition == null) { return R.fail("比赛编码不存在"); } // 2. 验证邀请码 MartialJudgeInvite invite = judgeInviteService.getOne( Wrappers.lambdaQuery() .eq(MartialJudgeInvite::getInviteCode, dto.getInviteCode()) .eq(MartialJudgeInvite::getCompetitionId, competition.getId()) .eq(MartialJudgeInvite::getIsUsed, 0) .eq(MartialJudgeInvite::getIsDeleted, 0) .gt(MartialJudgeInvite::getExpireTime, LocalDateTime.now()) ); if (invite == null) { return R.fail("邀请码错误或已失效"); } // 3. 生成Token(使用BladeX的Token生成机制) String token = generateToken(invite.getJudgeId()); // 4. 更新邀请码使用状态 invite.setIsUsed(1); invite.setUseTime(LocalDateTime.now()); invite.setAccessToken(token); judgeInviteService.updateById(invite); // 5. 查询评委信息 MartialJudge judge = judgeService.getById(invite.getJudgeId()); // 6. 查询场地信息(如果有) MartialVenue venue = null; if (invite.getVenueId() != null) { venue = venueService.getById(invite.getVenueId()); } // 7. 解析项目列表 List projects = JSON.parseArray(invite.getProjects(), String.class); // 8. 构建响应 LoginVO vo = new LoginVO(); vo.setToken(token); vo.setUserRole(invite.getRole()); // "judge" 或 "chief_judge" vo.setMatchId(competition.getId().toString()); vo.setMatchName(competition.getCompetitionName()); vo.setMatchTime(formatDateTime(competition.getStartTime())); vo.setJudgeId(judge.getId().toString()); vo.setJudgeName(judge.getJudgeName()); if (venue != null) { vo.setVenueId(venue.getId().toString()); vo.setVenueName(venue.getVenueName()); } vo.setProjects(projects); return R.success(vo); } ``` ### SQL示例 ```sql -- 验证邀请码 SELECT ji.id, ji.judge_id AS judgeId, ji.role, ji.venue_id AS venueId, ji.projects, j.judge_name AS judgeName, c.id AS matchId, c.competition_name AS matchName, c.start_time AS matchTime, v.venue_name AS venueName FROM martial_judge_invite ji LEFT JOIN martial_judge j ON ji.judge_id = j.id LEFT JOIN martial_competition c ON ji.competition_id = c.id LEFT JOIN martial_venue v ON ji.venue_id = v.id WHERE ji.invite_code = ? AND c.competition_code = ? AND ji.is_used = 0 AND ji.expire_time > NOW() AND ji.is_deleted = 0 ``` --- ## 🔴 第三步:实现选手列表接口(1天) ### 接口规范 ``` GET /api/mini/athletes?judgeId=456&venueId=1&projectId=5 响应: { "code": 200, "success": true, "msg": "操作成功", "data": [ { "athleteId": "1", "name": "张三", "idCard": "123456789000000000", "team": "少林寺武术大学院", "number": "123-4567898275", "myScore": 8.906, "totalScore": 8.907, "scored": true, "scoreTime": "2025-06-25 09:15:00" } ] } ``` ### SQL示例 ```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 = ? WHERE a.venue_id = ? AND a.project_id = ? AND a.is_deleted = 0 ORDER BY a.order_num ASC ``` --- ## 🟡 第四步:实现裁判长选手列表(1天) ### 接口规范 ``` GET /api/mini/athletes/admin?competitionId=123&venueId=1&projectId=5 响应: { "code": 200, "success": true, "msg": "操作成功", "data": [ { "athleteId": "1", "name": "张三", "idCard": "123456789000000000", "team": "少林寺武术大学院", "number": "123-4567898275", "totalScore": 8.907, "judgeCount": 6, "totalJudges": 6, "canModify": true } ] } ``` ### SQL示例 ```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_project jp WHERE jp.project_id = ? AND jp.is_deleted = 0) AS totalJudges, CASE WHEN COUNT(s.id) = (SELECT COUNT(*) FROM martial_judge_project jp WHERE jp.project_id = ? AND jp.is_deleted = 0) 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 = ? AND a.project_id = ? AND a.is_deleted = 0 GROUP BY a.id ORDER BY a.order_num ASC ``` --- ## 🟡 第五步:实现评分详情接口(1天) ### 接口规范 ``` GET /api/mini/score/detail/1 响应: { "code": 200, "success": true, "msg": "操作成功", "data": { "athleteInfo": { "athleteId": "1", "name": "张三", "idCard": "123456789000000000", "team": "少林寺武术大学院", "number": "123-4567898275", "totalScore": 8.907 }, "judgeScores": [ { "judgeId": "1", "judgeName": "欧阳丽娜", "score": 8.907, "scoreTime": "2025-06-25 09:15:00", "note": "" } ], "modification": null } } ``` ### SQL示例 ```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 FROM martial_athlete a WHERE a.id = ? -- 评委评分 SELECT s.judge_id AS judgeId, s.judge_name AS judgeName, s.score, s.score_time AS scoreTime, s.note FROM martial_score s WHERE s.athlete_id = ? ORDER BY s.score_time ASC ``` --- ## 🟡 第六步:实现修改评分接口(1天) ### 接口规范 ``` PUT /api/mini/score/modify 请求: { "athleteId": "1", "modifierId": "789", "modifiedScore": 8.910, "note": "修改原因" } 响应: { "code": 200, "success": true, "msg": "修改成功", "data": { "athleteId": "1", "originalScore": 8.907, "modifiedScore": 8.910, "modifyTime": "2025-06-25 10:00:00" } } ``` ### 实现逻辑 ```java @PutMapping("/score/modify") public R modifyScore(@RequestBody ModifyScoreDTO dto) { // 1. 验证权限(只有裁判长可以修改) MartialJudgeInvite invite = judgeInviteService.getOne( Wrappers.lambdaQuery() .eq(MartialJudgeInvite::getJudgeId, dto.getModifierId()) .eq(MartialJudgeInvite::getRole, "chief_judge") .eq(MartialJudgeInvite::getIsDeleted, 0) ); if (invite == null) { return R.fail("无权限修改评分"); } // 2. 查询当前总分 MartialAthlete athlete = athleteService.getById(dto.getAthleteId()); BigDecimal originalScore = athlete.getTotalScore(); // 3. 更新选手总分 athlete.setTotalScore(dto.getModifiedScore()); athlete.setUpdatedBy(dto.getModifierId()); athlete.setUpdateTime(LocalDateTime.now()); athleteService.updateById(athlete); // 4. 记录修改信息(可以在athlete表中添加字段,或创建修改记录表) // ... // 5. 构建响应 ModifyResultVO vo = new ModifyResultVO(); vo.setAthleteId(dto.getAthleteId().toString()); vo.setOriginalScore(originalScore); vo.setModifiedScore(dto.getModifiedScore()); vo.setModifyTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); return R.success(vo); } ``` --- ## 📝 创建VO类 ### LoginVO.java ```java package org.springblade.modules.martial.vo; import lombok.Data; import java.util.List; @Data public class LoginVO { private String token; private String userRole; private String matchId; private String matchName; private String matchTime; private String judgeId; private String judgeName; private String venueId; private String venueName; private List projects; } ``` ### AthleteScoreVO.java ```java package org.springblade.modules.martial.vo; import lombok.Data; import java.math.BigDecimal; @Data public class AthleteScoreVO { private String athleteId; private String name; private String idCard; private String team; private String number; private BigDecimal myScore; private BigDecimal totalScore; private Boolean scored; private String scoreTime; } ``` ### AthleteAdminVO.java ```java package org.springblade.modules.martial.vo; import lombok.Data; import java.math.BigDecimal; @Data public class AthleteAdminVO { private String athleteId; private String name; private String idCard; private String team; private String number; private BigDecimal totalScore; private Integer judgeCount; private Integer totalJudges; private Boolean canModify; } ``` --- ## 🧪 测试接口 ### 使用Postman测试 #### 1. 测试登录 ``` POST http://localhost:8080/api/mini/login Content-Type: application/json { "matchCode": "123", "inviteCode": "pub" } ``` #### 2. 测试选手列表 ``` GET http://localhost:8080/api/mini/athletes?judgeId=456&venueId=1&projectId=5 Blade-Auth: Bearer {token} ``` --- ## ✅ 检查清单 ### 开发前 - [ ] 阅读接口规范文档 - [ ] 了解现有数据库表结构 - [ ] 准备测试数据 ### 开发中 - [ ] 创建 MartialMiniController - [ ] 创建所有VO类 - [ ] 实现登录接口 - [ ] 实现选手列表接口(2个) - [ ] 实现评分详情接口 - [ ] 实现修改评分接口 ### 开发后 - [ ] 单元测试通过 - [ ] Postman测试通过 - [ ] 更新Swagger文档 - [ ] 通知前端联调 --- ## 📞 需要帮助? - **详细规范**: [后端接口开发清单.md](./后端接口开发清单.md) - **前端对接**: [前端API对接指南.md](./前端API对接指南.md) - **技术对比**: [后端实现对比报告.md](./后端实现对比报告.md) --- **预计完成时间**: 6人天(约1周) 开始开发吧! 🚀