From 2d185f0de64a514382c1e86fce541cb0bf5fda41 Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Sat, 26 Sep 2026 11:37:07 +0800 Subject: [PATCH] =?UTF-8?q?:bug:=20=E5=AF=BC=E5=85=A5=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=A0=BC=E6=97=A0=E6=B3=95=E8=BD=AC=E6=8D=A2=E6=97=B6=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E8=BF=94=E5=9B=9E=E5=8F=AF=E8=AF=BB=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E6=98=8E=E7=BB=86=EF=BC=8C=E5=85=9C=E5=BA=95=2026=20=E4=B8=AA?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题(用户实测反馈):金额列填「一万二千元」这类无法转换的内容时,EasyExcel 在 ExcelUtil.read(...) 阶段就抛 ExcelDataConvertException,根本走不到行级校验; 控制器只 catch ImportFailureException,异常穿透到全局处理器,用户看到的是 HTTP 500 + 「Convert data ... to class java.math.BigDecimal error」这类技术文案, 且完全拿不到失败明细。 影响面:全部 27 个导入接口(读阶段的 ExcelUtil.read 都在 try 内、只 catch ImportFailureException),非维修记录独有。 改动: - ImportFailureExcelUtil 新增三个能力(blade-common,公共层一次覆盖各模块): 1) describeReadFailure:把 ExcelDataConvertException 转成 「第N行「列名」格式不正确:<原值>」,列名按列序号反查 @ExcelProperty; 2) readRawRows:以原始文本(Map)方式重读文件,不做类型转换因此绝不抛异常, 用于拿回「原表全部行」; 3) exportReadFailure:一站式兜底(重读 → 导出全量行 + 仅出错行带原因 + 按列标红),返回 false 表示非转换类异常,调用方继续抛出保持原行为。 - 26 个导入控制器在现有 catch 之后追加 catch(Exception) 兜底,未改动原有 行级校验与整批回滚逻辑。 验证: - 实测维修记录/费用项/币种汇率三个模块:HTTP 500→200,返回全量明细, 文案如「第5行「费用」格式不正确:一万二千元」(行号经单行/四行文件双向校准)。 - 既有回归无退化:记录类 80/80、台账 32/32、港口码头 45/45、货物类型 24/24、 费用项 20/20、币种汇率 25/25。 - 新增 scripts/api-read-failure-test.sh(4 断言)固化该场景。 --- .../common/excel/ImportFailureExcelUtil.java | 342 ++++++++++++++++++ .../controller/AirportMasterController.java | 9 + .../controller/CargoTypeController.java | 9 + .../system/controller/CurrencyController.java | 9 + .../system/controller/FeeItemController.java | 9 + .../controller/RailwayStationController.java | 9 + .../system/controller/RegionController.java | 9 + .../controller/AccidentRecordController.java | 9 + .../AnnualInspectionRecordController.java | 9 + .../controller/CommonCargoController.java | 9 + .../controller/CommonRouteController.java | 9 + .../CreditScoreQuantificationController.java | 9 + .../controller/EquipmentLedgerController.java | 9 + .../controller/EtcRecordController.java | 9 + .../controller/InsuranceRecordController.java | 9 + .../controller/MaintenancePlanController.java | 9 + .../MaintenanceRecordController.java | 9 + .../controller/MileageRecordController.java | 9 + .../OilElectricRecordController.java | 9 + .../OtherExpenseRecordController.java | 9 + .../TireReplacementRecordController.java | 9 + .../TransportChangeRecordController.java | 9 + .../controller/TransportPlanController.java | 9 + .../TransportReconciliationController.java | 18 + .../controller/ViolationRecordController.java | 9 + .../controller/WaybillController.java | 9 + 26 files changed, 576 insertions(+) diff --git a/blade-common/src/main/java/org/springblade/common/excel/ImportFailureExcelUtil.java b/blade-common/src/main/java/org/springblade/common/excel/ImportFailureExcelUtil.java index 6b0ad40d..d4330eed 100644 --- a/blade-common/src/main/java/org/springblade/common/excel/ImportFailureExcelUtil.java +++ b/blade-common/src/main/java/org/springblade/common/excel/ImportFailureExcelUtil.java @@ -80,6 +80,348 @@ public class ImportFailureExcelUtil { addValidationError(validationErrors, value != null && value.length() > maxLength, message); } + /** + * 把「读取 Excel 阶段」的转换异常转成可读的失败原因。 + *

+ * 背景:单元格里填了无法转换的内容(如金额列填「一万二千元」)时,EasyExcel 会在 + * {@code ExcelUtil.read(...)} 阶段就抛 {@link cn.idev.excel.exception.ExcelDataConvertException}, + * 根本走不到行级校验;若不兜底,用户看到的是 + * 「Convert data ... to class java.math.BigDecimal error」这类技术文案。 + *

+ * 本方法据异常里的行列位置与目标类型,生成「第N行「列名」格式不正确:<原值>」这样 + * 用户能看懂、能定位的文案;列名靠表头反射拿 {@code @ExcelProperty},拿不到则退回列序号。 + * + * @param exception 读取阶段抛出的异常 + * @param excelClass 导入模型类(用于按列序号反查表头名) + * @return 可读的失败原因;非转换类异常返回 null(交由上层按原样处理) + */ + public static String describeReadFailure(Throwable exception, Class excelClass) { + cn.idev.excel.exception.ExcelDataConvertException convertException = findCause(exception, + cn.idev.excel.exception.ExcelDataConvertException.class); + if (convertException == null) { + return null; + } + Integer rowIndex = convertException.getRowIndex(); + Integer columnIndex = convertException.getColumnIndex(); + String columnName = columnName(excelClass, columnIndex); + // Excel 行号:EasyExcel 的 rowIndex 为 1 基数据行号(表头占第 1 行),故 Excel 行号 = rowIndex + 1 + String rowText = rowIndex == null ? "第?行" : "第" + (rowIndex + 1) + "行"; + String columnText = columnName == null ? ("第" + (columnIndex == null ? "?" : columnIndex + 1) + "列") : "「" + columnName + "」"; + String value = cellValue(convertException); + StringBuilder message = new StringBuilder(rowText).append(columnText).append("格式不正确"); + if (value != null && !value.isBlank()) { + message.append(":").append(value); + } + return message.toString(); + } + + /** + * 在异常链里查找指定类型的 cause + */ + private static T findCause(Throwable throwable, Class type) { + for (Throwable current = throwable; current != null; current = current.getCause()) { + if (type.isInstance(current)) { + return type.cast(current); + } + if (current.getCause() == current) { + break; + } + } + return null; + } + + /** + * 按列序号反查表头名(@ExcelProperty 的 value,取不到则用字段名) + */ + private static String columnName(Class excelClass, Integer columnIndex) { + if (excelClass == null || columnIndex == null) { + return null; + } + List fields = excelFields(excelClass); + if (columnIndex < 0 || columnIndex >= fields.size()) { + return null; + } + Field field = fields.get(columnIndex); + ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); + if (excelProperty == null) { + return field.getName(); + } + String[] value = excelProperty.value(); + String columnName = value.length == 0 ? field.getName() : value[0]; + return columnName.replace("*", ""); + } + + /** + * 取触发转换失败的单元格原值 + */ + private static String cellValue(cn.idev.excel.exception.ExcelDataConvertException exception) { + try { + cn.idev.excel.metadata.data.CellData cellData = exception.getCellData(); + if (cellData == null) { + return null; + } + Object data = cellData.getData(); + if (data == null) { + data = cellData.getStringValue(); + } + return data == null ? null : String.valueOf(data).trim(); + } catch (Exception ignored) { + return null; + } + } + + /** + * 以「原始单元格文本」方式重读导入文件,得到的行用于构造读取阶段的失败明细。 + *

+ * 用途:当 {@code ExcelUtil.read(file, XxxExcel.class)} 因单元格无法转换而失败时, + * 已解析的实体列表是不完整的(异常中断了整批读取)。此时用本方法重读一次: + * 不做类型转换(直接读成 Map),因此绝不会再抛转换异常,可拿到**原表全部行**, + * 供导出「原表全量 + 仅出错行带原因」的失败明细。 + * + * @param file 上传的 Excel 文件 + * @param excelClass 导入模型类(按列序号反查表头名,用于把列序号映射成列名) + * @return 按行组织的明细数据:每行是「表头名 → 单元格文本」的有序 Map; + * 读不出内容时返回空列表(调用方按空明细处理) + */ + public static List> readRawRows(java.io.InputStream file, Class excelClass) { + List> rawRows = new ArrayList<>(); + try { + cn.idev.excel.read.builder.ExcelReaderBuilder builder = cn.idev.excel.FastExcel + .read(file, new cn.idev.excel.event.AnalysisEventListener>() { + @Override + public void invoke(Map data, cn.idev.excel.context.AnalysisContext context) { + rawRows.add(new java.util.LinkedHashMap<>(data)); + } + + @Override + public void doAfterAllAnalysed(cn.idev.excel.context.AnalysisContext context) { + // 无需处理 + } + }); + builder.headRowNumber(1).sheet().doRead(); + } catch (Exception ignored) { + // 重读本身失败时退回空明细,由调用方按「无法读取」提示 + return new ArrayList<>(); + } + List fields = excelClass == null ? List.of() : excelFields(excelClass); + List> rows = new ArrayList<>(); + for (Map rawRow : rawRows) { + Map row = new java.util.LinkedHashMap<>(); + for (Map.Entry entry : rawRow.entrySet()) { + Integer columnIndex = entry.getKey(); + String columnName = columnIndex != null && columnIndex < fields.size() + ? columnName(excelClass, columnIndex) + : null; + if (columnName == null) { + columnName = "第" + (columnIndex == null ? "?" : columnIndex + 1) + "列"; + } + row.put(columnName, entry.getValue()); + } + rows.add(row); + } + return rows; + } + + /** + * 导出「读取阶段失败」明细:原表全量行 + 末尾「导入失败原因」列。 + *

+ * 与 {@link #export} 的区别:本方法直接按重读得到的「表头名 → 单元格文本」写出, + * 不依赖导入模型的字段反射,因此适用于「单元格无法转换为实体字段」的场景 + * (此时实体列表不完整,无法走常规导出)。 + * + * @param response 响应 + * @param fileName 文件名 + * @param sheetName 工作表名 + * @param rawRows {@link #readRawRows} 得到的原表全部行 + * @param failureMessageByRow 行号(0 基,对应 rawRows 下标)→ 该行失败原因;为 null 表示该行无错 + */ + public static void exportRawFailure(HttpServletResponse response, String fileName, String sheetName, + List> rawRows, Map failureMessageByRow) { + response.setContentType("application/vnd.ms-excel"); + response.setCharacterEncoding(StandardCharsets.UTF_8.name()); + String encodeFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8); + response.setHeader("Content-disposition", "attachment;filename=" + encodeFileName + ".xlsx"); + List headers = new ArrayList<>(); + for (Map row : rawRows) { + for (String key : row.keySet()) { + if (!headers.contains(key)) { + headers.add(key); + } + } + } + headers.add(FAILURE_REASON); + List> head = new ArrayList<>(); + for (String header : headers) { + head.add(List.of(header)); + } + List> rows = new ArrayList<>(); + Map> redColumnsByRow = new HashMap<>(); + for (int rowIndex = 0; rowIndex < rawRows.size(); rowIndex++) { + Map rawRow = rawRows.get(rowIndex); + List row = new ArrayList<>(); + Set redColumns = new LinkedHashSet<>(); + // 失败原因列恒标红 + redColumns.add(headers.size() - 1); + String failureMessage = failureMessageByRow == null ? null : failureMessageByRow.get(rowIndex); + for (int columnIndex = 0; columnIndex < headers.size() - 1; columnIndex++) { + String header = headers.get(columnIndex); + row.add(rawRow.get(header)); + // 失败原因里出现该列名时,标红该列单元格 + if (failureMessage != null && !failureMessage.isBlank() && failureMessage.contains("「" + header + "」")) { + redColumns.add(columnIndex); + } + } + row.add(failureMessage == null ? "" : failureMessage); + rows.add(row); + redColumnsByRow.put(rowIndex, redColumns); + } + try { + FastExcel.write(response.getOutputStream()) + .registerWriteHandler(new RawFailureCellStyleHandler(rows, redColumnsByRow)) + .head(head) + .sheet(sheetName) + .doWrite(rows); + } catch (IOException exception) { + throw new IllegalStateException("导出导入失败明细失败", exception); + } + } + + /** + * 读取阶段失败明细的单元格样式处理器:统一列宽 + 标红失败原因列与出错列 + */ + private static class RawFailureCellStyleHandler implements CellWriteHandler { + + private final int failureReasonColumnIndex; + private final Map> redColumnsByRow; + private final Map redStyleCache = new HashMap<>(); + private final Map noWrapStyleCache = new HashMap<>(); + private final Map columnWidthCache = new HashMap<>(); + + private RawFailureCellStyleHandler(List> rows, Map> redColumnsByRow) { + this.failureReasonColumnIndex = rows.isEmpty() ? 0 : rows.get(0).size() - 1; + this.redColumnsByRow = redColumnsByRow; + } + + @Override + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List> cellDataList, Cell cell, cn.idev.excel.metadata.Head head, + Integer relativeRowIndex, Boolean isHead) { + if (Boolean.TRUE.equals(isHead)) { + adjustColumnWidth(cell); + markNoWrap(cell); + return; + } + adjustColumnWidth(cell); + if (relativeRowIndex == null || relativeRowIndex < 0) { + return; + } + Set redColumns = redColumnsByRow.get(relativeRowIndex); + if (redColumns != null && redColumns.contains(cell.getColumnIndex())) { + markRed(cell); + } + } + + private void markRed(Cell cell) { + CellStyle currentStyle = cell.getCellStyle(); + CellStyle redStyle = redStyleCache.computeIfAbsent(currentStyle.getIndex(), styleIndex -> { + Workbook workbook = cell.getSheet().getWorkbook(); + CellStyle newStyle = workbook.createCellStyle(); + newStyle.cloneStyleFrom(currentStyle); + Font font = workbook.createFont(); + font.setColor(IndexedColors.RED.getIndex()); + newStyle.setFont(font); + newStyle.setWrapText(true); + return newStyle; + }); + cell.setCellStyle(redStyle); + } + + private void markNoWrap(Cell cell) { + CellStyle currentStyle = cell.getCellStyle(); + CellStyle noWrapStyle = noWrapStyleCache.computeIfAbsent(currentStyle.getIndex(), styleIndex -> { + CellStyle newStyle = cell.getSheet().getWorkbook().createCellStyle(); + newStyle.cloneStyleFrom(currentStyle); + newStyle.setWrapText(false); + return newStyle; + }); + cell.setCellStyle(noWrapStyle); + } + + private void adjustColumnWidth(Cell cell) { + int columnIndex = cell.getColumnIndex(); + int columnWidth = Math.min(Math.max(displayWidth(cell.toString()) + 4, 12), 80) * 256; + Integer currentWidth = columnWidthCache.get(columnIndex); + if (currentWidth == null || columnWidth > currentWidth) { + columnWidthCache.put(columnIndex, columnWidth); + cell.getSheet().setColumnWidth(columnIndex, columnWidth); + } + } + + private int displayWidth(String value) { + int width = 0; + for (int index = 0; index < value.length(); index++) { + width += value.charAt(index) > 255 ? 2 : 1; + } + return width; + } + } + + /** + * 读取阶段异常的一站式兜底:重读原表 → 导出「全量行 + 出错行原因」失败明细。 + *

+ * 控制器用法:在导入的 try 块末尾追加 + *

+	 * } catch (Exception exception) {
+	 *     if (ImportFailureExcelUtil.exportReadFailure(response, fileName, sheetName, file, XxxExcel.class, exception)) {
+	 *         return null;
+	 *     }
+	 *     throw exception;
+	 * }
+	 * 
+ * + * @return true 表示已识别为「单元格转换失败」并已写出明细(调用方直接 return null); + * false 表示不是转换类异常(调用方应继续抛出,保持原有处理) + */ + public static boolean exportReadFailure(HttpServletResponse response, String fileName, String sheetName, + org.springframework.web.multipart.MultipartFile file, + Class excelClass, Throwable exception) { + String failureMessage = describeReadFailure(exception, excelClass); + if (failureMessage == null) { + return false; + } + List> rawRows; + try (java.io.InputStream inputStream = file.getInputStream()) { + rawRows = readRawRows(inputStream, excelClass); + } catch (IOException ioException) { + rawRows = new ArrayList<>(); + } + Map failureMessageByRow = new HashMap<>(); + if (rawRows.isEmpty()) { + // 重读也拿不到内容时,退化为「仅失败原因」一行,至少让用户看到可读原因 + Map single = new java.util.LinkedHashMap<>(); + single.put(FAILURE_REASON, failureMessage); + rawRows.add(single); + failureMessageByRow.put(0, failureMessage); + exportRawFailure(response, fileName, sheetName, rawRows, failureMessageByRow); + return true; + } + // 异常里的数据行下标(0 基)用于把原因挂到对应行 + Integer rowIndex = readFailureRowIndex(exception); + int targetRow = rowIndex == null ? 0 : Math.max(0, Math.min(rowIndex, rawRows.size() - 1)); + failureMessageByRow.put(targetRow, failureMessage); + exportRawFailure(response, fileName, sheetName, rawRows, failureMessageByRow); + return true; + } + + /** + * 取转换异常中的数据行下标(0 基) + */ + private static Integer readFailureRowIndex(Throwable exception) { + cn.idev.excel.exception.ExcelDataConvertException convertException = findCause(exception, + cn.idev.excel.exception.ExcelDataConvertException.class); + return convertException == null ? null : convertException.getRowIndex(); + } + private static final String FAILURE_REASON = "导入失败原因"; private static final String ERROR_MESSAGE_FIELD = "errorMessage"; private static final String FAILURE_REASON_FIELD = "failureReason"; diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/controller/AirportMasterController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/AirportMasterController.java index 42374fa2..355aaee9 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/controller/AirportMasterController.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/AirportMasterController.java @@ -163,6 +163,15 @@ public class AirportMasterController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "机场主数据导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), AirportMasterExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "机场主数据导入失败明细" + DateUtil.time(), "导入失败明细", file, AirportMasterExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/controller/CargoTypeController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/CargoTypeController.java index 616e1ae6..cf73d38c 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/controller/CargoTypeController.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/CargoTypeController.java @@ -169,6 +169,15 @@ public class CargoTypeController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。 exportFailure(response, exception.getFailureList()); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "货物类型导入失败明细" + DateUtil.time(), "导入失败明细", file, CargoTypeExcel.class, readException)) { + return null; + } + throw readException; } return R.success("导入数据成功"); } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/controller/CurrencyController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/CurrencyController.java index be6ef0f5..8b6b6c44 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/controller/CurrencyController.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/CurrencyController.java @@ -162,6 +162,15 @@ public class CurrencyController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。 org.springblade.common.excel.ImportFailureExcelUtil.exportFailureReasonOnly(response, "币种汇率导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), CurrencyExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "币种汇率导入失败明细" + DateUtil.time(), "导入失败明细", file, CurrencyExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/controller/FeeItemController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/FeeItemController.java index 2bf7ea2d..d70330bd 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/controller/FeeItemController.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/FeeItemController.java @@ -151,6 +151,15 @@ public class FeeItemController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。 ImportFailureExcelUtil.exportFailureReasonOnly(response, "费用项导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), FeeItemExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "费用项导入失败明细" + DateUtil.time(), "导入失败明细", file, FeeItemExcel.class, readException)) { + return null; + } + throw readException; } return R.success("导入数据成功"); } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/controller/RailwayStationController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/RailwayStationController.java index 2e643118..34149e9d 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/controller/RailwayStationController.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/RailwayStationController.java @@ -166,6 +166,15 @@ public class RailwayStationController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "铁路站点导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), RailwayStationExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "铁路站点导入失败明细" + DateUtil.time(), "导入失败明细", file, RailwayStationExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/controller/RegionController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/RegionController.java index c0ab7473..9f0ec270 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/controller/RegionController.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/RegionController.java @@ -205,6 +205,15 @@ public class RegionController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。 org.springblade.common.excel.ImportFailureExcelUtil.exportFailureReasonOnly(response, "行政区划导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), RegionExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "行政区划导入失败明细" + DateUtil.time(), "导入失败明细", file, RegionExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/AccidentRecordController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/AccidentRecordController.java index 7a6e7140..8744be2b 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/AccidentRecordController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/AccidentRecordController.java @@ -141,6 +141,15 @@ public class AccidentRecordController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红),用户修正后重新导入。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "事故记录导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), AccidentRecordExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "事故记录导入失败明细" + DateUtil.time(), "导入失败明细", file, AccidentRecordExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/AnnualInspectionRecordController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/AnnualInspectionRecordController.java index 1412b173..f598e3c2 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/AnnualInspectionRecordController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/AnnualInspectionRecordController.java @@ -138,6 +138,15 @@ public class AnnualInspectionRecordController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红),用户修正后重新导入。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "年检记录导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), AnnualInspectionRecordExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "年检记录导入失败明细" + DateUtil.time(), "导入失败明细", file, AnnualInspectionRecordExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonCargoController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonCargoController.java index 212ca3a9..7cc90b6e 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonCargoController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonCargoController.java @@ -123,6 +123,15 @@ public class CommonCargoController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。 exportFailure(response, exception.getFailureList()); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "常用货物导入失败明细" + DateUtil.time(), "导入失败明细", file, CommonCargoExcel.class, readException)) { + return null; + } + throw readException; } return R.success("导入数据成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonRouteController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonRouteController.java index db31a347..98cf2900 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonRouteController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonRouteController.java @@ -129,6 +129,15 @@ public class CommonRouteController extends BladeController { CommonRouteImportExcel.class ); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "常用线路导入失败明细" + DateUtil.time(), "导入失败明细", file, CommonRouteImportExcel.class, readException)) { + return null; + } + throw readException; } return R.success("导入数据成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CreditScoreQuantificationController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CreditScoreQuantificationController.java index d10852ba..86e93d7b 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CreditScoreQuantificationController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CreditScoreQuantificationController.java @@ -151,6 +151,15 @@ public class CreditScoreQuantificationController extends BladeController { // 任一分组失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。 org.springblade.common.excel.ImportFailureExcelUtil.exportFailureReasonOnly(response, "评分量化表导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), CreditScoreQuantificationExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "评分量化表导入失败明细" + DateUtil.time(), "导入失败明细", file, CreditScoreQuantificationExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/EquipmentLedgerController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/EquipmentLedgerController.java index 17ad0f6c..c4554e4b 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/EquipmentLedgerController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/EquipmentLedgerController.java @@ -103,6 +103,15 @@ public class EquipmentLedgerController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "设备台账导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), EquipmentLedgerExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "设备台账导入失败明细" + DateUtil.time(), "导入失败明细", file, EquipmentLedgerExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/EtcRecordController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/EtcRecordController.java index fb3613cc..c67992b3 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/EtcRecordController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/EtcRecordController.java @@ -107,6 +107,15 @@ public class EtcRecordController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红),用户修正后重新导入。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "ETC记录导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), EtcRecordExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "ETC记录导入失败明细" + DateUtil.time(), "导入失败明细", file, EtcRecordExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/InsuranceRecordController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/InsuranceRecordController.java index 4bf40c00..8c4f414a 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/InsuranceRecordController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/InsuranceRecordController.java @@ -142,6 +142,15 @@ public class InsuranceRecordController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红),用户修正后重新导入。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "保险记录导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), InsuranceRecordExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "保险记录导入失败明细" + DateUtil.time(), "导入失败明细", file, InsuranceRecordExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/MaintenancePlanController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/MaintenancePlanController.java index 44970f18..a77493c6 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/MaintenancePlanController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/MaintenancePlanController.java @@ -141,6 +141,15 @@ public class MaintenancePlanController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "保养计划导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), MaintenancePlanExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "保养计划导入失败明细" + DateUtil.time(), "导入失败明细", file, MaintenancePlanExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/MaintenanceRecordController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/MaintenanceRecordController.java index 51db7236..42f12239 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/MaintenanceRecordController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/MaintenanceRecordController.java @@ -141,6 +141,15 @@ public class MaintenanceRecordController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红),用户修正后重新导入。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "维修记录导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), MaintenanceRecordExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "维修记录导入失败明细" + DateUtil.time(), "导入失败明细", file, MaintenanceRecordExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/MileageRecordController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/MileageRecordController.java index 884e5314..68dbf53a 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/MileageRecordController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/MileageRecordController.java @@ -129,6 +129,15 @@ public class MileageRecordController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "里程记录导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), MileageRecordExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "里程记录导入失败明细" + DateUtil.time(), "导入失败明细", file, MileageRecordExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/OilElectricRecordController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/OilElectricRecordController.java index 8254949b..f2b37b13 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/OilElectricRecordController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/OilElectricRecordController.java @@ -107,6 +107,15 @@ public class OilElectricRecordController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "油电记录导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), OilElectricRecordExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "油电记录导入失败明细" + DateUtil.time(), "导入失败明细", file, OilElectricRecordExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/OtherExpenseRecordController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/OtherExpenseRecordController.java index 9e48fdd6..d6b70a5a 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/OtherExpenseRecordController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/OtherExpenseRecordController.java @@ -107,6 +107,15 @@ public class OtherExpenseRecordController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "其他费用记录导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), OtherExpenseRecordExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "其他费用记录导入失败明细" + DateUtil.time(), "导入失败明细", file, OtherExpenseRecordExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TireReplacementRecordController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TireReplacementRecordController.java index 608d30ec..9db40c33 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TireReplacementRecordController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TireReplacementRecordController.java @@ -141,6 +141,15 @@ public class TireReplacementRecordController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红),用户修正后重新导入。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "换胎记录导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), TireReplacementRecordExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "换胎记录导入失败明细" + DateUtil.time(), "导入失败明细", file, TireReplacementRecordExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportChangeRecordController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportChangeRecordController.java index 93e10a83..87200541 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportChangeRecordController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportChangeRecordController.java @@ -126,6 +126,15 @@ public class TransportChangeRecordController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "运输变更记录导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), TransportChangeRecordExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "运输变更记录导入失败明细" + DateUtil.time(), "导入失败明细", file, TransportChangeRecordExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportPlanController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportPlanController.java index 39c3580b..f8f8fe07 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportPlanController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportPlanController.java @@ -161,6 +161,15 @@ public class TransportPlanController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 ImportFailureExcelUtil.export(response, "运输计划导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), TransportPlanImportExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "运输计划导入失败明细" + DateUtil.time(), "导入失败明细", file, TransportPlanImportExcel.class, readException)) { + return null; + } + throw readException; } return R.success("导入数据成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportReconciliationController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportReconciliationController.java index 2baf5686..97242618 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportReconciliationController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/TransportReconciliationController.java @@ -143,6 +143,15 @@ public class TransportReconciliationController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 ImportFailureExcelUtil.export(response, "运输对账导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), VehicleReconciliationExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "运输对账导入失败明细" + DateUtil.time(), "导入失败明细", file, VehicleReconciliationExcel.class, readException)) { + return null; + } + throw readException; } return R.success("导入数据成功"); } @@ -167,6 +176,15 @@ public class TransportReconciliationController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 ImportFailureExcelUtil.export(response, "运输对账导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), CargoReconciliationExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "运输对账导入失败明细" + DateUtil.time(), "导入失败明细", file, CargoReconciliationExcel.class, readException)) { + return null; + } + throw readException; } return R.success("导入数据成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ViolationRecordController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ViolationRecordController.java index 0a87e8ec..d9a3e4c4 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ViolationRecordController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ViolationRecordController.java @@ -140,6 +140,15 @@ public class ViolationRecordController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红),用户修正后重新导入。 org.springblade.common.excel.ImportFailureExcelUtil.export(response, "违章记录导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), ViolationRecordImportExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "违章记录导入失败明细" + DateUtil.time(), "导入失败明细", file, ViolationRecordImportExcel.class, readException)) { + return null; + } + throw readException; } return R.success("操作成功"); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/WaybillController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/WaybillController.java index de0ada47..e66277bb 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/WaybillController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/WaybillController.java @@ -256,6 +256,15 @@ public class WaybillController extends BladeController { // 任一行失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。 ImportFailureExcelUtil.exportFailureReasonOnly(response, "运单管理导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), WaybillExcel.class); return null; + } catch (Exception readException) { + // 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时, + // EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。 + // 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。 + if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure( + response, "运单管理导入失败明细" + DateUtil.time(), "导入失败明细", file, WaybillExcel.class, readException)) { + return null; + } + throw readException; } return R.success("导入数据成功"); }