Bläddra i källkod

feat(game): 添加小程序体验端成绩排行榜功能

- 新增 ExGameScoreVo 数据传输对象用于成绩排行展示
- 在 ExperienceVersionController 中添加 /score 接口支持成绩排行榜查询
- 实现 GameScoreMapper 的 selectExRankingBoardData 查询方法
- 完善 GameScoreService 的 getExRankingBoardData 业务逻辑
- 添加个人项目和团队项目的不同排名数据查询逻辑
- 实现成绩数据的格式化处理(计时类、距离类、计数类等)
- 集成计时格式转换和单位显示功能
zhou 1 dag sedan
förälder
incheckning
42499652f4

+ 15 - 5
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/app/ExperienceVersionController.java

@@ -8,13 +8,12 @@ import org.dromara.common.core.domain.R;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.tenant.helper.TenantHelper;
 import org.dromara.system.domain.GameEvent;
+import org.dromara.system.domain.vo.RankingBoardVO;
 import org.dromara.system.domain.vo.app.*;
 import org.dromara.system.domain.bo.ExperienceEnrollSubmitBo;
-import org.dromara.system.mapper.GameEventConfigMapper;
-import org.dromara.system.mapper.GameEventGroupMapper;
-import org.dromara.system.mapper.GameEventMapper;
-import org.dromara.system.mapper.GameEventProjectMapper;
+import org.dromara.system.mapper.*;
 import org.dromara.system.service.app.IUserEventService;
+import org.dromara.system.service.IGameScoreService;
 import org.dromara.system.mapper.app.GameUserMapper;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
@@ -34,11 +33,12 @@ import java.util.*;
 public class ExperienceVersionController {
 
     private final GameEventMapper gameEventMapper;
-    private final GameEventConfigMapper gameEventConfigMapper;
     private final IUserEventService userEventService;
     private final GameUserMapper gameUserMapper;
     private final GameEventProjectMapper projectMapper;
     private final GameEventGroupMapper groupMapper;
+    private final GameScoreMapper scoreMapper;
+    private final IGameScoreService gameScoreService;
 
     /**
      * 1、登录接口(微信小程序授权登录)
@@ -163,4 +163,14 @@ public class ExperienceVersionController {
     public R<List<ExGameGroupDetailVo>> getGroupDetail(@RequestParam("groupId") Long groupId) {
         return R.ok(TenantHelper.ignore(() -> projectMapper.selectGroupDetail(groupId)));
     }
+
+    /**
+     * 13、成绩排行榜接口
+     * classification: 0-个人 1-团队
+     */
+    @GetMapping("/score")
+    public R<List<ExGameScoreVo>> getScoreRank(@RequestParam("eventId") Long eventId, @RequestParam("projectId") Long projectId, @RequestParam("classification") String classification) {
+        return R.ok(TenantHelper.ignore(() -> gameScoreService.getExRankingBoardData(eventId, projectId, classification)));
+    }
+
 }

+ 51 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/ExGameScoreVo.java

@@ -0,0 +1,51 @@
+package org.dromara.system.domain.vo.app;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+@Data
+public class ExGameScoreVo implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 排名
+     */
+    private Integer rank;
+
+    /**
+     * 姓名/队伍名称
+     */
+    private String name;
+
+    /**
+     * 代表队名称
+     */
+    private String teamName;
+
+    /**
+     * 成绩显示文本
+     */
+    private String score;
+
+    /**
+     * 成绩类型(如 1-计时类等)
+     */
+    @JsonIgnore
+    private String scoreRule;
+    /**
+     * 计数单位
+     */
+    @JsonIgnore
+    private String countUnit;
+
+    /**
+     * 计时格式 (0---00:00:00.000, 1---00:00.00)
+     */
+    @JsonIgnore
+    private String timingFormat;
+
+}

+ 8 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/mapper/GameScoreMapper.java

@@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.dromara.system.domain.vo.GameProjectScoreVo;
+import org.dromara.system.domain.vo.app.ExGameScoreVo;
 import org.dromara.system.domain.vo.GameProjectStatsVo;
 import org.dromara.system.domain.GameScore;
 import org.dromara.system.domain.vo.GameScoreVo;
@@ -91,4 +92,11 @@ public interface GameScoreMapper extends BaseMapperPlus<GameScore, GameScoreVo>
 
     // 根据运动员ID删除运动员成绩
     int deleteByAthleteIds(@Param("athleteIds") Collection<Long> athleteIds);
