Răsfoiți Sursa

提交数据权限隔离

chenxiaofei 2 luni în urmă
părinte
comite
cfa080f24b

+ 183 - 1
sckw-modules/sckw-contract/src/main/java/com/sckw/contract/service/operateService/KwcContractLogisticsService.java

@@ -20,6 +20,7 @@ import com.sckw.contract.model.vo.res.*;
 import com.sckw.contract.repository.KwcContractLogisticsGoodsRepository;
 import com.sckw.contract.repository.KwcContractLogisticsRepository;
 import com.sckw.contract.repository.KwcContractLogisticsUnitRepository;
+import com.sckw.contract.repository.KwcContractTradeRepository;
 import com.sckw.contract.service.*;
 import com.sckw.core.common.enums.enums.DictEnum;
 import com.sckw.core.common.enums.enums.DictTypeEnum;
@@ -39,12 +40,17 @@ import com.sckw.product.api.dubbo.GoodsInfoService;
 import com.sckw.product.api.model.KwpGoods;
 import com.sckw.stream.enums.MessageEnum;
 import com.sckw.stream.model.UserInfo;
+import com.sckw.system.api.feign.DataPermissionFeignService;
 import com.sckw.system.api.RemoteSystemService;
 import com.sckw.system.api.RemoteUserService;
+import com.sckw.system.api.model.dto.req.DataPermissionFilterReqDto;
+import com.sckw.system.api.model.dto.res.DataPermissionDTO;
 import com.sckw.system.api.model.dto.res.EntCacheResDto;
 import com.sckw.system.api.model.dto.res.KwsUserResDto;
 import com.sckw.system.api.model.dto.res.SysDictResDto;
 import com.sckw.system.api.model.dto.res.UserCacheResDto;
+import com.sckw.order.api.dubbo.TradeOrderInfoService;
+import com.sckw.order.api.model.TradeOrderContractVo;
 import com.sckw.transport.api.dubbo.TransportRemoteService;
 import com.sckw.transport.api.model.vo.KwtLogisticsOrderVO;
 import com.sckw.transport.api.model.vo.RWaybillSubOrderVo;
