第234集SpringBoot Excel动态导出架构实战:通用模板引擎、大数据处理、异步导出的企业级解决方案

前言

在当今企业级应用中,Excel导出功能已成为业务系统不可或缺的重要组成部分。传统的Excel导出方式往往功能单一、扩展性差,无法满足复杂业务场景的动态导出需求。基于SpringBoot的Excel动态导出架构,不仅能够提供灵活的模板引擎,还能实现大数据量的高效处理、异步导出和智能缓存。随着业务复杂度的不断提升和数据量的持续增长,构建可扩展、高性能的Excel导出框架,已成为企业级架构师必须掌握的关键技能。

本文将深入探讨SpringBoot中Excel动态导出的架构设计与实战应用,从通用模板引擎到大数据处理,从异步导出到性能优化,为企业构建稳定、高效的Excel导出解决方案提供全面的技术指导。

一、SpringBoot Excel动态导出架构概述与核心原理

1.1 Excel动态导出架构设计

SpringBoot Excel动态导出系统采用分层架构设计,通过模板引擎、数据处理、异步导出等技术,实现高效的Excel导出能力。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
graph TB
A[导出请求] --> B[导出解析器]
B --> C[模板引擎]
C --> D[数据处理器]
D --> E[Excel生成器]
E --> F[文件管理器]
F --> G[响应返回]

H[模板引擎] --> I[动态模板]
H --> J[静态模板]
H --> K[自定义模板]
H --> L[模板缓存]

M[数据处理] --> N[数据查询]
M --> O[数据转换]
M --> P[数据分页]
M --> Q[数据过滤]

R[Excel生成] --> S[POI生成]
R --> T[EasyExcel生成]
R --> U[自定义生成器]
R --> V[样式处理]

W[异步处理] --> X[任务队列]
W --> Y[进度监控]
W --> Z[结果通知]
W --> AA[文件下载]

BB[监控体系] --> CC[导出监控]
BB --> DD[性能监控]
BB --> EE[异常监控]
BB --> FF[文件监控]

1.2 Excel动态导出核心特性

1.2.1 通用模板引擎

  • 动态模板:支持运行时动态生成Excel模板
  • 静态模板:支持预定义的Excel模板
  • 自定义模板:支持用户自定义Excel模板
  • 模板缓存:智能缓存模板提高性能

1.2.2 大数据处理能力

  • 数据分页:支持大数据量的分页处理
  • 流式处理:支持流式数据处理避免内存溢出
  • 数据转换:支持多种数据格式转换
  • 数据过滤:支持复杂的数据过滤条件

1.2.3 异步导出机制

  • 任务队列:使用消息队列管理导出任务
  • 进度监控:实时监控导出进度
  • 结果通知:导出完成后的通知机制
  • 文件管理:智能的文件存储和管理

二、SpringBoot Excel动态导出核心实现

