Browse Source

资质等级信息动态导出

wangxx 1 month ago
parent
commit
b41a1d4da8

+ 4 - 4
airport-admin/src/main/java/com/sundot/airport/web/controller/ledger/LedgerCertificateInfoController.java

@@ -19,6 +19,7 @@ import com.sundot.airport.common.enums.BusinessType;
19 19
 import com.sundot.airport.ledger.domain.LedgerCertificateInfo;
20 20
 import com.sundot.airport.ledger.dto.LedgerCertificateImportTemplate;
21 21
 import com.sundot.airport.ledger.service.ILedgerCertificateInfoService;
22
+import com.sundot.airport.ledger.utils.CertificateExcelExportUtil;
22 23
 import com.sundot.airport.common.utils.poi.ExcelUtil;
23 24
 import com.sundot.airport.common.core.page.TableDataInfo;
24 25
 import com.sundot.airport.ledger.utils.CertificateExcelImportUtil;
@@ -50,15 +51,14 @@ public class LedgerCertificateInfoController extends BaseController {
50 51
     }
51 52
 
52 53
     /**
53
-     * 导出证书信息列表
54
+     * 导出证书信息列表(含复审时间列)
54 55
      */
55 56
     @PreAuthorize("@ss.hasPermi('ledger:certificate:export')")
56 57
     @Log(title = "证书信息", businessType = BusinessType.EXPORT)
57 58
     @PostMapping("/export")
58 59
     public void export(HttpServletResponse response, LedgerCertificateInfo ledgerCertificateInfo) {
59
-        List<LedgerCertificateInfo> list = certificateInfoService.selectCertificateInfoList(ledgerCertificateInfo);
60
-        ExcelUtil<LedgerCertificateInfo> util = new ExcelUtil<LedgerCertificateInfo>(LedgerCertificateInfo.class);
61
-        util.exportExcel(response, list, "证书信息数据");
60
+        List<LedgerCertificateInfo> list = certificateInfoService.selectCertificateInfoListWithReviews(ledgerCertificateInfo);
61
+        CertificateExcelExportUtil.exportCertificateExcel(response, list, "证书信息数据");
62 62
     }
63 63
 
64 64
     /**

+ 0 - 1
airport-ledger/src/main/java/com/sundot/airport/ledger/domain/LedgerCertificateInfo.java

@@ -33,7 +33,6 @@ public class LedgerCertificateInfo extends BaseEntity {
33 33
     /**
34 34
      * 租户号
35 35
      */
36
-    @Excel(name = "租户号")
37 36
     private String tenantId;
38 37
 
39 38
     /**

+ 8 - 0
airport-ledger/src/main/java/com/sundot/airport/ledger/mapper/LedgerCertificateReviewRecordMapper.java

@@ -90,4 +90,12 @@ public interface LedgerCertificateReviewRecordMapper {
90 90
      * @return 结果
91 91
      */
92 92
     public int batchInsertReviewRecords(@Param("list") List<LedgerCertificateReviewRecord> list);
93
+
94
+    /**
95
+     * 根据多个证书ID批量查询复审记录
96
+     * 
97
+     * @param certIds 证书ID列表
98
+     * @return 复审记录列表
99
+     */
100
+    public List<LedgerCertificateReviewRecord> selectReviewRecordsByCertIds(@Param("certIds") List<Long> certIds);
93 101
 }

+ 9 - 0
airport-ledger/src/main/java/com/sundot/airport/ledger/service/ILedgerCertificateInfoService.java