+
+    /**
+     * 获取小程序体验端看板排名数据
+     */
+    List<ExGameScoreVo> selectExRankingBoardData(@Param("eventId") Long eventId,
+            @Param("projectId") Long projectId,
+            @Param("classification") String classification);
 }

+ 6 - 1
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameScoreService.java

@@ -1,12 +1,12 @@
 package org.dromara.system.service;
 
 import org.dromara.system.domain.vo.AppScoreVo;
+import org.dromara.system.domain.vo.app.ExGameScoreVo;
 import org.dromara.system.domain.vo.GameScoreVo;
 import org.dromara.system.domain.vo.RankingBoardVO;
 import org.dromara.system.domain.bo.GameScoreBo;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.mybatis.core.page.PageQuery;
-import org.dromara.system.domain.vo.GameTeamVo;
 
 import java.util.Collection;
 import java.util.List;
@@ -188,4 +188,9 @@ public interface IGameScoreService {
      * 获取导入模板
      */
     void importTemplate(Long eventId, Long projectId, String classification, HttpServletResponse response);
+
+    /**
+     * 获取小程序体验端看板排名数据,并按格式及单位进行成绩格式化
+     */
+    List<ExGameScoreVo> getExRankingBoardData(Long eventId, Long projectId, String classification);
 }

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

@@ -28,6 +28,7 @@ import org.dromara.system.domain.constant.GameEventConstant;
 import org.dromara.system.domain.constant.ProjectClassification;
 import org.dromara.system.domain.constant.SortType;
 import org.dromara.system.domain.vo.*;
+import org.dromara.system.domain.vo.app.ExGameScoreVo;
 import org.dromara.system.mapper.*;
 import org.dromara.system.service.*;
 import org.springframework.stereotype.Service;