2.1 Excel导出引擎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
// Excel导出引擎
@Service
@Slf4j
public class ExcelExportEngine {

@Autowired
private ExcelTemplateEngine templateEngine;

@Autowired
private ExcelDataProcessor dataProcessor;

@Autowired
private ExcelFileManager fileManager;

@Autowired
private ExcelAsyncProcessor asyncProcessor;

@Autowired
private ExcelMonitorService monitorService;

/**
* 同步导出Excel
*/
public ExcelExportResult exportExcelSync(ExcelExportRequest request) {
long startTime = System.currentTimeMillis();
String exportId = generateExportId();

try {
// 1. 验证导出请求
validateExportRequest(request);

// 2. 获取或创建模板
ExcelTemplate template = templateEngine.getOrCreateTemplate(request);

// 3. 处理数据
ExcelDataContext dataContext = dataProcessor.processData(request);

// 4. 生成Excel文件
ExcelFileInfo fileInfo = generateExcelFile(template, dataContext, request);

// 5. 记录监控信息
long executionTime = System.currentTimeMillis() - startTime;
monitorService.recordExportExecution(exportId, executionTime, dataContext.getTotalCount());

// 6. 返回导出结果
ExcelExportResult result = new ExcelExportResult();
result.setExportId(exportId);
result.setSuccess(true);
result.setFileInfo(fileInfo);
result.setExecutionTime(executionTime);
result.setTotalCount(dataContext.getTotalCount());

return result;

} catch (Exception e) {
log.error("Excel同步导出失败: exportId={}", exportId, e);

ExcelExportResult result = new ExcelExportResult();
result.setExportId(exportId);
result.setSuccess(false);
result.setErrorMessage(e.getMessage());
result.setExecutionTime(System.currentTimeMillis() - startTime);

return result;
}
}

/**
* 异步导出Excel
*/
public ExcelExportResult exportExcelAsync(ExcelExportRequest request) {
String exportId = generateExportId();

try {
// 1. 验证导出请求
validateExportRequest(request);

// 2. 创建异步导出任务
ExcelAsyncTask asyncTask = createAsyncExportTask(exportId, request);

// 3. 提交异步任务
asyncProcessor.submitAsyncTask(asyncTask);

// 4. 返回异步结果
ExcelExportResult result = new ExcelExportResult();
result.setExportId(exportId);
result.setSuccess(true);
result.setAsync(true);
result.setMessage("导出任务已提交,请稍后查询结果");

return result;

} catch (Exception e) {
log.error("Excel异步导出失败: exportId={}", exportId, e);

ExcelExportResult result = new ExcelExportResult();
result.setExportId(exportId);
result.setSuccess(false);
result.setErrorMessage(e.getMessage());

return result;
}
}

/**
* 批量导出Excel
*/
public CompletableFuture<List<ExcelExportResult>> exportExcelBatch(List<ExcelExportRequest> requests) {
List<CompletableFuture<ExcelExportResult>> futures = requests.stream()
.map(request -> CompletableFuture.supplyAsync(() -> exportExcelSync(request)))
.collect(Collectors.toList());

return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApply(v -> futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
}

/**
* 生成Excel文件
*/
private ExcelFileInfo generateExcelFile(ExcelTemplate template, ExcelDataContext dataContext,
ExcelExportRequest request) throws Exception {

// 1. 创建Excel工作簿
Workbook workbook = createWorkbook(template.getFormat());

try {
// 2. 创建样式
Map<String, CellStyle> styles = createStyles(workbook, template);

// 3. 创建表头
createHeaders(workbook, template, styles);

// 4. 填充数据
fillData(workbook, template, dataContext, styles, request);

// 5. 应用格式
applyFormatting(workbook, template);

// 6. 保存文件
String fileName = generateFileName(request);
String filePath = fileManager.saveFile(workbook, fileName);

// 7. 创建文件信息
ExcelFileInfo fileInfo = new ExcelFileInfo();
fileInfo.setFileName(fileName);
fileInfo.setFilePath(filePath);
fileInfo.setFileSize(calculateFileSize(filePath));
fileInfo.setCreateTime(System.currentTimeMillis());

return fileInfo;

} finally {
workbook.close();
}
}

/**
* 创建Excel工作簿
*/
private Workbook createWorkbook(ExcelFormat format) {
switch (format) {
case XLS:
return new HSSFWorkbook();
case XLSX:
return new XSSFWorkbook();
case XLSX_STREAMING:
return new SXSSFWorkbook();
default:
return new XSSFWorkbook();
}
}

/**
* 创建样式
*/
private Map<String, CellStyle> createStyles(Workbook workbook, ExcelTemplate template) {
Map<String, CellStyle> styles = new HashMap<>();

// 1. 创建表头样式
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerFont.setFontHeightInPoints((short) 12);
headerStyle.setFont(headerFont);
headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
headerStyle.setBorderBottom(BorderStyle.THIN);
headerStyle.setBorderTop(BorderStyle.THIN);
headerStyle.setBorderRight(BorderStyle.THIN);
headerStyle.setBorderLeft(BorderStyle.THIN);
styles.put("header", headerStyle);

// 2. 创建数据样式
CellStyle dataStyle = workbook.createCellStyle();
Font dataFont = workbook.createFont();
dataFont.setFontHeightInPoints((short) 10);
dataStyle.setFont(dataFont);
dataStyle.setBorderBottom(BorderStyle.THIN);
dataStyle.setBorderTop(BorderStyle.THIN);
dataStyle.setBorderRight(BorderStyle.THIN);
dataStyle.setBorderLeft(BorderStyle.THIN);
styles.put("data", dataStyle);

// 3. 创建数字样式
CellStyle numberStyle = workbook.createCellStyle();
numberStyle.cloneStyleFrom(dataStyle);
numberStyle.setDataFormat(workbook.createDataFormat().getFormat("#,##0.00"));
styles.put("number", numberStyle);

// 4. 创建日期样式
CellStyle dateStyle = workbook.createCellStyle();
dateStyle.cloneStyleFrom(dataStyle);
dateStyle.setDataFormat(workbook.createDataFormat().getFormat("yyyy-mm-dd hh:mm:ss"));
styles.put("date", dateStyle);

return styles;
}

/**
* 创建表头
*/
private void createHeaders(Workbook workbook, ExcelTemplate template, Map<String, CellStyle> styles) {
Sheet sheet = workbook.createSheet(template.getSheetName());

// 1. 创建表头行
Row headerRow = sheet.createRow(0);

// 2. 设置表头
List<ExcelColumn> columns = template.getColumns();
for (int i = 0; i < columns.size(); i++) {
ExcelColumn column = columns.get(i);
Cell cell = headerRow.createCell(i);
cell.setCellValue(column.getHeaderName());
cell.setCellStyle(styles.get("header"));

// 3. 设置列宽
sheet.setColumnWidth(i, column.getWidth() * 256);
}
}

/**
* 填充数据
*/
private void fillData(Workbook workbook, ExcelTemplate template, ExcelDataContext dataContext,
Map<String, CellStyle> styles, ExcelExportRequest request) throws Exception {

Sheet sheet = workbook.getSheetAt(0);
List<ExcelColumn> columns = template.getColumns();

// 1. 分页处理数据
int pageSize = request.getPageSize() != null ? request.getPageSize() : 1000;
int totalPages = (int) Math.ceil((double) dataContext.getTotalCount() / pageSize);

int currentRow = 1; // 从第二行开始(第一行是表头)

for (int page = 0; page < totalPages; page++) {
// 2. 获取当前页数据
List<Map<String, Object>> pageData = dataProcessor.getPageData(dataContext, page, pageSize);

// 3. 填充当前页数据
for (Map<String, Object> rowData : pageData) {
Row row = sheet.createRow(currentRow++);

for (int col = 0; col < columns.size(); col++) {
ExcelColumn column = columns.get(col);
Cell cell = row.createCell(col);

// 4. 设置单元格值
Object value = rowData.get(column.getFieldName());
setCellValue(cell, value, column.getDataType());

// 5. 设置单元格样式
CellStyle style = getCellStyle(styles, column.getDataType());
cell.setCellStyle(style);
}
}

// 6. 更新进度(如果是异步任务)
if (request.isAsync()) {
updateExportProgress(request.getExportId(), page + 1, totalPages);
}
}
}

/**
* 设置单元格值
*/
private void setCellValue(Cell cell, Object value, ExcelDataType dataType) {
if (value == null) {
cell.setCellValue("");
return;
}

switch (dataType) {
case STRING:
cell.setCellValue(value.toString());
break;
case NUMBER:
if (value instanceof Number) {
cell.setCellValue(((Number) value).doubleValue());
} else {
cell.setCellValue(Double.parseDouble(value.toString()));
}
break;
case DATE:
if (value instanceof Date) {
cell.setCellValue((Date) value);
} else if (value instanceof LocalDateTime) {
cell.setCellValue(Date.from(((LocalDateTime) value).atZone(ZoneId.systemDefault()).toInstant()));
} else {
cell.setCellValue(value.toString());
}
break;
case BOOLEAN:
if (value instanceof Boolean) {
cell.setCellValue((Boolean) value);
} else {
cell.setCellValue(Boolean.parseBoolean(value.toString()));
}
break;
default:
cell.setCellValue(value.toString());
break;
}
}

/**
* 获取单元格样式
*/
private CellStyle getCellStyle(Map<String, CellStyle> styles, ExcelDataType dataType) {
switch (dataType) {
case NUMBER:
return styles.get("number");
case DATE:
return styles.get("date");
default:
return styles.get("data");
}
}

/**
* 应用格式
*/
private void applyFormatting(Workbook workbook, ExcelTemplate template) {
Sheet sheet = workbook.getSheetAt(0);

// 1. 应用自动筛选
if (template.isAutoFilter()) {
sheet.setAutoFilter(new CellRangeAddress(0, sheet.getLastRowNum(), 0, template.getColumns().size() - 1));
}

// 2. 冻结窗格
if (template.isFreezePane()) {
sheet.createFreezePane(0, 1);
}

// 3. 应用条件格式
if (template.getConditionalFormats() != null) {
applyConditionalFormatting(sheet, template.getConditionalFormats());
}
}

/**
* 应用条件格式
*/
private void applyConditionalFormatting(Sheet sheet, List<ConditionalFormat> conditionalFormats) {
// 实现条件格式应用逻辑
for (ConditionalFormat format : conditionalFormats) {
// 根据条件格式类型应用不同的格式
}
}

/**
* 创建异步导出任务
*/
private ExcelAsyncTask createAsyncExportTask(String exportId, ExcelExportRequest request) {
ExcelAsyncTask task = new ExcelAsyncTask();
task.setExportId(exportId);
task.setRequest(request);
task.setCreateTime(System.currentTimeMillis());
task.setStatus(ExcelAsyncTaskStatus.PENDING);
return task;
}

/**
* 更新导出进度
*/
private void updateExportProgress(String exportId, int currentPage, int totalPages) {
// 实现进度更新逻辑
log.debug("更新导出进度: exportId={}, currentPage={}, totalPages={}", exportId, currentPage, totalPages);
}

/**
* 验证导出请求
*/
private void validateExportRequest(ExcelExportRequest request) {
if (request == null) {
throw new IllegalArgumentException("导出请求不能为空");
}

if (request.getTemplateId() == null && request.getColumns() == null) {
throw new IllegalArgumentException("模板ID和列配置不能同时为空");
}

if (request.getDataSource() == null) {
throw new IllegalArgumentException("数据源不能为空");
}
}

/**
* 生成文件名
*/
private String generateFileName(ExcelExportRequest request) {
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
String fileName = request.getFileName() != null ? request.getFileName() : "export";
return fileName + "_" + timestamp + ".xlsx";
}

/**
* 计算文件大小
*/
private long calculateFileSize(String filePath) {
try {
return Files.size(Paths.get(filePath));
} catch (IOException e) {
log.error("计算文件大小失败: filePath={}", filePath, e);
return 0;
}
}

/**
* 生成导出ID
*/
private String generateExportId() {
return "EXPORT_" + System.currentTimeMillis() + "_" + RandomUtils.nextInt(1000, 9999);
}
}

// Excel导出请求
public class ExcelExportRequest {
private String exportId;
private String templateId;
private String fileName;
private String dataSource;
private List<ExcelColumn> columns;
private Map<String, Object> parameters;
private ExcelFormat format;
private Integer pageSize;
private boolean async;
private String callbackUrl;

// 构造函数和getter/setter方法
}

// Excel导出结果
public class ExcelExportResult {
private String exportId;
private boolean success;
private String errorMessage;
private ExcelFileInfo fileInfo;
private long executionTime;
private long totalCount;
private boolean async;
private String message;

// 构造函数和getter/setter方法
}

// Excel文件信息
public class ExcelFileInfo {
private String fileName;
private String filePath;
private long fileSize;
private long createTime;
private String downloadUrl;

// 构造函数和getter/setter方法
}

// Excel列配置
public class ExcelColumn {
private String fieldName;
private String headerName;
private ExcelDataType dataType;
private int width;
private String format;
private boolean hidden;

// 构造函数和getter/setter方法
}

// Excel数据类型
public enum ExcelDataType {
STRING, NUMBER, DATE, BOOLEAN, FORMULA
}

// Excel格式
public enum ExcelFormat {
XLS, XLSX, XLSX_STREAMING
}

2.2 Excel模板引擎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
// Excel模板引擎
@Service
@Slf4j
public class ExcelTemplateEngine {

@Autowired
private ExcelTemplateRepository templateRepository;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private static final String TEMPLATE_CACHE_PREFIX = "excel_template:";
private static final long TEMPLATE_CACHE_TTL = 3600; // 1小时

/**
* 获取或创建模板
*/
public ExcelTemplate getOrCreateTemplate(ExcelExportRequest request) {
try {
// 1. 如果指定了模板ID,从缓存或数据库获取
if (request.getTemplateId() != null) {
ExcelTemplate template = getTemplateById(request.getTemplateId());
if (template != null) {
return template;
}
}

// 2. 如果指定了列配置,动态创建模板
if (request.getColumns() != null) {
return createDynamicTemplate(request);
}

// 3. 使用默认模板
return getDefaultTemplate(request);

} catch (Exception e) {
log.error("获取或创建模板失败: templateId={}", request.getTemplateId(), e);
throw new RuntimeException("获取或创建模板失败", e);
}
}

/**
* 根据ID获取模板
*/
public ExcelTemplate getTemplateById(String templateId) {
try {
// 1. 先从缓存获取
String cacheKey = TEMPLATE_CACHE_PREFIX + templateId;
ExcelTemplate cachedTemplate = (ExcelTemplate) redisTemplate.opsForValue().get(cacheKey);

if (cachedTemplate != null) {
log.debug("从缓存获取模板: templateId={}", templateId);
return cachedTemplate;
}

// 2. 从数据库获取
ExcelTemplate template = templateRepository.findById(templateId);

if (template != null) {
// 3. 缓存模板
redisTemplate.opsForValue().set(cacheKey, template, TEMPLATE_CACHE_TTL, TimeUnit.SECONDS);
log.debug("从数据库获取模板并缓存: templateId={}", templateId);
}

return template;

} catch (Exception e) {
log.error("根据ID获取模板失败: templateId={}", templateId, e);
return null;
}
}

/**
* 创建动态模板
*/
public ExcelTemplate createDynamicTemplate(ExcelExportRequest request) {
ExcelTemplate template = new ExcelTemplate();
template.setId(generateTemplateId());
template.setName("动态模板_" + System.currentTimeMillis());
template.setDescription("动态生成的Excel模板");
template.setFormat(ExcelFormat.XLSX);
template.setSheetName("Sheet1");
template.setColumns(request.getColumns());
template.setAutoFilter(true);
template.setFreezePane(true);
template.setCreateTime(System.currentTimeMillis());
template.setDynamic(true);

// 设置默认样式
setDefaultTemplateStyles(template);

log.info("创建动态模板: templateId={}, columnCount={}", template.getId(), template.getColumns().size());

return template;
}

/**
* 获取默认模板
*/
public ExcelTemplate getDefaultTemplate(ExcelExportRequest request) {
ExcelTemplate template = new ExcelTemplate();
template.setId("DEFAULT_TEMPLATE");
template.setName("默认模板");
template.setDescription("系统默认的Excel模板");
template.setFormat(ExcelFormat.XLSX);
template.setSheetName("Sheet1");
template.setAutoFilter(true);
template.setFreezePane(true);
template.setCreateTime(System.currentTimeMillis());
template.setDynamic(false);

// 设置默认列
List<ExcelColumn> defaultColumns = createDefaultColumns(request);
template.setColumns(defaultColumns);

return template;
}

/**
* 保存模板
*/
public ExcelTemplate saveTemplate(ExcelTemplate template) {
try {
// 1. 保存到数据库
ExcelTemplate savedTemplate = templateRepository.save(template);

// 2. 更新缓存
String cacheKey = TEMPLATE_CACHE_PREFIX + savedTemplate.getId();
redisTemplate.opsForValue().set(cacheKey, savedTemplate, TEMPLATE_CACHE_TTL, TimeUnit.SECONDS);

log.info("保存模板成功: templateId={}", savedTemplate.getId());

return savedTemplate;

} catch (Exception e) {
log.error("保存模板失败: templateId={}", template.getId(), e);
throw new RuntimeException("保存模板失败", e);
}
}

/**
* 删除模板
*/
public boolean deleteTemplate(String templateId) {
try {
// 1. 从数据库删除
boolean deleted = templateRepository.deleteById(templateId);

if (deleted) {
// 2. 从缓存删除
String cacheKey = TEMPLATE_CACHE_PREFIX + templateId;
redisTemplate.delete(cacheKey);

log.info("删除模板成功: templateId={}", templateId);
}

return deleted;

} catch (Exception e) {
log.error("删除模板失败: templateId={}", templateId, e);
return false;
}
}

/**
* 获取所有模板
*/
public List<ExcelTemplate> getAllTemplates() {
try {
return templateRepository.findAll();
} catch (Exception e) {
log.error("获取所有模板失败", e);
return new ArrayList<>();
}
}

/**
* 根据名称搜索模板
*/
public List<ExcelTemplate> searchTemplatesByName(String name) {
try {
return templateRepository.findByNameContaining(name);
} catch (Exception e) {
log.error("根据名称搜索模板失败: name={}", name, e);
return new ArrayList<>();
}
}

/**
* 复制模板
*/
public ExcelTemplate copyTemplate(String templateId, String newName) {
try {
// 1. 获取原模板
ExcelTemplate originalTemplate = getTemplateById(templateId);
if (originalTemplate == null) {
throw new IllegalArgumentException("模板不存在: " + templateId);
}

// 2. 创建新模板
ExcelTemplate newTemplate = new ExcelTemplate();
newTemplate.setId(generateTemplateId());
newTemplate.setName(newName);
newTemplate.setDescription("复制自: " + originalTemplate.getName());
newTemplate.setFormat(originalTemplate.getFormat());
newTemplate.setSheetName(originalTemplate.getSheetName());
newTemplate.setColumns(new ArrayList<>(originalTemplate.getColumns()));
newTemplate.setAutoFilter(originalTemplate.isAutoFilter());
newTemplate.setFreezePane(originalTemplate.isFreezePane());
newTemplate.setConditionalFormats(new ArrayList<>(originalTemplate.getConditionalFormats()));
newTemplate.setCreateTime(System.currentTimeMillis());
newTemplate.setDynamic(false);

// 3. 保存新模板
return saveTemplate(newTemplate);

} catch (Exception e) {
log.error("复制模板失败: templateId={}, newName={}", templateId, newName, e);
throw new RuntimeException("复制模板失败", e);
}
}

/**
* 设置默认模板样式
*/
private void setDefaultTemplateStyles(ExcelTemplate template) {
// 设置默认的列宽
for (ExcelColumn column : template.getColumns()) {
if (column.getWidth() == 0) {
column.setWidth(15); // 默认列宽
}
}
}

/**
* 创建默认列
*/
private List<ExcelColumn> createDefaultColumns(ExcelExportRequest request) {
List<ExcelColumn> columns = new ArrayList<>();

// 根据数据源创建默认列
if (request.getDataSource() != null) {
// 这里可以根据数据源动态创建列
// 实际实现中可能需要查询数据源的表结构
}

return columns;
}

/**
* 生成模板ID
*/
private String generateTemplateId() {
return "TEMPLATE_" + System.currentTimeMillis() + "_" + RandomUtils.nextInt(1000, 9999);
}
}

// Excel模板
public class ExcelTemplate {
private String id;
private String name;
private String description;
private ExcelFormat format;
private String sheetName;
private List<ExcelColumn> columns;
private boolean autoFilter;
private boolean freezePane;
private List<ConditionalFormat> conditionalFormats;
private long createTime;
private long updateTime;
private boolean dynamic;

// 构造函数和getter/setter方法
}

// 条件格式
public class ConditionalFormat {
private String type;
private String condition;
private String value;
private String style;

// 构造函数和getter/setter方法
}

// Excel模板仓库
@Repository
public interface ExcelTemplateRepository {

ExcelTemplate findById(String id);

ExcelTemplate save(ExcelTemplate template);

boolean deleteById(String id);

List<ExcelTemplate> findAll();

List<ExcelTemplate> findByNameContaining(String name);
}

2.3 Excel数据处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
// Excel数据处理器
@Service
@Slf4j
public class ExcelDataProcessor {

@Autowired
private DataSourceManager dataSourceManager;

@Autowired
private DataConverterService converterService;

@Autowired
private DataFilterService filterService;

/**
* 处理数据
*/
public ExcelDataContext processData(ExcelExportRequest request) {
try {
// 1. 获取数据源
DataSource dataSource = dataSourceManager.getDataSource(request.getDataSource());
if (dataSource == null) {
throw new IllegalArgumentException("数据源不存在: " + request.getDataSource());
}

// 2. 构建查询条件
QueryCondition condition = buildQueryCondition(request);

// 3. 执行数据查询
List<Map<String, Object>> rawData = executeDataQuery(dataSource, condition);

// 4. 数据转换
List<Map<String, Object>> convertedData = converterService.convertData(rawData, request.getColumns());

// 5. 数据过滤
List<Map<String, Object>> filteredData = filterService.filterData(convertedData, request.getParameters());

// 6. 创建数据上下文
ExcelDataContext context = new ExcelDataContext();
context.setDataSource(dataSource);
context.setRawData(rawData);
context.setConvertedData(convertedData);
context.setFilteredData(filteredData);
context.setTotalCount(filteredData.size());
context.setQueryCondition(condition);

log.info("数据处理完成: dataSource={}, totalCount={}", request.getDataSource(), context.getTotalCount());

return context;

} catch (Exception e) {
log.error("数据处理失败: dataSource={}", request.getDataSource(), e);
throw new RuntimeException("数据处理失败", e);
}
}

/**
* 获取分页数据
*/
public List<Map<String, Object>> getPageData(ExcelDataContext context, int page, int pageSize) {
try {
List<Map<String, Object>> allData = context.getFilteredData();

int startIndex = page * pageSize;
int endIndex = Math.min(startIndex + pageSize, allData.size());

if (startIndex >= allData.size()) {
return new ArrayList<>();
}

return allData.subList(startIndex, endIndex);

} catch (Exception e) {
log.error("获取分页数据失败: page={}, pageSize={}", page, pageSize, e);
return new ArrayList<>();
}
}

/**
* 构建查询条件
*/
private QueryCondition buildQueryCondition(ExcelExportRequest request) {
QueryCondition condition = new QueryCondition();

// 1. 设置基础查询条件
if (request.getParameters() != null) {
condition.setFilters(request.getParameters());
}

// 2. 设置排序条件
if (request.getSortFields() != null) {
condition.setSortFields(request.getSortFields());
}

// 3. 设置分页条件
if (request.getPageSize() != null) {
condition.setPageSize(request.getPageSize());
}

return condition;
}

/**
* 执行数据查询
*/
private List<Map<String, Object>> executeDataQuery(DataSource dataSource, QueryCondition condition) {
try {
// 根据数据源类型执行不同的查询逻辑
if (dataSource instanceof DatabaseDataSource) {
return executeDatabaseQuery((DatabaseDataSource) dataSource, condition);
} else if (dataSource instanceof ApiDataSource) {
return executeApiQuery((ApiDataSource) dataSource, condition);
} else if (dataSource instanceof CacheDataSource) {
return executeCacheQuery((CacheDataSource) dataSource, condition);
} else {
throw new IllegalArgumentException("不支持的数据源类型: " + dataSource.getClass().getSimpleName());
}

} catch (Exception e) {
log.error("执行数据查询失败: dataSource={}", dataSource.getName(), e);
throw new RuntimeException("执行数据查询失败", e);
}
}

/**
* 执行数据库查询
*/
private List<Map<String, Object>> executeDatabaseQuery(DatabaseDataSource dataSource, QueryCondition condition) {
List<Map<String, Object>> results = new ArrayList<>();

try (Connection connection = dataSource.getConnection()) {
// 1. 构建SQL查询
String sql = buildSqlQuery(dataSource, condition);

try (PreparedStatement statement = connection.prepareStatement(sql)) {
// 2. 设置查询参数
setQueryParameters(statement, condition);

// 3. 执行查询
try (ResultSet resultSet = statement.executeQuery()) {
// 4. 处理结果集
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();

while (resultSet.next()) {
Map<String, Object> row = new HashMap<>();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnName(i);
Object value = resultSet.getObject(i);
row.put(columnName, value);
}
results.add(row);
}
}
}

} catch (SQLException e) {
log.error("数据库查询失败: dataSource={}", dataSource.getName(), e);
throw new RuntimeException("数据库查询失败", e);
}

return results;
}

/**
* 执行API查询
*/
private List<Map<String, Object>> executeApiQuery(ApiDataSource dataSource, QueryCondition condition) {
try {
// 1. 构建API请求
String url = buildApiUrl(dataSource, condition);
Map<String, String> headers = buildApiHeaders(dataSource);
Map<String, Object> parameters = buildApiParameters(condition);

// 2. 发送HTTP请求
String response = sendHttpRequest(url, headers, parameters);

// 3. 解析响应
return parseApiResponse(response);

} catch (Exception e) {
log.error("API查询失败: dataSource={}", dataSource.getName(), e);
throw new RuntimeException("API查询失败", e);
}
}

/**
* 执行缓存查询
*/
private List<Map<String, Object>> executeCacheQuery(CacheDataSource dataSource, QueryCondition condition) {
try {
// 1. 构建缓存键
String cacheKey = buildCacheKey(dataSource, condition);

// 2. 从缓存获取数据
Object cachedData = dataSource.get(cacheKey);

if (cachedData instanceof List) {
return (List<Map<String, Object>>) cachedData;
} else {
return new ArrayList<>();
}

} catch (Exception e) {
log.error("缓存查询失败: dataSource={}", dataSource.getName(), e);
throw new RuntimeException("缓存查询失败", e);
}
}

/**
* 构建SQL查询
*/
private String buildSqlQuery(DatabaseDataSource dataSource, QueryCondition condition) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT * FROM ").append(dataSource.getTableName());

// 添加WHERE条件
if (condition.getFilters() != null && !condition.getFilters().isEmpty()) {
sql.append(" WHERE ");
List<String> whereConditions = new ArrayList<>();
for (Map.Entry<String, Object> entry : condition.getFilters().entrySet()) {
whereConditions.add(entry.getKey() + " = ?");
}
sql.append(String.join(" AND ", whereConditions));
}

// 添加ORDER BY
if (condition.getSortFields() != null && !condition.getSortFields().isEmpty()) {
sql.append(" ORDER BY ");
List<String> sortConditions = new ArrayList<>();
for (SortField sortField : condition.getSortFields()) {
sortConditions.add(sortField.getFieldName() + " " + sortField.getDirection());
}
sql.append(String.join(", ", sortConditions));
}

// 添加LIMIT
if (condition.getPageSize() != null) {
sql.append(" LIMIT ").append(condition.getPageSize());
}

return sql.toString();
}

