|
|
@@ -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
|
+}
|