From 30e1ada0ccd6773227069fca4d29d5dbdcbcd91d Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Thu, 24 Sep 2026 05:27:25 +0800 Subject: [PATCH] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E5=B8=81=E7=A7=8D?= =?UTF-8?q?=E6=B1=87=E7=8E=87=E5=AF=BC=E5=85=A5=E6=9C=AA=E6=95=B4=E6=89=B9?= =?UTF-8?q?=E5=9B=9E=E6=BB=9A=EF=BC=8C=E6=B4=BE=E7=94=9F=E5=86=99=E9=9A=8F?= =?UTF-8?q?=E4=BA=8B=E5=8A=A1=E4=B8=80=E8=B5=B7=E9=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:CurrencyServiceImpl.importCurrency 无 @Transactional,循环内吞掉异常 后正常 return,失败行之前的正确行已落库。且循环内除 save 外还有 rebuildEnabledValidity 派生写(按 code 重算启用记录有效期区间), 若只回滚主表遗漏派生写,会留下「主表回滚了、有效期却按失败批次算过」的脏数据。 改动: - 服务层:补 @Transactional(rollbackFor = Exception.class),错误收集完后 抛 ImportFailureException(org.springblade.common.excel), 主表 save 与派生写 rebuildEnabledValidity 在同一事务内一起回滚。 - CurrencyExcel.errorMessage 由 @ExcelProperty 改为 @ExcelIgnore: 原先导出模板会多出 errorMessage 空列(与 issue #12 运单模板同款问题)。 - 控制器:改 catch 异常导出 + 补上传文件校验,对齐 PortTerminal 写法。 验证:mvn install -DskipTests 通过;币种回归 25/25(含派生区间 正确性断言与零写入断言);同批修复顺带覆盖 issue #12 同类问题。 --- .../system/controller/CurrencyController.java | 15 ++++++++++++--- .../springblade/system/excel/CurrencyExcel.java | 2 +- .../system/service/impl/CurrencyServiceImpl.java | 11 +++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) 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 4bc43d95..be6ef0f5 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 @@ -149,9 +149,18 @@ public class CurrencyController extends BladeController { @ApiOperationSupport(order = 7) @Operation(summary = "导入币种汇率", description = "传入excel") public R importCurrency(MultipartFile file, HttpServletResponse response) { - List failureList = currencyService.importCurrency(ExcelUtil.read(file, CurrencyExcel.class)); - if (Func.isNotEmpty(failureList)) { - org.springblade.common.excel.ImportFailureExcelUtil.export(response, "币种汇率导入失败明细" + DateUtil.time(), "导入失败明细", failureList, CurrencyExcel.class); + if (file == null || file.isEmpty()) { + return R.fail("上传文件不能为空"); + } + String fileName = Func.toStrWithEmpty(file.getOriginalFilename(), "").toLowerCase(); + if (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")) { + return R.fail("请上传 .xls,.xlsx 标准格式文件"); + } + try { + currencyService.importCurrency(ExcelUtil.read(file, CurrencyExcel.class)); + } catch (org.springblade.common.excel.ImportFailureException exception) { + // 任一行失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。 + org.springblade.common.excel.ImportFailureExcelUtil.exportFailureReasonOnly(response, "币种汇率导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), CurrencyExcel.class); return null; } return R.success("操作成功"); diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/excel/CurrencyExcel.java b/blade-service/blade-system/src/main/java/org/springblade/system/excel/CurrencyExcel.java index b59573d5..998f3aac 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/excel/CurrencyExcel.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/excel/CurrencyExcel.java @@ -76,7 +76,7 @@ public class CurrencyExcel implements Serializable { @ExcelProperty("备注") private String remark; - @ExcelProperty + @ExcelIgnore private String errorMessage; } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/CurrencyServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/CurrencyServiceImpl.java index dea6d264..48aeedc9 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/CurrencyServiceImpl.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/CurrencyServiceImpl.java @@ -115,10 +115,16 @@ public class CurrencyServiceImpl extends BaseServiceImpl importCurrency(List data) { if (Func.isEmpty(data)) { throw new ServiceException("导入数据不能为空"); } + // 全量校验:任何一行失败都整批回滚,因此先收集所有错误再统一抛出。 + // 注意:循环内除 save 外还有 rebuildEnabledValidity 这个派生写—— + // 若吞掉异常正常 return,主表数据和派生的有效期区间都会落库; + // 必须抛异常让两者一起回滚,否则会出现「主表回滚了、有效期区间 + // 却按失败批次算过」的脏数据。 List errorList = new ArrayList<>(); int successCount = 0; for (int index = 0; index < data.size(); index++) { @@ -140,6 +146,11 @@ public class CurrencyServiceImpl extends BaseServiceImpl