/**
* 设置查询参数
*/
private void setQueryParameters(PreparedStatement statement, QueryCondition condition) throws SQLException {
if (condition.getFilters() != null) {
int index = 1;
for (Object value : condition.getFilters().values()) {
statement.setObject(index++, value);
}
}
}

/**
* 构建API URL
*/
private String buildApiUrl(ApiDataSource dataSource, QueryCondition condition) {
return dataSource.getBaseUrl() + "/query";
}

/**
* 构建API请求头
*/
private Map<String, String> buildApiHeaders(ApiDataSource dataSource) {
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
headers.put("Authorization", "Bearer " + dataSource.getApiKey());
return headers;
}

/**
* 构建API参数
*/
private Map<String, Object> buildApiParameters(QueryCondition condition) {
Map<String, Object> parameters = new HashMap<>();
if (condition.getFilters() != null) {
parameters.putAll(condition.getFilters());
}
return parameters;
}

/**
* 发送HTTP请求
*/
private String sendHttpRequest(String url, Map<String, String> headers, Map<String, Object> parameters) {
// 实现HTTP请求发送逻辑
return "{}";
}

/**
* 解析API响应
*/
private List<Map<String, Object>> parseApiResponse(String response) {
// 实现API响应解析逻辑
return new ArrayList<>();
}

