Jelajahi Sumber

```feat(app): 添加赛事条件导出功能在 `AppGameMapper.java` 中添加了新的方法 `exportCondition` 用于导出赛事条件。
在 `AppGameMapper.xml` 中添加了对应的 SQL 查询语句。
在 `AppOrderController.java` 中添加了三个新的 API 接口:- `exportConditionByName` 用于根据名称查询赛事
- `exportConditionByProjectName` 用于根据项目名称和赛事 ID 查询项目- `exportCondition` 用于导出赛事条件在 `AppOrderServiceImpl.java` 中实现了上述接口的业务逻辑,并添加了生成 Excel 文件的方法。
新增了 `ExportConditionDTO`, `ExportConditionOutVO` 和 `ExportConditionVO` 三个数据传输对象。
在 `IAppOrderService.java` 中声明了新的服务方法。
```

SheepHy 5 hari lalu
induk
melakukan
af7decf78c

+ 47 - 1
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/controller/AppOrderController.java

@@ -11,10 +11,12 @@ import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.aspect.annotation.AutoLog;
 import org.jeecg.common.system.base.controller.JeecgController;
 import org.jeecg.common.system.query.QueryGenerator;
+import org.jeecg.modules.system.app.dto.ExportConditionDTO;
 import org.jeecg.modules.system.app.entity.AppOrder;
 import org.jeecg.modules.system.app.form.AppOrderPageForm;
 import org.jeecg.modules.system.app.service.IAppOrderService;
 import org.jeecg.modules.system.app.vo.AppOrderInfoVO;
+import org.jeecg.modules.system.app.vo.ExportConditionVO;
 import org.jeecg.modules.system.app.vo.OrderPageVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
@@ -22,8 +24,12 @@ import org.springframework.web.servlet.ModelAndView;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.IOException;
 import java.util.Arrays;
- /**
+import java.util.List;
+
+/**
  * @Description: 订单表
  * @Author: jeecg-boot
  * @Date:   2025-07-03
@@ -187,4 +193,44 @@ public class AppOrderController extends JeecgController<AppOrder, IAppOrderServi
         return super.importExcel(request, response, AppOrder.class);
     }
 
+
+    /**
+     * @description 赛事条件导出-查询赛事
+     * params
+     * @author SheepHy
+     * @date 2025/9/17
+     * return   {@link }
+     **/
+    @Operation(summary="赛事条件导出-查询赛事")
+    @GetMapping(value = "/exportConditionByName")
+    public Result<List<ExportConditionVO>> exportConditionByName(@RequestParam(value = "name", required = false) String name){
+        return Result.OK(appOrderService.exportConditionByName(name));
+    }
+
+    /**
+     * @description 赛事条件导出-查询项目
+     * params
+     * @author SheepHy
+     * @date 2025/9/17
+     * return   {@link }
+     **/
+    @Operation(summary="赛事条件导出-查询项目")
+    @GetMapping(value = "/exportConditionByProjectName")
+    public Result<List<ExportConditionVO>> exportConditionByProjectName(@RequestParam(value = "projectName", required = false)String projectName,
+                                                                        @RequestParam(value = "gameId", required = false)String gameId){
+        return Result.OK(appOrderService.exportConditionByProjectName(projectName, gameId));
+    }
+
+    /**
+     * @description 赛事条件导出
+     * params
+     * @author SheepHy
+     * @date 2025/9/17
+     * return   {@link }
+     **/
+    @Operation(summary="赛事条件导出")
+    @PostMapping(value = "/exportCondition")
+    public Result<File> exportCondition(@RequestBody ExportConditionDTO exportConditionDTO) throws IOException {
+        return Result.OK(appOrderService.exportCondition(exportConditionDTO));
+    }
 }

+ 25 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/dto/ExportConditionDTO.java

