diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/excel/ImportFailureException.java b/blade-common/src/main/java/org/springblade/common/excel/ImportFailureException.java
similarity index 72%
rename from blade-service/blade-system/src/main/java/org/springblade/system/excel/ImportFailureException.java
rename to blade-common/src/main/java/org/springblade/common/excel/ImportFailureException.java
index 0277ec01..6065ce0f 100644
--- a/blade-service/blade-system/src/main/java/org/springblade/system/excel/ImportFailureException.java
+++ b/blade-common/src/main/java/org/springblade/common/excel/ImportFailureException.java
@@ -23,7 +23,7 @@
*
* Author: Chill Zhuang (bladejava@qq.com)
*/
-package org.springblade.system.excel;
+package org.springblade.common.excel;
import org.springblade.core.log.exception.ServiceException;
@@ -33,8 +33,14 @@ import java.util.List;
/**
* 导入失败异常,携带失败明细用于导出原表并标注错误。
*
- * 批量导入采用「全失败即整批回滚」语义:任一行校验失败都会抛出本异常触发事务回滚,
- * 失败明细在抛异常前已收集完毕,因此回滚不影响明细的内容。
+ * 批量导入采用「任一行失败即整批回滚」语义:循环内先收集完全部错误明细,
+ * 再抛出本异常触发事务回滚。明细在抛异常前已收集完毕,因此回滚不影响明细内容。
+ *
+ * 注意:仅标注 {@code @Transactional} 并不足够。若循环内把异常吞掉(catch 后
+ * 只往 errorList 塞数据并正常 return),方法会正常返回,Spring 会照常提交事务。
+ * 必须抛出异常才能让事务真正回滚。
+ *
+ * 本类位于 blade-common,供 blade-system 与 blade-transport 等模块共用。
*
* @author Chill
*/
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/controller/CargoTypeController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/CargoTypeController.java
index 745968b5..616e1ae6 100644
--- a/blade-service/blade-system/src/main/java/org/springblade/system/controller/CargoTypeController.java
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/CargoTypeController.java
@@ -46,9 +46,9 @@ 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.system.excel.CargoTypeExcel;
import org.springblade.system.excel.CargoTypeExportExcel;
-import org.springblade.system.excel.CargoTypeImportFailureExcel;
import org.springblade.system.pojo.entity.CargoType;
import org.springblade.system.pojo.vo.CargoTypeVO;
import org.springblade.system.service.ICargoTypeService;
@@ -156,15 +156,32 @@ public class CargoTypeController extends BladeController {
@ApiOperationSupport(order = 7)
@Operation(summary = "导入货物类型", description = "传入excel")
public R importCargoType(MultipartFile file, HttpServletResponse response) {
- List data = ExcelUtil.read(file, CargoTypeExcel.class);
- List failureList = cargoTypeService.importCargoType(data);
- if (Func.isNotEmpty(failureList)) {
- ImportFailureExcelUtil.export(response, "货物类型导入失败明细" + DateUtil.time(), "导入失败明细", failureList, CargoTypeExcel.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 {
+ cargoTypeService.importCargoType(ExcelUtil.read(file, CargoTypeExcel.class));
+ } catch (ImportFailureException exception) {
+ // 任一行失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。
+ exportFailure(response, exception.getFailureList());
return null;
}
return R.success("导入数据成功");
}
+ /**
+ * 导出导入失败明细,内容为原表全部数据并在末尾追加失败原因列。
+ *
+ * 失败数据仅标红出错单元格与失败原因列,表头保持默认样式。
+ */
+ private void exportFailure(HttpServletResponse response, List> failureList) {
+ ImportFailureExcelUtil.exportFailureReasonOnly(response, "货物类型导入失败明细" + DateUtil.time(), "导入失败明细", failureList, CargoTypeExcel.class);
+ }
+
/**
* 导出货物类型
*/
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/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/controller/PortTerminalController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/PortTerminalController.java
index 0639b52d..8c19eedc 100644
--- a/blade-service/blade-system/src/main/java/org/springblade/system/controller/PortTerminalController.java
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/PortTerminalController.java
@@ -44,7 +44,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.system.excel.ImportFailureException;
+import org.springblade.common.excel.ImportFailureException;
import org.springblade.system.excel.PortTerminalExcel;
import org.springblade.system.excel.PortTerminalExportExcel;
import org.springblade.system.excel.PortTerminalImporter;
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/controller/RegionController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/RegionController.java
index 0de1e680..c0ab7473 100644
--- a/blade-service/blade-system/src/main/java/org/springblade/system/controller/RegionController.java
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/RegionController.java
@@ -46,6 +46,7 @@ import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.DateUtil;
+import org.springblade.core.tool.utils.Func;
import org.springblade.system.pojo.entity.Region;
import org.springblade.system.excel.RegionExcel;
import org.springblade.system.excel.RegionExportExcel;
@@ -191,9 +192,18 @@ public class RegionController extends BladeController {
@ApiOperationSupport(order = 10)
@Operation(summary = "导入行政区划", description = "传入excel")
public R importRegion(MultipartFile file, Integer isCovered, HttpServletResponse response) {
- List failureList = regionService.importRegion(ExcelUtil.read(file, RegionExcel.class), Objects.equals(isCovered, 1));
- if (!failureList.isEmpty()) {
- org.springblade.common.excel.ImportFailureExcelUtil.export(response, "行政区划导入失败明细" + DateUtil.time(), "导入失败明细", failureList, RegionExcel.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 {
+ regionService.importRegion(ExcelUtil.read(file, RegionExcel.class), Objects.equals(isCovered, 1));
+ } catch (org.springblade.common.excel.ImportFailureException exception) {
+ // 任一行失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。
+ org.springblade.common.excel.ImportFailureExcelUtil.exportFailureReasonOnly(response, "行政区划导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), RegionExcel.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/CargoTypeServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/CargoTypeServiceImpl.java
index e7b97e2b..a1fc4f74 100644
--- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/CargoTypeServiceImpl.java
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/CargoTypeServiceImpl.java
@@ -29,6 +29,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
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.Func;
@@ -134,10 +135,14 @@ public class CargoTypeServiceImpl extends BaseServiceImpl importCargoType(List data) {
if (Func.isEmpty(data)) {
throw new ServiceException("导入数据不能为空");
}
+ // 全量校验:任何一行失败都整批回滚,因此先收集所有错误再统一抛出。
+ // 注意不能只往 errorList 里塞数据就 return——异常被吞掉后方法会正常返回,
+ // Spring 会照常提交事务,@Transactional 起不到回滚作用。
List failureList = new ArrayList<>();
for (int index = 0; index < data.size(); index++) {
CargoTypeExcel excel = data.get(index);
@@ -152,6 +157,11 @@ public class CargoTypeServiceImpl 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
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;
}
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/PortTerminalServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/PortTerminalServiceImpl.java
index 892f4d62..c7a08632 100644
--- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/PortTerminalServiceImpl.java
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/PortTerminalServiceImpl.java
@@ -34,8 +34,8 @@ import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
+import org.springblade.common.excel.ImportFailureException;
import org.springblade.system.cache.UserCache;
-import org.springblade.system.excel.ImportFailureException;
import org.springblade.system.excel.PortTerminalExcel;
import org.springblade.system.excel.PortTerminalExportExcel;
import org.springblade.system.mapper.PortTerminalMapper;
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/RegionServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/RegionServiceImpl.java
index 77ceabc1..a251af29 100644
--- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/RegionServiceImpl.java
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/RegionServiceImpl.java
@@ -41,6 +41,7 @@ import org.springblade.system.mapper.RegionMapper;
import org.springblade.system.service.IRegionService;
import org.springblade.system.pojo.vo.RegionVO;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Date;
@@ -60,6 +61,17 @@ public class RegionServiceImpl extends ServiceImpl impleme
@Override
public boolean submit(Region region) {
+ return submit(region, true);
+ }
+
+ /**
+ * 提交区划,clearCache 控制是否在保存成功后立即清理惰性树缓存。
+ *
+ * 批量导入路径传 false:导入内部会收集全部错误,只有整批成功才统一清缓存;
+ * 若逐行清缓存,失败批次中已成功行会把缓存清掉/重算成中间状态,
+ * 与随后的事务回滚形成「库回滚了、缓存却是脏的」。
+ */
+ private boolean submit(Region region, boolean clearCache) {
Date now = new Date();
Long currentUserId = AuthUtil.getUserId();
boolean isNew = StringUtil.isBlank(region.getOriginalCode());
@@ -138,7 +150,7 @@ public class RegionServiceImpl extends ServiceImpl impleme
region.setVillageName(name);
}
boolean result = StringUtil.isNotBlank(region.getOriginalCode()) ? this.updateById(region) : this.save(region);
- if (result) {
+ if (result && clearCache) {
clearLazyTree();
}
return result;
@@ -189,12 +201,15 @@ public class RegionServiceImpl extends ServiceImpl impleme
}
@Override
+ @Transactional(rollbackFor = Exception.class)
public List importRegion(List data, Boolean isCovered) {
if (Func.isEmpty(data)) {
throw new ServiceException("导入数据不能为空");
}
+ // 全量校验:任何一行失败都整批回滚,因此先收集所有错误再统一抛出。
+ // 注意不能只往 errorList 里塞数据就 return——异常被吞掉后方法会正常返回,
+ // Spring 会照常提交事务,@Transactional 起不到回滚作用。
List errorList = new ArrayList<>();
- boolean cacheChanged = false;
for (int index = 0; index < data.size(); index++) {
RegionExcel excel = data.get(index);
try {
@@ -210,15 +225,20 @@ public class RegionServiceImpl extends ServiceImpl impleme
if (Boolean.TRUE.equals(isCovered) && this.getById(region.getCode()) != null) {
region.setOriginalCode(region.getCode());
}
- cacheChanged = this.submit(region) || cacheChanged;
+ // 导入路径不逐行清缓存,缓存只在整批成功后统一清一次。
+ this.submit(region, false);
} catch (Exception exception) {
excel.setErrorMessage(exception.getMessage());
errorList.add(excel);
}
}
- if (cacheChanged) {
- clearLazyTree();
+ if (Func.isNotEmpty(errorList)) {
+ // 抛出携带失败明细的异常,触发事务回滚。缓存清理在异常抛出后不可达,
+ // 只会发生在成功路径上——避免「库回滚了、惰性树缓存却按失败批次
+ // 的中间状态被清掉/重算」的脏缓存。
+ throw new org.springblade.common.excel.ImportFailureException(data);
}
+ clearLazyTree();
return errorList;
}
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonCargoController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonCargoController.java
index 34b54fa5..212ca3a9 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonCargoController.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonCargoController.java
@@ -38,9 +38,9 @@ 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.CommonCargoExcel;
import org.springblade.transport.excel.CommonCargoExportExcel;
-import org.springblade.transport.excel.CommonCargoImportFailureExcel;
import org.springframework.web.multipart.MultipartFile;
import org.springblade.transport.pojo.entity.CommonCargo;
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
@@ -110,14 +110,32 @@ public class CommonCargoController extends BladeController {
@ApiOperationSupport(order = 6)
@Operation(summary = "导入常用货物", description = "传入excel")
public R importCommonCargo(MultipartFile file, HttpServletResponse response) {
- List failureList = commonCargoService.importCommonCargo(ExcelUtil.read(file, CommonCargoExcel.class));
- if (Func.isNotEmpty(failureList)) {
- ImportFailureExcelUtil.export(response, "常用货物导入失败明细" + DateUtil.time(), "导入失败明细", failureList, CommonCargoImportFailureExcel.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 {
+ commonCargoService.importCommonCargo(ExcelUtil.read(file, CommonCargoExcel.class));
+ } catch (ImportFailureException exception) {
+ // 任一行失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。
+ exportFailure(response, exception.getFailureList());
return null;
}
return R.success("导入数据成功");
}
+ /**
+ * 导出导入失败明细,内容为原表全部数据并在末尾追加失败原因列。
+ *
+ * 失败数据仅标红出错单元格与失败原因列,表头保持默认样式。
+ */
+ private void exportFailure(HttpServletResponse response, List> failureList) {
+ ImportFailureExcelUtil.exportFailureReasonOnly(response, "常用货物导入失败明细" + DateUtil.time(), "导入失败明细", failureList, CommonCargoExcel.class);
+ }
+
@GetMapping("/export-template")
@ApiOperationSupport(order = 7)
@Operation(summary = "导出模板")
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonRouteController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonRouteController.java
index 61966056..db31a347 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonRouteController.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CommonRouteController.java
@@ -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 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;
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CreditScoreQuantificationController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CreditScoreQuantificationController.java
index 59c404ff..d10852ba 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CreditScoreQuantificationController.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CreditScoreQuantificationController.java
@@ -138,9 +138,18 @@ public class CreditScoreQuantificationController extends BladeController {
@ApiOperationSupport(order = 7)
@Operation(summary = "导入评分量化表", description = "传入excel")
public R importCreditScoreQuantification(MultipartFile file, HttpServletResponse response) {
- List failureList = creditScoreQuantificationService.importCreditScoreQuantification(ExcelUtil.read(file, CreditScoreQuantificationExcel.class));
- if (Func.isNotEmpty(failureList)) {
- org.springblade.common.excel.ImportFailureExcelUtil.export(response, "评分量化表导入失败明细" + DateUtil.time(), "导入失败明细", failureList, CreditScoreQuantificationExcel.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 {
+ creditScoreQuantificationService.importCreditScoreQuantification(ExcelUtil.read(file, CreditScoreQuantificationExcel.class));
+ } catch (org.springblade.common.excel.ImportFailureException exception) {
+ // 任一分组失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。
+ org.springblade.common.excel.ImportFailureExcelUtil.exportFailureReasonOnly(response, "评分量化表导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), CreditScoreQuantificationExcel.class);
return null;
}
return R.success("操作成功");
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/WaybillController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/WaybillController.java
index 3de34089..de0ada47 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/WaybillController.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/WaybillController.java
@@ -34,6 +34,7 @@ import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.apache.poi.ss.usermodel.CellStyle;
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;
@@ -242,9 +243,18 @@ public class WaybillController extends BladeController {
@ApiOperationSupport(order = 13)
@Operation(summary = "导入运单管理", description = "传入excel")
public R importWaybill(MultipartFile file, HttpServletResponse response) {
- List failureList = waybillService.importWaybill(ExcelUtil.read(file, WaybillExcel.class));
- if (Func.isNotEmpty(failureList)) {
- ImportFailureExcelUtil.export(response, "运单管理导入失败明细" + DateUtil.time(), "导入失败明细", failureList, WaybillExcel.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 {
+ waybillService.importWaybill(ExcelUtil.read(file, WaybillExcel.class));
+ } catch (ImportFailureException exception) {
+ // 任一行失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。
+ ImportFailureExcelUtil.exportFailureReasonOnly(response, "运单管理导入失败明细" + DateUtil.time(), "导入失败明细", exception.getFailureList(), WaybillExcel.class);
return null;
}
return R.success("导入数据成功");
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/CommonCargoServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/CommonCargoServiceImpl.java
index f8b6b5c7..7ab5bc04 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/CommonCargoServiceImpl.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/CommonCargoServiceImpl.java
@@ -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;
@@ -143,6 +144,10 @@ public class CommonCargoServiceImpl extends BaseServiceImpl failureList = new ArrayList<>();
for (int index = 0; index < data.size(); index++) {
CommonCargoExcel excel = data.get(index);
@@ -170,12 +175,21 @@ public class CommonCargoServiceImpl extends BaseServiceImpl 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
errorList.add(excel);
}
}
+ if (Func.isNotEmpty(errorList)) {
+ // 抛出携带失败明细的异常,触发事务回滚:submit 内部一次写运单主表+
+ // 货物+配载路线等多张关联表,回滚边界必须覆盖全部关联表。
+ // 注意不能只往 errorList 里塞数据就 return——异常被吞掉后方法会正常返回,
+ // Spring 会照常提交事务,@Transactional 起不到回滚作用。
+ throw new org.springblade.common.excel.ImportFailureException(data);
+ }
return errorList;
}