/**
* 构建缓存键
*/
private String buildCacheKey(CacheDataSource dataSource, QueryCondition condition) {
StringBuilder keyBuilder = new StringBuilder();
keyBuilder.append(dataSource.getName()).append(":");
keyBuilder.append(condition.hashCode());
return keyBuilder.toString();
}
}

// Excel数据上下文
public class ExcelDataContext {
private DataSource dataSource;
private List<Map<String, Object>> rawData;
private List<Map<String, Object>> convertedData;
private List<Map<String, Object>> filteredData;
private long totalCount;
private QueryCondition queryCondition;

// 构造函数和getter/setter方法
}

// 查询条件
public class QueryCondition {
private Map<String, Object> filters;
private List<SortField> sortFields;
private Integer pageSize;
private Integer pageNumber;

// 构造函数和getter/setter方法
}

// 排序字段
public class SortField {
private String fieldName;
private SortDirection direction;

public enum SortDirection {
ASC, DESC
}

// 构造函数和getter/setter方法
}

2.4 Excel异步处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
// Excel异步处理器
@Service
@Slf4j
public class ExcelAsyncProcessor {

@Autowired
private MessageQueueService messageQueueService;

@Autowired
private ExcelExportEngine exportEngine;

@Autowired
private ExcelFileManager fileManager;

@Autowired
private ExcelCallbackService callbackService;

private final ThreadPoolExecutor asyncExecutor = new ThreadPoolExecutor(
5, 20, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(500),
new ThreadFactory() {
private final AtomicInteger threadNumber = new AtomicInteger(1);

@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "excel-async-" + threadNumber.getAndIncrement());
thread.setDaemon(false);
return thread;
}
},
new ThreadPoolExecutor.CallerRunsPolicy()
);

