瀏覽代碼

添加报名模板导入

wenkai 2 周之前
父節點
當前提交
491b7796dc

+ 10 - 1
ruoyi-modules/ruoyi-game-event/pom.xml

@@ -98,7 +98,16 @@
             <groupId>org.dromara</groupId>
             <artifactId>ruoyi-common-websocket</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>3.17</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>3.17</version>
+        </dependency>
         <dependency>
             <groupId>org.dromara</groupId>
             <artifactId>ruoyi-common-sse</artifactId>

+ 327 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/TestPoi.java

@@ -0,0 +1,327 @@
+package org.dromara.system.controller;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.*;
+import org.dromara.system.domain.vo.EnrollProjectVo;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicLong;
+
+@Slf4j
+public class TestPoi {
+
+    /**
+     * @return
+     */
+    public static void main(String[] args) throws IOException {
+        System.out.println(importData());
+    }
+
+    private static List<EnrollProjectVo> importData() throws IOException {
+        List<EnrollProjectVo> enrolls = new ArrayList<>();
+        File file = new File("D:\\poi_demo.xlsx");
+
+        try (FileInputStream fis = new FileInputStream(file);
+             Workbook workbook = new XSSFWorkbook(fis)) {
+
+            Sheet sheet = workbook.getSheetAt(0);
+
+            // 获取第一行(表头),用于获取项目名称
+            Row headerRow = sheet.getRow(2);
+            // 动态确定有效最大列数(去除表头末尾的空列)
+            int lastCellIndex = findValidLastColumnIndex(headerRow);
+            List<String> projectNames = new ArrayList<>();
+
+            // 从第6列(F列,索引5)开始收集项目名称
+            for (int i = 6; i < lastCellIndex; i++) {
+                Cell cell = headerRow.getCell(i);
+                if (cell != null && cell.getStringCellValue() != null && !cell.getStringCellValue().trim().isEmpty()) {
+                    projectNames.add(cell.getStringCellValue().trim());
+                } else {
+                    projectNames.add("项目_" + i); // 防止空标题
+                }
+            }
+
+            // 从第3行开始读数据
+            for (int i = 3; i <= sheet.getLastRowNum(); i++) {
+                Row row = sheet.getRow(i);
+                if (row == null) continue;
+                if (isRowEmpty(row, 0, lastCellIndex)) {
+                    continue;
+                }
+
+                EnrollProjectVo enroll = new EnrollProjectVo();
+                Map<String, Boolean> selections = new LinkedHashMap<>(); // 保持顺序
+
+                // A列:姓名
+                Cell nameCell = row.getCell(0);
+                if (nameCell != null) {
+                    enroll.setName(getCellValueAsString(nameCell));
+                }
+
+                // B列:性别
+                Cell sexCell = row.getCell(1);
+                if (sexCell != null) {
+                    enroll.setSex(getCellValueAsString(sexCell));
+                }
+
+                // C列:年龄
+                Cell ageCell = row.getCell(2);
+                if (ageCell != null) {
+                    enroll.setAge(getCellValueAsString(ageCell));
+                }
+
+                // D列:队伍名称
+                Cell teamCell = row.getCell(3);
+                if (teamCell != null) {
+                    enroll.setTeamName(getCellValueAsString(teamCell));
+                }
+
+                // E列:领队
+                Cell leaderCell = row.getCell(4);
+                if (leaderCell != null) {
+                    enroll.setLeader(getCellValueAsString(leaderCell));
+                }
+
+                // F列:联系方式
+                Cell phoneCell = row.getCell(5);
+                if (phoneCell != null) {
+                    enroll.setPhone(getCellValueAsString(phoneCell));
+                }
+
+                // 从第6列开始读取项目名称
+                for (int j = 6; j < lastCellIndex; j++) {
+                    Cell cell = row.getCell(j);
+                    String projectName = projectNames.get(j - 6); // 对应项目名
+                    boolean selected = isCellSelected(cell);
+                    selections.put(projectName, selected);
+                }
+
+                enroll.setProjectSelections(selections);
+                enrolls.add(enroll);
+            }
+        }
+        return enrolls;
+    }
+
+    // 辅助方法:将 Cell 转为字符串
+    private static String getCellValueAsString(Cell cell) {
+        if (cell == null) return "";
+
+        switch (cell.getCellType()) {
+            case Cell.CELL_TYPE_STRING:
+                return cell.getStringCellValue().trim();
+            case Cell.CELL_TYPE_NUMERIC:
+                if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) {
+                    return cell.getDateCellValue().toString();
+                } else {
+                    return String.valueOf((int) cell.getNumericCellValue()); // 或者保留小数用 double
+                }
+            case Cell.CELL_TYPE_BOOLEAN:
+                return String.valueOf(cell.getBooleanCellValue());
+            default:
+                return "";
+        }
+    }
+
+    /**
+     * 判断单元格是否表示“已选择”
+     * 支持:是、yes、true、1、✔、✅、√ 等
+     */
+    private static boolean isCellSelected(Cell cell) {
+        if (cell == null) return false;
+
+        // 先检查单元格类型
+        if (cell.getCellType() == Cell.CELL_TYPE_BOOLEAN) {
+            return cell.getBooleanCellValue();
+        }
+        if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
+            return cell.getNumericCellValue() == 1;
+        }
+
+        String value = getCellValueAsString(cell).trim().toLowerCase();
+        return !value.isEmpty() &&
+            (value.equals("是") || value.equals("yes") || value.equals("true") ||
+                value.equals("1") || value.contains("✔") || value.contains("✅") ||
+                value.contains("√") || value.equals("×"));
+    }
+
+    /**
+     * 判断某行在指定范围内是否为空(无有效数据)
+     *
+     * @param row      行对象
+     * @param startCol 起始列索引
+     * @param endCol   结束列索引(不包含)
+     * @return true 表示该行为空
+     */
+    private static boolean isRowEmpty(Row row, int startCol, int endCol) {
+        if (row == null) return true;
+
+        for (int i = startCol; i < endCol; i++) {
+            Cell cell = row.getCell(i);
+            if (cell != null) {
+                // 如果单元格不为null,检查其是否有非空值
+                String value = getCellValueAsString(cell);
+                if (value != null && !value.trim().isEmpty()) {
+                    return false; // 有有效数据
+                }
+            }
+        }
+        return true; // 所有列都为空
+    }
+
+    /**
+     * 找到行中最后一个“有效”单元格的索引(从右往左找第一个非空单元格)
+     *
+     * @param row 行对象
+     * @return 有效最大列索引(不包含),即 getLastCellNum() 的合理值
+     */
+    private static int findValidLastColumnIndex(Row row) {
+        if (row == null) return 0;
+
+        int lastCellNum = row.getLastCellNum(); // 物理最后一列
+        if (lastCellNum <= 0) return 0;
+
+        // 从右往左扫描,找到第一个非空单元格
+        for (int i = lastCellNum - 1; i >= 0; i--) {
+            Cell cell = row.getCell(i);
+            String value = getCellValueAsString(cell);
+            if (value != null && !value.trim().isEmpty()) {
+                return i + 1; // 返回有效列数(索引+1)
+            }
+        }
+        return 0; // 全为空
+    }
+
+    private static void export() {
+        //1.加载Excel模板文件
+        // String template = "template/enroll_template.xlsx";
+        String template = "D:\\enroll_template.xlsx";
+        System.out.println(template);
+        XSSFSheet sheet = null;
+        try (
+            // InputStream inputStream = TestPoi.class.getClassLoader().getResourceAsStream(template);
+            InputStream inputStream = new FileInputStream(template);
+            XSSFWorkbook xwb = new XSSFWorkbook(inputStream)) {
+            sheet = xwb.getSheetAt(0);
+            assert sheet != null;
+            //7列3行开始横着渲染 excel表对应6行2列
+            // 2. 获取默认赛事动态项目(赛事项目)
+            // Map<String, List<String>> projectMap = gameEventProjectService.mapProjectTypeAndProject(eventId);
+            Map<String, List<String>> projectMap = new HashMap<>();
+            projectMap.put("径赛项目", List.of("4*100米接力", "4*400米接力", "跳高"));
+            projectMap.put("田赛项目", List.of("跨栏", "接力", "50米"));
+
+            // 3. 渲染分类
+            int currentColumnIndex = 6;
+            Row row = sheet.getRow(2);
+            if (row == null) {
+                row = sheet.createRow(2);
+            }
+
+            for (Map.Entry<String, List<String>> entry : projectMap.entrySet()) {
+                String categoryName = entry.getKey();
+                List<String> projectList = entry.getValue();
+                int projectCount = projectList.size();
+                // 3.1 创建单元格并设置分类名称
+                Cell cell = row.createCell(currentColumnIndex);
+                cell.setCellValue(categoryName);
+
+                // 3.2 合并单元格:从 currentColumnIndex 开始,合并 projectCount 个单元格
+                int lastColumnIndex = currentColumnIndex + projectCount - 1;
+                CellRangeAddress region = new CellRangeAddress(2, 2, currentColumnIndex, lastColumnIndex);
+                sheet.addMergedRegion(region);
+
+                // 3.3 创建样式:边框 + 居中 + 加粗
+                CellStyle style = xwb.createCellStyle();
+                style.setBorderTop(BorderStyle.THIN);
+                style.setBorderBottom(BorderStyle.THIN);
+                style.setBorderLeft(BorderStyle.THIN);
+                style.setBorderRight(BorderStyle.THIN);
+                style.setTopBorderColor(IndexedColors.BLACK.getIndex());
+                style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
+                style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
+                style.setRightBorderColor(IndexedColors.BLACK.getIndex());
+
+                style.setAlignment(HorizontalAlignment.CENTER);
+                style.setVerticalAlignment(VerticalAlignment.CENTER);
+
+                // 字体加粗
+                Font font = xwb.createFont();
+                font.setBold(true);
+                style.setFont(font);
+
+                // 应用样式
+                cell.setCellStyle(style);
+
+                // 3.4  更新起始列:为下一个分类腾出位置
+                currentColumnIndex = lastColumnIndex + 1;
+            }
+
+            // 4. 渲染项目(在第4行,索引为3)
+            currentColumnIndex = 6;
+            Row projectRow = sheet.getRow(3);
+            if (projectRow == null) {
+                projectRow = sheet.createRow(3); // 如果第4行不存在,创建它
+            }
+            for (Map.Entry<String, List<String>> entry : projectMap.entrySet()) {
+                List<String> projectList = entry.getValue();
+                // 4.1 遍历当前分类下的每个项目
+                for (String projectName : projectList) {
+                    Cell cell = projectRow.createCell(currentColumnIndex);
+                    cell.setCellValue(projectName);
+
+                    // 3.3 创建样式:边框 + 居中 + 加粗
+                    CellStyle style = xwb.createCellStyle();
+                    style.setBorderTop(BorderStyle.THIN);
+                    style.setBorderBottom(BorderStyle.THIN);
+                    style.setBorderLeft(BorderStyle.THIN);
+                    style.setBorderRight(BorderStyle.THIN);
+                    style.setTopBorderColor(IndexedColors.BLACK.getIndex());
+                    style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
+                    style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
+                    style.setRightBorderColor(IndexedColors.BLACK.getIndex());
+
+                    style.setAlignment(HorizontalAlignment.CENTER);
+                    style.setVerticalAlignment(VerticalAlignment.CENTER);
+
+                    // 字体加粗
+                    Font font = xwb.createFont();
+                    font.setBold(true);
+                    style.setFont(font);
+
+                    // 应用样式
+                    cell.setCellStyle(style);
+
+                    // 移动到下一列
+                    currentColumnIndex++;
+                }
+            }
+
+            //3.将Excel文件通过Response输出
+            // 设置Excel文件路径
+            File target = new File("D:\\poi_demo.xlsx");
+            try {
+                // 创建指向该路径的输出流
+                FileOutputStream stream = new FileOutputStream(target);
+                // 将数据导出到Excel表格
+                xwb.write(stream);
+                // 关闭输出流
+                stream.close();
+            } catch (FileNotFoundException e) {
+                e.printStackTrace();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } catch (IOException e) {
+            log.error("下载Excel模板异常,异常信息为:【{}】", e.getMessage(), e);
+        }
+    }
+}
+

二進制
ruoyi-modules/ruoyi-game-event/src/main/resources/template/enroll_template.xlsx