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 b8eae386..2bf7ea2d 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 @@ -36,6 +36,7 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import lombok.AllArgsConstructor; import org.springblade.common.excel.ImportFailureExcelUtil; +import org.springblade.common.excel.ImportFailureException; import org.springblade.core.boot.ctrl.BladeController; import org.springblade.core.excel.util.ExcelUtil; import org.springblade.core.mp.support.Condition; @@ -47,7 +48,6 @@ import org.springblade.core.tool.utils.DateUtil; import org.springblade.core.tool.utils.Func; import org.springblade.system.excel.FeeItemExcel; import org.springblade.system.excel.FeeItemExportExcel; -import org.springblade.system.excel.FeeItemImportFailureExcel; import org.springblade.system.pojo.entity.FeeItem; import org.springblade.system.pojo.vo.FeeItemVO; import org.springblade.system.service.IFeeItemService; @@ -138,10 +138,18 @@ public class FeeItemController extends BladeController { @ApiOperationSupport(order = 6) @Operation(summary = "导入费用项", description = "传入excel") public R importFeeItem(MultipartFile file, HttpServletResponse response) { - List data = ExcelUtil.read(file, FeeItemExcel.class); - List failureList = feeItemService.importFeeItem(data); - if (Func.isNotEmpty(failureList)) { - ImportFailureExcelUtil.export(response, "费用项导入失败明细" + DateUtil.time(), "导入失败明细", failureList, FeeItemExcel.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 { + feeItemService.importFeeItem(ExcelUtil.read(file, FeeItemExcel.class)); + } catch (ImportFailureException exception) { + // 任一行失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。 + ImportFailureExcelUtil.exportFailureReasonOnly(response, "费用项导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), FeeItemExcel.class); return null; } return R.success("导入数据成功"); diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/FeeItemServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/FeeItemServiceImpl.java index a3825a46..4221d262 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/FeeItemServiceImpl.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/FeeItemServiceImpl.java @@ -108,6 +108,9 @@ public class FeeItemServiceImpl extends BaseServiceImpl if (Func.isEmpty(data)) { throw new ServiceException("导入数据不能为空"); } + // 全量校验:任何一行失败都整批回滚,因此先收集所有错误再统一抛出。 + // 注意不能只往 failureList 里塞数据就 return——异常被吞掉后方法会正常返回, + // Spring 会照常提交事务,@Transactional 起不到回滚作用。 List failureList = new ArrayList<>(); for (int index = 0; index < data.size(); index++) { FeeItemExcel excel = data.get(index); @@ -118,9 +121,18 @@ public class FeeItemServiceImpl extends BaseServiceImpl save(feeItem); } catch (Exception exception) { String message = exception instanceof ServiceException ? exception.getMessage() : "导入失败"; + // 除收集到 failureList 外,还必须把原因写回原行对象: + // 抛出异常时携带的是原表 data,导出工具从行对象的 errorMessage 字段取原因, + // 只写在副本上会导致失败明细里错误行的原因为空。 + excel.setErrorMessage("第" + (index + 2) + "行:" + message); failureList.add(toImportFailureExcel(excel, "第" + (index + 2) + "行:" + message)); } } + if (Func.isNotEmpty(failureList)) { + // 抛出携带失败明细的异常,触发事务回滚。 + // 明细为原表全部行(未出错行仅无错误原因),用户可对照原表修正后重新导入。 + throw new org.springblade.common.excel.ImportFailureException(data); + } return failureList; }