/**
* 提交异步任务
*/
public void submitAsyncTask(ExcelAsyncTask task) {
try {
// 1. 保存任务到数据库
saveAsyncTask(task);

// 2. 发送异步处理消息
ExcelAsyncMessage message = new ExcelAsyncMessage();
message.setTaskId(task.getExportId());
message.setTask(task);
message.setCreateTime(System.currentTimeMillis());

messageQueueService.sendExcelAsyncMessage(message);

log.info("提交异步Excel导出任务: taskId={}", task.getExportId());

} catch (Exception e) {
log.error("提交异步任务失败: taskId={}", task.getExportId(), e);
throw new RuntimeException("提交异步任务失败", e);
}
}

/**
* 处理异步Excel导出消息
*/
@RabbitListener(queues = "excel.async.queue")
public void handleExcelAsyncMessage(ExcelAsyncMessage message) {
String taskId = message.getTaskId();
ExcelAsyncTask task = message.getTask();

try {
log.info("开始处理异步Excel导出: taskId={}", taskId);

// 1. 更新任务状态
updateTaskStatus(taskId, ExcelAsyncTaskStatus.PROCESSING);

// 2. 执行Excel导出
ExcelExportResult result = exportEngine.exportExcelSync(task.getRequest());

// 3. 更新任务状态
if (result.isSuccess()) {
updateTaskStatus(taskId, ExcelAsyncTaskStatus.COMPLETED);
updateTaskResult(taskId, result);

log.info("异步Excel导出完成: taskId={}", taskId);
} else {
updateTaskStatus(taskId, ExcelAsyncTaskStatus.FAILED);
updateTaskError(taskId, result.getErrorMessage());

log.error("异步Excel导出失败: taskId={}, error={}", taskId, result.getErrorMessage());
}

// 4. 执行回调
executeCallback(taskId, result);

} catch (Exception e) {
log.error("异步Excel导出异常: taskId={}", taskId, e);

// 更新任务状态
updateTaskStatus(taskId, ExcelAsyncTaskStatus.FAILED);
updateTaskError(taskId, e.getMessage());

// 执行错误回调
ExcelExportResult errorResult = new ExcelExportResult();
errorResult.setExportId(taskId);
errorResult.setSuccess(false);
errorResult.setErrorMessage(e.getMessage());
executeCallback(taskId, errorResult);
}
}