@@ -0,0 +1,25 @@
+package org.jeecg.modules.system.app.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
+
+@Schema(description = "赛事条件导出入参数据")
+@Data
+@Accessors(chain = true)
+public class ExportConditionDTO {
+    @Schema(description = "赛事Id")
+    private String gameId;
+    @Schema(description = "项目ID")
+    private String projectId;
+    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+    @Schema(description = "开始时间")
+    private String startTime;
+    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+    @Schema(description = "结束时间")
+    private String endTime;
+}

+ 14 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/mapper/AppGameMapper.java

@@ -12,7 +12,12 @@ import org.jeecg.modules.app.vo.game.FindByIdResponse;
 import org.jeecg.modules.app.vo.game.FindPagResponse;
 import org.jeecg.modules.system.app.dto.AppGameDTO;
 import org.jeecg.modules.system.app.dto.AppGamePageDTO;
+import org.jeecg.modules.system.app.dto.ExportConditionDTO;
 import org.jeecg.modules.system.app.entity.AppGame;
+import org.jeecg.modules.system.app.vo.ExportConditionOutVO;
+
+import java.io.File;
+import java.util.List;
 
 /**
  * @Description: 赛事表
@@ -45,4 +50,13 @@ public interface AppGameMapper extends BaseMapper<AppGame> {
             " left join  nm_game_price_rules b on  a.id =b.game_id and b.del_flag =0 " +
             " where b.id = #{priceRulesId}")
     AppGame  findByPriceRules(@Param("priceRulesId") String priceRulesId);
+
+    /**
+     * @description 赛事条件导出
+     * params
+     * @author SheepHy
+     * @date 2025/9/17
+     * return   {@link }
+     **/
+    List<ExportConditionOutVO> exportCondition(@Param("exportConditionDTO")ExportConditionDTO exportConditionDTO);
 }

+ 30 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/mapper/xml/AppGameMapper.xml

@@ -271,4 +271,34 @@
         where a.del_flag=0 and a.id=#{id}
         group by a.id, b.type
     </select>
+
+    <select id="exportCondition" resultType="org.jeecg.modules.system.app.vo.ExportConditionOutVO">
+        SELECT
+            d.`name`,
+            b.type,
+            e.NAME AS projectName,
+            a.game_certification,
+            b.user_name,
+            b.user_phone
+        FROM
+            nm_order a
+                INNER JOIN nm_order_pro_info b ON a.id = b.order_id
+                AND (b.type = 3 OR b.type = 4)
+                LEFT JOIN nm_game_price_rules c ON a.product_ids = c.id
+                LEFT JOIN nm_game d ON c.game_id = d.id
+                LEFT JOIN nm_category e ON c.category_id = e.id
+        WHERE 1=1
+        <if test="exportConditionDTO.gameId != null and exportConditionDTO.gameId != '' and exportConditionDTO.gameId != 'ALL'">
+            AND c.game_id = {exportConditionDTO.gameId}
+        </if>
+        <if test="exportConditionDTO.projectId != null and exportConditionDTO.projectId != '' and exportConditionDTO.projectId != 'ALL'">
+            AND c.category_id = {exportConditionDTO.projectId}
+        </if>
+        <if test="exportConditionDTO.startTime != null and exportConditionDTO.startTime != ''">
+            AND d.start_time <![CDATA[ >= ]]> #{exportConditionDTO.startTime}
+        </if>
+        <if test="exportConditionDTO.endTime != null and exportConditionDTO.endTime != ''">
+            AND d.end_time <![CDATA[ <= ]]> #{exportConditionDTO.endTime}
+        </if>
+    </select>
 </mapper>

+ 32 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/service/IAppOrderService.java

@@ -6,11 +6,16 @@ import org.jeecg.modules.app.vo.ScanCodeQueryOrderVO;
 import org.jeecg.modules.quartz.vo.JobClassNoticeVo;
 import org.jeecg.modules.quartz.vo.JobReservationSiteVo;
 import org.jeecg.modules.system.app.dto.AppOrderDTO;
+import org.jeecg.modules.system.app.dto.ExportConditionDTO;
+import org.jeecg.modules.system.app.entity.AppGame;
 import org.jeecg.modules.system.app.entity.AppOrder;
 import org.jeecg.modules.system.app.form.AppOrderPageForm;
 import org.jeecg.modules.system.app.vo.AppOrderInfoVO;
