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 ae64bb22..39c3580b 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 @@ -147,10 +147,19 @@ public class TransportPlanController extends BladeController { public R importTransportPlan(MultipartFile file, @RequestParam Long projectId, @RequestParam String projectName, @RequestParam Long contractId, @RequestParam String contractName, @RequestParam String customerName, HttpServletResponse response) { - List failureList = transportPlanService.importTransportPlan( - ExcelUtil.read(file, TransportPlanImportExcel.class), projectId, projectName, contractId, contractName, customerName); - if (Func.isNotEmpty(failureList)) { - ImportFailureExcelUtil.export(response, "运输计划导入失败明细" + DateUtil.time(), "导入失败明细", failureList, TransportPlanImportExcel.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 { + transportPlanService.importTransportPlan( + ExcelUtil.read(file, TransportPlanImportExcel.class), projectId, projectName, contractId, contractName, customerName); + } catch (org.springblade.common.excel.ImportFailureException exception) { + // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 + ImportFailureExcelUtil.export(response, "运输计划导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), TransportPlanImportExcel.class); return null; } 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 2b5d7772..2baf5686 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 @@ -19,6 +19,7 @@ import org.springblade.core.mp.support.Query; import org.springblade.core.secure.annotation.PreAuth; import org.springblade.core.tool.api.R; import org.springblade.core.tool.utils.DateUtil; +import org.springblade.core.tool.utils.Func; import org.springblade.core.excel.util.ExcelUtil; import org.springblade.transport.excel.CargoReconciliationExcel; import org.springblade.transport.excel.CargoReconciliationFeeReader; @@ -126,12 +127,21 @@ public class TransportReconciliationController extends BladeController { @ApiOperationSupport(order = 6) @Operation(summary = "导入整车总额外部账单") public R importVehicle(@RequestParam Long id, MultipartFile file, HttpServletResponse response) { - List rows = ExcelUtil.read(file, VehicleReconciliationExcel.class); - List> feeItems = VehicleReconciliationFeeReader.read(file); - for (int index = 0; index < rows.size() && index < feeItems.size(); index++) rows.get(index).setFeeItems(feeItems.get(index)); - List failures = reconciliationService.importVehicles(id, rows); - if (!failures.isEmpty()) { - ImportFailureExcelUtil.export(response, "运输对账导入失败明细" + DateUtil.time(), "导入失败明细", failures, VehicleReconciliationFailureExcel.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 { + List rows = ExcelUtil.read(file, VehicleReconciliationExcel.class); + List> feeItems = VehicleReconciliationFeeReader.read(file); + for (int index = 0; index < rows.size() && index < feeItems.size(); index++) rows.get(index).setFeeItems(feeItems.get(index)); + reconciliationService.importVehicles(id, rows); + } catch (org.springblade.common.excel.ImportFailureException exception) { + // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 + ImportFailureExcelUtil.export(response, "运输对账导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), VehicleReconciliationExcel.class); return null; } return R.success("导入数据成功"); @@ -141,12 +151,21 @@ public class TransportReconciliationController extends BladeController { @ApiOperationSupport(order = 7) @Operation(summary = "导入货物明细外部账单") public R importCargo(@RequestParam Long id, MultipartFile file, HttpServletResponse response) { - List rows = ExcelUtil.read(file, CargoReconciliationExcel.class); - List> feeItems = CargoReconciliationFeeReader.read(file); - for (int index = 0; index < rows.size() && index < feeItems.size(); index++) rows.get(index).setFeeItems(feeItems.get(index)); - List failures = reconciliationService.importCargoes(id, rows); - if (!failures.isEmpty()) { - ImportFailureExcelUtil.export(response, "运输对账导入失败明细" + DateUtil.time(), "导入失败明细", failures, CargoReconciliationFailureExcel.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 { + List rows = ExcelUtil.read(file, CargoReconciliationExcel.class); + List> feeItems = CargoReconciliationFeeReader.read(file); + for (int index = 0; index < rows.size() && index < feeItems.size(); index++) rows.get(index).setFeeItems(feeItems.get(index)); + reconciliationService.importCargoes(id, rows); + } catch (org.springblade.common.excel.ImportFailureException exception) { + // 任一行失败即整批回滚,导出原表全部数据并标注错误(含按列标红)。 + ImportFailureExcelUtil.export(response, "运输对账导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), CargoReconciliationExcel.class); return null; } return R.success("导入数据成功"); diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportPlanServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportPlanServiceImpl.java index 9c5620c6..d466d882 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportPlanServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportPlanServiceImpl.java @@ -294,8 +294,9 @@ public class TransportPlanServiceImpl extends BaseServiceImpl(errorMap.values()); + // 任一行失败即整批回滚:抛异常触发事务回滚(替代旧的 setRollbackOnly), + // 明细为原表全部行(未出错行仅无错误原因),用户可对照原表修正后重新导入。 + throw new org.springblade.common.excel.ImportFailureException(data); } // 第二阶段:批量导入 diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportReconciliationServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportReconciliationServiceImpl.java index 05e01c29..2039b426 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportReconciliationServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportReconciliationServiceImpl.java @@ -300,22 +300,33 @@ public class TransportReconciliationServiceImpl VehicleReconciliationExcel row = rows.get(index); List validationErrors = validateImportVehicle(row); if (Func.isNotEmpty(validationErrors)) { + // 原因同时写回原行:异常携带的是原表 rows, + // 导出工具从行对象的 errorMessage 字段取原因,只写副本会丢原因。 + row.setErrorMessage(org.springblade.common.excel.ImportFailureExcelUtil.formatErrorMessage(validationErrors)); VehicleReconciliationFailureExcel failure = new VehicleReconciliationFailureExcel(); BeanUtil.copyProperties(row, failure); - failure.setErrorMessage(org.springblade.common.excel.ImportFailureExcelUtil.formatErrorMessage(validationErrors)); + failure.setErrorMessage(row.getErrorMessage()); failures.add(failure); continue; } try { validExternals.add(buildVehicleExternal(id, index, row)); } catch (Exception exception) { + String message = org.springblade.common.excel.ImportFailureExcelUtil.formatErrorMessage(List.of( + exception instanceof ServiceException ? exception.getMessage() : "导入失败")); + row.setErrorMessage(message); VehicleReconciliationFailureExcel failure = new VehicleReconciliationFailureExcel(); BeanUtil.copyProperties(row, failure); - failure.setErrorMessage(org.springblade.common.excel.ImportFailureExcelUtil.formatErrorMessage(List.of( - exception instanceof ServiceException ? exception.getMessage() : "导入失败"))); + failure.setErrorMessage(message); failures.add(failure); } } + if (Func.isNotEmpty(failures)) { + // 任一行失败即整批回滚:抛异常触发事务回滚,使前置的 resetExternal + // 清空与本批正确行的插入一并退掉;否则会出现「旧外部账单被清空、 + // 正确行却已入库」的数据损坏。 + throw new org.springblade.common.excel.ImportFailureException(rows); + } for (TransportReconciliationExternal external : validExternals) { if (externalMapper.insert(external) <= 0) throw new ServiceException("外部账单保存失败"); } @@ -418,22 +429,33 @@ public class TransportReconciliationServiceImpl CargoReconciliationExcel row = rows.get(index); List validationErrors = validateImportCargo(row); if (Func.isNotEmpty(validationErrors)) { + // 原因同时写回原行:异常携带的是原表 rows, + // 导出工具从行对象的 errorMessage 字段取原因,只写副本会丢原因。 + row.setErrorMessage(org.springblade.common.excel.ImportFailureExcelUtil.formatErrorMessage(validationErrors)); CargoReconciliationFailureExcel failure = new CargoReconciliationFailureExcel(); BeanUtil.copyProperties(row, failure); - failure.setErrorMessage(org.springblade.common.excel.ImportFailureExcelUtil.formatErrorMessage(validationErrors)); + failure.setErrorMessage(row.getErrorMessage()); failures.add(failure); continue; } try { validExternals.add(buildCargoExternal(id, index, row)); } catch (Exception exception) { + String message = org.springblade.common.excel.ImportFailureExcelUtil.formatErrorMessage(List.of( + exception instanceof ServiceException ? exception.getMessage() : "导入失败")); + row.setErrorMessage(message); CargoReconciliationFailureExcel failure = new CargoReconciliationFailureExcel(); BeanUtil.copyProperties(row, failure); - failure.setErrorMessage(org.springblade.common.excel.ImportFailureExcelUtil.formatErrorMessage(List.of( - exception instanceof ServiceException ? exception.getMessage() : "导入失败"))); + failure.setErrorMessage(message); failures.add(failure); } } + if (Func.isNotEmpty(failures)) { + // 任一行失败即整批回滚:抛异常触发事务回滚,使前置的 resetExternal + // 清空与本批正确行的插入一并退掉;否则会出现「旧外部账单被清空、 + // 正确行却已入库」的数据损坏。 + throw new org.springblade.common.excel.ImportFailureException(rows); + } for (TransportReconciliationExternal external : validExternals) { if (externalMapper.insert(external) <= 0) throw new ServiceException("外部账单保存失败"); }