Explorar o código

refactor(game): 重构游戏事件模块的数据结构和成绩处理逻辑

- 移除GameAppProject类的TenantEntity继承和EqualsAndHashCode注解
- 简化GameAppProject类的比赛相关字段,只保留核心属性
- 移除GameAppScoreDetailBo中的attemptIndex、shiwu、isValid和remark字段
- 在ScorePreviewUpdateBo和ScorePreviewVo中添加失误次数和完成用时字段
- 移除ScoreSheetDetailVo中的attemptIndex和shiwu字段
- 在ScoreSheetVo中添加成绩数量字段
- 移除ScoreSubmitDetailBo中的attemptIndex和shiwu字段
- 更新数据库映射文件以支持新的字段结构
- 修改客户端服务实现以适配重构后的数据模型
- 添加计数和排名类成绩的整数转换功能
- 优化成绩提交和查询的处理逻辑
zhou hai 1 semana
pai
achega
88620d99b5

+ 2 - 118
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/GameAppProject.java

@@ -1,10 +1,9 @@
 package org.dromara.system.domain;
 
 import lombok.Data;
-import lombok.EqualsAndHashCode;
-import org.dromara.common.tenant.core.TenantEntity;
 
 import java.io.Serial;
+import java.io.Serializable;
 import java.util.Date;
 
 /**
@@ -14,8 +13,7 @@ import java.util.Date;
  * @date 2025-07-30
  */
 @Data
