Merge remote-tracking branch 'refs/remotes/origin/master' into feature/payment-apply-bfi-sync
# Conflicts: # blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/CustomerArchiveServiceImpl.java
This commit is contained in:
+14
-3
@@ -157,10 +157,21 @@ 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;
|
||||
} catch (Exception readException) {
|
||||
// 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时,
|
||||
// EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。
|
||||
// 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。
|
||||
if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure(
|
||||
response, "机场主数据导入失败明细" + DateUtil.time(), "导入失败明细", file, AirportMasterExcel.class, readException)) {
|
||||
return null;
|
||||
}
|
||||
throw readException;
|
||||
}
|
||||
return R.success("操作成功");
|
||||
}
|
||||
|
||||
+31
-5
@@ -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,41 @@ public class CargoTypeController extends BladeController {
|
||||
@ApiOperationSupport(order = 7)
|
||||
@Operation(summary = "导入货物类型", description = "传入excel")
|
||||
public R importCargoType(MultipartFile file, HttpServletResponse response) {
|
||||
List<CargoTypeExcel> data = ExcelUtil.read(file, CargoTypeExcel.class);
|
||||
List<CargoTypeImportFailureExcel> 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;
|
||||
} catch (Exception readException) {
|
||||
// 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时,
|
||||
// EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。
|
||||
// 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。
|
||||
if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure(
|
||||
response, "货物类型导入失败明细" + DateUtil.time(), "导入失败明细", file, CargoTypeExcel.class, readException)) {
|
||||
return null;
|
||||
}
|
||||
throw readException;
|
||||
}
|
||||
return R.success("导入数据成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出导入失败明细,内容为原表全部数据并在末尾追加失败原因列。
|
||||
* <p>
|
||||
* 失败数据仅标红出错单元格与失败原因列,表头保持默认样式。
|
||||
*/
|
||||
private void exportFailure(HttpServletResponse response, List<?> failureList) {
|
||||
ImportFailureExcelUtil.exportFailureReasonOnly(response, "货物类型导入失败明细" + DateUtil.time(), "导入失败明细", failureList, CargoTypeExcel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出货物类型
|
||||
*/
|
||||
|
||||
+21
-3
@@ -149,10 +149,28 @@ public class CurrencyController extends BladeController {
|
||||
@ApiOperationSupport(order = 7)
|
||||
@Operation(summary = "导入币种汇率", description = "传入excel")
|
||||
public R importCurrency(MultipartFile file, HttpServletResponse response) {
|
||||
List<CurrencyExcel> 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;
|
||||
} catch (Exception readException) {
|
||||
// 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时,
|
||||
// EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。
|
||||
// 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。
|
||||
if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure(
|
||||
response, "币种汇率导入失败明细" + DateUtil.time(), "导入失败明细", file, CurrencyExcel.class, readException)) {
|
||||
return null;
|
||||
}
|
||||
throw readException;
|
||||
}
|
||||
return R.success("操作成功");
|
||||
}
|
||||
|
||||
+2
-3
@@ -44,11 +44,10 @@ import org.springblade.core.tenant.annotation.NonDS;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.support.Kv;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.cache.DictCache;
|
||||
import org.springblade.system.cache.DictBizCache;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.system.pojo.entity.Dept;
|
||||
import org.springblade.system.pojo.entity.User;
|
||||
import org.springblade.system.pojo.enums.DictEnum;
|
||||
import org.springblade.system.pojo.vo.DeptVO;
|
||||
import org.springblade.system.pojo.vo.OaOrgSyncPageVO;
|
||||
import org.springblade.system.pojo.vo.UserVO;
|
||||
@@ -156,7 +155,7 @@ public class DeptController extends BladeController {
|
||||
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
|
||||
// 返回懒加载树更新节点所需字段
|
||||
Kv kv = Kv.create().set("id", String.valueOf(dept.getId())).set("tenantId", dept.getTenantId())
|
||||
.set("deptCategoryName", DictCache.getValue(DictEnum.ORG_CATEGORY, dept.getDeptCategory()));
|
||||
.set("deptCategoryName", DictBizCache.getValue("organization_type", dept.getDeptCategory()));
|
||||
return R.data(kv);
|
||||
}
|
||||
return R.fail("操作失败");
|
||||
|
||||
+22
-5
@@ -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,11 +138,28 @@ public class FeeItemController extends BladeController {
|
||||
@ApiOperationSupport(order = 6)
|
||||
@Operation(summary = "导入费用项", description = "传入excel")
|
||||
public R importFeeItem(MultipartFile file, HttpServletResponse response) {
|
||||
List<FeeItemExcel> data = ExcelUtil.read(file, FeeItemExcel.class);
|
||||
List<FeeItemImportFailureExcel> 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;
|
||||
} catch (Exception readException) {
|
||||
// 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时,
|
||||
// EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。
|
||||
// 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。
|
||||
if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure(
|
||||
response, "费用项导入失败明细" + DateUtil.time(), "导入失败明细", file, FeeItemExcel.class, readException)) {
|
||||
return null;
|
||||
}
|
||||
throw readException;
|
||||
}
|
||||
return R.success("导入数据成功");
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+14
-3
@@ -160,10 +160,21 @@ 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;
|
||||
} catch (Exception readException) {
|
||||
// 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时,
|
||||
// EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。
|
||||
// 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。
|
||||
if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure(
|
||||
response, "铁路站点导入失败明细" + DateUtil.time(), "导入失败明细", file, RailwayStationExcel.class, readException)) {
|
||||
return null;
|
||||
}
|
||||
throw readException;
|
||||
}
|
||||
return R.success("操作成功");
|
||||
}
|
||||
|
||||
+22
-3
@@ -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,10 +192,28 @@ public class RegionController extends BladeController {
|
||||
@ApiOperationSupport(order = 10)
|
||||
@Operation(summary = "导入行政区划", description = "传入excel")
|
||||
public R importRegion(MultipartFile file, Integer isCovered, HttpServletResponse response) {
|
||||
List<RegionExcel> 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;
|
||||
} catch (Exception readException) {
|
||||
// 读取阶段异常兜底:单元格内容无法转换(如金额列填「一万二千元」)时,
|
||||
// EasyExcel 在 ExcelUtil.read 阶段即抛异常,走不到行级校验。
|
||||
// 此处用原始文本重读原表,导出「全量行 + 出错行原因」,避免用户看到 500 技术文案。
|
||||
if (org.springblade.common.excel.ImportFailureExcelUtil.exportReadFailure(
|
||||
response, "行政区划导入失败明细" + DateUtil.time(), "导入失败明细", file, RegionExcel.class, readException)) {
|
||||
return null;
|
||||
}
|
||||
throw readException;
|
||||
}
|
||||
return R.success("操作成功");
|
||||
}
|
||||
|
||||
+3
@@ -102,6 +102,9 @@ public class AirportMasterExportExcel implements Serializable {
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty("创建人")
|
||||
private String createUserName;
|
||||
|
||||
@ExcelProperty("更新人")
|
||||
private String updateUserName;
|
||||
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ public class CurrencyExcel implements Serializable {
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty
|
||||
@ExcelIgnore
|
||||
private String errorMessage;
|
||||
|
||||
}
|
||||
|
||||
+3
@@ -50,6 +50,9 @@ public class FeeItemExportExcel implements Serializable {
|
||||
@ExcelProperty("组织")
|
||||
private String createDeptName;
|
||||
|
||||
@ExcelProperty("创建人")
|
||||
private String createUserName;
|
||||
|
||||
@ExcelProperty("更新人")
|
||||
private String updateUserName;
|
||||
|
||||
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.excel;
|
||||
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 导入失败异常,携带失败明细用于导出原表并标注错误。
|
||||
* <p>
|
||||
* 批量导入采用「全失败即整批回滚」语义:任一行校验失败都会抛出本异常触发事务回滚,
|
||||
* 失败明细在抛异常前已收集完毕,因此回滚不影响明细的内容。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class ImportFailureException extends ServiceException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 失败明细,包含原表全部数据,错误行已标注错误原因。
|
||||
*/
|
||||
private final transient List<?> failureList;
|
||||
|
||||
public ImportFailureException(List<?> failureList) {
|
||||
super("导入失败,已回滚全部数据");
|
||||
this.failureList = failureList;
|
||||
}
|
||||
|
||||
public List<?> getFailureList() {
|
||||
return failureList;
|
||||
}
|
||||
|
||||
}
|
||||
+3
@@ -80,6 +80,9 @@ public class PortTerminalExportExcel implements Serializable {
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty("创建人")
|
||||
private String createUserName;
|
||||
|
||||
@ExcelProperty("更新人")
|
||||
private String updateUserName;
|
||||
|
||||
|
||||
+3
@@ -92,6 +92,9 @@ public class RailwayStationExportExcel implements Serializable {
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty("创建人")
|
||||
private String createUserName;
|
||||
|
||||
@ExcelProperty("更新人")
|
||||
private String updateUserName;
|
||||
|
||||
|
||||
+13
@@ -26,6 +26,7 @@
|
||||
package org.springblade.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springblade.system.pojo.entity.Dept;
|
||||
import org.springblade.system.pojo.vo.DeptVO;
|
||||
|
||||
@@ -66,6 +67,18 @@ public interface DeptMapper extends BaseMapper<Dept> {
|
||||
*/
|
||||
List<DeptVO> lazyTree(String tenantId, Long parentId);
|
||||
|
||||
/**
|
||||
* 按组织 ID 集合查询树节点(用于数据权限过滤后的根节点回显)
|
||||
*
|
||||
* @param tenantId 租户ID,空表示不限
|
||||
* @param ids 需要返回的组织 ID
|
||||
* @param childIds 计算 hasChildren 时允许计入的子组织 ID;为空则 hasChildren 恒为 false
|
||||
* @return 树节点列表
|
||||
*/
|
||||
List<DeptVO> treeNodesByIds(@Param("tenantId") String tenantId,
|
||||
@Param("ids") List<Long> ids,
|
||||
@Param("childIds") List<Long> childIds);
|
||||
|
||||
/**
|
||||
* 获取部门名
|
||||
*
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<result column="leader_id" property="leaderId"/>
|
||||
<result column="dept_category" property="deptCategory"/>
|
||||
<result column="dept_code" property="deptCode"/>
|
||||
<result column="kingdee_dept_code" property="kingdeeDeptCode"/>
|
||||
<result column="short_name" property="shortName"/>
|
||||
<result column="pinyin_mnemonic" property="pinyinMnemonic"/>
|
||||
<result column="mnemonic_code" property="mnemonicCode"/>
|
||||
@@ -32,6 +33,7 @@
|
||||
<result column="leader_id" property="leaderId"/>
|
||||
<result column="dept_category" property="deptCategory"/>
|
||||
<result column="dept_code" property="deptCode"/>
|
||||
<result column="kingdee_dept_code" property="kingdeeDeptCode"/>
|
||||
<result column="short_name" property="shortName"/>
|
||||
<result column="pinyin_mnemonic" property="pinyinMnemonic"/>
|
||||
<result column="mnemonic_code" property="mnemonicCode"/>
|
||||
@@ -115,6 +117,47 @@
|
||||
ORDER BY dept.sort
|
||||
</select>
|
||||
|
||||
<select id="treeNodesByIds" resultMap="treeNodeResultMap">
|
||||
SELECT
|
||||
dept.id,
|
||||
dept.parent_id,
|
||||
dept.dept_name AS title,
|
||||
dept.id AS "value",
|
||||
dept.id AS "key",
|
||||
(
|
||||
SELECT
|
||||
CASE WHEN count(1) > 0 THEN 1 ELSE 0 END
|
||||
FROM
|
||||
blade_dept child
|
||||
WHERE
|
||||
child.parent_id = dept.id
|
||||
AND child.is_deleted = 0
|
||||
<choose>
|
||||
<when test="childIds != null and childIds.size() > 0">
|
||||
AND child.id IN
|
||||
<foreach collection="childIds" item="childId" open="(" close=")" separator=",">
|
||||
#{childId}
|
||||
</foreach>
|
||||
</when>
|
||||
<otherwise>
|
||||
AND 1 = 0
|
||||
</otherwise>
|
||||
</choose>
|
||||
) AS "has_children"
|
||||
FROM
|
||||
blade_dept dept
|
||||
WHERE
|
||||
dept.is_deleted = 0
|
||||
AND dept.id IN
|
||||
<foreach collection="ids" item="id" open="(" close=")" separator=",">
|
||||
#{id}
|
||||
</foreach>
|
||||
<if test="tenantId != null and tenantId != ''">
|
||||
AND dept.tenant_id = #{tenantId}
|
||||
</if>
|
||||
ORDER BY dept.sort, dept.id
|
||||
</select>
|
||||
|
||||
<select id="getDeptNames" resultType="java.lang.String">
|
||||
SELECT
|
||||
dept_name
|
||||
|
||||
+5
-3
@@ -68,10 +68,10 @@ public interface IDeptService extends IService<Dept> {
|
||||
List<DeptVO> lazyList(String tenantId, Long parentId, Map<String, Object> param);
|
||||
|
||||
/**
|
||||
* 树形结构(含超管判定 + 当前租户隔离)
|
||||
* 树形结构(含超管判定 + 当前租户隔离 + 用户组织数据权限)
|
||||
* <p>
|
||||
* 仅超级管理员可指定任意 tenantId 查询;其他用户传入的 tenantId 一律被忽略,
|
||||
* 强制使用当前会话租户。
|
||||
* 强制使用当前会话租户,并按用户「数据权限范围」「数据层级范围」过滤可见组织。
|
||||
*
|
||||
* @param tenantId 入参 tenantId(仅超管生效)
|
||||
* @return 部门树
|
||||
@@ -79,9 +79,11 @@ public interface IDeptService extends IService<Dept> {
|
||||
List<DeptVO> tree(String tenantId);
|
||||
|
||||
/**
|
||||
* 懒加载树形结构(含超管判定 + 当前租户隔离)
|
||||
* 懒加载树形结构(含超管判定 + 当前租户隔离 + 用户组织数据权限)
|
||||
* <p>
|
||||
* 仅超级管理员可指定任意 tenantId,空值表示查询全部;其他用户强制使用当前会话租户。
|
||||
* 非超管按用户「数据权限范围」「数据层级范围」过滤可见组织:
|
||||
* 仅所属组织 / 自定义组织,并可选择是否包含无限下级。
|
||||
*
|
||||
* @param tenantId 入参 tenantId(仅超管生效)
|
||||
* @param parentId 父级部门ID
|
||||
|
||||
+5
-3
@@ -127,7 +127,7 @@ public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMappe
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<AirportMasterExcel> importAirportMaster(List<AirportMasterExcel> data) {
|
||||
if (Func.isEmpty(data)) {
|
||||
throw new ServiceException("导入数据不能为空");
|
||||
throw new ServiceException("未读取到数据行,请确认表头下方已填写内容");
|
||||
}
|
||||
List<AirportMasterExcel> errorList = new ArrayList<>();
|
||||
List<AirportMaster> airportMasterList = new ArrayList<>();
|
||||
@@ -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) {
|
||||
// 编码命中逻辑删除记录时复用原主键,恢复后更新,避免唯一索引冲突。
|
||||
@@ -322,6 +323,7 @@ public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMappe
|
||||
excel.setLatitude(scaleCoordinate(airportMaster.getLatitude(), MIN_LATITUDE, MAX_LATITUDE));
|
||||
excel.setDataSource(normalizeDataSource(airportMaster.getDataSource()));
|
||||
excel.setStatusName(Objects.equals(airportMaster.getStatus(), STATUS_ENABLED) ? "启用" : "停用");
|
||||
excel.setCreateUserName(UserCache.getUserRealName(airportMaster.getCreateUser()));
|
||||
excel.setUpdateUserName(UserCache.getUserRealName(airportMaster.getUpdateUser()));
|
||||
return excel;
|
||||
}).toList();
|
||||
|
||||
+11
-1
@@ -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<CargoTypeMapper, Cargo
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<CargoTypeImportFailureExcel> importCargoType(List<CargoTypeExcel> data) {
|
||||
if (Func.isEmpty(data)) {
|
||||
throw new ServiceException("导入数据不能为空");
|
||||
throw new ServiceException("未读取到数据行,请确认表头下方已填写内容");
|
||||
}
|
||||
// 全量校验:任何一行失败都整批回滚,因此先收集所有错误再统一抛出。
|
||||
// 注意不能只往 errorList 里塞数据就 return——异常被吞掉后方法会正常返回,
|
||||
// Spring 会照常提交事务,@Transactional 起不到回滚作用。
|
||||
List<CargoTypeImportFailureExcel> 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<CargoTypeMapper, Cargo
|
||||
failureList.add(toImportFailureExcel(excel, message));
|
||||
}
|
||||
}
|
||||
if (Func.isNotEmpty(failureList)) {
|
||||
// 抛出携带失败明细的异常,触发事务回滚。
|
||||
// 明细为原表全部行(未出错行仅无错误原因),用户可对照原表修正后重新导入。
|
||||
throw new ImportFailureException(data);
|
||||
}
|
||||
return failureList;
|
||||
}
|
||||
|
||||
|
||||
+12
-1
@@ -115,10 +115,16 @@ public class CurrencyServiceImpl extends BaseServiceImpl<CurrencyMapper, Currenc
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<CurrencyExcel> importCurrency(List<CurrencyExcel> data) {
|
||||
if (Func.isEmpty(data)) {
|
||||
throw new ServiceException("导入数据不能为空");
|
||||
throw new ServiceException("未读取到数据行,请确认表头下方已填写内容");
|
||||
}
|
||||
// 全量校验:任何一行失败都整批回滚,因此先收集所有错误再统一抛出。
|
||||
// 注意:循环内除 save 外还有 rebuildEnabledValidity 这个派生写——
|
||||
// 若吞掉异常正常 return,主表数据和派生的有效期区间都会落库;
|
||||
// 必须抛异常让两者一起回滚,否则会出现「主表回滚了、有效期区间
|
||||
// 却按失败批次算过」的脏数据。
|
||||
List<CurrencyExcel> errorList = new ArrayList<>();
|
||||
int successCount = 0;
|
||||
for (int index = 0; index < data.size(); index++) {
|
||||
@@ -140,6 +146,11 @@ public class CurrencyServiceImpl extends BaseServiceImpl<CurrencyMapper, Currenc
|
||||
errorList.add(excel);
|
||||
}
|
||||
}
|
||||
if (Func.isNotEmpty(errorList)) {
|
||||
// 抛出携带失败明细的异常,触发事务回滚(含 rebuildEnabledValidity 的派生写)。
|
||||
// 明细为原表全部行(未出错行仅无错误原因),用户可对照原表修正后重新导入。
|
||||
throw new org.springblade.common.excel.ImportFailureException(data);
|
||||
}
|
||||
return errorList;
|
||||
}
|
||||
|
||||
|
||||
+154
-2
@@ -44,8 +44,10 @@ import org.springblade.core.tool.node.ForestNodeMerger;
|
||||
import org.springblade.core.tool.utils.*;
|
||||
import org.springblade.system.cache.SysCache;
|
||||
import org.springblade.system.mapper.DeptMapper;
|
||||
import org.springblade.system.mapper.UserDataScopeMapper;
|
||||
import org.springblade.system.pojo.entity.Dept;
|
||||
import org.springblade.system.pojo.entity.User;
|
||||
import org.springblade.system.pojo.entity.UserDataScope;
|
||||
import org.springblade.system.pojo.enums.DeptCategory;
|
||||
import org.springblade.system.pojo.vo.DeptVO;
|
||||
import org.springblade.system.pojo.vo.UserVO;
|
||||
@@ -90,8 +92,19 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
private static final String IAM_SYNC_TENANT_ID = "000000";
|
||||
private static final Long IAM_SYNC_PARENT_ID = 1123598813738675201L;
|
||||
private static final HttpClient IAM_HTTP_CLIENT = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
|
||||
/** 数据权限范围:仅所属组织 */
|
||||
private static final int DATA_SCOPE_OWN = 1;
|
||||
/** 数据权限范围:全部组织 */
|
||||
private static final int DATA_SCOPE_ALL = 2;
|
||||
/** 数据权限范围:自定义 */
|
||||
private static final int DATA_SCOPE_CUSTOM = 3;
|
||||
/** 数据层级范围:包含下级 */
|
||||
private static final int DATA_LEVEL_INCLUDE_CHILDREN = 1;
|
||||
/** 数据层级范围:仅本级 */
|
||||
private static final int DATA_LEVEL_CURRENT_ONLY = 2;
|
||||
|
||||
private final IUserService userService;
|
||||
private final UserDataScopeMapper userDataScopeMapper;
|
||||
private final IamSyncProperties iamSyncProperties;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@@ -144,7 +157,11 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
String resolvedTenantId = AuthUtil.isAdministrator()
|
||||
? Func.toStrWithEmpty(tenantId, AuthUtil.getTenantId())
|
||||
: AuthUtil.getTenantId();
|
||||
return ForestNodeMerger.merge(baseMapper.tree(resolvedTenantId));
|
||||
DeptDataScopeFilter scopeFilter = resolveDeptDataScopeFilter();
|
||||
if (scopeFilter == null || scopeFilter.allDept()) {
|
||||
return ForestNodeMerger.merge(baseMapper.tree(resolvedTenantId));
|
||||
}
|
||||
return buildScopedDeptTree(resolvedTenantId, scopeFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -153,7 +170,142 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
String resolvedTenantId = AuthUtil.isAdministrator()
|
||||
? Func.toStr(tenantId, StringPool.EMPTY)
|
||||
: AuthUtil.getTenantId();
|
||||
return ForestNodeMerger.merge(baseMapper.lazyTree(resolvedTenantId, parentId));
|
||||
Long resolvedParentId = parentId == null ? BladeConstant.TOP_PARENT_ID : parentId;
|
||||
DeptDataScopeFilter scopeFilter = resolveDeptDataScopeFilter();
|
||||
if (scopeFilter == null || scopeFilter.allDept()) {
|
||||
return ForestNodeMerger.merge(baseMapper.lazyTree(resolvedTenantId, resolvedParentId));
|
||||
}
|
||||
Set<Long> allowedIds = scopeFilter.allowedDeptIds();
|
||||
if (Func.isEmpty(allowedIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
boolean includeChildren = scopeFilter.includeChildren();
|
||||
List<Long> childIdsForHasChildren = includeChildren ? new ArrayList<>(allowedIds) : Collections.emptyList();
|
||||
// 根层:返回「可见根」——权限集合中父节点不在集合内的组织,避免用户看不到深层权限节点。
|
||||
if (Objects.equals(resolvedParentId, BladeConstant.TOP_PARENT_ID)) {
|
||||
List<Long> visibleRootIds = resolveVisibleRootIds(allowedIds);
|
||||
if (Func.isEmpty(visibleRootIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return ForestNodeMerger.merge(baseMapper.treeNodesByIds(resolvedTenantId, visibleRootIds, childIdsForHasChildren));
|
||||
}
|
||||
// 非根层:无权限的父节点不允许展开;仅本级时不允许继续下钻。
|
||||
if (!includeChildren || !allowedIds.contains(resolvedParentId)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<DeptVO> children = baseMapper.lazyTree(resolvedTenantId, resolvedParentId);
|
||||
List<Long> filteredChildIds = children.stream()
|
||||
.map(DeptVO::getId)
|
||||
.filter(allowedIds::contains)
|
||||
.toList();
|
||||
if (Func.isEmpty(filteredChildIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return ForestNodeMerger.merge(baseMapper.treeNodesByIds(resolvedTenantId, filteredChildIds, childIdsForHasChildren));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按数据权限构建完整组织树(用于 /dept/tree)。
|
||||
*/
|
||||
private List<DeptVO> buildScopedDeptTree(String tenantId, DeptDataScopeFilter scopeFilter) {
|
||||
Set<Long> allowedIds = scopeFilter.allowedDeptIds();
|
||||
if (Func.isEmpty(allowedIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Long> childIdsForHasChildren = scopeFilter.includeChildren()
|
||||
? new ArrayList<>(allowedIds)
|
||||
: Collections.emptyList();
|
||||
return ForestNodeMerger.merge(baseMapper.treeNodesByIds(tenantId, new ArrayList<>(allowedIds), childIdsForHasChildren));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算懒加载首层可见根:父级不在权限集合内的组织。
|
||||
*/
|
||||
private List<Long> resolveVisibleRootIds(Set<Long> allowedIds) {
|
||||
return listByIds(allowedIds).stream()
|
||||
.filter(dept -> dept.getParentId() == null
|
||||
|| Objects.equals(dept.getParentId(), BladeConstant.TOP_PARENT_ID)
|
||||
|| !allowedIds.contains(dept.getParentId()))
|
||||
.sorted(Comparator.comparing(Dept::getSort, Comparator.nullsLast(Integer::compareTo))
|
||||
.thenComparing(Dept::getId, Comparator.nullsLast(Long::compareTo)))
|
||||
.map(Dept::getId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析当前登录用户的组织树数据权限。
|
||||
* <p>
|
||||
* 超管或「全部组织」返回 allDept=true;其余按所属组织/自定义组织,并结合「包含下级/仅本级」展开。
|
||||
*
|
||||
* @return 过滤上下文;无法识别用户时按空权限处理
|
||||
*/
|
||||
private DeptDataScopeFilter resolveDeptDataScopeFilter() {
|
||||
if (AuthUtil.isAdministrator()) {
|
||||
return DeptDataScopeFilter.all();
|
||||
}
|
||||
Long userId = AuthUtil.getUserId();
|
||||
if (Func.isEmpty(userId) || userId <= 0) {
|
||||
return DeptDataScopeFilter.empty();
|
||||
}
|
||||
User user = userService.getById(userId);
|
||||
if (Func.isEmpty(user)) {
|
||||
return DeptDataScopeFilter.empty();
|
||||
}
|
||||
Integer dataScopeRange = user.getDataScopeRange() == null ? DATA_SCOPE_OWN : user.getDataScopeRange();
|
||||
Integer dataLevelRange = user.getDataLevelRange() == null ? DATA_LEVEL_INCLUDE_CHILDREN : user.getDataLevelRange();
|
||||
if (Objects.equals(dataScopeRange, DATA_SCOPE_ALL)) {
|
||||
return DeptDataScopeFilter.all();
|
||||
}
|
||||
List<Long> baseDeptIds;
|
||||
if (Objects.equals(dataScopeRange, DATA_SCOPE_CUSTOM)) {
|
||||
baseDeptIds = userDataScopeMapper.selectList(Wrappers.<UserDataScope>lambdaQuery()
|
||||
.eq(UserDataScope::getUserId, userId))
|
||||
.stream()
|
||||
.map(UserDataScope::getDeptId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.toList();
|
||||
} else {
|
||||
// 仅所属组织:优先用户档案中的所属组织,其次当前会话组织。
|
||||
baseDeptIds = Func.toLongList(user.getDeptId());
|
||||
if (Func.isEmpty(baseDeptIds)) {
|
||||
baseDeptIds = Func.toLongList(AuthUtil.getDeptId());
|
||||
}
|
||||
}
|
||||
if (Func.isEmpty(baseDeptIds)) {
|
||||
return DeptDataScopeFilter.empty();
|
||||
}
|
||||
boolean includeChildren = !Objects.equals(dataLevelRange, DATA_LEVEL_CURRENT_ONLY);
|
||||
LinkedHashSet<Long> allowedDeptIds = new LinkedHashSet<>();
|
||||
for (Long deptId : baseDeptIds) {
|
||||
if (Func.isEmpty(deptId) || deptId <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (includeChildren) {
|
||||
List<Long> childIds = SysCache.getDeptChildIds(deptId);
|
||||
if (Func.isNotEmpty(childIds)) {
|
||||
allowedDeptIds.addAll(childIds);
|
||||
} else {
|
||||
allowedDeptIds.add(deptId);
|
||||
}
|
||||
} else {
|
||||
allowedDeptIds.add(deptId);
|
||||
}
|
||||
}
|
||||
return new DeptDataScopeFilter(false, includeChildren, allowedDeptIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 组织懒加载树的数据权限过滤上下文。
|
||||
*/
|
||||
private record DeptDataScopeFilter(boolean allDept, boolean includeChildren, Set<Long> allowedDeptIds) {
|
||||
private static DeptDataScopeFilter all() {
|
||||
return new DeptDataScopeFilter(true, true, Collections.emptySet());
|
||||
}
|
||||
|
||||
private static DeptDataScopeFilter empty() {
|
||||
return new DeptDataScopeFilter(false, false, Collections.emptySet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+14
-1
@@ -106,8 +106,11 @@ public class FeeItemServiceImpl extends BaseServiceImpl<FeeItemMapper, FeeItem>
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<FeeItemImportFailureExcel> importFeeItem(List<FeeItemExcel> data) {
|
||||
if (Func.isEmpty(data)) {
|
||||
throw new ServiceException("导入数据不能为空");
|
||||
throw new ServiceException("未读取到数据行,请确认表头下方已填写内容");
|
||||
}
|
||||
// 全量校验:任何一行失败都整批回滚,因此先收集所有错误再统一抛出。
|
||||
// 注意不能只往 failureList 里塞数据就 return——异常被吞掉后方法会正常返回,
|
||||
// Spring 会照常提交事务,@Transactional 起不到回滚作用。
|
||||
List<FeeItemImportFailureExcel> 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<FeeItemMapper, FeeItem>
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -216,6 +228,7 @@ public class FeeItemServiceImpl extends BaseServiceImpl<FeeItemMapper, FeeItem>
|
||||
excel.setCreateDeptName(
|
||||
Func.isEmpty(feeItem.getCreateDept()) ? "" : SysCache.getDeptName(feeItem.getCreateDept())
|
||||
);
|
||||
excel.setCreateUserName(UserCache.getUserRealName(feeItem.getCreateUser()));
|
||||
excel.setUpdateUserName(UserCache.getUserRealName(feeItem.getUpdateUser()));
|
||||
excel.setStatusName(Objects.equals(feeItem.getStatus(), STATUS_ENABLED) ? "启用" : "停用");
|
||||
return excel;
|
||||
|
||||
+3
-2
@@ -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;
|
||||
@@ -140,7 +140,7 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<PortTerminalExcel> importPortTerminal(List<PortTerminalExcel> data) {
|
||||
if (Func.isEmpty(data)) {
|
||||
throw new ServiceException("导入数据不能为空");
|
||||
throw new ServiceException("未读取到数据行,请确认表头下方已填写内容");
|
||||
}
|
||||
// 全量校验:任何一行失败都整批回滚,因此先收集所有错误再统一抛出。
|
||||
List<PortTerminalExcel> failureList = new ArrayList<>();
|
||||
@@ -357,6 +357,7 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
excel.setLatitude(scaleCoordinate(portTerminal.getLatitude(), MIN_LATITUDE, MAX_LATITUDE));
|
||||
excel.setDataSource(normalizeDataSource(portTerminal.getDataSource()));
|
||||
excel.setStatusName(Objects.equals(portTerminal.getStatus(), STATUS_ENABLED) ? "启用" : "停用");
|
||||
excel.setCreateUserName(UserCache.getUserRealName(portTerminal.getCreateUser()));
|
||||
excel.setUpdateUserName(UserCache.getUserRealName(portTerminal.getUpdateUser()));
|
||||
return excel;
|
||||
}).toList();
|
||||
|
||||
+5
-3
@@ -129,7 +129,7 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<RailwayStationExcel> importRailwayStation(List<RailwayStationExcel> data) {
|
||||
if (Func.isEmpty(data)) {
|
||||
throw new ServiceException("导入数据不能为空");
|
||||
throw new ServiceException("未读取到数据行,请确认表头下方已填写内容");
|
||||
}
|
||||
List<RailwayStationExcel> errorList = new ArrayList<>();
|
||||
List<RailwayStation> railwayStationList = new ArrayList<>();
|
||||
@@ -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);
|
||||
@@ -348,6 +349,7 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
|
||||
excel.setLatitude(formatCoordinate(railwayStation.getLatitude(), MIN_LATITUDE, MAX_LATITUDE));
|
||||
excel.setDataSource(normalizeDataSource(railwayStation.getDataSource()));
|
||||
excel.setStatusName(Objects.equals(railwayStation.getStatus(), STATUS_ENABLED) ? "启用" : "停用");
|
||||
excel.setCreateUserName(UserCache.getUserRealName(railwayStation.getCreateUser()));
|
||||
excel.setUpdateUserName(UserCache.getUserRealName(railwayStation.getUpdateUser()));
|
||||
return excel;
|
||||
}).toList();
|
||||
|
||||
+29
-6
@@ -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<RegionMapper, Region> impleme
|
||||
|
||||
@Override
|
||||
public boolean submit(Region region) {
|
||||
return submit(region, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交区划,clearCache 控制是否在保存成功后立即清理惰性树缓存。
|
||||
* <p>
|
||||
* 批量导入路径传 false:导入内部会收集全部错误,只有整批成功才统一清缓存;
|
||||
* 若逐行清缓存,失败批次中已成功行会把缓存清掉/重算成中间状态,
|
||||
* 与随后的事务回滚形成「库回滚了、缓存却是脏的」。
|
||||
*/
|
||||
private boolean submit(Region region, boolean clearCache) {
|
||||
Date now = new Date();
|
||||
Long currentUserId = AuthUtil.getUserId();
|
||||
boolean isNew = StringUtil.isBlank(region.getOriginalCode());
|
||||
@@ -98,6 +110,9 @@ public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> impleme
|
||||
validateUniqueCode(region);
|
||||
// 设置祖区划编号
|
||||
Region parent = getByCode(region.getParentCode());
|
||||
if (level != null && level > COUNTRY_LEVEL && (Func.isEmpty(parent) || Func.isEmpty(parent.getCode()))) {
|
||||
throw new ServiceException("父区划不存在,请重新选择父区划");
|
||||
}
|
||||
if (Func.isNotEmpty(parent) && Func.isNotEmpty(parent.getCode())) {
|
||||
String ancestors = parent.getRegionLevel() != null && parent.getRegionLevel() == COUNTRY_LEVEL
|
||||
? parent.getCode()
|
||||
@@ -138,7 +153,7 @@ public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> 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 +204,15 @@ public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<RegionExcel> importRegion(List<RegionExcel> data, Boolean isCovered) {
|
||||
if (Func.isEmpty(data)) {
|
||||
throw new ServiceException("导入数据不能为空");
|
||||
throw new ServiceException("未读取到数据行,请确认表头下方已填写内容");
|
||||
}
|
||||
// 全量校验:任何一行失败都整批回滚,因此先收集所有错误再统一抛出。
|
||||
// 注意不能只往 errorList 里塞数据就 return——异常被吞掉后方法会正常返回,
|
||||
// Spring 会照常提交事务,@Transactional 起不到回滚作用。
|
||||
List<RegionExcel> errorList = new ArrayList<>();
|
||||
boolean cacheChanged = false;
|
||||
for (int index = 0; index < data.size(); index++) {
|
||||
RegionExcel excel = data.get(index);
|
||||
try {
|
||||
@@ -210,15 +228,20 @@ public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> 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;
|
||||
}
|
||||
|
||||
|
||||
+12
-12
@@ -1130,22 +1130,22 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
|
||||
|
||||
@Override
|
||||
public void decryptPhone(IPhone user) {
|
||||
// if (user == null) {
|
||||
// return;
|
||||
// }
|
||||
// if (user.getPhone() != null && user.getPhone().length() > 20) {
|
||||
// user.setPhone(SM2Util.decrypt(user.getPhone(), properties.getPublicKey(), properties.getPrivateKey()));
|
||||
// }
|
||||
if (user == null) {
|
||||
return;
|
||||
}
|
||||
if (user.getPhone() != null && user.getPhone().length() > 20) {
|
||||
user.setPhone(SM2Util.decrypt(user.getPhone(), properties.getPublicKey(), properties.getPrivateKey()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encryptPhone(IPhone user) {
|
||||
// if (user == null) {
|
||||
// return;
|
||||
// }
|
||||
// if(StringUtil.isNotBlank(user.getPhone())) {
|
||||
// user.setPhone(Hex.toHexString(SM2Util.encrypt(user.getPhone(),properties.getPublicKey())));
|
||||
// }
|
||||
if (user == null) {
|
||||
return;
|
||||
}
|
||||
if (StringUtil.isNotBlank(user.getPhone())) {
|
||||
user.setPhone(Hex.toHexString(SM2Util.encrypt(user.getPhone(), properties.getPublicKey())));
|
||||
}
|
||||
}
|
||||
|
||||
private void validatePlainPassword(String password) {
|
||||
|
||||
+1
@@ -48,6 +48,7 @@ public class AirportMasterWrapper extends BaseEntityWrapper<AirportMaster, Airpo
|
||||
public AirportMasterVO entityVO(AirportMaster airportMaster) {
|
||||
AirportMasterVO airportMasterVO = Objects.requireNonNull(BeanUtil.copyProperties(airportMaster, AirportMasterVO.class));
|
||||
airportMasterVO.setRegionCode(airportMaster.getDistrictCode());
|
||||
airportMasterVO.setCreateUserName(UserCache.getUserRealName(airportMaster.getCreateUser()));
|
||||
airportMasterVO.setUpdateUserName(UserCache.getUserRealName(airportMaster.getUpdateUser()));
|
||||
return airportMasterVO;
|
||||
}
|
||||
|
||||
+9
-5
@@ -30,10 +30,9 @@ import org.springblade.core.tool.constant.BladeConstant;
|
||||
import org.springblade.core.tool.node.ForestNodeMerger;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.cache.DictCache;
|
||||
import org.springblade.system.cache.DictBizCache;
|
||||
import org.springblade.system.cache.SysCache;
|
||||
import org.springblade.system.pojo.entity.Dept;
|
||||
import org.springblade.system.pojo.enums.DictEnum;
|
||||
import org.springblade.system.pojo.vo.DeptVO;
|
||||
|
||||
import java.util.List;
|
||||
@@ -47,6 +46,11 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public class DeptWrapper extends BaseEntityWrapper<Dept, DeptVO> {
|
||||
|
||||
/**
|
||||
* 组织类型业务字典编码
|
||||
*/
|
||||
private static final String ORGANIZATION_TYPE_CODE = "organization_type";
|
||||
|
||||
public static DeptWrapper build() {
|
||||
return new DeptWrapper();
|
||||
}
|
||||
@@ -60,7 +64,7 @@ public class DeptWrapper extends BaseEntityWrapper<Dept, DeptVO> {
|
||||
Dept parent = SysCache.getDept(dept.getParentId());
|
||||
deptVO.setParentName(parent.getDeptName());
|
||||
}
|
||||
String category = DictCache.getValue(DictEnum.ORG_CATEGORY, dept.getDeptCategory());
|
||||
String category = DictBizCache.getValue(ORGANIZATION_TYPE_CODE, dept.getDeptCategory());
|
||||
deptVO.setDeptCategoryName(category);
|
||||
return deptVO;
|
||||
}
|
||||
@@ -69,7 +73,7 @@ public class DeptWrapper extends BaseEntityWrapper<Dept, DeptVO> {
|
||||
public List<DeptVO> listNodeVO(List<Dept> list) {
|
||||
List<DeptVO> collect = list.stream().map(dept -> {
|
||||
DeptVO deptVO = BeanUtil.copyProperties(dept, DeptVO.class);
|
||||
String category = DictCache.getValue(DictEnum.ORG_CATEGORY, dept.getDeptCategory());
|
||||
String category = DictBizCache.getValue(ORGANIZATION_TYPE_CODE, dept.getDeptCategory());
|
||||
Objects.requireNonNull(deptVO).setDeptCategoryName(category);
|
||||
return deptVO;
|
||||
}).collect(Collectors.toList());
|
||||
@@ -78,7 +82,7 @@ public class DeptWrapper extends BaseEntityWrapper<Dept, DeptVO> {
|
||||
|
||||
public List<DeptVO> listNodeLazyVO(List<DeptVO> list) {
|
||||
List<DeptVO> collect = list.stream().peek(dept -> {
|
||||
String category = DictCache.getValue(DictEnum.ORG_CATEGORY, dept.getDeptCategory());
|
||||
String category = DictBizCache.getValue(ORGANIZATION_TYPE_CODE, dept.getDeptCategory());
|
||||
Objects.requireNonNull(dept).setDeptCategoryName(category);
|
||||
}).collect(Collectors.toList());
|
||||
return ForestNodeMerger.merge(collect);
|
||||
|
||||
+1
@@ -52,6 +52,7 @@ public class FeeItemWrapper extends BaseEntityWrapper<FeeItem, FeeItemVO> {
|
||||
feeItemVO.setCreateDeptName(
|
||||
Func.isEmpty(feeItem.getCreateDept()) ? "" : SysCache.getDeptName(feeItem.getCreateDept())
|
||||
);
|
||||
feeItemVO.setCreateUserName(UserCache.getUserRealName(feeItem.getCreateUser()));
|
||||
feeItemVO.setUpdateUserName(UserCache.getUserRealName(feeItem.getUpdateUser()));
|
||||
return feeItemVO;
|
||||
}
|
||||
|
||||
+1
@@ -48,6 +48,7 @@ public class PortTerminalWrapper extends BaseEntityWrapper<PortTerminal, PortTer
|
||||
public PortTerminalVO entityVO(PortTerminal portTerminal) {
|
||||
PortTerminalVO portTerminalVO = Objects.requireNonNull(BeanUtil.copyProperties(portTerminal, PortTerminalVO.class));
|
||||
portTerminalVO.setRegionCode(portTerminal.getDistrictCode());
|
||||
portTerminalVO.setCreateUserName(UserCache.getUserRealName(portTerminal.getCreateUser()));
|
||||
portTerminalVO.setUpdateUserName(UserCache.getUserRealName(portTerminal.getUpdateUser()));
|
||||
return portTerminalVO;
|
||||
}
|
||||
|
||||
+1
@@ -48,6 +48,7 @@ public class RailwayStationWrapper extends BaseEntityWrapper<RailwayStation, Rai
|
||||
public RailwayStationVO entityVO(RailwayStation railwayStation) {
|
||||
RailwayStationVO railwayStationVO = Objects.requireNonNull(BeanUtil.copyProperties(railwayStation, RailwayStationVO.class));
|
||||
railwayStationVO.setRegionCode(railwayStation.getDistrictCode());
|
||||
railwayStationVO.setCreateUserName(UserCache.getUserRealName(railwayStation.getCreateUser()));
|
||||
railwayStationVO.setUpdateUserName(UserCache.getUserRealName(railwayStation.getUpdateUser()));
|
||||
return railwayStationVO;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user