@@ -27,6 +27,15 @@ public interface ILedgerCertificateInfoService {
27 27
     public List<LedgerCertificateInfo> selectCertificateInfoList(LedgerCertificateInfo ledgerCertificateInfo);
28 28
 
29 29
     /**
30
+     * 查询证书信息列表(包含复审记录)
31
+     * 用于导出场景
32
+     * 
33
+     * @param ledgerCertificateInfo 证书信息查询条件
34
+     * @return 证书信息集合(包含复审记录)
35
+     */
36
+    public List<LedgerCertificateInfo> selectCertificateInfoListWithReviews(LedgerCertificateInfo ledgerCertificateInfo);
37
+
38
+    /**
30 39
      * 新增证书信息
31 40
      * 
32 41
      * @param ledgerCertificateInfo 证书信息

+ 33 - 0
airport-ledger/src/main/java/com/sundot/airport/ledger/service/impl/LedgerLedgerCertificateInfoServiceImpl.java

@@ -71,6 +71,39 @@ public class LedgerLedgerCertificateInfoServiceImpl implements ILedgerCertificat
71 71
     }
72 72
 
73 73
     /**
74
+     * 查询证书信息列表(包含复审记录)
75
+     * 用于导出场景,批量加载复审记录避免N+1查询
76
+     */
77
+    @Override
78
+    public List<LedgerCertificateInfo> selectCertificateInfoListWithReviews(LedgerCertificateInfo ledgerCertificateInfo) {
79
+        List<LedgerCertificateInfo> certList = ledgerCertificateInfoMapper.selectCertificateInfoList(ledgerCertificateInfo);
80
+        if (certList == null || certList.isEmpty()) {
81
+            return certList;
82
+        }
83
+        // 收集所有证书ID
84
+        List<Long> certIds = certList.stream()
85
+                .map(LedgerCertificateInfo::getId)
86
+                .filter(id -> id != null)
87
+                .collect(Collectors.toList());
88
+        if (certIds.isEmpty()) {
89
+            return certList;
90
+        }
91
+        // 批量查询所有复审记录
92
+        List<LedgerCertificateReviewRecord> allRecords = ledgerCertificateReviewRecordMapper.selectReviewRecordsByCertIds(certIds);
93
+        // 按证书ID分组
94
+        Map<Long, List<LedgerCertificateReviewRecord>> recordsByCertId = new HashMap<>();
95
+        for (LedgerCertificateReviewRecord record : allRecords) {
96
+            recordsByCertId.computeIfAbsent(record.getCertId(), k -> new ArrayList<>()).add(record);
97
+        }
98
+        // 设置到每个证书对象
99
+        for (LedgerCertificateInfo cert : certList) {
100
+            List<LedgerCertificateReviewRecord> records = recordsByCertId.get(cert.getId());
101
+            cert.setReviewRecords(records != null ? records : new ArrayList<>());
102
+        }
103
+        return certList;
104
+    }
105
+
106
+    /**
74 107
      * 新增证书信息
75 108
      * 
76 109
      * @param ledgerCertificateInfo 证书信息

+ 234 - 0
airport-ledger/src/main/java/com/sundot/airport/ledger/utils/CertificateExcelExportUtil.java

@@ -0,0 +1,234 @@
1
+package com.sundot.airport.ledger.utils;
2
+
3
+import java.math.BigDecimal;
4
+import java.text.SimpleDateFormat;
5
+import java.util.Date;
6
+import java.util.List;
7
+import java.util.TreeMap;
8
+
9
+import javax.servlet.http.HttpServletResponse;
10
+
11
+import com.sundot.airport.ledger.domain.LedgerCertificateInfo;
12
+import com.sundot.airport.ledger.domain.LedgerCertificateReviewRecord;
13
+import org.apache.poi.ss.usermodel.*;
14
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
15
+import org.apache.poi.util.IOUtils;
16
+
17
+/**
18
+ * 证书Excel导出工具类
19
+ * 支持动态复审时间列导出,与导入模板格式一致
20
+ *
21
+ * @author ruoyi
22
+ * @date 2026-07-02
23
+ */
24
+public class CertificateExcelExportUtil {
25
+
26
+    /** 标准列头定义(与@Excel注解顺序一致) */
27
+    private static final String[] STANDARD_HEADERS = {
28
+            "单位", "姓名", "性别", "出生日期", "身份证号",
29
+            "证书等级", "发证日期", "证书编号", "理论考核成绩", "实操考核成绩",
30
+            "评定成绩", "用工形式", "服务期(月)", "备注"
31
+    };
32
+
33
+    /** 标准列宽度 */
34
+    private static final int[] STANDARD_WIDTHS = {
35
+            16, 16, 16, 30, 16,
36
+            16, 30, 16, 16, 16,
37
+            16, 16, 16, 16
38
+    };
39
+
40
+    /**
41
+     * 导出证书信息到Excel(含动态复审时间列)
42
+     *
43
+     * @param response  HTTP响应
44
+     * @param list      证书信息列表
45
+     * @param sheetName 工作表名称
46
+     */
47
+    public static void exportCertificateExcel(HttpServletResponse response, List<LedgerCertificateInfo> list, String sheetName) {
48
+        Workbook wb = null;
49
+        try {
50
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
51
+            response.setCharacterEncoding("utf-8");
52
+
53
+            wb = new SXSSFWorkbook();
54
+            Sheet sheet = wb.createSheet(sheetName);
55
+
56
+            // 创建样式
57
+            CellStyle headerStyle = createHeaderStyle(wb);
58
+            CellStyle dataStyle = createDataStyle(wb);
59
+            CellStyle dateStyle = createDateStyle(wb);
60
+
61
+            // 计算最大复审次数
62
+            int maxReviewSeq = getMaxReviewSeq(list);
63
+
64
+            // 构建完整列头
65
+            int totalCols = STANDARD_HEADERS.length + maxReviewSeq;
66
+            String[] headers = new String[totalCols];
67
+            System.arraycopy(STANDARD_HEADERS, 0, headers, 0, STANDARD_HEADERS.length);
68
+            for (int i = 0; i < maxReviewSeq; i++) {
69
+                headers[STANDARD_HEADERS.length + i] = "第" + (i + 1) + "次复审时间";
70
+            }
71
+
72
+            // 写入表头
73
+            Row headerRow = sheet.createRow(0);
74
+            headerRow.setHeightInPoints(30);
75
+            for (int i = 0; i < totalCols; i++) {
76
+                Cell cell = headerRow.createCell(i);
77
+                cell.setCellValue(headers[i]);
78
+                cell.setCellStyle(headerStyle);
79
+                // 设置列宽
80
+                if (i < STANDARD_WIDTHS.length) {
81
+                    sheet.setColumnWidth(i, (int) ((STANDARD_WIDTHS[i] + 0.72) * 256));
82
+                } else {
83
+                    sheet.setColumnWidth(i, (int) ((30 + 0.72) * 256)); // 复审时间列宽度30
84
+                }
85
+            }
86
+
87
+            // 写入数据行
88
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
89
+            for (int i = 0; i < list.size(); i++) {
90
+                LedgerCertificateInfo cert = list.get(i);
91
+                Row dataRow = sheet.createRow(i + 1);
92
+                int col = 0;
93
+
94
+                // 标准字段
95
+                setCellValue(dataRow, col++, cert.getUnitName(), dataStyle);
96
+                setCellValue(dataRow, col++, cert.getPersonName(), dataStyle);
97
+                setCellValue(dataRow, col++, cert.getGender(), dataStyle);
98
+                setDateCellValue(dataRow, col++, cert.getBirthDate(), sdf, dateStyle);
99
+                setCellValue(dataRow, col++, cert.getIdCard(), dataStyle);
100
+                setCellValue(dataRow, col++, cert.getCertLevel(), dataStyle);
101
+                setDateCellValue(dataRow, col++, cert.getIssueDate(), sdf, dateStyle);
102
+                setCellValue(dataRow, col++, cert.getCertNo(), dataStyle);
103
+                setBigDecimalCellValue(dataRow, col++, cert.getTheoryScore(), dataStyle);
104
+                setBigDecimalCellValue(dataRow, col++, cert.getPracticeScore(), dataStyle);
105
+                setCellValue(dataRow, col++, cert.getEvalScore(), dataStyle);
106
+                setCellValue(dataRow, col++, cert.getEmploymentType(), dataStyle);
107
+                setCellValue(dataRow, col++, cert.getServiceMonths() != null ? String.valueOf(cert.getServiceMonths()) : null, dataStyle);
108
+                setCellValue(dataRow, col++, cert.getRemark(), dataStyle);
109
+
110
+                // 复审时间字段(按次序填充)
111
+                TreeMap<Integer, Date> reviewTimeMap = getReviewTimeMap(cert);
112
+                for (int seq = 1; seq <= maxReviewSeq; seq++) {
113
+                    Date reviewTime = reviewTimeMap.get(seq);
114
+                    setDateCellValue(dataRow, col++, reviewTime, sdf, dateStyle);
115
+                }
116
+            }
117
+
118
+            wb.write(response.getOutputStream());
119
+        } catch (Exception e) {
120
+            throw new RuntimeException("导出证书信息失败", e);
121
+        } finally {
122
+            IOUtils.closeQuietly(wb);
123
+        }
124
+    }
125
+
126
+    /**
127
+     * 获取列表中最大的复审次序
128
+     */
129
+    private static int getMaxReviewSeq(List<LedgerCertificateInfo> list) {
130
+        int maxSeq = 0;
131
+        if (list == null) {
132
+            return 0;
133
+        }
134
+        for (LedgerCertificateInfo cert : list) {
135
+            List<LedgerCertificateReviewRecord> records = cert.getReviewRecords();
136
+            if (records != null) {
137
+                for (LedgerCertificateReviewRecord record : records) {
138
+                    if (record.getReviewSeq() != null && record.getReviewSeq() > maxSeq) {
139
+                        maxSeq = record.getReviewSeq();
140
+                    }
141
+                }
142
+            }
143
+        }
144
+        return maxSeq;
145
+    }
146
+
147
+    /**
148
+     * 将复审记录转为 seq -> reviewTime 的有序Map
149
+     */
150
+    private static TreeMap<Integer, Date> getReviewTimeMap(LedgerCertificateInfo cert) {
151
+        TreeMap<Integer, Date> map = new TreeMap<>();
152
+        List<LedgerCertificateReviewRecord> records = cert.getReviewRecords();
153
+        if (records != null) {
154
+            for (LedgerCertificateReviewRecord record : records) {
155
+                if (record.getReviewSeq() != null) {
156
+                    map.put(record.getReviewSeq(), record.getReviewTime());
157
+                }
158
+            }
159
+        }
160
+        return map;
161
+    }
162
+
163
+    /** 创建表头样式 */
164
+    private static CellStyle createHeaderStyle(Workbook wb) {
165
+        CellStyle style = wb.createCellStyle();
166
+        style.setAlignment(HorizontalAlignment.CENTER);
167
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
168
+        style.setBorderRight(BorderStyle.THIN);
169
+        style.setBorderLeft(BorderStyle.THIN);
170
+        style.setBorderTop(BorderStyle.THIN);
171
+        style.setBorderBottom(BorderStyle.THIN);
172
+        style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
173
+        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
174
+        Font font = wb.createFont();
175
+        font.setFontName("Arial");
176
+        font.setFontHeightInPoints((short) 10);
177
+        font.setBold(true);
178
+        font.setColor(IndexedColors.WHITE.getIndex());
179
+        style.setFont(font);
180
+        return style;
181
+    }
182
+
183
+    /** 创建数据样式 */
184
+    private static CellStyle createDataStyle(Workbook wb) {
185
+        CellStyle style = wb.createCellStyle();
186
+        style.setAlignment(HorizontalAlignment.CENTER);
187
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
188
+        style.setBorderRight(BorderStyle.THIN);
189
+        style.setBorderLeft(BorderStyle.THIN);
190
+        style.setBorderTop(BorderStyle.THIN);
191
+        style.setBorderBottom(BorderStyle.THIN);
192
+        Font font = wb.createFont();
193
+        font.setFontName("Arial");
194
+        font.setFontHeightInPoints((short) 10);
195
+        style.setFont(font);
196
+        return style;
197
+    }
198
+
199
+    /** 创建日期样式 */
200
+    private static CellStyle createDateStyle(Workbook wb) {
201
+        CellStyle style = createDataStyle(wb);
202
+        DataFormat dataFormat = wb.createDataFormat();
203
+        style.setDataFormat(dataFormat.getFormat("yyyy-MM-dd"));
204
+        return style;
205
+    }
206
+
207
+    /** 设置字符串单元格值 */
208
+    private static void setCellValue(Row row, int col, String value, CellStyle style) {
209
+        Cell cell = row.createCell(col);
210
+        cell.setCellStyle(style);
211
+        cell.setCellValue(value != null ? value : "");
212
+    }
213
+
214
+    /** 设置日期单元格值 */
215
+    private static void setDateCellValue(Row row, int col, Date value, SimpleDateFormat sdf, CellStyle dateStyle) {
216
+        Cell cell = row.createCell(col);
217
+        if (value != null) {
218
+            cell.setCellStyle(dateStyle);
219
+            cell.setCellValue(value);
220
+        } else {
221
+            cell.setCellStyle(dateStyle);
222
+            cell.setCellValue("");
223
+        }
224
+    }
225
+
226
+    /** 设置BigDecimal单元格值 */
227
+    private static void setBigDecimalCellValue(Row row, int col, BigDecimal value, CellStyle style) {
228
+        Cell cell = row.createCell(col);
229
+        cell.setCellStyle(style);
230
+        if (value != null) {
231
+            cell.setCellValue(value.doubleValue());
232
+        }
233
+    }
234
+}

+ 9 - 0
airport-ledger/src/main/resources/mapper/personnel/LedgerCertificateReviewRecordMapper.xml

@@ -107,6 +107,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
107 107
         </foreach>
108 108
     </delete>
109 109
 
110
+    <select id="selectReviewRecordsByCertIds" resultMap="CertificateReviewRecordResult">
111
+        <include refid="selectCertificateReviewRecordVo"/>
112
+        where del_flag = '0' and cert_id in
113
+        <foreach collection="certIds" item="certId" open="(" separator="," close=")">
114
+            #{certId}
115
+        </foreach>
116
+        order by cert_id asc, review_seq asc
117
+    </select>
118
+
110 119
     <insert id="batchInsertReviewRecords">
111 120
         insert into ledger_certificate_review_record
112 121
         (cert_id, review_seq, review_time, remark, create_by, create_time, del_flag)