+import org.jeecg.modules.system.app.vo.ExportConditionVO;
 import org.jeecg.modules.system.app.vo.OrderPageVO;
 
+import java.io.File;
+import java.io.IOException;
 import java.time.LocalDate;
 import java.time.LocalTime;
 import java.util.Date;
@@ -63,4 +68,31 @@ public interface IAppOrderService extends IService<AppOrder> {
     List<JobClassNoticeVo> findByJob(Integer hoursBefore,Date date, int maxRetry);
 
     List<JobReservationSiteVo> findBySite(LocalDate currentDate, LocalTime localTime);
+
+    /**
+     * @description 赛事条件导出-查询赛事
+     * params
+     * @author SheepHy
+     * @date 2025/9/17
+     * return   {@link }
+     **/
+    List<ExportConditionVO> exportConditionByName(String name);
+
+    /**
+     * @description 赛事条件导出-查询项目
+     * params
+     * @author SheepHy
+     * @date 2025/9/17
+     * return   {@link }
+     **/
+    List<ExportConditionVO> exportConditionByProjectName(String projectName, String gameId);
+
+    /**
+     * @description 赛事条件导出
+     * params
+     * @author SheepHy
+     * @date 2025/9/17
+     * return   {@link }
+     **/
+    File exportCondition(ExportConditionDTO exportConditionDTO) throws IOException;
 }

+ 374 - 3
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/service/impl/AppOrderServiceImpl.java

@@ -2,9 +2,17 @@ package org.jeecg.modules.system.app.service.impl;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
+import org.apache.poi.xssf.usermodel.XSSFPicture;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.apache.shiro.SecurityUtils;
 import org.jeecg.common.constant.CommonConstant;
 import org.jeecg.common.exception.JeecgBootException;
@@ -16,24 +24,32 @@ import org.jeecg.modules.app.vo.ScanCodeQueryOrderVO;
 import org.jeecg.modules.quartz.vo.JobClassNoticeVo;
 import org.jeecg.modules.quartz.vo.JobReservationSiteVo;
 import org.jeecg.modules.system.app.dto.AppOrderDTO;
+import org.jeecg.modules.system.app.dto.ExportConditionDTO;
 import org.jeecg.modules.system.app.dto.IsinUserInfoDTO;
 import org.jeecg.modules.system.app.dto.VerificationRecordDTO;
 import org.jeecg.modules.system.app.entity.*;
 import org.jeecg.modules.system.app.form.AppOrderPageForm;
 import org.jeecg.modules.system.app.mapper.*;
 import org.jeecg.modules.system.app.service.IAppOrderService;
-import org.jeecg.modules.system.app.vo.AppIsinVO;
-import org.jeecg.modules.system.app.vo.AppOrderInfoVO;
-import org.jeecg.modules.system.app.vo.OrderPageVO;
+import org.jeecg.modules.system.app.vo.*;
 import org.jeecg.modules.system.entity.SysUser;
 import org.jeecg.modules.system.mapper.SysUserMapper;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
 import java.time.LocalDate;
 import java.time.LocalTime;
 import java.util.*;
+import java.util.List;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -83,6 +99,8 @@ public class AppOrderServiceImpl extends ServiceImpl<AppOrderMapper, AppOrder> i
     private AppCoursesMapper appCoursesMapper;
     @Resource
     private AppCoursesPriceRulesMapper appCoursesPriceRulesMapper;
+    @Resource
+    private AppCategoryMapper appCategoryMapper;
 
 
     @Override
@@ -355,4 +373,357 @@ public class AppOrderServiceImpl extends ServiceImpl<AppOrderMapper, AppOrder> i
         return jobReservationSiteVosTwe;
     }
 
