From 72d378c577ec4159715730d7b8d16e51afc58669 Mon Sep 17 00:00:00 2001 From: wsm <8454518+new-twice@user.noreply.gitee.com> Date: Fri, 24 Jul 2026 07:53:09 +0000 Subject: [PATCH] =?UTF-8?q?!91=20feat(excel):=20=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E8=87=AA=E5=8A=A8=E7=94=9F=E6=88=90=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B=E6=95=B0=E6=8D=AE=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(excel): 导出模板自动生成示例数据行 * fix(excel): 字典导入兼容中英文逗号分隔多值,修复BgPartymatter导入Map.of参数错位 * fix(import): Excel导入时注入bpmStatus和delFlag默认值,避免新增记录缺少流程状态 --- .../base/controller/JeecgController.java | 8 ++ .../util/excel/ExcelAnnotationUtils.java | 77 +++++++++++++++++-- .../controller/BgXiSpeakController.java | 5 +- .../controller/BgPartymatterController.java | 13 +++- .../controller/BgTakepulseController.java | 15 +++- .../controller/DqInspectTaskController.java | 5 +- .../DjInspectImproveController.java | 10 ++- 7 files changed, 116 insertions(+), 17 deletions(-) diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/controller/JeecgController.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/controller/JeecgController.java index 13d4d25..360e5e3 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/controller/JeecgController.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/controller/JeecgController.java @@ -176,6 +176,14 @@ public class JeecgController> { writeExcel(response, clazz, title, Collections.emptyList()); } + /** + * 导出自定义数据(含表头),供子类导出模板示例行 + */ + protected void exportXlsWithData(HttpServletResponse response, + Class clazz, String title, List data) throws IOException { + writeExcel(response, clazz, title, data); + } + private void writeExcel(HttpServletResponse response, Class clazz, String title, List data) throws IOException { ExcelAnnotationUtils.write(response, clazz, title, data, dictQuery()); diff --git a/jeecg-boot-module/jeecg-module-tools/src/main/java/org/jeecg/common/util/excel/ExcelAnnotationUtils.java b/jeecg-boot-module/jeecg-module-tools/src/main/java/org/jeecg/common/util/excel/ExcelAnnotationUtils.java index f5fc052..b22802f 100644 --- a/jeecg-boot-module/jeecg-module-tools/src/main/java/org/jeecg/common/util/excel/ExcelAnnotationUtils.java +++ b/jeecg-boot-module/jeecg-module-tools/src/main/java/org/jeecg/common/util/excel/ExcelAnnotationUtils.java @@ -111,6 +111,54 @@ public final class ExcelAnnotationUtils { return columns; } + /** + * 根据 @ExcelColumn 注解自动构建一个填充了示例数据的实体实例,用于导出模板。 + * String 字段填 "示例" + 列名,dict 字段填 "示例文本",日期填当天,数字填 1。 + */ + public static T buildExample(Class clazz) { + try { + T obj = clazz.getDeclaredConstructor().newInstance(); + for (Field field : getAllFields(clazz)) { + ExcelColumn col = field.getAnnotation(ExcelColumn.class); + if (col == null) continue; + field.setAccessible(true); + setExampleValue(field, obj, col); + } + return obj; + } catch (Exception e) { + throw new RuntimeException("【Excel工具】构建示例数据失败, class: " + clazz.getSimpleName(), e); + } + } + + private static void setExampleValue(Field field, Object obj, ExcelColumn col) throws Exception { + Class type = field.getType(); + boolean hasDict = StringUtils.isNotEmpty(col.dictTable()) || StringUtils.isNotEmpty(col.dicCode()); + + if (type == String.class) { + field.set(obj, hasDict ? "示例文本" : "示例" + col.name()); + } else if (type == Date.class) { + field.set(obj, new Date()); + } else if (type == LocalDate.class) { + field.set(obj, LocalDate.now()); + } else if (type == LocalDateTime.class) { + field.set(obj, LocalDateTime.now()); + } else if (type == Integer.class || type == int.class) { + field.set(obj, 1); + } else if (type == Long.class || type == long.class) { + field.set(obj, 1L); + } else if (type == Double.class || type == double.class) { + field.set(obj, 1.0); + } else if (type == Float.class || type == float.class) { + field.set(obj, 1.0f); + } else if (type == Short.class || type == short.class) { + field.set(obj, (short) 1); + } else if (type == BigDecimal.class) { + field.set(obj, BigDecimal.ONE); + } else if (type == Boolean.class || type == boolean.class) { + field.set(obj, false); + } + } + // ==================== 导出 ==================== /** @@ -214,15 +262,16 @@ public final class ExcelAnnotationUtils { // ==================== 内部辅助方法 ==================== static boolean isValidDictDisplay(String[] replace, String value) { + String normalized = normalizeComma(value); for (String s : replace) { String[] arr = s.split(","); - if (arr.length >= 2 && arr[0].equals(value)) { + if (arr.length >= 2 && arr[0].equals(normalized)) { return true; } } - // 逗号分隔多值:逐个验证每个部分是否在字典范围内 - if (value.contains(",")) { - String[] parts = value.split(","); + // 逗号分隔多值:逐个验证每个部分是否在字典范围内(兼容中英文逗号) + if (normalized.contains(",")) { + String[] parts = normalized.split(","); for (String part : parts) { String trimmed = part.trim(); if (trimmed.isEmpty()) continue; @@ -234,7 +283,10 @@ public final class ExcelAnnotationUtils { break; } } - if (!found) return false; + if (!found) { + log.warn("【Excel导入-字典校验】多值中'{}'不在字典范围, 输入值: {}, replace长度: {}", trimmed, value, replace.length); + return false; + } } return true; } @@ -249,9 +301,10 @@ public final class ExcelAnnotationUtils { String resolved = displayToCode.getOrDefault(value, value); if (type == String.class) { - // 逗号分隔多值:逐个翻译后再拼回(如 "部门A,部门B" → "id_a,id_b") - if (resolved.equals(value) && value.contains(",")) { - String[] parts = value.split(","); + // 逗号分隔多值:逐个翻译后再拼回(如 "部门A,部门B" → "id_a,id_b"),兼容中英文逗号 + String normalized = normalizeComma(value); + if (resolved.equals(value) && normalized.contains(",")) { + String[] parts = normalized.split(","); StringBuilder sb = new StringBuilder(); for (int i = 0; i < parts.length; i++) { if (i > 0) sb.append(","); @@ -406,6 +459,14 @@ public final class ExcelAnnotationUtils { return sb.toString(); } + /** + * 将中文逗号归一化为英文逗号,兼容 Excel 中两种逗号混用。 + */ + private static String normalizeComma(String value) { + if (value == null) return null; + return value.replace(',', ','); + } + private static String[] mergeReplace(String[] original, String[] dictReplace) { if (original == null || original.length == 0) return dictReplace; if (dictReplace == null || dictReplace.length == 0) return original; diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/controller/BgXiSpeakController.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/controller/BgXiSpeakController.java index 8f38989..ce05a92 100644 --- a/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/controller/BgXiSpeakController.java +++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/controller/BgXiSpeakController.java @@ -18,6 +18,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.jeecg.common.api.vo.Result; import org.jeecg.common.system.query.QueryGenerator; +import org.jeecg.common.util.excel.ExcelAnnotationUtils; import org.jeecg.common.util.oConvertUtils; import org.jeecg.common.system.vo.SelectTreeModel; import org.jeecg.modules.bg.constant.BpmStatus; @@ -456,7 +457,9 @@ public class BgXiSpeakController extends JeecgController importExcelByEasyExcel(HttpServletRequest request) { - return super.importExcelByEasyExcel(request, BgTakepulse.class); + return super.importExcelByEasyExcel(request, BgTakepulse.class, + Map.of( + SupervisionConstant.ClassFieldName.BPM_STATUS_NAME, + BpmStatus.NOT_START.getCode(), + SupervisionConstant.ClassFieldName.DEL_FLAG_NAME, + SupervisionConstant.DelFlag.NORMAL)); } /** @@ -468,7 +477,9 @@ public class BgTakepulseController extends JeecgController importExcelByEasyExcel(HttpServletRequest request) { - return super.importExcelByEasyExcel(request, DjInspectImprove.class); + return super.importExcelByEasyExcel(request, DjInspectImprove.class, + Map.of( + SupervisionConstant.ClassFieldName.BPM_STATUS_NAME, + BpmStatus.NOT_START.getCode(), + SupervisionConstant.ClassFieldName.DEL_FLAG_NAME, + SupervisionConstant.DelFlag.NORMAL)); } /**