📚 目录
-
前言\](#前言)
-
整体方案设计\](#整体方案设计)
-
1. 表头构造\](#1-表头构造)
-
3. 字典值转换\](#3-字典值转换)
-
5. 列号转字母\](#5-列号转字母)
-
完整工具类代码\](#完整工具类代码)
-
Controller 示例接口\](#controller-示例接口)
前言
在 **监控告警系统**、**规则引擎**、**数据计算配置** 等场景中,我们经常需要导出"规则模板 Excel",让用户在表格里填写参数后再回传。
这个 Excel 模板的特点是:
-
固定字段(规则编码、名称、类型等)
-
动态字段(根据规则描述自动提取的变量 x1、y1、n 等)
-
备注列(通过 Excel 公式动态替换变量值)
> ⚡ 本文将演示如何用 **EasyExcel** 实现"动态列 + 公式备注"的模板导出,最终效果是用户在 Excel 中输入变量值后,备注列会**实时更新**。
需求与目标
目标 Excel 结构
| 规则编码 | 规则名称 | 规则类型 | 规则等级 | 描述 | x1 | y1 | n | 备注 |
| -------- | -------- | -------- | -------- | ---- | -- | -- | - | ---- |
| R001 | 浆液循环泵告警 | 高级告警 | 1 | 当 x1 大于 y1 持续 n 分钟时告警 | 10 | 20 | 5 | 当 10 大于 20 持续 5 分钟时告警 |
其中:
-
**x1、y1、n** 是根据 `描述` 自动识别出的变量
-
**备注列** 是公式生成:`=SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(E2,"x1",F2),"y1",G2),"n",H2)`
整体方案设计
- **提取变量**
-
使用正则匹配 `x\d+`、`y\d+` 等模式
-
额外匹配 `n`(Z次/Z分钟等)
- **构造表头**
-
固定列(从实体类注解读取)
-
动态列(变量)
-
备注列
- **构造数据行**
-
固定字段填充
-
动态列占位
-
备注列写公式字符串
- **写出 Excel**
-
用 EasyExcel 写入
-
注册公式处理器(防止公式变成普通文本)
-
列宽自适应
核心功能拆解
1. 表头构造
```java
public static List<List<String>> buildDynamicHeader(Class<?> clazz, List<String> dynamicHeaders) {
List<List<String>> headers = new ArrayList<>();
// 固定列
for (Field field : clazz.getDeclaredFields()) {
ExcelProperty prop = field.getAnnotation(ExcelProperty.class);
if (prop != null && prop.value().length > 0) {
headers.add(Collections.singletonList(prop.value()[0]));
}
}
// 动态列
for (String dynamic : dynamicHeaders) {
headers.add(Collections.singletonList(dynamic));
}
// 备注列
headers.add(Collections.singletonList("备注"));
return headers;
}
- 数据行构造
java
public static List<Object> buildRowFromEntity(RuleExcelTemplate.ExportTemplate rule, int dynamicColSize) {
List<Object> row = new ArrayList<>();
row.add(rule.getRuleCode());
row.add(rule.getRuleName());
row.add(convertDict(CustomConstant.ALARM_TYPE, rule.getRuleType()));
row.add(rule.getRuleLevel());
row.add(rule.getDescription());
// 动态列占位
for (int i = 0; i < dynamicColSize; i++) {
row.add("");
}
// 备注列公式
row.add(buildRemarkFormula(rule.getDescription()));
return row;
}
- 字典值转换
java
private static String convertDict(String dictKey, String rawValue) {
List<DictModel> dictList = CustomUtils.dictModelList(dictKey);
if (CollectionUtils.isEmpty(dictList)) {
return rawValue;
}
for (DictModel item : dictList) {
if (Objects.equals(item.getId(), rawValue)) {
return item.getName();
}
}
return rawValue;
}
- 备注公式生成
java
public static String buildRemarkFormula(String description) {
if (description == null || description.isEmpty()) {
return "";
}
LinkedHashSet<String> variables = new LinkedHashSet<>();
Matcher xyMatcher = CustomConstant.XY_PATTERN.matcher(description);
while (xyMatcher.find()) {
variables.add(xyMatcher.group());
}
if (CustomConstant.Z_PATTERN.matcher(description).find()) {
variables.add("n");
}
String formula = "$E$2"; // 描述列
int colIndex = 5; // 从F列开始
for (String var : variables) {
String colLetter = getExcelColLetter(colIndex++);
formula = String.format("SUBSTITUTE(%s,\"%s\",%s2)", formula, var, colLetter);
}
return "=" + formula;
}
- 列号转字母
java
public static String getExcelColLetter(int colIndex) {
StringBuilder sb = new StringBuilder();
while (colIndex >= 0) {
sb.insert(0, (char) ('A' + (colIndex % 26)));
colIndex = colIndex / 26 - 1;
}
return sb.toString();
}
- 导出主流程
java
public static void exportRuleExcel(RuleExcelTemplate.ExportTemplate rule, List<String> dynamicHeaders, HttpServletResponse response) throws Exception {
List<List<String>> headList = buildDynamicHeader(RuleExcelTemplate.ExportTemplate.class, dynamicHeaders);
List<Object> dataRow = buildRowFromEntity(rule, dynamicHeaders.size());
String fileName = URLEncoder.encode(rule.getRuleName() + "模板导出.xlsx", StandardCharsets.UTF_8);
try (OutputStream out = getOutputStream(fileName, response)) {
ExcelWriter writer = EasyExcel.write(out)
.registerWriteHandler(new RemarkFormulaCellHandler())
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.excelType(ExcelTypeEnum.XLSX)
.build();
WriteSheet sheet = EasyExcel.writerSheet("规则导出")
.head(headList)
.build();
writer.write(Collections.singletonList(dataRow), sheet);
writer.finish();
}
}
总结与优化方向
-
✅ 动态列生成公式,变量替换即时可见
-
✅ 支持字典映射,提升可读性
-
✅ 列宽自适应,表头固定 + 动态可扩展
可优化点:
-
动态列映射缓存,保证批量导出一致性
-
多规则多 Sheet 导出
-
前端进度提示 & 异步导出
-
支持 Excel 模板反向导入