@@ -52,6 +58,7 @@ import com.sckw.transport.api.model.vo.WayContaractbillOrderVo;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.MapUtils;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.jetbrains.annotations.NotNull;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -98,6 +105,8 @@ public class KwcContractLogisticsService {
     private final KwcContractLogisticsRepository kwcContractLogisticsRepository;
     private final KwcContractLogisticsUnitRepository kwcContractLogisticsUnitRepository;
     private final KwcContractLogisticsGoodsRepository kwcContractLogisticsGoodsRepository;
+    private final KwcContractTradeRepository kwcContractTradeRepository;
+    private final DataPermissionFeignService dataPermissionFeignService;
 
     @DubboReference(version = "1.0.0", group = "design", check = false)
     private RemoteSystemService remoteSystemService;
@@ -108,6 +117,9 @@ public class KwcContractLogisticsService {
     @DubboReference(version = "1.0.0", group = "design", check = false)
     private GoodsInfoService goodsInfoService;
 
+    @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 8000)
+    private TradeOrderInfoService tradeOrderInfoService;
+
     @DubboReference(version = "1.0.0", group = "design", check = false)
     private TransportRemoteService transportRemoteService;
 
@@ -1092,6 +1104,22 @@ public class KwcContractLogisticsService {
         if (CollectionUtils.isEmpty(contractIdList) && CollectionUtils.isNotEmpty(entIdList)){
             return PageDataResult.empty(req.getPageNum(),req.getPageSize());
         }
+        // 获取当前用户的数据权限配置
+        DataPermissionDTO perm = fetchDataPermissionForCurrentUser();
+        // 如果存在数据权限且需要过滤,则执行权限过滤逻辑
+        if (perm != null && perm.needFilter()) {
+            // 根据合同ID列表及查询条件获取物流合同列表
+            List<KwcContractLogistics> permissionSource = kwcContractLogisticsRepository.queryList(contractIdList,
+                    req.getContractNo(), req.getContractName(), req.getStatus());
+            // 根据数据权限规则过滤合同列表
+            permissionSource = filterLogisticsContractsByPermission(permissionSource, perm);
+            // 如果过滤后无数据,直接返回空分页结果
+            if (CollectionUtils.isEmpty(permissionSource)) {
+                return PageDataResult.empty(req.getPageNum(), req.getPageSize());
+            }
+            // 将过滤后的合同ID重新赋值给contractIdList,用于后续的分页查询
+            contractIdList = permissionSource.stream().map(KwcContractLogistics::getId).collect(Collectors.toSet());
+        }
 
         IPage<KwcContractLogistics> page =kwcContractLogisticsRepository.queryByPage(req.getPageNum(),req.getPageSize(),
                 req.getContractNo(),
@@ -1686,7 +1714,11 @@ public class KwcContractLogisticsService {
 
 
         List<KwcContractLogistics> contractLogistics = kwcContractLogisticsRepository.queryList(contractIdList,req.getContractNo(),req.getContractName(),req.getStatus());
+        // 应用数据权限过滤:获取当前用户的数据权限配置,并对物流合同列表进行过滤(个人数据权限与企业层级数据权限为并集关系)
+        DataPermissionDTO perm = fetchDataPermissionForCurrentUser();
+        contractLogistics = filterLogisticsContractsByPermission(contractLogistics, perm);
 
+        // 如果过滤后无数据,则返回各状态计数均为0的结果
         if (CollectionUtils.isEmpty(contractLogistics)){
             Map<Integer, List<KwcContractLogistics>> statusAndLogOrdersMap = new HashMap<>();
             List<ContractStatusCountResp.ContractStatusCount> statusCounts = statusEnums.stream()
@@ -1719,4 +1751,154 @@ public class KwcContractLogisticsService {
         contractStatusCount.setOrderNum(String.valueOf(finalStatusAndLogOrdersMap1.getOrDefault(statusEnum.getCode(), new ArrayList<>()).size()));
         return contractStatusCount;
     }
-}
+
+    /**
+     * 获取当前用户的数据权限配置
+     * <p>
+     * 通过远程服务获取当前登录用户的数据权限过滤条件,包括可见企业ID、个人数据权限开关等。
+     * 如果获取失败,记录警告日志并返回null,后续逻辑将跳过权限过滤。
+     * </p>
+     *
+     * @return 数据权限DTO,如果获取失败则返回null
+     */
+    private DataPermissionDTO fetchDataPermissionForCurrentUser() {
+        try {
+            DataPermissionFilterReqDto reqDto = new DataPermissionFilterReqDto();
+            reqDto.setUserId(LoginUserHolder.getUserId());
+            reqDto.setRoleId(LoginUserHolder.getCurrentRoleId());
+            reqDto.setManager(LoginUserHolder.isManager());
+            return dataPermissionFeignService.getDataPermissionFilter(reqDto);
+        } catch (Exception e) {
+            log.warn("获取物流合同数据权限失败,跳过权限过滤: {}", e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 根据数据权限过滤物流合同列表
+     * <p>
+     * 过滤逻辑:
+     * 1. 通过物流合同ID查询关联的运单子单信息,获取贸易订单ID。
+     * 2. 通过贸易订单ID查询关联的交易合同ID。
+     * 3. 根据交易合同ID查询交易合同详情。
+     * 4. 结合数据权限配置(可见企业ID、个人数据权限),判断当前用户是否有权限查看该物流合同。
+     *    - 若非全部可见,则检查交易合同所属企业是否在可见企业列表中。
+     *    - 若启用个人数据权限,则检查交易合同的销售员是否为当前用户。
+     *    - 只有当至少一个关联的交易合同满足权限要求时,该物流合同才会被保留。
+     * </p>
+     *
+     * @param records 待过滤的物流合同列表
+     * @param perm    数据权限配置
+     * @return 过滤后的物流合同列表
+     */
+    private List<KwcContractLogistics> filterLogisticsContractsByPermission(List<KwcContractLogistics> records, DataPermissionDTO perm) {
+        // 如果记录为空、权限对象为空或不需要过滤,直接返回原列表
+        if (CollectionUtils.isEmpty(records) || perm == null || !perm.needFilter()) {
+            return records;
+        }
+
+        // 1. 提取所有物流合同ID
+        Set<Long> logContractIds = records.stream().map(KwcContractLogistics::getId).collect(Collectors.toSet());
+
+        // 2. 查询关联的运单子单信息
+        List<RWaybillSubOrderVo> orderLinks = transportRemoteService.queryWaybillOrderByLogContractIds(logContractIds);
+        if (CollectionUtils.isEmpty(orderLinks)) {
+            return Collections.emptyList();
+        }
+
+        // 3. 提取贸易订单ID
+        Set<Long> tradeOrderIds = orderLinks.stream()
+                .map(RWaybillSubOrderVo::getTradeId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        if (CollectionUtils.isEmpty(tradeOrderIds)) {
+            return Collections.emptyList();
+        }
+
+        // 4. 查询贸易订单关联的交易合同信息
+        List<TradeOrderContractVo> tradeOrderContracts = tradeOrderInfoService.queryTradeOrderIds(tradeOrderIds);
+        if (CollectionUtils.isEmpty(tradeOrderContracts)) {
+            return Collections.emptyList();
+        }
+
+        // 5. 构建物流合同ID到交易合同ID集合的映射关系
+        Map<Long, Set<Long>> logContractIdTradeContractIdsMap = buildLogContractTradeContractIdsMap(orderLinks, tradeOrderContracts);
+        if (MapUtils.isEmpty(logContractIdTradeContractIdsMap)) {
+            return Collections.emptyList();
+        }
+
+        // 6. 收集所有相关的交易合同ID
+        Set<Long> tradeContractIds = logContractIdTradeContractIdsMap.values().stream()
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
+
+        // 7. 批量查询交易合同详情,并构建ID到对象的映射
+        Map<Long, KwcContractTrade> tradeContractMap = kwcContractTradeRepository.findByContractIds(tradeContractIds).stream()
+                .collect(Collectors.toMap(KwcContractTrade::getId, Function.identity(), (a, b) -> a));
+
+        // 8. 根据权限规则过滤物流合同
+        return records.stream().filter(record -> {
+            // 获取当前物流合同关联的所有交易合同ID
+            Set<Long> relationTradeContractIds = logContractIdTradeContractIdsMap.get(record.getId());
+            if (CollectionUtils.isEmpty(relationTradeContractIds)) {
+                return false;
+            }
+
+            // 只要有一个关联的交易合同满足权限要求,则该物流合同可见
+            return relationTradeContractIds.stream()
+                    .map(tradeContractMap::get)
+                    .filter(Objects::nonNull)
+                    .anyMatch(contract -> {
+                        // 校验企业权限
+                        boolean entOk = true;
+                        if (!perm.isAllVisible()) {
+                            if (CollectionUtils.isEmpty(perm.getVisibleEntIds())) {
+                                entOk = false;
+                            } else {
+                                entOk = contract.getEntId() != null && perm.getVisibleEntIds().contains(contract.getEntId());
+                            }
+                        }
+
+                        // 校验个人数据权限(销售员权限)
+                        boolean salesOk = true;
+                        if (perm.isPersonalDataEnabled()) {
+                            salesOk = perm.getUserId() != null && perm.getUserId().equals(contract.getSalesmanId());
+                        }
+
+                        return entOk && salesOk;
+                    });
+        }).collect(Collectors.toList());
+    }
+
+    /**
+     * 构建物流合同ID与关联的交易合同ID集合的映射关系
+     *
+     * @param orderLinks         运单子单信息列表,包含物流合同ID和贸易订单ID
+     * @param tradeOrderContracts 贸易订单与交易合同的关联信息列表
+     * @return Map<物流合同ID, 交易合同ID集合>
+     */
+    private Map<Long, Set<Long>> buildLogContractTradeContractIdsMap(List<RWaybillSubOrderVo> orderLinks,
+                                                                     List<TradeOrderContractVo> tradeOrderContracts) {
+        // 构建贸易订单ID到交易合同ID的映射关系,过滤掉无效数据
+        Map<Long, Long> tradeOrderIdTradeContractIdMap = tradeOrderContracts.stream()
+                .filter(item -> item.getTOrderId() != null && item.getContractId() != null)
+                .collect(Collectors.toMap(TradeOrderContractVo::getTOrderId, TradeOrderContractVo::getContractId, (a, b) -> a));
+
+        // 初始化结果Map,key为物流合同ID,value为关联的交易合同ID集合
+        Map<Long, Set<Long>> result = new HashMap<>();
+        for (RWaybillSubOrderVo orderLink : orderLinks) {
+            // 跳过物流合同ID或贸易订单ID为空的数据
+            if (orderLink.getLogContractId() == null || orderLink.getTradeId() == null) {
+                continue;
+            }
+            // 获取贸易订单对应的交易合同ID
+            Long tradeContractId = tradeOrderIdTradeContractIdMap.get(orderLink.getTradeId());
+            if (tradeContractId == null) {
+                continue;
+            }
+            // 将交易合同ID添加到对应物流合同ID的集合中
+            result.computeIfAbsent(orderLink.getLogContractId(), key -> new HashSet<>()).add(tradeContractId);
+        }
+        return result;
+    }
+}

+ 69 - 0
sckw-modules/sckw-contract/src/main/java/com/sckw/contract/service/operateService/KwcContractTradeService.java

@@ -50,8 +50,11 @@ import com.sckw.product.api.dubbo.GoodsInfoService;
 import com.sckw.product.api.model.KwpGoods;
 import com.sckw.stream.enums.MessageEnum;
 import com.sckw.stream.model.UserInfo;
+import com.sckw.system.api.feign.DataPermissionFeignService;
 import com.sckw.system.api.RemoteSystemService;
 import com.sckw.system.api.RemoteUserService;
+import com.sckw.system.api.model.dto.req.DataPermissionFilterReqDto;
+import com.sckw.system.api.model.dto.res.DataPermissionDTO;
 import com.sckw.system.api.model.dto.res.EntCacheResDto;
 import com.sckw.system.api.model.dto.res.EntTypeResDto;
 import com.sckw.system.api.model.dto.res.SysDictResDto;
@@ -112,6 +115,7 @@ public class KwcContractTradeService {
     private final KwcContractLogisticsGoodsRepository kwcContractLogisticsGoodsRepository;
     private final KwcContractLogisticsRepository contractLogisticsRepository;
     private final KwcContractLogisticsScoreRepository contractLogisticsScoreRepository;
+    private final DataPermissionFeignService dataPermissionFeignService;
 
     @DubboReference(version = "1.0.0", group = "design", check = false)
     private RemoteSystemService remoteSystemService;
@@ -1646,6 +1650,11 @@ public class KwcContractTradeService {
         if (org.apache.commons.collections4.CollectionUtils.isEmpty(records)) {
             return Collections.emptyList();
         }
+        DataPermissionDTO perm = fetchDataPermissionForCurrentUser();
+        records = filterTradeContractsByPermission(records, perm);
+        if (org.apache.commons.collections4.CollectionUtils.isEmpty(records)) {
+            return Collections.emptyList();
+        }
         entIds.addAll(records.stream().map(KwcContractTrade::getEntId).collect(Collectors.toSet()));
         List<EntTypeResDto> ents = remoteSystemService.queryEntTypeByIds(entIds);
         Map<Long, EntTypeResDto> entIdAndEntMap = Maps.newHashMap();
@@ -1807,6 +1816,11 @@ public class KwcContractTradeService {
         if (org.apache.commons.collections4.CollectionUtils.isEmpty(records)) {
             return 0L;
         }
+        DataPermissionDTO perm = fetchDataPermissionForCurrentUser();
+        records = filterTradeContractsByPermission(records, perm);
+        if (org.apache.commons.collections4.CollectionUtils.isEmpty(records)) {
+            return 0L;
+        }
         entIds.addAll(records.stream().map(KwcContractTrade::getEntId).collect(Collectors.toSet()));
         List<EntTypeResDto> ents = remoteSystemService.queryEntTypeByIds(entIds);
         Map<Long, EntTypeResDto> entIdAndEntMap = Maps.newHashMap();
@@ -2564,5 +2578,60 @@ public class KwcContractTradeService {
     public Long querySign(Long entId, Integer type) {
         return kwcContractTradeMapper.selectSignCount(entId, type);
     }
+
+    /**
+     * 获取当前用户的数据权限配置
+     *
+     * @return 数据权限DTO,如果获取失败则返回null
+     */
+    private DataPermissionDTO fetchDataPermissionForCurrentUser() {
+        try {
+            DataPermissionFilterReqDto reqDto = new DataPermissionFilterReqDto();
+            reqDto.setUserId(LoginUserHolder.getUserId());
+            reqDto.setRoleId(LoginUserHolder.getCurrentRoleId());
+            reqDto.setManager(LoginUserHolder.isManager());
+            return dataPermissionFeignService.getDataPermissionFilter(reqDto);
+        } catch (Exception e) {
+            log.warn("获取贸易合同数据权限失败,跳过权限过滤: {}", e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 根据数据权限过滤贸易合同列表
+     *
+     * @param records 待过滤的合同列表
+     * @param perm    数据权限配置
+     * @return 过滤后的合同列表
+     */
+    private List<KwcContractTrade> filterTradeContractsByPermission(List<KwcContractTrade> records, DataPermissionDTO perm) {
+        // 如果记录为空、权限配置为空或不需要过滤,直接返回原列表
+        if (CollectionUtils.isEmpty(records) || perm == null || !perm.needFilter()) {
+            return records;
+        }
+        return records.stream().filter(record -> {
+            // 校验企业权限
+            boolean entOk = true;
+            if (!perm.isAllVisible()) {
+                // 如果不是所有可见,检查可见企业ID列表
+                if (CollectionUtils.isEmpty(perm.getVisibleEntIds())) {
+                    entOk = false;
+                } else {
+                    // 检查当前合同所属企业是否在可见列表中
+                    entOk = record.getEntId() != null && perm.getVisibleEntIds().contains(record.getEntId());
+                }
+            }
+            
+            // 校验销售人员个人数据权限
+            boolean salesOk = true;
+            if (perm.isPersonalDataEnabled()) {
+                // 如果开启了个人数据权限,检查当前用户是否为该合同的销售人员
+                salesOk = perm.getUserId() != null && perm.getUserId().equals(record.getSalesmanId());
+            }
+            
+            // 同时满足企业权限和销售人员权限
+            return entOk && salesOk;
+        }).collect(Collectors.toList());
+    }
 }
 

+ 1 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/KwtLogisticsConsignmentService.java

@@ -3749,6 +3749,7 @@ public class KwtLogisticsConsignmentService {
 
         //根据商品名称查询物流订单
         Set<Long> logOrderIds = getLogOrderIds(req, entList, allEnt);
+        logOrderIds = applyDataPermissionFilterToLogOrderIds(logOrderIds);
         if (org.apache.commons.collections4.CollectionUtils.isEmpty(logOrderIds) && org.apache.commons.collections4.CollectionUtils.isNotEmpty(entList)) {
             Map<Integer, List<KwtLogisticsOrder>> statusAndLogOrdersMap = Maps.newHashMap();
             List<OrderStatusStatisticsResp.OrderStatusStatistics> statistics = orderV1Enums.stream()

+ 2 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/KwtWaybillOrderV1Service.java

@@ -5210,6 +5210,8 @@ public class KwtWaybillOrderV1Service {
        // entIds.add(entId);
         log.debug("获取企业ID集合: {}", entIds);
         Set<Long> logOrderIds = getLogOrderIds(req, entIds,entId);
+        // 应用数据权限过滤:个人数据权限与企业层级数据权限为并集关系
+        logOrderIds = applyDataPermissionFilterToLogOrderIds(logOrderIds);
         boolean b1 = !org.apache.commons.lang3.StringUtils.isAllBlank(req.getLogisticsOrderNo(), req.getGoodsName(),
                 req.getCarrierId(), req.getConsignorId()) || Objects.nonNull(entId);
         if(org.apache.commons.collections4.CollectionUtils.isEmpty(logOrderIds) && b1){