/**
* 查询异步任务状态
*/
public ExcelAsyncTaskStatus getTaskStatus(String taskId) {
try {
ExcelAsyncTask task = getAsyncTask(taskId);
return task != null ? task.getStatus() : null;
} catch (Exception e) {
log.error("查询任务状态失败: taskId={}", taskId, e);
return null;
}
}

/**
* 查询异步任务结果
*/
public ExcelExportResult getTaskResult(String taskId) {
try {
ExcelAsyncTask task = getAsyncTask(taskId);
if (task != null && task.getStatus() == ExcelAsyncTaskStatus.COMPLETED) {
return task.getResult();
}
return null;
} catch (Exception e) {
log.error("查询任务结果失败: taskId={}", taskId, e);
return null;
}
}

/**
* 取消异步任务
*/
public boolean cancelTask(String taskId) {
try {
ExcelAsyncTask task = getAsyncTask(taskId);
if (task != null && task.getStatus() == ExcelAsyncTaskStatus.PROCESSING) {
updateTaskStatus(taskId, ExcelAsyncTaskStatus.CANCELLED);

// 清理相关资源
cleanupTaskResources(taskId);

log.info("取消异步任务: taskId={}", taskId);
return true;
}
return false;
} catch (Exception e) {
log.error("取消任务失败: taskId={}", taskId, e);
return false;
}
}

/**
* 清理过期任务
*/
@Scheduled(fixedRate = 3600000) // 每小时执行一次
public void cleanupExpiredTasks() {
try {
long expireTime = System.currentTimeMillis() - 24 * 3600000L; // 24小时前

List<ExcelAsyncTask> expiredTasks = getExpiredTasks(expireTime);

for (ExcelAsyncTask task : expiredTasks) {
// 清理任务资源
cleanupTaskResources(task.getExportId());

// 删除任务记录
deleteAsyncTask(task.getExportId());

log.info("清理过期任务: taskId={}", task.getExportId());
}

if (!expiredTasks.isEmpty()) {
log.info("清理过期任务完成: count={}", expiredTasks.size());
}

} catch (Exception e) {
log.error("清理过期任务失败", e);
}
}

