🐛 台账/主数据类导入(4 个模块)失败明细返回全量原表

覆盖:设备台账 / 保养计划(blade-transport)、铁路站点 / 机场主数据(blade-system)。

问题同前两批:收集 errorList 后 setRollbackOnly 并 return,控制器据此导出
「仅错误行」——失败明细不是原表全量,用户无法对照原表修正。

改动(同构):
- 服务层:抛 ImportFailureException(data),明细携带原表全部行;
  回滚由异常触发(替代 setRollbackOnly)。注意 blade-system 的两个模块
  用的是静态导入 TransactionAspectSupport,模式略有差异。
- 控制器:catch 异常取 getFailureList() 导出(用 export 保留按列标红),
  补上传文件校验。

验证:mvn install -DskipTests 通过;台账回归 32/32,记录类回归 80/80。
This commit is contained in:
2026-09-26 10:58:15 +08:00
parent 96010cf542
commit 962bcc7879
8 changed files with 46 additions and 20 deletions
@@ -157,9 +157,11 @@ public class AirportMasterController extends BladeController {
if (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")) {
return R.fail("请上传 .xls,.xlsx 标准格式文件");
}
List<AirportMasterExcel> failureList = airportMasterService.importAirportMaster(ExcelUtil.read(file, AirportMasterExcel.class));
if (Func.isNotEmpty(failureList)) {
org.springblade.common.excel.ImportFailureExcelUtil.exportFailureReasonOnly(response, "空港机场主数据导入失败明细" + DateUtil.time(), "导入失败明细", failureList, AirportMasterExcel.class);
try {
airportMasterService.importAirportMaster(ExcelUtil.read(file, AirportMasterExcel.class));
} catch (org.springblade.common.excel.ImportFailureException exception) {
// 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。
org.springblade.common.excel.ImportFailureExcelUtil.export(response, "机场主数据导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), AirportMasterExcel.class);
return null;
}
return R.success("操作成功");
@@ -160,9 +160,11 @@ public class RailwayStationController extends BladeController {
if (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")) {
return R.fail("请上传 .xls,.xlsx 标准格式文件");
}
List<RailwayStationExcel> failureList = railwayStationService.importRailwayStation(ExcelUtil.read(file, RailwayStationExcel.class));
if (Func.isNotEmpty(failureList)) {
org.springblade.common.excel.ImportFailureExcelUtil.exportFailureReasonOnly(response, "铁路车站主数据导入失败明细" + DateUtil.time(), "导入失败明细", failureList, RailwayStationExcel.class);
try {
railwayStationService.importRailwayStation(ExcelUtil.read(file, RailwayStationExcel.class));
} catch (org.springblade.common.excel.ImportFailureException exception) {
// 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。
org.springblade.common.excel.ImportFailureExcelUtil.export(response, "铁路站点导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), RailwayStationExcel.class);
return null;
}
return R.success("操作成功");
@@ -160,8 +160,9 @@ public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMappe
}
}
if (Func.isNotEmpty(errorList)) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return errorList;
// 抛出携带失败明细的异常,触发事务回滚(替代旧的 setRollbackOnly)。
// 明细为原表全部行(未出错行仅无错误原因),用户可对照原表修正后重新导入。
throw new org.springblade.common.excel.ImportFailureException(data);
}
for (AirportMaster airportMaster : airportMasterList) {
// 编码命中逻辑删除记录时复用原主键,恢复后更新,避免唯一索引冲突。
@@ -165,8 +165,9 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
}
}
if (Func.isNotEmpty(errorList)) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return errorList;
// 抛出携带失败明细的异常,触发事务回滚(替代旧的 setRollbackOnly)。
// 明细为原表全部行(未出错行仅无错误原因),用户可对照原表修正后重新导入。
throw new org.springblade.common.excel.ImportFailureException(data);
}
for (RailwayStation railwayStation : railwayStationList) {
prepareImportTarget(railwayStation);
@@ -90,9 +90,18 @@ public class EquipmentLedgerController extends BladeController {
@ApiOperationSupport(order = 6)
@Operation(summary = "导入设备台账")
public R importEquipmentLedger(MultipartFile file, HttpServletResponse response) {
List<EquipmentLedgerExcel> failureList = equipmentLedgerService.importEquipmentLedger(ExcelUtil.read(file, EquipmentLedgerExcel.class));
if (Func.isNotEmpty(failureList)) {
org.springblade.common.excel.ImportFailureExcelUtil.export(response, "设备台账导入失败明细" + DateUtil.time(), "导入失败明细", failureList, EquipmentLedgerExcel.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 {
equipmentLedgerService.importEquipmentLedger(ExcelUtil.read(file, EquipmentLedgerExcel.class));
} catch (org.springblade.common.excel.ImportFailureException exception) {
// 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。
org.springblade.common.excel.ImportFailureExcelUtil.export(response, "设备台账导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), EquipmentLedgerExcel.class);
return null;
}
return R.success("操作成功");
@@ -128,9 +128,18 @@ public class MaintenancePlanController extends BladeController {
@ApiOperationSupport(order = 5)
@Operation(summary = "导入保养记录", description = "传入excel")
public R importMaintenancePlan(MultipartFile file, HttpServletResponse response) {
List<MaintenancePlanExcel> failureList = maintenancePlanService.importMaintenancePlan(ExcelUtil.read(file, MaintenancePlanExcel.class));
if (Func.isNotEmpty(failureList)) {
org.springblade.common.excel.ImportFailureExcelUtil.export(response, "保养记录导入失败明细" + DateUtil.time(), "导入失败明细", failureList, MaintenancePlanExcel.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 {
maintenancePlanService.importMaintenancePlan(ExcelUtil.read(file, MaintenancePlanExcel.class));
} catch (org.springblade.common.excel.ImportFailureException exception) {
// 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。
org.springblade.common.excel.ImportFailureExcelUtil.export(response, "保养计划导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), MaintenancePlanExcel.class);
return null;
}
return R.success("操作成功");
@@ -114,8 +114,9 @@ public class EquipmentLedgerServiceImpl extends BaseServiceImpl<EquipmentLedgerM
}
}
if (Func.isNotEmpty(errorList)) {
org.springframework.transaction.interceptor.TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return errorList;
// 抛出携带失败明细的异常,触发事务回滚(替代旧的 setRollbackOnly)。
// 明细为原表全部行(未出错行仅无错误原因),用户可对照原表修正后重新导入。
throw new org.springblade.common.excel.ImportFailureException(data);
}
for (EquipmentLedger equipmentLedger : equipmentLedgerList) {
if (!save(equipmentLedger)) {
@@ -103,8 +103,9 @@ public class MaintenancePlanServiceImpl extends BaseServiceImpl<MaintenancePlanM
}
}
if (Func.isNotEmpty(errorList)) {
org.springframework.transaction.interceptor.TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return errorList;
// 抛出携带失败明细的异常,触发事务回滚(替代旧的 setRollbackOnly)。
// 明细为原表全部行(未出错行仅无错误原因),用户可对照原表修正后重新导入。
throw new org.springblade.common.excel.ImportFailureException(data);
}
for (MaintenancePlan maintenancePlan : maintenancePlanList) {
if (!save(maintenancePlan)) {