-@EqualsAndHashCode(callSuper = true)
-public class GameAppProject extends TenantEntity {
+public class GameAppProject implements Serializable {
 
     @Serial
     private static final long serialVersionUID = 1L;
@@ -47,128 +45,14 @@ public class GameAppProject extends TenantEntity {
      */
     private String classification;
 
-    /**
-     * 裁判组
-     */
-    private String refereeGroup;
-
-    /**
-     * 比赛场地
-     */
-    private String location;
-
-    /**
-     * 开始时间
-     */
-    private Date startTime;
-
-    /**
-     * 结束时间
-     */
-    private Date endTime;
-
-    /**
-     * 参赛组数
-     */
-    private Long groupNum;
-
-    /**
-     * 参赛人数
-     */
-    private Long participateNum;
-
-    /**
-     * 项目限报人数
-     */
-    private Integer limitPerson;
-
-    /**
-     * 录取名次
-     */
-    private String pmnum;
-
-    /**
-     * 排序方式
-     */
-    private String paiMing;
-
-    /**
-     * 计算规则
-     */
-    private String chengjiType;
-
-    /**
-     * 计时格式
-     */
-    private String guize;
-
-    /**
-     * 距离模式
-     */
-    private String distanceMode;
-
-    /**
-     * 计数单位
-     */
-    private String countUnit;
-
     /**
      * 成绩数量
      */
     private Integer ChengJiNum;
 
-    /**
-     * 积分分值
-     */
-    private String jifen;
-
-    /**
-     * 比赛轮次
-     */
-    private String lunci;
-
-    /**
-     * 比赛阶段
-     */
-    private String jieduan;
-
     /**
      * 状态(0正常 1已结束)
      */
     private String state;
-    /**
-     * 参赛性别
-     */
-    private String sex;
-
-    /**
-     * 参赛组别
-     */
-    private String zu;
-
-    /**
-     * 排名分组ID
-     */
-    private Long rgId;
-
-    /**
-     * 限报男生人数 (个人项目为总人数 / 团体项目为每队人数)
-     */
-    private Integer limitMale;
-
-    /**
-     * 限报女生人数 (个人项目为总人数 / 团体项目为每队人数)
-     */
-    private Integer limitFemale;
-
-    /**
-     * 团体项目限报队伍数
-     */
-    private Integer limitTeam;
-
-    /**
-     * 备注
-     */
-    private String remark;
 
 }

+ 0 - 21
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/GameAppScoreDetailBo.java

@@ -37,32 +37,11 @@ public class GameAppScoreDetailBo {
      */
     private Long id;
 
-    /**
-     * 轮次/尝试序号(如1,2,3表示第1,2,3次)
-     */
-    private Integer attemptIndex;
-
     /**
      * 单次原始成绩
      */
     @NotNull(message = "单次原始成绩不能为空", groups = { AddGroup.class, EditGroup.class })
     private BigDecimal num;
 
-    /**
-     * 单次失误次数
-     */
-    private Integer shiwu;
-
-    /**
-     * 是否有效(0有效 1犯规/无效)
-     */
-    private String isValid;
-
-    /**
-     * 单次备注(如:踩线)
-     */
-    private String remark;
-
-
 
 }

+ 4 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/ScorePreviewUpdateBo.java

@@ -28,6 +28,10 @@ public class ScorePreviewUpdateBo {
     /** 最终成绩 */
     @NotBlank(message = "最终成绩不能为空")
     private String score;
+    /** 失误次数 */
+    private Integer shiwu;
+    /** 完成用时 */
+    private String yongshi;
 
     /** 成绩明细列表 */
     private List<GameAppScoreDetailBo> chengji;

+ 0 - 4
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/ScoreSubmitDetailBo.java

@@ -16,10 +16,6 @@ public class ScoreSubmitDetailBo implements Serializable {
 
     /** 明细ID */
     private Long detailId;
-    /** 尝试序号 */
-    private Integer attemptIndex;
     /** 成绩值 */
     private BigDecimal num;
-    /** 失误次数 */
-    private Integer shiwu;
 }

+ 4 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/ScorePreviewVo.java

@@ -29,6 +29,10 @@ public class ScorePreviewVo implements Serializable {
     private String duiwu;
     /** 成绩 */
     private String score;
+    /** 失误次数 */
+    private Integer shiwu;
+    /** 完成用时 */
+    private String yongshi;
     /** 积分 */
     private Integer scorePoint;
     /** 名次 */

+ 3 - 6
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/ScoreSheetDetailVo.java

@@ -17,14 +17,11 @@ public class ScoreSheetDetailVo implements Serializable {
     private Long detailId;
     /** 成绩ID */
     private Long scoreId;
-    /** 第attemptIndex轮的成绩 */
-    private Integer attemptIndex;
-    /** 成绩值 */
-    private String num;
-    /** 失误次数 */
-    private Integer shiwu;
     /** 运动员ID (逻辑关联使用) */
     private Long Id;
     /** 队伍ID (逻辑关联使用) */
     private Long teamId;
+    /** 成绩值 */
+    private String num;
+
 }

+ 2 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/ScoreSheetVo.java

@@ -21,6 +21,8 @@ public class ScoreSheetVo implements Serializable {
     /** 成绩类型--字典game_score_type
      1-计时类;2-距离类;3-单次计数类;4-多次计数类;5-排名类;6-远度距离类;7-高度距离类  */
     private String chengjiType;
+    /** 成绩数量 */
+    private String ChengJiNum;
     /** 计算规则(game_order_type) 0-升序(1...9);1-降序(9...1);2-失误次数A(1...9);
      * 3-失误次数B(9...1);4-求和 ;5-最大值;6-最小值;7-平均值 */
     private String paiMing;

+ 12 - 5
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameScoreServiceImpl.java

@@ -599,8 +599,8 @@ public class GameScoreServiceImpl implements IGameScoreService {
         String rule = project.getScoreRule();
 
         // 1. 汇总失误次数 (通常是所有轮次的累加)
-        int totalFault = details.stream().mapToInt(d -> d.getFaultA() != null ? d.getFaultA() : 0).sum();
-        bo.setFaultA(totalFault);
+//        int totalFault = details.stream().mapToInt(d -> d.getFaultA() != null ? d.getFaultA() : 0).sum();
+//        bo.setFaultA(totalFault);
 
         // 2. 汇总主成绩
         List<BigDecimal> values = details.stream()
@@ -680,7 +680,14 @@ public class GameScoreServiceImpl implements IGameScoreService {
                     .apply("(JSON_CONTAINS(project_value, CAST({0} AS JSON)) OR JSON_CONTAINS(project_value, JSON_ARRAY(CAST({0} AS CHAR))))",
                             bo.getProjectId())
                     .eq("del_flag", "0"));
-            if (athleteIds.isEmpty()) {
+
+            // 将查出的运动员列表包装为可修改的 ArrayList,并强行加入当前上传的运动员(防止其因未绑定项目而被忽略)
+            List<Long> athleteIdList = new ArrayList<>(athleteIds);
+            if (bo.getAthleteId() != null && !athleteIdList.contains(bo.getAthleteId())) {
+                athleteIdList.add(bo.getAthleteId());
+            }
+
+            if (athleteIdList.isEmpty()) {
                 log.warn("未找到队伍 {} 的运动员", bo.getTeamId());
                 return false;
             }
@@ -689,13 +696,13 @@ public class GameScoreServiceImpl implements IGameScoreService {
             Map<Long, Long> athleteScoreIdMap = baseMapper.selectList(Wrappers.<GameScore>lambdaQuery()
                     .select(GameScore::getScoreId, GameScore::getAthleteId)
                     .eq(GameScore::getProjectId, bo.getProjectId())
-                    .in(GameScore::getAthleteId, athleteIds))
+                    .in(GameScore::getAthleteId, athleteIdList))
                     .stream()
                     .collect(Collectors.toMap(GameScore::getAthleteId, GameScore::getScoreId, (v1, v2) -> v1));
 
             List<GameScore> scoreList = new ArrayList<>();
             // 为每个运动员创建或更新成绩记录
-            for (Long athleteId : athleteIds) {
+            for (Long athleteId : athleteIdList) {
                 GameScore athleteScore = new GameScore();
                 athleteScore.setScoreId(athleteScoreIdMap.get(athleteId)); // 设置已存在的 ID
                 athleteScore.setEventId(bo.getEventId());

+ 65 - 18
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/app/ToClientServiceImpl.java

@@ -35,6 +35,7 @@ import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -220,21 +221,13 @@ public class ToClientServiceImpl implements IToClientService {
                 .in(GameEventProject::getProjectId, projectIds)
                 .select(GameEventProject::getProjectId, GameEventProject::getProjectType,
                         GameEventProject::getClassification, GameEventProject::getProjectName,
-                        GameEventProject::getStatus));
+                        GameEventProject::getStatus, GameEventProject::getScoreCount));
         return res.isEmpty() ? new ArrayList<>() : res.stream().map(p -> {
             GameAppProject appProject = BeanUtil.copyProperties(p, GameAppProject.class);
             appProject.setId(p.getEventId());
             appProject.setState(p.getStatus());
-            appProject.setSex(p.getGender());
             appProject.setType(typeMaps.get(p.getProjectType()));
-            appProject.setPmnum(p.getRoundType());
-            appProject.setPaiMing(p.getOrderType());
             appProject.setChengJiNum(p.getScoreCount());
-            appProject.setJifen(p.getScoreValue());
-            appProject.setGuize(p.getTimingFormat());
-            appProject.setLunci(p.getGameRound());
-            appProject.setJieduan(p.getGameStage());
-            appProject.setZu(p.getRgName());
             return appProject;
         }).toList();
     }
@@ -246,6 +239,8 @@ public class ToClientServiceImpl implements IToClientService {
         if (vo == null) {
             throw new RuntimeException("该赛事下不存在此项目");
         }
+        // 暂存原始成绩类型,用于后续判断是否是计数/排名类
+        String rawScoreRule = vo.getChengjiType();
 
         // 批量查询字典数据,避免多次调用 Redis/MySQL 交互
         Map<String, List<SysDictDataVo>> dictMap = dictService.selectDictDataByTypes(List.of(
@@ -322,6 +317,23 @@ public class ToClientServiceImpl implements IToClientService {
             });
         }
 
+        // 5. 处理计数和排名类的成绩,转换为整数
+        String rule = rawScoreRule != null ? rawScoreRule.trim() : "";
+        if ("3".equals(rule) || "4".equals(rule) || "5".equals(rule)) {
+            items.forEach(item -> {
+                if (StringUtils.isNotBlank(item.getScore())) {
+                    item.setScore(convertToIntegerScore(item.getScore()));
+                }
+                if (CollectionUtils.isNotEmpty(item.getChengji())) {
+                    item.getChengji().forEach(detail -> {
+                        if (StringUtils.isNotBlank(detail.getNum())) {
+                            detail.setNum(convertToIntegerScore(detail.getNum()));
+                        }
+                    });
+                }
+            });
+        }
+
         vo.setUser(items);
         return vo;
     }
@@ -348,9 +360,7 @@ public class ToClientServiceImpl implements IToClientService {
         ScoreSheetDetailVo dvo = new ScoreSheetDetailVo();
         dvo.setDetailId(d.getDetailId());
         dvo.setScoreId(d.getScoreId());
-        dvo.setAttemptIndex(d.getAttemptIndex());
         dvo.setNum(d.getPerformanceValue() != null ? d.getPerformanceValue().toString() : null);
-        dvo.setShiwu(d.getFaultA());
         // 传递关联 ID 供分组使用
         dvo.setId(d.getAthleteId());
         dvo.setTeamId(d.getTeamId());
@@ -402,6 +412,25 @@ public class ToClientServiceImpl implements IToClientService {
         }
     }
 
+    /**
+     * 将计数或排名类成绩转换为整数,去除小数点后面的部分
+     *
+     * @param scoreStr 成绩字符串
+     * @return 整数成绩字符串
+     */
+    private String convertToIntegerScore(String scoreStr) {
+        if (StringUtils.isBlank(scoreStr)) {
+            return scoreStr;
+        }
+        try {
+            BigDecimal decimal = new BigDecimal(scoreStr.trim());
+            return decimal.setScale(0, RoundingMode.HALF_UP).toPlainString();
+        } catch (Exception e) {
+            // 解析失败(例如DNF等非数值特殊状态),保留原样
+            return scoreStr;
+        }
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void submitScoreSheet(ScoreSubmitBo bo) {
@@ -426,9 +455,7 @@ public class ToClientServiceImpl implements IToClientService {
             if (CollectionUtils.isNotEmpty(item.getChengji())) {
                 for (ScoreSubmitDetailBo detail : item.getChengji()) {
                     if (detail.getNum() == null) {
-                        String attemptStr = detail.getAttemptIndex() != null ? "第 " + detail.getAttemptIndex() + " 轮"
-                                : "明细";
-                        throw new RuntimeException("提交失败:" + displayName + " 的" + attemptStr + "成绩不能为空");
+                        throw new RuntimeException("提交失败:" + displayName + " 的" + "明细成绩不能为空");
                     }
                 }
             } else {
@@ -540,12 +567,12 @@ public class ToClientServiceImpl implements IToClientService {
 
             // 统一模式:优先处理明细列表
             if (CollectionUtils.isNotEmpty(item.getChengji())) {
+                AtomicInteger attemptIndex = new AtomicInteger(1);
                 List<GameScoreDetailBo> details = item.getChengji().stream().map(d -> {
                     GameScoreDetailBo dbo = new GameScoreDetailBo();
                     dbo.setDetailId(d.getDetailId());
-                    dbo.setAttemptIndex(d.getAttemptIndex());
+                    dbo.setAttemptIndex(attemptIndex.getAndIncrement());
                     dbo.setPerformanceValue(d.getNum());
-                    dbo.setFaultA(d.getShiwu());
                     return dbo;
                 }).toList();
                 scoreBo.setDetails(details);
@@ -657,6 +684,24 @@ public class ToClientServiceImpl implements IToClientService {
                 }
             });
         }
+
+        // 3. 处理计数和排名类的成绩,转换为整数
+        String rawScoreRule = project.getScoreRule();
+        String rule = rawScoreRule != null ? rawScoreRule.trim() : "";
+        if ("3".equals(rule) || "4".equals(rule) || "5".equals(rule)) {
+            list.forEach(item -> {
+                if (StringUtils.isNotBlank(item.getScore())) {
+                    item.setScore(convertToIntegerScore(item.getScore()));
+                }
+                if (item.getChengji() != null) {
+                    item.getChengji().forEach(detail -> {
+                        if (StringUtils.isNotBlank(detail.getNum())) {
+                            detail.setNum(convertToIntegerScore(detail.getNum()));
+                        }
+                    });
+                }
+            });
+        }
         return list;
     }
 
@@ -676,15 +721,17 @@ public class ToClientServiceImpl implements IToClientService {
         scoreBo.setProjectId(score.getProjectId());
         scoreBo.setAthleteId(score.getAthleteId());
         scoreBo.setTeamId(score.getTeamId());
+        scoreBo.setFaultA(bo.getShiwu());
+        scoreBo.setCompleteTime(parsePerformanceValue(bo.getYongshi()));
 
         if (CollectionUtils.isNotEmpty(bo.getChengji())) {
             // 如果提交了明细列表,则优先更新明细
+            AtomicInteger index = new AtomicInteger(1);
             List<GameScoreDetailBo> details = bo.getChengji().stream().map(d -> {
                 GameScoreDetailBo dbo = new GameScoreDetailBo();
                 dbo.setDetailId(d.getDetailId());
-                dbo.setAttemptIndex(d.getAttemptIndex());
+                dbo.setAttemptIndex(index.getAndIncrement());
                 dbo.setPerformanceValue(d.getNum());
-                dbo.setFaultA(d.getShiwu());
                 return dbo;
             }).toList();
             scoreBo.setDetails(details);

+ 18 - 0
ruoyi-modules/ruoyi-game-event/src/main/resources/mapper/system/app/ToClientMapper.xml

@@ -8,6 +8,7 @@
             e.event_name as name,
             e.event_code as bianhao,
             p.score_rule as chengjiType,
+            p.score_count as ChengJiNum,
             p.order_type as paiMing,
             p.project_name as projectName,
             p.project_type as type,
@@ -82,6 +83,8 @@
                 WHEN p.classification = '0' THEN CAST(s.individual_performance AS CHAR)
                 ELSE CAST(s.team_performance AS CHAR)
             END as score,
+            s.fault_a as shiwu,
+            s.complete_time as yongshi,
             s.score_point as scorePoint,
             s.score_rank as scoreRank
         FROM game_score s
@@ -89,6 +92,21 @@
         LEFT JOIN game_team t ON s.team_id = t.team_id AND t.del_flag = '0'
         JOIN game_event_project p ON s.project_id = p.project_id
         WHERE s.project_id = #{projectId} AND s.del_flag = '0'
+          AND (
+              (p.classification = '0' AND a.athlete_id IS NOT NULL AND (
+                  JSON_CONTAINS(a.project_value, JSON_ARRAY(#{projectId}))
+                  OR JSON_CONTAINS(a.project_value, JSON_ARRAY(CAST(#{projectId} AS CHAR)))
+              ))
+              OR
+              (p.classification = '1' AND t.team_id IS NOT NULL AND EXISTS (
+                  SELECT 1 FROM game_athlete a2
+                  WHERE a2.team_id = s.team_id AND a2.del_flag = '0'
+                  AND (
+                      JSON_CONTAINS(a2.project_value, JSON_ARRAY(#{projectId}))
+                      OR JSON_CONTAINS(a2.project_value, JSON_ARRAY(CAST(#{projectId} AS CHAR)))
+                  )
+              ))
+          )
         ORDER BY s.score_rank ASC, s.score_id DESC
     </select>
 </mapper>