/**
* 保存异步任务
*/
private void saveAsyncTask(ExcelAsyncTask task) {
// 实现任务保存逻辑
log.debug("保存异步任务: taskId={}", task.getExportId());
}

/**
* 获取异步任务
*/
private ExcelAsyncTask getAsyncTask(String taskId) {
// 实现任务获取逻辑
return null;
}

/**
* 更新任务状态
*/
private void updateTaskStatus(String taskId, ExcelAsyncTaskStatus status) {
// 实现任务状态更新逻辑
log.debug("更新任务状态: taskId={}, status={}", taskId, status);
}

/**
* 更新任务结果
*/
private void updateTaskResult(String taskId, ExcelExportResult result) {
// 实现任务结果更新逻辑
log.debug("更新任务结果: taskId={}", taskId);
}

/**
* 更新任务错误
*/
private void updateTaskError(String taskId, String errorMessage) {
// 实现任务错误更新逻辑
log.debug("更新任务错误: taskId={}, error={}", taskId, errorMessage);
}

/**
* 执行回调
*/
private void executeCallback(String taskId, ExcelExportResult result) {
try {
callbackService.executeCallback(taskId, result);
} catch (Exception e) {
log.error("执行回调失败: taskId={}", taskId, e);
}
}

/**
* 清理任务资源
*/
private void cleanupTaskResources(String taskId) {
try {
// 清理临时文件
fileManager.cleanupTempFiles(taskId);

// 清理缓存
// 其他资源清理

} catch (Exception e) {
log.error("清理任务资源失败: taskId={}", taskId, e);
}
}

/**
* 删除异步任务
*/
private void deleteAsyncTask(String taskId) {
// 实现任务删除逻辑
log.debug("删除异步任务: taskId={}", taskId);
}

/**
* 获取过期任务
*/
private List<ExcelAsyncTask> getExpiredTasks(long expireTime) {
// 实现过期任务获取逻辑
return new ArrayList<>();
}

@PreDestroy
public void destroy() {
asyncExecutor.shutdown();
try {
if (!asyncExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
asyncExecutor.shutdownNow();
}
} catch (InterruptedException e) {
asyncExecutor.shutdownNow();
Thread.currentThread().interrupt();
}

log.info("Excel异步处理器已关闭");
}
}

// Excel异步任务
public class ExcelAsyncTask {
private String exportId;
private ExcelExportRequest request;
private ExcelAsyncTaskStatus status;
private ExcelExportResult result;
private String errorMessage;
private long createTime;
private long startTime;
private long endTime;
private int progress;

// 构造函数和getter/setter方法
}

// Excel异步消息
public class ExcelAsyncMessage {
private String taskId;
private ExcelAsyncTask task;
private long createTime;

// 构造函数和getter/setter方法
}

// Excel异步任务状态
public enum ExcelAsyncTaskStatus {
PENDING, // 等待中
PROCESSING, // 处理中
COMPLETED, // 已完成
FAILED, // 失败
CANCELLED // 已取消
}

三、Excel导出控制器