@@ -212,9 +213,9 @@ public class GameScoreServiceImpl implements IGameScoreService {
         List<RankingBoardVO> list = baseMapper.selectRankingBoardData(eventId, projectId, classification, rgId);
 
         // 3. 处理成绩显示格式(针对计时类进行转换)
-        if (isTiming && list != null) {
+        if (list != null) {
             for (RankingBoardVO item : list) {
-                if (StringUtils.isNotBlank(item.getScore())) {
+                if (isTiming && StringUtils.isNotBlank(item.getScore())) {
                     try {
                         BigDecimal decimalScore = new BigDecimal(item.getScore());
                         String timeFormat = convertDecimalToTimeScore(decimalScore);
@@ -224,12 +225,104 @@ public class GameScoreServiceImpl implements IGameScoreService {
                     } catch (Exception e) {
                         log.warn("看板排名数据成绩转换失败: {}, score: {}", item.getName(), item.getScore());
                     }
+                }else {
+                    BigDecimal decimalScore = new BigDecimal(item.getScore());
+                    // 去除尾随零
+                    String scoreStr = decimalScore.stripTrailingZeros().toPlainString();
+                    item.setScore(scoreStr);
                 }
             }
         }
         return list;
     }
 
+    @Override
+    public List<ExGameScoreVo> getExRankingBoardData(Long eventId, Long projectId, String classification) {
+        // 1. 调用 Mapper 获取原始排名数据
+        List<ExGameScoreVo> list = baseMapper.selectExRankingBoardData(eventId, projectId, classification);
+        if (list == null || list.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        // 2. 数据格式化等加工操作
+        for (ExGameScoreVo item : list) {
+            if (StringUtils.isNotBlank(item.getScore())) {
+                try {
+                    BigDecimal decimalScore = new BigDecimal(item.getScore());
+                    // 去除尾随零
+                    String scoreStr = decimalScore.stripTrailingZeros().toPlainString();
+                    String rule = item.getScoreRule();
+
+                    if ("1".equals(rule)) {
+                        // 计时类根据 timingFormat 计时格式进行格式化
+                        String format = item.getTimingFormat();
+                        if ("1".equals(format)) {
+                            // 1---00:00.00 分:秒.毫秒 (保留两位毫秒/百分秒)
+                            long totalSeconds = decimalScore.longValue();
+                            BigDecimal fraction = decimalScore.subtract(BigDecimal.valueOf(totalSeconds));
+                            long hundredths = fraction.multiply(BigDecimal.valueOf(100))
+                                    .setScale(0, java.math.RoundingMode.HALF_UP)
+                                    .longValue();
+                            long minutes = totalSeconds / 60;
+                            long seconds = totalSeconds % 60;
+                            if (hundredths >= 100) {
+                                seconds += hundredths / 100;
+                                hundredths = hundredths % 100;
+                            }
+                            if (seconds >= 60) {
+                                minutes += seconds / 60;
+                                seconds = seconds % 60;
+                            }
+                            item.setScore(String.format("%02d:%02d.%02d", minutes, seconds, hundredths));
+                        } else {
+                            // 默认计时格式或 0---00:00:00.000 时:分:秒.毫秒
+                            long totalSeconds = decimalScore.longValue();
+                            BigDecimal fraction = decimalScore.subtract(BigDecimal.valueOf(totalSeconds));
+                            long millis = fraction.multiply(BigDecimal.valueOf(1000))
+                                    .setScale(0, java.math.RoundingMode.HALF_UP)
+                                    .longValue();
+                            long hours = totalSeconds / 3600;
+                            long minutes = (totalSeconds % 3600) / 60;
+                            long seconds = totalSeconds % 60;
+                            if (millis >= 1000) {
+                                seconds += millis / 1000;
+                                millis = millis % 1000;
+                            }
+                            if (seconds >= 60) {
+                                minutes += seconds / 60;
+                                seconds = seconds % 60;
+                            }
+                            if (minutes >= 60) {
+                                hours += minutes / 60;
+                                minutes = minutes % 60;
+                            }
+                            item.setScore(String.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, millis));
+                        }
+                    } else if ("2".equals(rule) || "6".equals(rule) || "7".equals(rule)) {
+                        // 距离类、远度距离类、高度距离类附加 m
+                        item.setScore(scoreStr + "m");
+                    } else if ("3".equals(rule) || "4".equals(rule)) {
+                        // 计数类附加项目配置的计数单位
+                        String unit = item.getCountUnit();
+                        if (StringUtils.isNotBlank(unit)) {
+                            item.setScore(scoreStr + unit);
+                        } else {
+                            item.setScore(scoreStr);
+                        }
+                    } else {
+                        // 其他类型直接设置
+                        item.setScore(scoreStr);
+                    }
+                } catch (Exception e) {
+                    log.warn("体验端排名数据成绩转换失败: {}, score: {}", item.getName(), item.getScore());
+                }
+            } else {
+                item.setScore("");
+            }
+        }
+        return list;
+    }
+
     /**
      * 校验并批量删除成绩信息
      *
@@ -599,8 +692,9 @@ 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()

+ 46 - 0
ruoyi-modules/ruoyi-game-event/src/main/resources/mapper/system/GameScoreMapper.xml

@@ -213,4 +213,50 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
           </if>
         ORDER BY t.create_time ASC
     </select>
+
+    <select id="selectExRankingBoardData" resultType="org.dromara.system.domain.vo.app.ExGameScoreVo">
+        <choose>
+            <when test='classification == "0"'>
+                <!-- 个人项目 -->
+                SELECT
+                    s.score_rank as `rank`,
+                    a.name as `name`,
+                    t.team_name as teamName,
+                    CAST(s.individual_performance AS CHAR) as score,
+                    p.score_rule as scoreRule,
+                    p.count_unit as countUnit,
+                    p.timing_format as timingFormat
+                FROM game_score s
+                LEFT JOIN game_athlete a ON s.athlete_id = a.athlete_id
+                LEFT JOIN game_team t ON a.team_id = t.team_id
+                LEFT JOIN game_event_project p ON s.project_id = p.project_id AND p.del_flag = '0'
+                WHERE s.event_id = #{eventId}
+                  AND s.project_id = #{projectId}
+                  AND s.del_flag = '0'
+                  AND s.score_rank IS NOT NULL AND s.score_rank > 0
+                ORDER BY s.score_rank ASC
+            </when>
+            <otherwise>
+                <!-- 团体项目 -->
+                SELECT
+                    s.score_rank as `rank`,
+                    t.team_name as `name`,
+                    t.team_name as teamName,
+                    CAST(s.team_performance AS CHAR) as score,
+                    p.score_rule as scoreRule,
+                    p.count_unit as countUnit,
+                    p.timing_format as timingFormat
+                FROM (
+                    SELECT DISTINCT team_id, score_rank, team_performance, event_id, project_id
+                    FROM game_score
+                    WHERE event_id = #{eventId} AND project_id = #{projectId} AND del_flag = '0'
+                ) s
+                LEFT JOIN game_team t ON s.team_id = t.team_id
+                LEFT JOIN game_event_project p ON s.project_id = p.project_id AND p.del_flag = '0'
+                WHERE s.score_rank IS NOT NULL AND s.score_rank > 0
+                ORDER BY s.score_rank ASC
+            </otherwise>
+        </choose>
+    </select>
+
 </mapper>