Jelajahi Sumber

物流订单物流运单数据权限

chenxiaofei 2 bulan lalu
induk
melakukan
28e85d9e04

+ 117 - 37
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/KwtLogisticsConsignmentService.java

@@ -2274,6 +2274,7 @@ public class KwtLogisticsConsignmentService {
         // 3. 获取物流订单ID集合
         log.debug("获取物流订单ID集合");
         Set<Long> logOrderIds = getLogOrderIds(req, entList, allEnt);
+        // 应用数据权限过滤
         logOrderIds = applyDataPermissionFilterToLogOrderIds(logOrderIds);
 
         // 4. 检查是否需要返回空结果
@@ -2719,95 +2720,177 @@ public class KwtLogisticsConsignmentService {
      */
 
 
+
     /**
-     * 按数据权限收缩物流订单ID:
-     * 企业权限:贸易合同 ent_id 落在已勾选企业范围内;
-     * 个人权限:贸易合同 salesman_id 为当前用户;
-     * 两者同时开启时为 AND(与系统模块 DataPermissionDTO 规则一致)。
+     * 应用数据权限过滤到物流订单ID集合
+     * <p>
+     * 根据当前登录用户的数据权限配置,对传入的物流订单ID集合进行过滤。
+     * 支持企业维度权限(可见企业范围)和个人维度权限(销售员匹配)。
+     * </p>
+     *
+     * @param logOrderIds 待过滤的物流订单ID集合
+     * @return 过滤后的物流订单ID集合
      */
     private Set<Long> applyDataPermissionFilterToLogOrderIds(Set<Long> logOrderIds) {
         // 1. 获取当前用户的数据权限配置
         DataPermissionDTO perm = fetchDataPermissionForCurrentUser();
-        // 如果无需过滤或权限对象为空,直接返回原始ID集合
+        
+        // 如果未获取到权限配置或不需要进行数据过滤,直接返回原始ID集合da
         if (perm == null || !perm.needFilter()) {
+            log.debug("数据权限过滤:无需过滤或权限配置为空,返回原始订单ID集合,大小: {}", 
+                    CollectionUtils.isEmpty(logOrderIds) ? 0 : logOrderIds.size());
             return logOrderIds;
         }
-        // 如果输入ID集合为空,直接返回
+
+        // 如果输入的订单ID集合为空,直接返回
         if (CollectionUtils.isEmpty(logOrderIds)) {
+            log.debug("数据权限过滤:输入订单ID集合为空");
             return logOrderIds;
         }
 
-        // 2. 查询物流订单详情,获取关联的贸易订单ID
+        log.info("开始应用数据权限过滤,初始订单ID数量: {}, 用户ID: {}", logOrderIds.size(), LoginUserHolder.getUserId());
+        Set<Long> result = new HashSet<>(logOrderIds);
+
+        // 2. 处理企业维度数据权限
+        // 如果不是所有数据可见,则需要根据可见企业ID进行过滤
+        if (!perm.isAllVisible()) {
+            // 如果可见企业ID列表为空,说明该用户无权查看任何企业数据,返回空集合
+            if (CollectionUtils.isEmpty(perm.getVisibleEntIds())) {
+                log.warn("数据权限过滤:用户无可见企业权限,返回空集合");
+                return Sets.newHashSet();
+            }
+
+            log.debug("数据权限过滤:根据可见企业ID过滤,可见企业数量: {}", perm.getVisibleEntIds().size());
+            // 查询这些企业关联的所有物流订单单位信息
+            List<KwtLogisticsOrderUnit> units =
+                    logisticsOrderUnitRepository.queryByEntIds(new HashSet<>(perm.getVisibleEntIds()));
+            
+            // 提取出这些单位对应的物流订单ID
+            Set<Long> entScopeLogOrderIds = units.stream()
+                    .map(KwtLogisticsOrderUnit::getLOrderId)
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toSet());
+            
+            log.debug("数据权限过滤:企业维度关联的订单ID数量: {}", entScopeLogOrderIds.size());
+            
+            // 取交集:只保留在当前用户可见企业范围内的订单ID
+            result.retainAll(entScopeLogOrderIds);
+            log.debug("数据权限过滤:企业维度过滤后剩余订单ID数量: {}", result.size());
+        }
+
+        // 如果经过企业维度过滤后结果为空,直接返回
+        if (CollectionUtils.isEmpty(result)) {
+            log.info("数据权限过滤:企业维度过滤后无剩余订单,提前返回");
+            return result;
+        }
+
+        // 3. 处理个人维度数据权限
+        // 如果未启用个人数据权限控制,则直接返回当前结果
+        if (!perm.isPersonalDataEnabled()) {
+            log.info("数据权限过滤:未启用个人数据权限,最终订单ID数量: {}", result.size());
+            return result;
+        }
+
+        // 启用个人数据权限时,进一步过滤:仅保留关联贸易合同中销售员为当前用户的物流订单
+        log.debug("数据权限过滤:启用个人数据权限,开始按销售员过滤");
+        Set<Long> finalResult = filterLogOrderIdsByTradeContractSalesman(result, perm);
+        log.info("数据权限过滤完成,最终订单ID数量: {}", finalResult.size());
+        
+        return finalResult;
+    }
+
+    /**
+     * 个人数据权限过滤:仅保留关联贸易合同中销售员为当前用户的物流订单
+     * <p>
+     * 逻辑链路:物流订单 -> 贸易订单ID -> 贸易合同ID -> 合同销售员ID == 当前用户ID
+     * </p>
+     *
+     * @param logOrderIds 待过滤的物流订单ID集合
+     * @param perm        当前用户的数据权限DTO
+     * @return 过滤后的物流订单ID集合
+     */
+    private Set<Long> filterLogOrderIdsByTradeContractSalesman(Set<Long> logOrderIds, DataPermissionDTO perm) {
+        // 校验用户ID,若为空则无法进行个人权限匹配,返回空集合
+        if (perm.getUserId() == null) {
+            log.warn("个人数据权限过滤:用户ID为空,返回空集合");
+            return Sets.newHashSet();
+        }
+
+        Long currentUserId = perm.getUserId();
+        log.debug("个人数据权限过滤:开始处理,待过滤订单数: {}, 当前用户ID: {}", logOrderIds.size(), currentUserId);
+
+        // 1. 查询物流订单详情,获取关联的贸易订单ID
         List<KwtLogisticsOrder> orders = logisticsOrderRepository.queryByLogOrderIds(logOrderIds);
         if (CollectionUtils.isEmpty(orders)) {
+            log.debug("个人数据权限过滤:未查询到物流订单详情,返回空集合");
             return Sets.newHashSet();
         }
+
+        // 提取所有非空的贸易订单ID
         Set<Long> tradeIds = orders.stream()
                 .map(KwtLogisticsOrder::getTOrderId)
                 .filter(Objects::nonNull)
                 .collect(Collectors.toSet());
+        
         if (CollectionUtils.isEmpty(tradeIds)) {
+            log.warn("个人数据权限过滤:物流订单中未找到关联的贸易订单ID,返回空集合");
             return Sets.newHashSet();
         }
+        log.debug("个人数据权限过滤:提取到贸易订单ID数量: {}", tradeIds.size());
 
-        // 3. 通过贸易订单ID查询关联的合同ID
+        // 2. 查询贸易订单与合同的关联关系
         List<TradeOrderContractVo> contractLinks = tradeOrderInfoService.queryTradeOrderIds(tradeIds);
         Map<Long, Long> tradeIdToContractId = new HashMap<>();
         if (CollectionUtils.isNotEmpty(contractLinks)) {
             for (TradeOrderContractVo link : contractLinks) {
+                // 确保贸易订单ID和合同ID均不为空
                 if (link.getTOrderId() == null || link.getContractId() == null) {
                     continue;
                 }
+                // 使用 putIfAbsent 避免覆盖,通常一个贸易订单对应一个主合同
                 tradeIdToContractId.putIfAbsent(link.getTOrderId(), link.getContractId());
             }
         }
+        log.debug("个人数据权限过滤:贸易订单-合同映射关系数量: {}", tradeIdToContractId.size());
 
-        // 4. 批量查询合同基础信息
+        // 3. 批量查询合同基础信息,获取销售员ID
         List<Long> contractIdList = tradeIdToContractId.values().stream().distinct().collect(Collectors.toList());
         Map<Long, ContractCommonInfoResDto> contractMap = Maps.newHashMap();
         if (CollectionUtils.isNotEmpty(contractIdList)) {
             contractMap = contractService.queryContractBaseInfo(contractIdList);
+            log.debug("个人数据权限过滤:查询到合同信息数量: {}", contractMap.size());
+        } else {
+            log.warn("个人数据权限过滤:未找到关联的合同ID列表");
         }
 
-        // 5. 根据数据权限规则过滤允许的贸易订单ID
+        // 4. 筛选出销售员为当前用户的贸易订单ID
         Set<Long> allowedTradeIds = new HashSet<>();
         for (Long tid : tradeIds) {
             Long cid = tradeIdToContractId.get(tid);
             if (cid == null) {
+                // 该贸易订单未关联合同,根据个人权限策略,通常视为不可见或需特殊处理,此处跳过
                 continue;
             }
-            ContractCommonInfoResDto c = contractMap.get(cid);
-            if (c == null) {
+            ContractCommonInfoResDto contractInfo = contractMap.get(cid);
+            if (contractInfo == null) {
+                // 合同信息缺失,跳过
                 continue;
             }
-
-            // 校验企业权限:如果不是全部可见,则检查合同所属企业是否在可见列表中
-            boolean entOk = true;
-            if (!perm.isAllVisible()) {
-                if (CollectionUtils.isEmpty(perm.getVisibleEntIds())) {
-                    entOk = false;
-                } else {
-                    entOk = c.getEntId() != null && perm.getVisibleEntIds().contains(c.getEntId());
-                }
-            }
-
-            // 校验个人权限:如果开启了个人数据隔离,则检查合同销售员是否为当前用户
-            boolean salesOk = true;
-            if (perm.isPersonalDataEnabled()) {
-                salesOk = perm.getUserId() != null && perm.getUserId().equals(c.getSalesmanId());
-            }
-
-            // 同时满足企业权限和个人权限(AND逻辑)
-            if (entOk && salesOk) {
+            // 判断合同销售员是否等于当前用户
+            if (currentUserId.equals(contractInfo.getSalesmanId())) {
                 allowedTradeIds.add(tid);
             }
         }
+        log.debug("个人数据权限过滤:匹配到当前用户作为销售员的贸易订单数量: {}", allowedTradeIds.size());
 
-        // 6. 返回过滤后的物流订单ID集合
-        return orders.stream()
-                .filter(o -> o.getTOrderId() != null && allowedTradeIds.contains(o.getTOrderId()))
+        // 5. 根据允许的贸易订单ID,反向过滤出最终的物流订单ID
+        Set<Long> finalLogOrderIds = orders.stream()
+                .filter(order -> order.getTOrderId() != null && allowedTradeIds.contains(order.getTOrderId()))
                 .map(KwtLogisticsOrder::getId)
                 .collect(Collectors.toSet());
+        
+        log.debug("个人数据权限过滤:最终保留的物流订单数量: {}", finalLogOrderIds.size());
+        return finalLogOrderIds;
     }
 
     private DataPermissionDTO fetchDataPermissionForCurrentUser() {
@@ -2930,7 +3013,6 @@ public class KwtLogisticsConsignmentService {
         return logOrderIds;
     }
 
-    @NotNull
     private Long getAllEnt(String entId) {
         //根据企业类型查询企业
         Long enterId = null;
@@ -2942,8 +3024,6 @@ public class KwtLogisticsConsignmentService {
 
         return enterId;
     }
-
-    @NotNull
     private static LogisticsOrderResp getLogisticsOrderResp(KwtLogisticsOrder kwtLogisticsOrder,
                                                             Map<String, KwtLogisticsOrderUnit> finalLogOrderIdAndUnitTypeKeyAndUnitMap,
                                                             Map<Long, KwtLogisticsOrderGoods> finalLogIdAndGoodsMap,

+ 102 - 38
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/KwtWaybillOrderV1Service.java

@@ -3036,7 +3036,7 @@ public class KwtWaybillOrderV1Service {
         Set<Long> billOrderIds = billOrderIdsFuture.join();
         log.debug("物流订单ID查询结果数量: {}, 运单ID查询结果数量: {}", logOrderIds.size(), billOrderIds.size());
 
-        // 应用数据权限过滤:个人数据权限与企业层级数据权限为并集关系
+        // 数据权限:企业按 kwt_logistics_order_unit;个人按贸易合同销售员(见 applyDataPermissionFilterToLogOrderIds)
         logOrderIds = applyDataPermissionFilterToLogOrderIds(logOrderIds);
         log.debug("数据权限过滤后物流订单ID数量: {}", logOrderIds.size());
 
@@ -3654,44 +3654,118 @@ public class KwtWaybillOrderV1Service {
     }
 
     /**
-     * 根据数据权限过滤物流订单ID集合
-     * <p>
-     * 个人数据权限与企业层级数据权限为并集关系:
-     * 勾选后只查已勾选的企业并且贸易合同中销售人员是自己的数据
+     * 根据数据权限过滤物流订单ID集合({@link #findBillOrderListPage}、{@link #statisticsWaybillOrder} 等共用)。
      * <p>
-     * 过滤链路:物流订单 → 贸易订单 → 贸易合同 → 校验企业ID + 销售人员
+     * 企业数据权限:按 {@code kwt_logistics_order_unit} 中托运/承运企业的 {@code ent_id} 是否落在角色可见企业范围内(与物流订单企业维度一致)。
+     * 个人数据权限:关联贸易合同的 {@code salesman_id} 为当前用户;与企业在 {@link DataPermissionDTO} 下为 AND 组合。
+     * </p>
      *
      * @param logOrderIds 待过滤的物流订单ID集合
      * @return 过滤后的物流订单ID集合
      */
     private Set<Long> applyDataPermissionFilterToLogOrderIds(Set<Long> logOrderIds) {
-        // 获取当前用户的数据权限配置
+        log.debug("开始应用数据权限过滤,原始物流订单ID数量: {}", CollectionUtils.isEmpty(logOrderIds) ? 0 : logOrderIds.size());
+        
+        // 1. 获取当前用户的数据权限配置
         DataPermissionDTO perm = fetchDataPermissionForCurrentUser();
-        // 如果无需过滤或权限配置为空,直接返回原集合
+        
+        // 如果无需过滤或权限配置为空,直接返回原始ID集合
         if (perm == null || !perm.needFilter()) {
+            log.debug("无需进行数据权限过滤,直接返回原始ID集合");
             return logOrderIds;
         }
-        // 如果待过滤集合为空,直接返回
+        
+        // 如果原始ID集合为空,直接返回
         if (CollectionUtils.isEmpty(logOrderIds)) {
+            log.debug("原始物流订单ID集合为空,无需过滤");
             return logOrderIds;
         }
 
-        // 1. 查询物流订单详情
+        Set<Long> result = new HashSet<>(logOrderIds);
+
+        // 2. 处理企业维度的数据权限
+        if (!perm.isAllVisible()) {
+            log.debug("非全量可见模式,开始执行企业维度权限过滤");
+            
+            // 如果可见企业ID列表为空,说明该企业维度下无权限,返回空集合
+            if (CollectionUtils.isEmpty(perm.getVisibleEntIds())) {
+                log.warn("当前用户可见企业ID列表为空,过滤结果为空");
+                return Sets.newHashSet();
+            }
+            
+            // 查询可见企业ID关联的所有物流订单单位信息
+            List<KwtLogisticsOrderUnit> units =
+                    kwtLogisticsOrderUnitRepository.queryByEntIds(new HashSet<>(perm.getVisibleEntIds()));
+            
+            // 提取这些单位关联的物流订单ID
+            Set<Long> entScopeLogOrderIds = units.stream()
+                    .map(KwtLogisticsOrderUnit::getLOrderId)
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toSet());
+            
+            log.debug("企业维度过滤:可见企业关联的物流订单ID数量: {}", entScopeLogOrderIds.size());
+            
+            // 取交集:只保留既在原始列表中,又在可见企业关联列表中的ID
+            result.retainAll(entScopeLogOrderIds);
+            log.debug("企业维度过滤后剩余ID数量: {}", result.size());
+        }
+
+        // 如果经过企业维度过滤后结果为空,直接返回
+        if (CollectionUtils.isEmpty(result)) {
+            log.debug("企业维度过滤后结果为空,提前返回");
+            return result;
+        }
+
+        // 3. 处理个人维度的数据权限
+        if (!perm.isPersonalDataEnabled()) {
+            log.debug("未启用个人数据权限,返回当前过滤结果");
+            return result;
+        }
+
+        log.debug("启用个人数据权限,开始执行销售员维度过滤");
+        return filterLogOrderIdsByTradeContractSalesman(result, perm);
+    }
+
+    /**
+     * 个人数据权限过滤:仅保留关联贸易合同中销售员为当前用户的物流订单。
+     * <p>
+     * 逻辑链路:物流订单 -> 贸易订单ID -> 贸易合同ID -> 合同基本信息(销售员ID)
+     * </p>
+     *
+     * @param logOrderIds 待过滤的物流订单ID集合
+     * @param perm        数据权限配置对象
+     * @return 过滤后的物流订单ID集合
+     */
+    private Set<Long> filterLogOrderIdsByTradeContractSalesman(Set<Long> logOrderIds, DataPermissionDTO perm) {
+        Long currentUserId = perm.getUserId();
+        log.debug("开始个人数据权限过滤,当前用户ID: {}, 待过滤物流订单ID数量: {}", currentUserId, logOrderIds.size());
+
+        // 校验用户ID
+        if (currentUserId == null) {
+            log.warn("个人数据权限启用但用户ID为空,返回空集合");
+            return Sets.newHashSet();
+        }
+
+        // 1. 查询物流订单详情,获取关联的贸易订单ID
         List<KwtLogisticsOrder> orders = logisticsOrderRepository.queryByLogOrderIds(logOrderIds);
         if (CollectionUtils.isEmpty(orders)) {
+            log.debug("未查询到对应的物流订单详情,返回空集合");
             return Sets.newHashSet();
         }
 
-        // 2. 提取关联的贸易订单ID
+        // 提取所有非空的贸易订单ID
         Set<Long> tradeIds = orders.stream()
                 .map(KwtLogisticsOrder::getTOrderId)
                 .filter(Objects::nonNull)
                 .collect(Collectors.toSet());
+                
         if (CollectionUtils.isEmpty(tradeIds)) {
+            log.debug("物流订单中未关联有效的贸易订单ID,返回空集合");
             return Sets.newHashSet();
         }
+        log.debug("提取到贸易订单ID数量: {}", tradeIds.size());
 
-        // 3. 查询贸易订单与合同的关联关系
+        // 2. 查询贸易订单与贸易合同的关联关系
         List<TradeOrderContractVo> contractLinks = tradeOrderInfoService.queryTradeOrderIds(tradeIds);
         Map<Long, Long> tradeIdToContractId = new HashMap<>();
         if (CollectionUtils.isNotEmpty(contractLinks)) {
@@ -3699,56 +3773,46 @@ public class KwtWaybillOrderV1Service {
                 if (link.getTOrderId() == null || link.getContractId() == null) {
                     continue;
                 }
+                // 使用 putIfAbsent 确保一个贸易订单只映射一个合同ID(通常是一对一)
                 tradeIdToContractId.putIfAbsent(link.getTOrderId(), link.getContractId());
             }
         }
+        log.debug("建立贸易订单到合同ID的映射,映射数量: {}", tradeIdToContractId.size());
 
-        // 4. 批量查询合同基础信息
+        // 3. 批量查询合同基本信息(包含销售员ID)
         List<Long> contractIdList = tradeIdToContractId.values().stream().distinct().collect(Collectors.toList());
         Map<Long, ContractCommonInfoResDto> contractMap = Maps.newHashMap();
         if (CollectionUtils.isNotEmpty(contractIdList)) {
             contractMap = remoteContractService.queryContractBaseInfo(contractIdList);
+            log.debug("批量查询合同基本信息完成,获取合同数量: {}", contractMap.size());
         }
 
-        // 5. 根据权限规则筛选允许的贸易订单ID
+        // 4. 筛选出销售员为当前用户的贸易订单ID
         Set<Long> allowedTradeIds = new HashSet<>();
         for (Long tid : tradeIds) {
             Long cid = tradeIdToContractId.get(tid);
             if (cid == null) {
                 continue;
             }
-            ContractCommonInfoResDto c = contractMap.get(cid);
-            if (c == null) {
+            ContractCommonInfoResDto contractInfo = contractMap.get(cid);
+            if (contractInfo == null) {
                 continue;
             }
-
-            // 校验企业权限:如果不是全部可见,则检查合同所属企业是否在可见列表中
-            boolean entOk = true;
-            if (!perm.isAllVisible()) {
-                if (CollectionUtils.isEmpty(perm.getVisibleEntIds())) {
-                    entOk = false;
-                } else {
-                    entOk = c.getEntId() != null && perm.getVisibleEntIds().contains(c.getEntId());
-                }
-            }
-
-            // 校验个人数据权限:如果启用个人数据权限,则检查合同销售员是否为当前用户
-            boolean salesOk = true;
-            if (perm.isPersonalDataEnabled()) {
-                salesOk = perm.getUserId() != null && perm.getUserId().equals(c.getSalesmanId());
-            }
-
-            // 同时满足企业权限和个人数据权限才允许访问
-            if (entOk && salesOk) {
+            // 判断合同的销售员是否为当前用户
+            if (currentUserId.equals(contractInfo.getSalesmanId())) {
                 allowedTradeIds.add(tid);
             }
         }
+        log.debug("个人权限过滤:当前用户作为销售员的贸易订单ID数量: {}", allowedTradeIds.size());
 
-        // 6. 返回符合权限要求的物流订单ID集合
-        return orders.stream()
-                .filter(o -> o.getTOrderId() != null && allowedTradeIds.contains(o.getTOrderId()))
+        // 5. 根据允许的贸易订单ID,反查并返回对应的物流订单ID
+        Set<Long> finalResult = orders.stream()
+                .filter(order -> order.getTOrderId() != null && allowedTradeIds.contains(order.getTOrderId()))
                 .map(KwtLogisticsOrder::getId)
                 .collect(Collectors.toSet());
+                
+        log.debug("个人数据权限过滤完成,最终保留物流订单ID数量: {}", finalResult.size());
+        return finalResult;
     }
 
     /**