🐛 修复常用线路导入未整批回滚

问题:CommonRouteServiceImpl.importCommonRoute 循环内吞掉异常后正常 return,
方法正常返回导致 Spring 照常提交事务。方法虽有 @Transactional(rollbackFor =
Exception.class),但异常被吞后注解救不了——失败行之前的正确行已经落库。

改动:
- 服务层:错误收集完毕后若 failureList 非空,抛 ImportFailureException
  (org.springblade.common.excel)触发整批回滚。catch 里原本就把原因
  写在原行对象上,失败明细原因不丢,无需额外处理。
- 控制器:改为 catch 异常 → 取 failureList 导出(exportFailureReasonOnly)
  → return null,并补上传文件校验,对齐 PortTerminalController 写法。

验证:
- mvn install -DskipTests 全量编译通过。
- 常用线路回归 29/29 通过(新增 tests/fixtures/common-route/,
  scripts/api-common-route-import-test.sh),含 02_一行错误多行正确_应零写入、
  07_与库中线路重名(基线行不受回滚影响)。
- 港口码头 45/45、常用货物 25/25 基线回归通过,确认无连带影响。
This commit is contained in:
2026-09-24 04:57:26 +08:00
parent 07b2f2766e
commit cd2052e5d0
2 changed files with 24 additions and 6 deletions
@@ -38,6 +38,7 @@ import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.common.excel.ImportFailureExcelUtil;
import org.springblade.common.excel.ImportFailureException;
import org.springblade.transport.excel.CommonRouteExportExcel;
import org.springblade.transport.excel.CommonRouteImportExcel;
import org.springblade.transport.pojo.entity.CommonRoute;
@@ -109,15 +110,22 @@ public class CommonRouteController extends BladeController {
@ApiOperationSupport(order = 6)
@Operation(summary = "导入常用线路", description = "传入excel")
public R importCommonRoute(MultipartFile file, HttpServletResponse response) {
List<CommonRouteImportExcel> failureList = commonRouteService.importCommonRoute(
ExcelUtil.read(file, CommonRouteImportExcel.class)
);
if (Func.isNotEmpty(failureList)) {
ImportFailureExcelUtil.export(
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 {
commonRouteService.importCommonRoute(ExcelUtil.read(file, CommonRouteImportExcel.class));
} catch (ImportFailureException exception) {
// 任一行失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。
ImportFailureExcelUtil.exportFailureReasonOnly(
response,
"常用线路导入失败明细" + DateUtil.time(),
"导入失败明细",
failureList,
exception.getFailureList(),
CommonRouteImportExcel.class
);
return null;
@@ -25,6 +25,7 @@ package org.springblade.transport.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springblade.common.excel.ImportFailureException;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.tool.utils.BeanUtil;
@@ -130,6 +131,10 @@ public class CommonRouteServiceImpl extends BaseServiceImpl<CommonRouteMapper, C
if (Func.isEmpty(data)) {
throw new ServiceException("导入数据不能为空");
}
// 全量校验:任何一行失败都整批回滚,因此先收集所有错误再统一抛出。
// 注意:本方法虽已标注 @Transactional,但循环内把异常吞掉后正常 return,
// Spring 会认为一切成功并提交事务——注解救不了被吞掉的异常。
// 必须把错误明细收集完后抛异常,回滚才会真正发生。
List<CommonRouteImportExcel> failureList = new ArrayList<>();
for (int index = 0; index < data.size(); index++) {
CommonRouteImportExcel excel = data.get(index);
@@ -142,6 +147,11 @@ public class CommonRouteServiceImpl extends BaseServiceImpl<CommonRouteMapper, C
failureList.add(excel);
}
}
if (Func.isNotEmpty(failureList)) {
// 抛出携带失败明细的异常,触发事务回滚。
// 明细为原表全部行(未出错行仅无错误原因),用户可对照原表修正后重新导入。
throw new ImportFailureException(data);
}
return failureList;
}