Bladeren bron

feat(game-event): 优化赛事分组功能并添加运动员统计

- 在 GameAthleteServiceImpl 中新增 countEligibleAthletes 方法用于统计符合条件的运动员
- 修改 GameEventGroupMapper 添加 selectPageWithAthleteCount 方法支持单SQL查询分组及运动员统计
- 在 GameEventGroupServiceImpl 中重构分页查询逻辑,使用单条SQL完成分组和人数统计
- 预获取混合性别字典值用于SQL统计逻辑,提升查询性能
- 添加删除标志过滤确保只查询未删除的分组数据
- 优化项目名称批量查询逻辑,减少数据库访问次数
- 更新注释和代码格式化,统一代码风格
- 添加符合条件的运动员人数字段到 GameEventGroupVo 中用于展示
zhou 1 week geleden
bovenliggende
commit
118c2378c9

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

@@ -128,6 +128,12 @@ public class GameEventGroupVo implements Serializable {
     @ExcelProperty(value = "备注")
     private String remark;
 
+    /**
+     * 符合条件的人数
+     */
+    @ExcelProperty(value = "符合条件的人数")
+    private Integer eligibleAthleteCount;
+
     /**
      * 道数显示模式(0第N道 1序号N)
      */

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

@@ -3,6 +3,8 @@ package org.dromara.system.mapper;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 import org.dromara.common.mybatis.annotation.DataColumn;
 import org.dromara.common.mybatis.annotation.DataPermission;
 import org.dromara.system.domain.GameEventGroup;
@@ -21,23 +23,32 @@ import java.util.List;
  */
 @Mapper
 public interface GameEventGroupMapper extends BaseMapperPlus<GameEventGroup, GameEventGroupVo> {
+
     /**
-     * 分页查询队伍列表,并进行数据权限控制
+     * 分页查询赛事分组列表,并关联查询符合条件的运动员人数
      */
     @DataPermission({
-        @DataColumn(key = "deptName", value = "create_dept"),
-        @DataColumn(key = "userName", value = "create_by")
+            @DataColumn(key = "deptName", value = "create_dept"),
+            @DataColumn(key = "userName", value = "create_by")
     })
-    default Page<GameEventGroupVo> selectPageEventGroupList(Page<GameEventGroup> page, Wrapper<GameEventGroup> queryWrapper) {
-        return this.selectVoPage(page, queryWrapper);
-    }
+    @Select("SELECT g.*, " +
+            "(SELECT COUNT(*) FROM game_athlete a " +
+            " WHERE a.event_id = g.event_id " +
+            " and a.del_flag = '0'"+
+            " AND JSON_CONTAINS(a.project_value, CAST(g.project_id AS CHAR)) " +
+            " AND (g.member_gender = '' OR g.member_gender IS NULL OR a.gender = g.member_gender OR g.member_gender = #{mixGenderValue}) "
+            +
+            ") AS eligible_athlete_count " +
+            "FROM game_event_group g ${ew.customSqlSegment}")
+    Page<GameEventGroupVo> selectPageWithAthleteCount(@Param("page") Page<GameEventGroup> page,
+            @Param("ew") Wrapper<GameEventGroup> queryWrapper, @Param("mixGenderValue") String mixGenderValue);
 
     /**
      * 获取队伍列表,并进行数据权限控制
      */
     @DataPermission({
-        @DataColumn(key = "deptName", value = "create_dept"),
-        @DataColumn(key = "userName", value = "create_by")
+            @DataColumn(key = "deptName", value = "create_dept"),
+            @DataColumn(key = "userName", value = "create_by")
     })
     default List<GameEventGroupVo> selectEventGroupList(Wrapper<GameEventGroup> queryWrapper) {
         return this.selectVoList(queryWrapper);

+ 10 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameAthleteService.java

@@ -130,4 +130,14 @@ public interface IGameAthleteService {
      * @return 运动员人数
      */
     Long selectAthleteCountByProjectId(Long eventId, Long projectId);
+
+    /**
+     * 统计符合条件的运动员人数
+     * @param eventId 赛事ID
+     * @param projectId 项目ID
+     * @param gender 性别限制
+     * @param mixGenderValue 混合性别的字典值
+     * @return 人数
+     */
+    Long countEligibleAthletes(Long eventId, Long projectId, String gender, String mixGenderValue);
 }

+ 14 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameAthleteServiceImpl.java

@@ -847,4 +847,18 @@ public class GameAthleteServiceImpl implements IGameAthleteService {
             .eq(eventId != null, GameAthlete::getEventId, eventId)
             .apply("JSON_CONTAINS(project_value, JSON_ARRAY({0}))", projectId));
     }
+
+    @Override
+    public Long countEligibleAthletes(Long eventId, Long projectId, String gender, String mixGenderValue) {
+        LambdaQueryWrapper<GameAthlete> lqw = new LambdaQueryWrapper<>();
+        lqw.eq(eventId != null, GameAthlete::getEventId, eventId);
+        lqw.apply(projectId != null, "JSON_CONTAINS(project_value, JSON_ARRAY({0}))", projectId);
+
+        // 性别过滤:如果不为空且不是“混合”模式,则增加性别过滤
+        if (StringUtils.isNotBlank(gender) && !gender.equals(mixGenderValue)) {
+            lqw.eq(GameAthlete::getGender, gender);
+        }
+
+        return baseMapper.selectCount(lqw);
+    }
 }

+ 150 - 111
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameEventGroupServiceImpl.java

@@ -113,32 +113,61 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
         if (bo.getEventId() == null) {
             Object cacheObject = RedisUtils.getCacheObject(GameEventConstant.DEFAULT_EVENT_ID);
             if (cacheObject instanceof Integer) {
-                bo.setEventId(((Integer) cacheObject).longValue());  // 显式转换为 Long 类型
+                bo.setEventId(((Integer) cacheObject).longValue());
             } else if (cacheObject instanceof Long) {
                 bo.setEventId((Long) cacheObject);
             }
         }
+
+        // 1. 预获取混合性别字典值,用于 SQL 统计逻辑
+        List<SysDictDataVo> genderDict = dictTypeService.selectDictDataByType("sys_group_sex");
+        String mixGenderValue = genderDict.stream()
+            .filter(g -> "混合".equals(g.getDictLabel()))
+            .map(SysDictDataVo::getDictValue)
+            .findFirst().orElse("");
+
         LambdaQueryWrapper<GameEventGroup> lqw = buildQueryWrapper(bo);
-//        Page<GameEventGroupVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
-        Page<GameEventGroupVo> result = baseMapper.selectPageEventGroupList(pageQuery.build(), lqw);
-        result.getRecords().stream()
-            .map(vo -> {
-                Optional.ofNullable(vo.getProjectList())
-                    .filter(StringUtils::isNotBlank)
-                    .ifPresent(projectValue -> {
-                        List<Long> projectIds = JSONUtil.toList(projectValue, Long.class);
-                        List<GameEventProjectVo> projects =
-                            gameEventProjectService.listProjectsByEventIdAndProjectIndex(
-                                vo.getEventId(),
-                                projectIds);
-                        vo.setProjectList(projects.stream()
-                            .map(GameEventProjectVo::getProjectName)
-                            .filter(StringUtils::isNotBlank)
-                            .collect(Collectors.joining(",")));
-                    });
-                return vo;
-            })
-            .toList();  // 收集结果
+        // 2. 使用单条 SQL 完成分页查询与符合条件的人数统计(标量子查询),实现最佳性能
+        Page<GameEventGroupVo> result = baseMapper.selectPageWithAthleteCount(pageQuery.build(), lqw, mixGenderValue);
+
+        List<GameEventGroupVo> records = result.getRecords();
+        if (records.isEmpty()) {
+            return TableDataInfo.build(result);
+        }
+
+        // 3. 收集所有涉及的项目ID,用于批量查询项目名称(保留此性能优化)
+        Set<Long> allProjectIds = new HashSet<>();
+        records.forEach(vo -> {
+            if (vo.getProjectId() != null) allProjectIds.add(vo.getProjectId());
+            if (StringUtils.isNotBlank(vo.getProjectList())) {
+                try {
+                    allProjectIds.addAll(JSONUtil.toList(vo.getProjectList(), Long.class));
+                } catch (Exception ignored) {}
+            }
+        });
+
+        // 批量查询项目信息并建立缓存 Map
+        Map<Long, String> projectNameMap = new HashMap<>();
+        if (!allProjectIds.isEmpty()) {
+            List<GameEventProjectVo> projects = gameEventProjectService.listProjectsByEventIdAndProjectIndex(bo.getEventId(), new ArrayList<>(allProjectIds));
+            projectNameMap = projects.stream().collect(Collectors.toMap(GameEventProjectVo::getProjectId, GameEventProjectVo::getProjectName, (k1, k2) -> k1));
+        }
+
+        // 4. 遍历处理结果:此时 eligibleAthleteCount 已经由 SQL 自动填充
+        for (GameEventGroupVo vo : records) {
+            // 处理项目列表展示名称:从内存 Map 中获取
+            if (StringUtils.isNotBlank(vo.getProjectList())) {
+                try {
+                    List<Long> pIds = JSONUtil.toList(vo.getProjectList(), Long.class);
+                    Map<Long, String> finalProjectNameMap = projectNameMap;
+                    vo.setProjectList(pIds.stream()
+                        .map(finalProjectNameMap::get)
+                        .filter(StringUtils::isNotBlank)
+                        .collect(Collectors.joining(",")));
+                } catch (Exception ignored) {}
+            }
+        }
+
         return TableDataInfo.build(result);
     }
 
