ソースを参照

feat(gameEventGroup): 添加赛事分组详情导出功能

- 新增 exportDetail 方法用于导出分组详情矩阵 Excel
- 实现分组数据按组别和道次组织的矩阵展示
- 集成 FastExcel 库支持 Excel 文件生成
- 添加自动换行和居中对齐的单元格样式
- 支持不同道次显示模式(第N道/序号N)
- 在控制器中添加 /exportDetail 接口映射
- 整合 POI 和 FileUtils 实现文件下载响应
- 优化代码结构并调整导入包顺序
zhou 1 週間 前
コミット
b9e4fbc24b

+ 15 - 10
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameEventGroupController.java

@@ -1,8 +1,6 @@
 package org.dromara.system.controller;
 
 import java.util.List;
-import java.util.Map;
-
 import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.*;
@@ -22,6 +20,7 @@ import org.dromara.system.domain.vo.GameEventGroupVo;
 import org.dromara.system.domain.bo.GameEventGroupBo;
 import org.dromara.system.service.IGameEventGroupService;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
+import java.util.Map;
 
 /**
  * 赛事分组
@@ -57,6 +56,16 @@ public class GameEventGroupController extends BaseController {
         ExcelUtil.exportExcel(list, "赛事分组", GameEventGroupVo.class, response);
     }
 
+    /**
+     * 导出赛事分组详情
+     */
+    @SaCheckPermission("system:gameEventGroup:export")
+    @Log(title = "赛事分组详情", businessType = BusinessType.EXPORT)
+    @PostMapping("/exportDetail")
+    public void exportDetail(GameEventGroupBo bo, HttpServletResponse response) {
+        gameEventGroupService.exportDetail(bo, response);
+    }
+
     /**
      * 获取赛事分组详细信息
      *
@@ -64,8 +73,7 @@ public class GameEventGroupController extends BaseController {
      */
     @SaCheckPermission("system:gameEventGroup:query")
     @GetMapping("/{groupId}")
-    public R<GameEventGroupVo> getInfo(@NotNull(message = "主键不能为空")
-                                     @PathVariable Long groupId) {
+    public R<GameEventGroupVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long groupId) {
         return R.ok(gameEventGroupService.queryById(groupId));
     }
 
@@ -99,8 +107,7 @@ public class GameEventGroupController extends BaseController {
     @SaCheckPermission("system:gameEventGroup:remove")
     @Log(title = "赛事分组", businessType = BusinessType.DELETE)
     @DeleteMapping("/{groupIds}")
-    public R<Void> remove(@NotEmpty(message = "主键不能为空")
-                          @PathVariable Long[] groupIds) {
+    public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] groupIds) {
         return toAjax(gameEventGroupService.deleteWithValidByIds(List.of(groupIds), true));
     }
 