3.1 Excel导出控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
// Excel导出控制器
@RestController
@RequestMapping("/api/excel")
@Slf4j
public class ExcelExportController {

@Autowired
private ExcelExportEngine exportEngine;

@Autowired
private ExcelAsyncProcessor asyncProcessor;

@Autowired
private ExcelTemplateEngine templateEngine;

@Autowired
private ExcelFileManager fileManager;

/**
* 同步导出Excel
*/
@PostMapping("/export/sync")
public ResponseEntity<ExcelExportResult> exportExcelSync(@RequestBody ExcelExportRequest request) {
try {
// 1. 验证请求
validateExportRequest(request);

// 2. 执行同步导出
ExcelExportResult result = exportEngine.exportExcelSync(request);

if (result.isSuccess()) {
return ResponseEntity.ok(result);
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}

} catch (Exception e) {
log.error("同步导出Excel失败", e);

ExcelExportResult result = new ExcelExportResult();
result.setSuccess(false);
result.setErrorMessage("导出失败: " + e.getMessage());

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}

/**
* 异步导出Excel
*/
@PostMapping("/export/async")
public ResponseEntity<ExcelExportResult> exportExcelAsync(@RequestBody ExcelExportRequest request) {
try {
// 1. 验证请求
validateExportRequest(request);

// 2. 设置异步标志
request.setAsync(true);

// 3. 执行异步导出
ExcelExportResult result = exportEngine.exportExcelAsync(request);

return ResponseEntity.ok(result);

} catch (Exception e) {
log.error("异步导出Excel失败", e);

ExcelExportResult result = new ExcelExportResult();
result.setSuccess(false);
result.setErrorMessage("导出失败: " + e.getMessage());

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}

/**
* 批量导出Excel
*/
@PostMapping("/export/batch")
public ResponseEntity<CompletableFuture<List<ExcelExportResult>>> exportExcelBatch(
@RequestBody List<ExcelExportRequest> requests) {
try {
// 1. 验证请求
validateBatchExportRequest(requests);

// 2. 执行批量导出
CompletableFuture<List<ExcelExportResult>> result = exportEngine.exportExcelBatch(requests);

return ResponseEntity.ok(result);

} catch (Exception e) {
log.error("批量导出Excel失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 查询异步任务状态
*/
@GetMapping("/task/status/{taskId}")
public ResponseEntity<ExcelAsyncTaskStatus> getTaskStatus(@PathVariable String taskId) {
try {
ExcelAsyncTaskStatus status = asyncProcessor.getTaskStatus(taskId);

if (status != null) {
return ResponseEntity.ok(status);
} else {
return ResponseEntity.notFound().build();
}

} catch (Exception e) {
log.error("查询任务状态失败: taskId={}", taskId, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 查询异步任务结果
*/
@GetMapping("/task/result/{taskId}")
public ResponseEntity<ExcelExportResult> getTaskResult(@PathVariable String taskId) {
try {
ExcelExportResult result = asyncProcessor.getTaskResult(taskId);

if (result != null) {
return ResponseEntity.ok(result);
} else {
return ResponseEntity.notFound().build();
}

} catch (Exception e) {
log.error("查询任务结果失败: taskId={}", taskId, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 取消异步任务
*/
@PostMapping("/task/cancel/{taskId}")
public ResponseEntity<Void> cancelTask(@PathVariable String taskId) {
try {
boolean cancelled = asyncProcessor.cancelTask(taskId);

if (cancelled) {
return ResponseEntity.ok().build();
} else {
return ResponseEntity.notFound().build();
}

} catch (Exception e) {
log.error("取消任务失败: taskId={}", taskId, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 下载Excel文件
*/
@GetMapping("/download/{fileId}")
public ResponseEntity<Resource> downloadExcelFile(@PathVariable String fileId) {
try {
// 1. 获取文件信息
ExcelFileInfo fileInfo = fileManager.getFileInfo(fileId);
if (fileInfo == null) {
return ResponseEntity.notFound().build();
}

// 2. 创建文件资源
Path filePath = Paths.get(fileInfo.getFilePath());
Resource resource = new UrlResource(filePath.toUri());

if (!resource.exists()) {
return ResponseEntity.notFound().build();
}

// 3. 设置响应头
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + fileInfo.getFileName() + "\"");
headers.add(HttpHeaders.CONTENT_TYPE, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

return ResponseEntity.ok()
.headers(headers)
.contentLength(fileInfo.getFileSize())
.body(resource);

} catch (Exception e) {
log.error("下载Excel文件失败: fileId={}", fileId, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 获取模板列表
*/
@GetMapping("/templates")
public ResponseEntity<List<ExcelTemplate>> getTemplates() {
try {
List<ExcelTemplate> templates = templateEngine.getAllTemplates();
return ResponseEntity.ok(templates);
} catch (Exception e) {
log.error("获取模板列表失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 获取模板详情
*/
@GetMapping("/templates/{templateId}")
public ResponseEntity<ExcelTemplate> getTemplate(@PathVariable String templateId) {
try {
ExcelTemplate template = templateEngine.getTemplateById(templateId);

if (template != null) {
return ResponseEntity.ok(template);
} else {
return ResponseEntity.notFound().build();
}

} catch (Exception e) {
log.error("获取模板详情失败: templateId={}", templateId, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 保存模板
*/
@PostMapping("/templates")
public ResponseEntity<ExcelTemplate> saveTemplate(@RequestBody ExcelTemplate template) {
try {
ExcelTemplate savedTemplate = templateEngine.saveTemplate(template);
return ResponseEntity.ok(savedTemplate);
} catch (Exception e) {
log.error("保存模板失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 删除模板
*/
@DeleteMapping("/templates/{templateId}")
public ResponseEntity<Void> deleteTemplate(@PathVariable String templateId) {
try {
boolean deleted = templateEngine.deleteTemplate(templateId);

if (deleted) {
return ResponseEntity.ok().build();
} else {
return ResponseEntity.notFound().build();
}

} catch (Exception e) {
log.error("删除模板失败: templateId={}", templateId, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 复制模板
*/
@PostMapping("/templates/{templateId}/copy")
public ResponseEntity<ExcelTemplate> copyTemplate(@PathVariable String templateId,
@RequestParam String newName) {
try {
ExcelTemplate copiedTemplate = templateEngine.copyTemplate(templateId, newName);
return ResponseEntity.ok(copiedTemplate);
} catch (Exception e) {
log.error("复制模板失败: templateId={}, newName={}", templateId, newName, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 验证导出请求
*/
private void validateExportRequest(ExcelExportRequest request) {
if (request == null) {
throw new IllegalArgumentException("导出请求不能为空");
}

if (request.getTemplateId() == null && request.getColumns() == null) {
throw new IllegalArgumentException("模板ID和列配置不能同时为空");
}

if (request.getDataSource() == null) {
throw new IllegalArgumentException("数据源不能为空");
}
}

/**
* 验证批量导出请求
*/
private void validateBatchExportRequest(List<ExcelExportRequest> requests) {
if (requests == null || requests.isEmpty()) {
throw new IllegalArgumentException("批量导出请求不能为空");
}

if (requests.size() > 10) {
throw new IllegalArgumentException("批量导出请求数量不能超过10个");
}

for (ExcelExportRequest request : requests) {
validateExportRequest(request);
}
}
}

四、最佳实践与总结

4.1 SpringBoot Excel动态导出最佳实践

4.1.1 模板设计策略

  • 动态模板:支持运行时动态生成模板
  • 静态模板:预定义常用模板提高效率
  • 模板缓存:智能缓存模板减少重复创建
  • 模板复用:支持模板复制和复用

4.1.2 数据处理策略

  • 数据分页:大数据量分页处理避免内存溢出
  • 流式处理:使用SXSSFWorkbook支持大数据量
  • 数据转换:灵活的数据格式转换
  • 数据过滤:支持复杂的数据过滤条件

4.1.3 异步处理策略

  • 任务队列:使用消息队列管理导出任务
  • 进度监控:实时监控导出进度
  • 结果通知:导出完成后的通知机制
  • 资源清理:自动清理过期任务和文件

4.1.4 性能优化策略

  • 连接池优化:合理配置数据库连接池
  • 缓存策略:使用Redis缓存模板和数据
  • 文件管理:智能的文件存储和清理
  • 批量处理:支持批量导出提高效率

4.2 架构演进建议

4.2.1 微服务架构演进

  • 服务拆分:将Excel导出服务拆分为独立微服务
  • 服务治理:实现服务的注册发现、负载均衡
  • 数据一致性:保证导出数据的一致性
  • 容错处理:实现熔断、降级、重试机制

4.2.2 云原生架构演进

  • 容器化部署:使用Docker等容器技术部署
  • 弹性伸缩:实现基于负载的自动扩缩容
  • 服务网格:使用Istio等服务网格技术
  • 云原生存储:使用云原生的文件存储服务

4.2.3 智能化导出

  • AI驱动优化:使用机器学习优化导出策略
  • 自适应模板:根据数据特征自动调整模板
  • 预测性导出:预测用户导出需求并提前准备
  • 智能缓存:基于访问模式的智能缓存

4.3 总结

SpringBoot Excel动态导出是企业级应用数据展示的重要手段,通过灵活的模板引擎,高效的数据处理,可靠的异步机制,可以实现稳定、高效的Excel导出解决方案。随着业务复杂度的不断提升和数据量的持续增长,Excel导出系统将更加智能化和自动化。

在未来的发展中,企业需要持续关注技术发展趋势,不断优化和完善Excel导出策略,以适应不断变化的业务需求和技术环境。通过本文的深入分析和实践指导,希望能够为企业构建高质量的Excel动态导出解决方案提供有价值的参考和帮助,推动企业级应用在数据导出场景下的稳定运行和持续发展。