@@ -153,13 +182,13 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
         if (bo.getEventId() == null) {
             Object cacheObject = RedisUtils.getCacheObject(GameEventConstant.DEFAULT_EVENT_ID);
             if (cacheObject instanceof Integer) {
-                bo.setEventId(((Integer) cacheObject).longValue());  // 显式转换为 Long 类型
+                bo.setEventId(((Integer) cacheObject).longValue()); // 显式转换为 Long 类型
             } else if (cacheObject instanceof Long) {
                 bo.setEventId((Long) cacheObject);
             }
         }
         LambdaQueryWrapper<GameEventGroup> lqw = buildQueryWrapper(bo);
-//        return baseMapper.selectVoList(lqw);
+        // return baseMapper.selectVoList(lqw);
         return baseMapper.selectEventGroupList(lqw);
     }
 
@@ -182,6 +211,7 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
         lqw.eq(bo.getTrackNum() != null, GameEventGroup::getTrackNum, bo.getTrackNum());
         lqw.eq(bo.getFieldNum() != null, GameEventGroup::getFieldNum, bo.getFieldNum());
         lqw.eq(bo.getDuration() != null, GameEventGroup::getDuration, bo.getDuration());
+        lqw.eq(GameEventGroup::getDelFlag, "0");
         return lqw;
     }
 