+    @Override
+    public List<ExportConditionVO> exportConditionByName(String name) {
+        List<AppGame> appGames = null;
+        if(null != name && !name.isEmpty()){
+            appGames = appGameMapper.selectList(Wrappers.<AppGame>lambdaQuery().like(AppGame::getName, name).eq(AppGame::getDelFlag, 0));
+        }else {
+            appGames = appGameMapper.selectList(Wrappers.<AppGame>lambdaQuery().eq(AppGame::getDelFlag, 0));
+        }
+        List<ExportConditionVO> collect = appGames.stream()
+                .map(game -> {
+                    ExportConditionVO vo = new ExportConditionVO();
+                    vo.setId(game.getId());
+                    vo.setName(game.getName());
+                    return vo;
+                })
+                .collect(Collectors.toList());
+        collect.add(new ExportConditionVO().setId("ALL").setName("全部"));
+        return collect;
+    }
+
+    @Override
+    public List<ExportConditionVO> exportConditionByProjectName(String projectName, String gameId) {
+        List<ExportConditionVO> collect = new ArrayList<>();
+        collect.add(new ExportConditionVO().setId("ALL").setName("全部"));
+        if(null != projectName && !projectName.isEmpty()){
+            AppCategory appCategory = appCategoryMapper.selectOne(Wrappers.<AppCategory>lambdaQuery().like(AppCategory::getName, projectName)
+                    .eq(AppCategory::getDelFlag, 0)
+                    .eq(AppCategory::getStatus, 0).last("limit 1"));
+            List<AppGamePriceRules> appGamePriceRules = appGamePriceRulesMapper.selectList(Wrappers.<AppGamePriceRules>lambdaQuery()
+                    .eq(AppGamePriceRules::getGameId, gameId)
+                    .eq(AppGamePriceRules::getCategoryId, appCategory.getId()));
+            collect = appGamePriceRules.stream()
+                    .map(priceRules -> {
+                        ExportConditionVO vo = new ExportConditionVO();
+                        vo.setId(priceRules.getId());
+                        vo.setName((priceRules.getType() == 0 ? "个人-": "团队-") + appCategoryMapper.selectById(priceRules.getCategoryId()).getName());
+                        return vo;
+                    })
+                    .collect(Collectors.toList());
+        }else {
+            List<AppGamePriceRules> appGamePriceRules = appGamePriceRulesMapper.selectList(Wrappers.<AppGamePriceRules>lambdaQuery()
+                    .eq(AppGamePriceRules::getGameId, gameId));
+            collect = appGamePriceRules.stream()
+                    .map(priceRules -> {
+                        ExportConditionVO vo = new ExportConditionVO();
+                        vo.setId(priceRules.getId());
+                        vo.setName((priceRules.getType() == 0 ? "个人-": "团队-") + appCategoryMapper.selectById(priceRules.getCategoryId()).getName());
+                        return vo;
+                    })
+                    .collect(Collectors.toList());
+            collect.add(new ExportConditionVO().setId("ALL").setName("全部"));
+        }
+        return collect;
+
+    }
+
+    @Override
+    public File exportCondition(ExportConditionDTO exportConditionDTO) throws IOException {
+        List<ExportConditionOutVO> exportConditionOutVO = appGameMapper.exportCondition(exportConditionDTO);
+        List<Map<String, Object>> list = new ArrayList<>();
+        int index = 1;
+        for (ExportConditionOutVO outVO : exportConditionOutVO){
+            String gameCertification = outVO.getGameCertification();
+
+            // 使用 FastJSON 解析 gameCertification 字段
+            JSONObject certificationJson = JSONObject.parseObject(gameCertification);
+
+            Map<String, Object> map = new HashMap<>();
+            map.put("序号", index);
+            map.put("赛事名称", outVO.getName());
+            map.put("个人/团队赛", outVO.getType());
+            map.put("报名项目", outVO.getProjectName());
+
+            // 提取 teamName 放到 队名
+            String teamName = certificationJson.getString("teamName");
+            map.put("队名", teamName != null ? teamName : "");
+
+            // 提取 teamEmblemImg 放到 队徽
+            String teamEmblemImg = certificationJson.getString("teamEmblemImg");
+            map.put("队徽", teamEmblemImg != null ? teamEmblemImg : "");
+
+            // 提取 certificationDTOS 放到 社保证明
+            JSONArray certificationDTOS = certificationJson.getJSONArray("certificationDTOS");
+            if (certificationDTOS != null && !certificationDTOS.isEmpty()) {
+                StringBuilder certificationBuilder = new StringBuilder();
+                for (int i = 0; i < certificationDTOS.size(); i++) {
+                    JSONObject cert = certificationDTOS.getJSONObject(i);
+                    String name = cert.getString("name");
+                    String certificationImg = cert.getString("certificationImg");
+                    certificationBuilder.append(name).append(": ").append(certificationImg).append(";");
+                }
+                map.put("社保证明", certificationBuilder.toString());
+            } else {
+                map.put("社保证明", "");
+            }
+
+            map.put("报名人员", outVO.getUserName());
+            map.put("联系电话", outVO.getUserPhone());
+            list.add(map);
+            index++;
+        }
+
+        // 生成 Excel 文件
+        return generateExcel(list);
+    }
+
+    // 优化后的 generateExcel 方法
+    private File generateExcel(List<Map<String, Object>> dataList) throws IOException {
+        // 创建工作簿和工作表
+        Workbook workbook = new XSSFWorkbook();
+        Sheet sheet = workbook.createSheet("报名信息");
+
+        // 设置列宽
+        sheet.setColumnWidth(0, 3000);  // 序号
+        sheet.setColumnWidth(1, 8000);  // 赛事名称
+        sheet.setColumnWidth(2, 6000);  // 个人/团队赛
+        sheet.setColumnWidth(3, 6000);  // 报名项目
+        sheet.setColumnWidth(4, 6000);  // 队名
+        sheet.setColumnWidth(5, 6000);  // 队徽
+        sheet.setColumnWidth(6, 12000); // 社保证明
+        sheet.setColumnWidth(7, 4000);  // 报名人员
+        sheet.setColumnWidth(8, 4000);  // 联系电话
+
+        // 创建标题样式
+        CellStyle headerStyle = createHeaderStyle(workbook);
+
+        // 创建内容样式
+        CellStyle contentStyle = createContentStyle(workbook);
+
+        // 写入表头
+        Row headerRow = sheet.createRow(0);
+        String[] headers = {"序号", "赛事名称", "个人/团队赛", "报名项目", "队名", "队徽", "社保证明", "报名人员", "联系电话"};
+        for (int i = 0; i < headers.length; i++) {
+            Cell cell = headerRow.createCell(i);
+            cell.setCellValue(headers[i]);
+            cell.setCellStyle(headerStyle);
+        }
+
+        // 写入数据
+        for (int i = 0; i < dataList.size(); i++) {
+            Map<String, Object> rowMap = dataList.get(i);
+            Row dataRow = sheet.createRow(i + 1);
+            // 设置行高,为图片留出空间
+            dataRow.setHeightInPoints(80);
+
+            // 序号
+            Cell cell0 = dataRow.createCell(0);
+            cell0.setCellValue((Integer) rowMap.get("序号"));
+            cell0.setCellStyle(contentStyle);
+
+            // 赛事名称
+            Cell cell1 = dataRow.createCell(1);
+            cell1.setCellValue((String) rowMap.get("赛事名称"));
+            cell1.setCellStyle(contentStyle);
+
+            // 个人/团队赛
+            Cell cell2 = dataRow.createCell(2);
+            cell2.setCellValue((int) rowMap.get("个人/团队赛") == 3 ? "个人" : "团队");
+            cell2.setCellStyle(contentStyle);
+
+            // 报名项目
+            Cell cell3 = dataRow.createCell(3);
+            cell3.setCellValue((String) rowMap.get("报名项目"));
+            cell3.setCellStyle(contentStyle);
+
+            // 队名
+            Cell cell4 = dataRow.createCell(4);
+            cell4.setCellValue((String) rowMap.get("队名"));
+            cell4.setCellStyle(contentStyle);
+
+            // 队徽:插入图片(嵌入单元格)
+            String teamEmblemImg = (String) rowMap.get("队徽");
+            Cell cell5 = dataRow.createCell(5);
+            cell5.setCellStyle(contentStyle);
+            if (teamEmblemImg != null && !teamEmblemImg.isEmpty()) {
+                insertImageToCell(workbook, sheet, teamEmblemImg, 5, i + 1);
+            }
+
+            // 社保证明:多个图片,放在同一单元格内,按行排列
+            String certificationStr = (String) rowMap.get("社保证明");
+            Cell cell6 = dataRow.createCell(6);
+            cell6.setCellStyle(contentStyle);
+
+            if (certificationStr != null && !certificationStr.isEmpty()) {
+                List<String> allImageUrls = extractImageUrls(certificationStr);
+                if (!allImageUrls.isEmpty()) {
+                    insertMultipleImagesToCell(workbook, sheet, allImageUrls, 6, i + 1);
+                }
+            }
+
+            // 报名人员
+            Cell cell7 = dataRow.createCell(7);
+            cell7.setCellValue((String) rowMap.get("报名人员"));
+            cell7.setCellStyle(contentStyle);
+
+            // 联系电话
+            Cell cell8 = dataRow.createCell(8);
+            cell8.setCellValue((String) rowMap.get("联系电话"));
+            cell8.setCellStyle(contentStyle);
+        }
+
+        // 输出文件
+        File file = File.createTempFile("export_condition_", ".xlsx");
+        try (FileOutputStream fos = new FileOutputStream(file)) {
+            workbook.write(fos);
+        }
+        workbook.close();
+        return file;
+    }
+
+    // 创建表头样式
+    private CellStyle createHeaderStyle(Workbook workbook) {
+        CellStyle headerStyle = workbook.createCellStyle();
+        Font headerFont = workbook.createFont();
+        headerFont.setBold(true);
+        headerStyle.setFont(headerFont);
+        headerStyle.setAlignment(HorizontalAlignment.CENTER);
+        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+        headerStyle.setBorderTop(BorderStyle.THIN);
+        headerStyle.setBorderBottom(BorderStyle.THIN);
+        headerStyle.setBorderLeft(BorderStyle.THIN);
+        headerStyle.setBorderRight(BorderStyle.THIN);
+        return headerStyle;
+    }
+
+    // 创建内容样式
+    private CellStyle createContentStyle(Workbook workbook) {
+        CellStyle contentStyle = workbook.createCellStyle();
+        contentStyle.setAlignment(HorizontalAlignment.CENTER);
+        contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        contentStyle.setBorderTop(BorderStyle.THIN);
+        contentStyle.setBorderBottom(BorderStyle.THIN);
+        contentStyle.setBorderLeft(BorderStyle.THIN);
+        contentStyle.setBorderRight(BorderStyle.THIN);
+        return contentStyle;
+    }
+
+    // 提取图片URL
+    private List<String> extractImageUrls(String certificationStr) {
+        List<String> allImageUrls = new ArrayList<>();
+        String[] certLines = certificationStr.split(";");
+
+        for (String line : certLines) {
+            line = line.trim();
+            if (line.contains(":")) {
+                String[] parts = line.split(": ", 2);
+                if (parts.length > 1) {
+                    String imgUrls = parts[1];
+                    String[] urls = imgUrls.split(",");
+                    for (String url : urls) {
+                        String trimmedUrl = url.trim();
+                        if (!trimmedUrl.isEmpty()) {
+                            allImageUrls.add(trimmedUrl);
+                        }
+                    }
+                }
+            }
+        }
+        return allImageUrls;
+    }
+
+    // 在单元格中插入单张图片
+    private void insertImageToCell(Workbook workbook, Sheet sheet, String imageUrl, int column, int row) {
+        byte[] imageBytes = downloadImage(imageUrl);
+        if (imageBytes != null) {
+            int pictureIdx = workbook.addPicture(imageBytes, Workbook.PICTURE_TYPE_PNG);
+            Drawing<?> drawing = sheet.createDrawingPatriarch();
+            CreationHelper helper = workbook.getCreationHelper();
+            ClientAnchor anchor = helper.createClientAnchor();
+
+            // 设置图片锚点
+            anchor.setCol1(column);
+            anchor.setCol2(column + 1);
+            anchor.setRow1(row);
+            anchor.setRow2(row + 1);
+            anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE);
+
+            drawing.createPicture(anchor, pictureIdx);
+        }
+    }
+
+    // 在单元格中插入多张图片
+    private void insertMultipleImagesToCell(Workbook workbook, Sheet sheet, List<String> imageUrls, int column, int row) {
+        Drawing<?> drawing = sheet.createDrawingPatriarch();
+        CreationHelper helper = workbook.getCreationHelper();
+
+        // 图片排列参数
+        int maxPerRow = 4; // 每行最多3张图片
+        int imageWidth = 800000;  // 图片宽度
+        int imageHeight = 800000; // 图片高度
+        int horizontalSpacing = 15000; // 水平间距
+        int verticalSpacing = 15000;   // 垂直间距
+
+        for (int i = 0; i < imageUrls.size(); i++) {
+            String imageUrl = imageUrls.get(i);
+            byte[] imageBytes = downloadImage(imageUrl);
+
+            if (imageBytes != null) {
+                int pictureIdx = workbook.addPicture(imageBytes, Workbook.PICTURE_TYPE_PNG);
+
+                // 计算当前图片在网格中的位置
+                int rowIndex = i / maxPerRow;
+                int colIndex = i % maxPerRow;
+
+                ClientAnchor anchor = helper.createClientAnchor();
+
+                // 设置图片在单元格内的相对位置
+                anchor.setCol1(column);
+                anchor.setCol2(column);
+                anchor.setRow1(row);
+                anchor.setRow2(row);
+
+                // 计算图片的精确位置
+                int dx1 = colIndex * (imageWidth + horizontalSpacing) + 20000;
+                int dy1 = rowIndex * (imageHeight + verticalSpacing) + 20000;
+                int dx2 = dx1 + imageWidth;
+                int dy2 = dy1 + imageHeight;
+
+                anchor.setDx1(dx1);
+                anchor.setDy1(dy1);
+                anchor.setDx2(dx2);
+                anchor.setDy2(dy2);
+
+                drawing.createPicture(anchor, pictureIdx);
+            }
+        }
+    }
+
+    // 下载图片字节流
+    private byte[] downloadImage(String imageUrl) {
+        try {
+            URL url = new URL(imageUrl);
+            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+            conn.setRequestMethod("GET");
+            conn.setConnectTimeout(5000);
+            conn.setReadTimeout(5000);
+            InputStream is = conn.getInputStream();
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            byte[] buffer = new byte[1024];
+            int len;
+            while ((len = is.read(buffer)) > 0) {
+                bos.write(buffer, 0, len);
+            }
+            is.close();
+            return bos.toByteArray();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
 }

+ 23 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/vo/ExportConditionOutVO.java

@@ -0,0 +1,23 @@
+package org.jeecg.modules.system.app.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Schema(description = "赛事条件导出返回数据")
+@Data
+@Accessors(chain = true)
+public class ExportConditionOutVO {
+    @Schema(description = "产品名称")
+    private String name;
+    @Schema(description = "比赛类型")
+    private int type;
+    @Schema(description = "项目名称")
+    private String projectName;
+    @Schema(description = "资质材料")
+    private String gameCertification;
+    @Schema(description = "用户名称")
+    private String userName;
+    @Schema(description = "用户手机号")
+    private String userPhone;
+}

+ 16 - 0
national-motion-module-system/national-motion-system-biz/src/main/java/org/jeecg/modules/system/app/vo/ExportConditionVO.java

@@ -0,0 +1,16 @@
+package org.jeecg.modules.system.app.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Schema(description = "赛事条件导出返回数据")
+@Data
+@Accessors(chain = true)
+public class ExportConditionVO {
+    @Schema(description = "id")
+    private String id;
+    @Schema(description = "产品名称")
+    private String name;
+
+}