@@ -113,8 +120,7 @@ public class GameEventGroupController extends BaseController {
     @Log(title = "赛事生成分组结果", businessType = BusinessType.INSERT)
     @RepeatSubmit()
     @GetMapping("/generateGroups/{groupId}")
-    public R<Map<String, Object>> generateGroups(@NotNull(message = "分组ID不能为空")
-                                               @PathVariable Long groupId) {
+    public R<Map<String, Object>> generateGroups(@NotNull(message = "分组ID不能为空") @PathVariable Long groupId) {
         return R.ok(gameEventGroupService.generateGroups(groupId));
     }
 
@@ -125,8 +131,7 @@ public class GameEventGroupController extends BaseController {
      */
     @SaCheckPermission("system:gameEventGroup:query")
     @GetMapping("/getGroupResultFromDB/{groupId}")
-    public R<Map<String, Object>> getGroupResultFromDB(@NotNull(message = "分组ID不能为空")
-                                                     @PathVariable Long groupId) {
+    public R<Map<String, Object>> getGroupResultFromDB(@NotNull(message = "分组ID不能为空") @PathVariable Long groupId) {
         return R.ok(gameEventGroupService.getGroupResultFromDB(groupId));
     }
 }

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

@@ -6,6 +6,8 @@ import org.dromara.system.domain.bo.GameEventGroupBo;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.mybatis.core.page.PageQuery;
 
+import jakarta.servlet.http.HttpServletResponse;
+
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -90,4 +92,12 @@ public interface IGameEventGroupService {
      * @return 分组结果
      */
     Map<String, Object> getGroupResultFromDB(Long groupId);
+
+    /**
+     * 导出分组详情矩阵 Excel
+     *
+     * @param bo 查询条件
+     * @param response 响应体
+     */
+    void exportDetail(GameEventGroupBo bo, HttpServletResponse response);
 }

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

@@ -19,6 +19,14 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.dromara.system.domain.bo.GameEventGroupBo;
 import org.dromara.system.domain.constant.GameEventConstant;
 import org.dromara.system.domain.constant.ProjectClassification;
+import cn.hutool.core.convert.Convert;
+import cn.idev.excel.FastExcel;
+import cn.idev.excel.write.metadata.style.WriteCellStyle;
+import cn.idev.excel.write.style.HorizontalCellStyleStrategy;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.dromara.common.core.utils.file.FileUtils;
 import org.dromara.system.domain.GameEventGroup;
 import org.dromara.system.mapper.GameEventGroupMapper;
 import jakarta.annotation.PostConstruct;
@@ -635,4 +643,96 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
 
         return result;
     }
+
+    /**
+     * 导出分组详情矩阵 Excel
+     *
+     * @param bo       查询条件
+     * @param response 响应体
+     */
+    @Override
+    public void exportDetail(GameEventGroupBo bo, HttpServletResponse response) {
+        if (bo.getGroupId() == null) {
+            return;
+        }
+        Map<String, Object> groupData = getGroupResultFromDB(bo.getGroupId());
+        if (groupData == null || !groupData.containsKey("groupResult")) {
+            return;
+        }
+
+        // 获取分组原始信息,以取得正确的道次数和显示模式
+        GameEventGroupVo groupInfo = queryById(bo.getGroupId());
+        if (groupInfo == null) {
+            return;
+        }
+
+        Map<String, Object> groupResult = (Map<String, Object>) groupData.get("groupResult");
+        int trackNumTotal = Convert.toInt(groupInfo.getTrackNum(), 0);
+        String displayMode = groupInfo.getTrackDisplayMode(); // 0=第N道 1=序号N
+
+        // 1. 构造表头: List<List<String>>
+        List<List<String>> head = new ArrayList<>();
+        head.add(Collections.singletonList("组别"));
+        for (int i = 1; i <= trackNumTotal; i++) {
+            String trackName = "1".equals(displayMode) ? String.valueOf(i) : "第" + i + "道";
+            head.add(Collections.singletonList(trackName));
+        }
+
+        // 2. 构造数据行: List<List<Object>>
+        // 先按组号组织数据: Map<组号, Map<道次, 运动员信息>>
+        Map<Integer, Map<Integer, String>> matrixData = new TreeMap<>();
+        for (Map.Entry<String, Object> entry : groupResult.entrySet()) {
+            String key = entry.getKey();
+            if (!key.contains("-")) continue;
+
+            String[] split = key.split("-");
+            try {
+                Integer groupIndex = Integer.valueOf(split[0]);
+                Integer trackIndex = Integer.valueOf(split[1]);
+
+                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());
+
+                matrixData.computeIfAbsent(groupIndex, k -> new HashMap<>()).put(trackIndex, cellContent);
+            } catch (Exception ignored) {
+            }
+        }
+
+        List<List<Object>> dataList = new ArrayList<>();
+        for (Map.Entry<Integer, Map<Integer, String>> rowEntry : matrixData.entrySet()) {
+            List<Object> row = new ArrayList<>();
+            row.add("第" + rowEntry.getKey() + "组"); // 第一列是组别
+
+            Map<Integer, String> tracks = rowEntry.getValue();
+            for (int i = 1; i <= trackNumTotal; i++) {
+                row.add(tracks.getOrDefault(i, "")); // 填充各道次数据
+            }
+            dataList.add(row);
+        }
+
+        try {
+            String fileName = "分组详情_" + System.currentTimeMillis() + ".xlsx";
+            FileUtils.setAttachmentResponseHeader(response, fileName);
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
+
+            // 内容样式:自动换行 + 居中
+            WriteCellStyle contentStyle = new WriteCellStyle();
+            contentStyle.setWrapped(true);
+            contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+            HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(null, contentStyle);
+
+            FastExcel.write(response.getOutputStream())
+                .head(head)
+                .registerWriteHandler(styleStrategy)
+                .sheet("分组详情")
+                .doWrite(dataList);
+        } catch (Exception e) {
+            log.error("导出分组详情矩阵失败", e);
+            throw new RuntimeException("导出分组详情失败", e);
+        }
+    }
 }