@@ -196,7 +226,7 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
         if (bo.getEventId() == null) {
             Object cacheObject = RedisUtils.getCacheObject(GameEventConstant.DEFAULT_EVENT_ID);
             if (cacheObject instanceof Integer) {
-                bo.setEventId(((Integer) cacheObject).longValue());  // 显式转换为 Long 类型
+                bo.setEventId(((Integer) cacheObject).longValue()); // 显式转换为 Long 类型
             } else if (cacheObject instanceof Long) {
                 bo.setEventId((Long) cacheObject);
             }
@@ -221,7 +251,7 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
         if (bo.getEventId() == null) {
             Object cacheObject = RedisUtils.getCacheObject(GameEventConstant.DEFAULT_EVENT_ID);
             if (cacheObject instanceof Integer) {
-                bo.setEventId(((Integer) cacheObject).longValue());  // 显式转换为 Long 类型
+                bo.setEventId(((Integer) cacheObject).longValue()); // 显式转换为 Long 类型
             } else if (cacheObject instanceof Long) {
                 bo.setEventId((Long) cacheObject);
             }
@@ -235,7 +265,7 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
      * 保存前的数据校验
      */
     private void validEntityBeforeSave(GameEventGroup entity) {
-        //TODO 做一些数据校验,如唯一约束
+        // TODO 做一些数据校验,如唯一约束
     }
 
     /**
@@ -248,7 +278,7 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
         if (isValid) {
-            //TODO 做一些业务上的校验,判断是否需要校验
+            // TODO 做一些业务上的校验,判断是否需要校验
         }
         ids.forEach(id -> getGameAthleteCompetitionGroupService().deleteGroupResultByGroupId(id));
         return baseMapper.deleteByIds(ids) > 0;
@@ -263,9 +293,9 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
     @Override
     public GameEventGroup queryByEventId(Long defaultEventId) {
         return baseMapper.selectList(
-            Wrappers.lambdaQuery(GameEventGroup.class)
-                .eq(GameEventGroup::getEventId, defaultEventId)
-        ).get(0);
+                Wrappers.lambdaQuery(GameEventGroup.class)
+                        .eq(GameEventGroup::getEventId, defaultEventId))
+                .get(0);
     }
 
     /**
@@ -290,7 +320,7 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
         Map<String, Object> result = new HashMap<>();
 
         try {
-            //获取性别字典数据
+            // 获取性别字典数据
             List<SysDictDataVo> genderDict = dictTypeService.selectDictDataByType("sys_group_sex");
             // 获取分组信息
             GameEventGroupVo groupInfo = queryById(groupId);
@@ -318,50 +348,50 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
 
             // 筛选符合条件的运动员
             List<GameAthleteVo> eligibleAthletes = athletes.stream()
-                .filter(athlete -> {
-                    // 检查是否参与该项目
-                    if (athlete.getProjectList() == null) {
-                        return false;
-                    }
+                    .filter(athlete -> {
+                        // 检查是否参与该项目
+                        if (athlete.getProjectList() == null) {
+                            return false;
+                        }
 
-                    // 处理项目列表
-                    List<String> projectIds = new ArrayList<>();
-                    if (athlete.getProjectList() != null) {
-                        projectIds = ((List<?>) athlete.getProjectList()).stream()
-                            .map(Object::toString)
-                            .toList();
-                    }
-//                    else if (athlete.getProjectList() instanceof String) {
-//                        String projectListStr = (String) athlete.getProjectList();
-//                        try {
-//                            projectIds = JSONUtil.toList(projectListStr, String.class);
-//                        } catch (Exception e) {
-//                            // 如果不是JSON格式,可能是逗号分隔的字符串
-//                            projectIds = List.of(projectListStr.split(","));
-//                        }
-//                    }
-
-                    String targetProjectId = groupInfo.getProjectId() != null ?
-                        groupInfo.getProjectId().toString() : null;
-                    if (targetProjectId == null || !projectIds.contains(targetProjectId)) {
-                        return false;
-                    }
+                        // 处理项目列表
+                        List<String> projectIds = new ArrayList<>();
+                        if (athlete.getProjectList() != null) {
+                            projectIds = ((List<?>) athlete.getProjectList()).stream()
+                                    .map(Object::toString)
+                                    .toList();
+                        }
+                        // else if (athlete.getProjectList() instanceof String) {
+                        // String projectListStr = (String) athlete.getProjectList();
+                        // try {
+                        // projectIds = JSONUtil.toList(projectListStr, String.class);
+                        // } catch (Exception e) {
+                        // // 如果不是JSON格式,可能是逗号分隔的字符串
+                        // projectIds = List.of(projectListStr.split(","));
+                        // }
+                        // }
+
+                        String targetProjectId = groupInfo.getProjectId() != null ? groupInfo.getProjectId().toString()
+                                : null;
+                        if (targetProjectId == null || !projectIds.contains(targetProjectId)) {
+                            return false;
+                        }
 
-                    // 检查性别是否匹配
-                    Optional<String> mixGender = genderDict.stream()
-                        .filter(g -> g.getDictLabel().equals("混合"))
-                        .map(SysDictDataVo::getDictValue)
-                        .findFirst();
-                    if (StringUtils.isNotBlank(groupInfo.getMemberGender())) {
-                        if (!groupInfo.getMemberGender().equals(
-                            athlete.getGender() != null ? athlete.getGender() : null)) {
-                            return groupInfo.getMemberGender().equals(mixGender.orElse(null));
+                        // 检查性别是否匹配
+                        Optional<String> mixGender = genderDict.stream()
+                                .filter(g -> g.getDictLabel().equals("混合"))
+                                .map(SysDictDataVo::getDictValue)
+                                .findFirst();
+                        if (StringUtils.isNotBlank(groupInfo.getMemberGender())) {
+                            if (!groupInfo.getMemberGender().equals(
+                                    athlete.getGender() != null ? athlete.getGender() : null)) {
+                                return groupInfo.getMemberGender().equals(mixGender.orElse(null));
+                            }
                         }
-                    }
 
-                    return true;
-                })
-                .toList();
+                        return true;
+                    })
+                    .toList();
 
             if (eligibleAthletes.isEmpty()) {
                 result.put("success", false);
@@ -386,7 +416,8 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
             int totalGroups = groupInfo.getIncludeGroupNum().intValue();
 
             // 每组分配人数
-            Long personsPerGroup = groupInfo.getPersonNum() != null ? groupInfo.getPersonNum() : groupInfo.getTrackNum();
+            Long personsPerGroup = groupInfo.getPersonNum() != null ? groupInfo.getPersonNum()
+                    : groupInfo.getTrackNum();
             if (personsPerGroup > groupInfo.getTrackNum()) {
                 log.warn("每组人数({})超过道数({}),将限制为道数", personsPerGroup, groupInfo.getTrackNum());
                 personsPerGroup = groupInfo.getTrackNum();
@@ -432,23 +463,25 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
 
                         // 只有项目属于团体项目才检查同一组中是否已有同一队伍的运动员
                         boolean isValidCandidate = true;
-                        if (project != null && ProjectClassification.TEAM.getValue().equals(project.getClassification())) {
+                        if (project != null
+                                && ProjectClassification.TEAM.getValue().equals(project.getClassification())) {
                             // 团体项目:检查同一组中是否已有同一队伍的运动员
                             boolean hasSameTeamInGroup = groupResult.entrySet().stream()
-                                .anyMatch(entry -> {
-                                    String[] keyParts = entry.getKey().split("-");
-                                    if (keyParts.length >= 1) {
-                                        String existingGroup = keyParts[0];
-                                        return existingGroup.equals(String.valueOf(finalGroupIndex)) &&
-                                               entry.getValue().getTeamId() != null &&
-                                               entry.getValue().getTeamId().equals(candidateAthlete.getTeamId());
-                                    }
-                                    return false;
-                                });
+                                    .anyMatch(entry -> {
+                                        String[] keyParts = entry.getKey().split("-");
+                                        if (keyParts.length >= 1) {
+                                            String existingGroup = keyParts[0];
+                                            return existingGroup.equals(String.valueOf(finalGroupIndex)) &&
+                                                    entry.getValue().getTeamId() != null &&
+                                                    entry.getValue().getTeamId().equals(candidateAthlete.getTeamId());
+                                        }
+                                        return false;
+                                    });
                             if (hasSameTeamInGroup) {
                                 isValidCandidate = false;
                                 log.debug("第{}组拒绝运动员{}:团体项目中同一组不能有同一队伍({})的运动员",
-                                    finalGroupIndex, candidateAthlete.getAthleteCode(), candidateAthlete.getTeamName());
+                                        finalGroupIndex, candidateAthlete.getAthleteCode(),
+                                        candidateAthlete.getTeamName());
                             }
                         }
 
@@ -466,19 +499,19 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
                         String key = finalGroupIndex + "-" + track;
                         groupResult.put(key, selectedAthlete);
                         log.debug("分配成功 - 第{}组第{}道: 运动员编号={}, 姓名={}, 队伍={}",
-                            finalGroupIndex, track, selectedAthlete.getAthleteCode(),
-                            selectedAthlete.getName(), selectedAthlete.getTeamName());
+                                finalGroupIndex, track, selectedAthlete.getAthleteCode(),
+                                selectedAthlete.getName(), selectedAthlete.getTeamName());
                     } else {
                         // 检查是否有可用但未分配的运动员
                         long availableCount = eligibleAthletes.stream()
-                            .filter(a -> !assignedAthleteIds.contains(a.getAthleteId()))
-                            .count();
+                                .filter(a -> !assignedAthleteIds.contains(a.getAthleteId()))
+                                .count();
                         if (availableCount > 0) {
                             log.warn("第{}组第{}道未找到合适的运动员,但仍有{}个符合条件的运动员未分配",
-                                finalGroupIndex, track, availableCount);
+                                    finalGroupIndex, track, availableCount);
                         } else {
                             log.warn("第{}组第{}道未找到合适的运动员,所有符合条件的运动员都已被分配",
-                                finalGroupIndex, track);
+                                    finalGroupIndex, track);
                         }
                     }
                 }
@@ -492,16 +525,16 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
             log.info("【已分配的运动员】");
             groupResult.forEach((key, athlete) -> {
                 log.info("  已分配 - 位置: {}, 运动员编号: {}, 姓名: {}, 队伍ID: {}, 队伍名称: {}",
-                    key, athlete.getAthleteCode(), athlete.getName(),
-                    athlete.getTeamId(), athlete.getTeamName());
+                        key, athlete.getAthleteCode(), athlete.getName(),
+                        athlete.getTeamId(), athlete.getTeamName());
             });
             log.info("已分配运动员数量: {}", groupResult.size());
 
             // 找出并记录未分配的运动员及原因
             log.info("【未分配的运动员】");
             List<GameAthleteVo> unassignedAthletes = eligibleAthletes.stream()
-                .filter(athlete -> !assignedAthleteIds.contains(athlete.getAthleteId()))
-                .toList();
+                    .filter(athlete -> !assignedAthleteIds.contains(athlete.getAthleteId()))
+                    .toList();
 
             if (unassignedAthletes.isEmpty()) {
                 log.info("  所有符合条件的运动员都已成功分配");
@@ -515,14 +548,16 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
                     // 分析未分配原因
                     if (groupResult.size() >= totalTracksNeeded) {
                         reason = String.format("道次已满(需要%d个道次,已分配%d个运动员)", totalTracksNeeded, groupResult.size());
-                    } else if (project != null && ProjectClassification.TEAM.getValue().equals(project.getClassification())) {
+                    } else if (project != null
+                            && ProjectClassification.TEAM.getValue().equals(project.getClassification())) {
                         // 团体项目:分析每个组中是否有该队伍
                         Map<Integer, Set<Long>> groupTeamMap = new HashMap<>();
                         groupResult.forEach((key, assignedAthlete) -> {
                             String[] keyParts = key.split("-");
                             if (keyParts.length >= 1) {
                                 int group = Integer.parseInt(keyParts[0]);
-                                groupTeamMap.computeIfAbsent(group, k -> new HashSet<>()).add(assignedAthlete.getTeamId());
+                                groupTeamMap.computeIfAbsent(group, k -> new HashSet<>())
+                                        .add(assignedAthlete.getTeamId());
                             }
                         });
 
@@ -533,26 +568,27 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
                             Set<Long> teamsInGroup = groupTeamMap.getOrDefault(g, new HashSet<>());
                             if (teamsInGroup.contains(athlete.getTeamId())) {
                                 blockedByTeamRule = true;
-                                if (!groupDetails.isEmpty()) groupDetails.append("、");
+                                if (!groupDetails.isEmpty())
+                                    groupDetails.append("、");
                                 groupDetails.append("第").append(g).append("组");
                             }
                         }
 
                         if (blockedByTeamRule) {
                             reason = String.format("团体项目限制:队伍(%s)在第%s组已有运动员,同组不能重复分配",
-                                athlete.getTeamName(), groupDetails.toString());
+                                    athlete.getTeamName(), groupDetails.toString());
                         } else {
                             reason = "未知原因:可能是分配算法逻辑问题";
                         }
                     } else {
                         // 个人项目:道次未满但没有分配,可能是算法问题
                         reason = String.format("道次未满(已分配%d/%d个道次),可能是分配算法限制",
-                            groupResult.size(), totalTracksNeeded);
+                                groupResult.size(), totalTracksNeeded);
                     }
 
                     log.warn("  未分配 - 运动员编号: {}, 姓名: {}, 队伍ID: {}, 队伍名称: {}, 原因: {}",
-                        athlete.getAthleteCode(), athlete.getName(),
-                        athlete.getTeamId(), athlete.getTeamName(), reason);
+                            athlete.getAthleteCode(), athlete.getName(),
+                            athlete.getTeamId(), athlete.getTeamName(), reason);
                 }
                 log.info("未分配运动员数量: {}", unassignedAthletes.size());
             }
@@ -560,7 +596,8 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
 
             // 保存分组结果到数据库
             try {
-                boolean saveSuccess = getGameAthleteCompetitionGroupService().saveGroupResult(groupId, groupResult, groupInfo);
+                boolean saveSuccess = getGameAthleteCompetitionGroupService().saveGroupResult(groupId, groupResult,
+                        groupInfo);
                 if (saveSuccess) {
                     log.info("分组结果已保存到数据库,分组ID: {}", groupId);
                 } else {
@@ -590,6 +627,7 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
     /**
      * 动态生成最优跑道分配顺序
      * 根据任意道数,先填所有组的中间道,然后填比中间道小1的道,再填比中间道大1的道,以此类推
+     *
      * @param totalTracks 总道数
      * @return 最优分配顺序的道次列表
      */
@@ -607,7 +645,7 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
         }
 
         // 计算中间位置
-        int middle = totalTracks%2==0 ? totalTracks / 2 : (totalTracks + 1) / 2;
+        int middle = totalTracks % 2 == 0 ? totalTracks / 2 : (totalTracks + 1) / 2;
         log.info("为{}道生成中间位置: {}", totalTracks, middle);
 
         // 从中间向两侧展开,先左后右(小的道次优先)
@@ -683,7 +721,8 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
         Map<Integer, Map<Integer, String>> matrixData = new TreeMap<>();
         for (Map.Entry<String, Object> entry : groupResult.entrySet()) {
             String key = entry.getKey();
-            if (!key.contains("-")) continue;
+            if (!key.contains("-"))
+                continue;
 
             String[] split = key.split("-");
             try {
@@ -692,9 +731,9 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
 
                 GameAthleteVo athleteVo = JSONUtil.toBean(JSONUtil.toJsonStr(entry.getValue()), GameAthleteVo.class);
                 String cellContent = String.format("%s\n%s\n%s",
-                    athleteVo.getAthleteCode(),
-                    athleteVo.getName(),
-                    athleteVo.getTeamName());
+                        athleteVo.getAthleteCode(),
+                        athleteVo.getName(),
+                        athleteVo.getTeamName());
 
                 matrixData.computeIfAbsent(groupIndex, k -> new HashMap<>()).put(trackIndex, cellContent);
             } catch (Exception ignored) {
@@ -726,10 +765,10 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
             HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(null, contentStyle);
 
             FastExcel.write(response.getOutputStream())
-                .head(head)
-                .registerWriteHandler(styleStrategy)
-                .sheet("分组详情")
-                .doWrite(dataList);
+                    .head(head)
+                    .registerWriteHandler(styleStrategy)
+                    .sheet("分组详情")
+                    .doWrite(dataList);
         } catch (Exception e) {
             log.error("导出分组详情矩阵失败", e);
             throw new RuntimeException("导出分组详情失败", e);