chenxiaofei 12 часов назад
Родитель
Сommit
c4adac43c6

+ 8 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/dao/KwsEnterpriseDao.java

@@ -243,4 +243,12 @@ public interface KwsEnterpriseDao extends BaseMapper<KwsEnterprise> {
     EntInfo query(@Param("entName") String entName);
     EntInfo query(@Param("entName") String entName);
 
 
     List<EntBaseInfo> queryCte(Long entId);
     List<EntBaseInfo> queryCte(Long entId);
+
+    /**
+     * 批量查询多个根企业及其全部下级子企业(递归CTE,一次查询)。
+     *
+     * @param entIds 根企业ID列表
+     * @return 去重后的企业树节点
+     */
+    List<EntBaseInfo> queryCteByEntIds(@Param(value = "list") List<Long> entIds);
 }
 }

+ 280 - 7
sckw-modules/sckw-system/src/main/java/com/sckw/system/service/KwsMenuService.java

@@ -3,6 +3,7 @@ package com.sckw.system.service;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.sckw.contract.api.RemoteContractService;
 import com.sckw.contract.api.RemoteContractService;
@@ -25,6 +26,7 @@ import com.sckw.system.dao.*;
 import com.sckw.system.dubbo.RemoteBaseService;
 import com.sckw.system.dubbo.RemoteBaseService;
 import com.sckw.system.model.*;
 import com.sckw.system.model.*;
 import com.sckw.system.model.pojo.FindEntListPojo;
 import com.sckw.system.model.pojo.FindEntListPojo;
+import com.sckw.system.model.pojo.FindManagePojo;
 import com.sckw.system.model.pojo.FindMenuTreePojo;
 import com.sckw.system.model.pojo.FindMenuTreePojo;
 import com.sckw.system.model.pojo.FindPojoParam;
 import com.sckw.system.model.pojo.FindPojoParam;
 import com.sckw.system.model.vo.req.*;
 import com.sckw.system.model.vo.req.*;
@@ -137,7 +139,6 @@ public class KwsMenuService {
         if (kwsMenuDao.insert(params) <= 0) {
         if (kwsMenuDao.insert(params) <= 0) {
             throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.INSERT_FAIL);
             throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.INSERT_FAIL);
         }
         }
-
 //        List<FindEntListResVo> list = kwsEnterpriseDao.findList(new FindListReqVo());
 //        List<FindEntListResVo> list = kwsEnterpriseDao.findList(new FindListReqVo());
 //        if (CollectionUtils.isNotEmpty(list)) {
 //        if (CollectionUtils.isNotEmpty(list)) {
 //            List<Long> entIds = list.stream().map(FindEntListResVo::getId).distinct().toList();
 //            List<Long> entIds = list.stream().map(FindEntListResVo::getId).distinct().toList();
@@ -161,7 +162,275 @@ public class KwsMenuService {
 //                throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.INSERT_FAIL);
 //                throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.INSERT_FAIL);
 //            }
 //            }
 //        }
 //        }
+        initMenuRightsByUsingRoles(menuId, params.getUsingRoles());
+    }
+
+    /**
+     * 根据菜单适用企业类型,为对应企业主账号的管理角色初始化菜单权限。
+     *
+     * @param menuId     新增菜单ID
+     * @param usingRoles 适用企业类型,逗号分隔(如 {@code "1,2,3"})
+     * @see #initMenuRightsByEntTypes(Long, List)
+     */
+    private void initMenuRightsByUsingRoles(Long menuId, String usingRoles) {
+        initMenuRightsByEntTypes(menuId, parseUsingRolesToEntTypes(usingRoles, menuId));
+    }
+
+    /**
+     * 解析菜单适用企业类型编码。
+     *
+     * @param usingRoles 适用企业类型字符串
+     * @param menuId     菜单ID,仅用于日志
+     * @return 去重后的企业类型编码列表,无有效值时返回空列表
+     */
+    private List<Integer> parseUsingRolesToEntTypes(String usingRoles, Long menuId) {
+        if (StringUtils.isBlank(usingRoles)) {
+            return Collections.emptyList();
+        }
+        return Arrays.stream(usingRoles.split(Global.COMMA))
+                .map(String::trim)
+                .filter(StringUtils::isNotBlank)
+                .map(type -> {
+                    try {
+                        return Integer.valueOf(type);
+                    } catch (NumberFormatException e) {
+                        log.warn("菜单权限处理忽略非法企业类型编码, menuId={}, 非法编码={}", menuId, type);
+                        return null;
+                    }
+                })
+                .filter(Objects::nonNull)
+                .distinct()
+                .toList();
+    }
+
+    /**
+     * 根据企业类型查询有效企业ID。
+     */
+    private List<Long> findEntIdsByEntTypes(List<Integer> entTypeList) {
+        if (CollectionUtils.isEmpty(entTypeList)) {
+            return Collections.emptyList();
+        }
+        List<KwsEntType> entTypeRecords = kwsEntTypeDao.selectList(Wrappers.lambdaQuery(KwsEntType.class)
+                .eq(KwsEntType::getDelFlag, Global.NO)
+                .eq(KwsEntType::getStatus, Global.NO)
+                .in(KwsEntType::getType, entTypeList));
+        if (CollectionUtils.isEmpty(entTypeRecords)) {
+            return Collections.emptyList();
+        }
+        return entTypeRecords.stream()
+                .map(KwsEntType::getEntId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .toList();
+    }
+
+    /**
+     * 为指定企业类型对应的管理企业主账号角色新增菜单权限。
+     * <p>
+     * 仅写入管理企业权限,不包含子企业。
+     */
+    private void initMenuRightsByEntTypes(Long menuId, List<Integer> entTypeList) {
+        log.info("开始按企业类型新增菜单权限, menuId={}, 企业类型列表={}", menuId, JSON.toJSONString(entTypeList));
+        if (CollectionUtils.isEmpty(entTypeList)) {
+            log.info("按企业类型新增菜单权限跳过:企业类型列表为空, menuId={}", menuId);
+            return;
+        }
+
+        List<Long> entIds = findEntIdsByEntTypes(entTypeList);
+        if (CollectionUtils.isEmpty(entIds)) {
+            log.info("按企业类型新增菜单权限跳过:未找到匹配企业, menuId={}, 企业类型列表={}", menuId, JSON.toJSONString(entTypeList));
+            return;
+        }
+        log.info("按企业类型新增菜单权限匹配到企业, menuId={}, 企业数量={}, 企业ID列表={}", menuId, entIds.size(), entIds);
+
+        List<FindManagePojo> manageList = kwsEnterpriseDao.findManageInfoByEntIds(entIds);
+        if (CollectionUtils.isEmpty(manageList)) {
+            log.info("按企业类型新增菜单权限跳过:未找到企业主账号管理角色, menuId={}, 企业ID列表={}", menuId, JSON.toJSONString(entIds));
+            return;
+        }
+
+        Set<String> existingKeys = kwsMenuRightsDao.selectByMenuIds(Collections.singletonList(menuId)).stream()
+                .filter(item -> item.getEntId() != null && item.getRoleId() != null)
+                .map(item -> item.getEntId() + "_" + item.getRoleId())
+                .collect(Collectors.toSet());
+
+        Date date = new Date();
+        Long operatorId = LoginUserHolder.getUserId();
+        List<KwsMenuRights> kwsMenuRightsList = new ArrayList<>();
+        int skippedCount = 0;
+        int duplicateCount = 0;
+        for (FindManagePojo manage : manageList) {
+            if (manage.getEntId() == null || manage.getRoleId() == null) {
+                skippedCount++;
+                log.warn("按企业类型新增菜单权限跳过无效管理角色记录, menuId={}, entId={}, roleId={}, userId={}",
+                        menuId, manage.getEntId(), manage.getRoleId(), manage.getUserId());
+                continue;
+            }
+            String rightsKey = manage.getEntId() + "_" + manage.getRoleId();
+            if (existingKeys.contains(rightsKey)) {
+                duplicateCount++;
+                continue;
+            }
+            KwsMenuRights kwsMenuRights = new KwsMenuRights();
+            kwsMenuRights.setEntId(manage.getEntId());
+            kwsMenuRights.setMenuId(menuId);
+            kwsMenuRights.setRoleId(manage.getRoleId());
+            kwsMenuRights.setId(new IdWorker(1L).nextId());
+            kwsMenuRights.setDelFlag(Global.NO);
+            kwsMenuRights.setStatus(Global.NO);
+            kwsMenuRights.setCreateBy(operatorId);
+            kwsMenuRights.setUpdateBy(operatorId);
+            kwsMenuRights.setCreateTime(date);
+            kwsMenuRights.setUpdateTime(date);
+            kwsMenuRightsList.add(kwsMenuRights);
+        }
+
+        if (CollectionUtils.isEmpty(kwsMenuRightsList)) {
+            log.info("按企业类型新增菜单权限结束:无可写入记录, menuId={}, 跳过无效记录数={}, 已存在记录数={}",
+                    menuId, skippedCount, duplicateCount);
+            return;
+        }
+
+        int savedCount = kwsMenuRightsDao.saveBatch(kwsMenuRightsList);
+        if (savedCount < kwsMenuRightsList.size()) {
+            log.error("按企业类型新增菜单权限失败:批量写入不完整, menuId={}, 待写入数={}, 实际写入数={}",
+                    menuId, kwsMenuRightsList.size(), savedCount);
+            throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.INSERT_FAIL);
+        }
+        log.info("按企业类型新增菜单权限成功, menuId={}, 写入权限记录数={}, 跳过无效记录数={}, 已存在记录数={}, 操作人={}",
+                menuId, savedCount, skippedCount, duplicateCount, operatorId);
+    }
+
+    /**
+     * 将企业ID扩展为「本企业 + 全部下级子企业」。
+     * <p>
+     * 使用递归 CTE 批量查询,避免按根企业循环查库。
+     */
+    private List<Long> expandEntIdsWithSubEnterprises(List<Long> rootEntIds) {
+        if (CollectionUtils.isEmpty(rootEntIds)) {
+            return Collections.emptyList();
+        }
+        List<Long> distinctRootEntIds = rootEntIds.stream()
+                .filter(Objects::nonNull)
+                .distinct()
+                .toList();
+        if (CollectionUtils.isEmpty(distinctRootEntIds)) {
+            return Collections.emptyList();
+        }
+
+        List<EntBaseInfo> entTree = kwsEnterpriseDao.queryCteByEntIds(distinctRootEntIds);
+        if (CollectionUtils.isEmpty(entTree)) {
+            return new ArrayList<>(distinctRootEntIds);
+        }
+
+        Set<Long> allEntIds = entTree.stream()
+                .map(EntBaseInfo::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+        // 根企业若在库中无记录,仍保留入参ID,与原逻辑一致
+        distinctRootEntIds.forEach(allEntIds::add);
+        return new ArrayList<>(allEntIds);
+    }
+
+    /**
+     * 按企业类型移除菜单权限,范围包含匹配企业管理企业及其全部子企业。
+     */
+    private void removeMenuRightsByEntTypes(Long menuId, List<Integer> removedEntTypes) {
+        log.info("开始按企业类型移除菜单权限, menuId={}, 移除企业类型列表={}", menuId, removedEntTypes);
+        if (CollectionUtils.isEmpty(removedEntTypes)) {
+            return;
+        }
+
+        List<Long> manageEntIds = findEntIdsByEntTypes(removedEntTypes);
+        if (CollectionUtils.isEmpty(manageEntIds)) {
+            log.info("按企业类型移除菜单权限跳过:未找到匹配企业管理企业, menuId={}, 移除企业类型列表={}", menuId, removedEntTypes);
+            return;
+        }
+
+        List<Long> affectedEntIds = expandEntIdsWithSubEnterprises(manageEntIds);
+        log.info("按企业类型移除菜单权限影响范围, menuId={}, 管理企业数={}, 含子企业后企业数={}, 企业ID列表={}",
+                menuId, manageEntIds.size(), affectedEntIds.size(), affectedEntIds);
+
+        List<KwsMenuRights> menuRights = kwsMenuRightsDao.selectByMenuIds(Collections.singletonList(menuId));
+        if (CollectionUtils.isEmpty(menuRights)) {
+            log.info("按企业类型移除菜单权限跳过:当前菜单无权限记录, menuId={}", menuId);
+            return;
+        }
+
+        Set<Long> affectedEntIdSet = new HashSet<>(affectedEntIds);
+        List<KwsMenuRights> toRemove = menuRights.stream()
+                .filter(item -> item.getEntId() != null && affectedEntIdSet.contains(item.getEntId()))
+                .toList();
+        if (CollectionUtils.isEmpty(toRemove)) {
+            log.info("按企业类型移除菜单权限跳过:影响范围内无待删除权限, menuId={}", menuId);
+            return;
+        }
 
 
+        deleteMenuRights(toRemove);
+        log.info("按企业类型移除菜单权限成功, menuId={}, 删除权限记录数={}", menuId, toRemove.size());
+    }
+
+    /**
+     * 菜单适用企业类型({@code usingRoles})变更后,同步增删 {@code kws_menu_rights} 权限数据。
+     * <p>
+     * 调用时机:{@link #update(KwsMenu)} 更新菜单成功后,对比数据库原值与入参新值触发。
+     * <p>
+     * 企业类型编码约定:1-供应商、2-采购商、3-4PL物流、4-3PL物流。
+     * <p>
+     * 同步规则:
+     * <ol>
+     *     <li>类型集合未变化(仅顺序不同视为未变化):不做任何处理</li>
+     *     <li>减少的类型:调用 {@link #removeMenuRightsByEntTypes},
+     *         移除对应管理企业及其全部下级子企业的该菜单权限</li>
+     *     <li>新增的类型:调用 {@link #initMenuRightsByEntTypes},
+     *         仅为对应管理企业主账号的管理角色新增权限(不含子企业)</li>
+     * </ol>
+     * <p>
+     * 示例:{@code usingRoles} 由 {@code "1,2,3"} 改为 {@code "1,2"} 时,
+     * 移除类型 {@code 3} 关联企业的菜单权限;由 {@code "1,2"} 改为 {@code "1,2,3"} 时,
+     * 仅为类型 {@code 3} 的管理企业新增权限。
+     *
+     * @param menuId        菜单ID
+     * @param oldUsingRoles 变更前的适用企业类型,逗号分隔
+     * @param newUsingRoles 变更后的适用企业类型,逗号分隔
+     */
+    private void syncMenuRightsOnUsingRolesChange(Long menuId, String oldUsingRoles, String newUsingRoles) {
+        log.info("开始同步菜单权限, menuId={}, 原usingRoles={}, 新usingRoles={}", menuId, oldUsingRoles, newUsingRoles);
+
+        // 1. 解析变更前后的企业类型编码(去重、过滤非法值)
+        List<Integer> oldEntTypes = parseUsingRolesToEntTypes(oldUsingRoles, menuId);
+        List<Integer> newEntTypes = parseUsingRolesToEntTypes(newUsingRoles, menuId);
+        log.info("菜单权限同步解析完成, menuId={}, 原企业类型列表={}, 新企业类型列表={}", menuId, JSON.toJSONString(oldEntTypes),JSON.toJSONString(newEntTypes));
+
+        // 2. 集合比较(忽略顺序),无变化则跳过
+        if (Objects.equals(new HashSet<>(oldEntTypes), new HashSet<>(newEntTypes))) {
+            log.info("菜单权限同步跳过:适用企业类型未变化, menuId={}, usingRoles={}", menuId, newUsingRoles);
+            return;
+        }
+
+        // 3. 计算差异:被移除的类型、新增的类型
+        List<Integer> removedEntTypes = oldEntTypes.stream().filter(type -> !newEntTypes.contains(type)).toList();
+        List<Integer> addedEntTypes = newEntTypes.stream().filter(type -> !oldEntTypes.contains(type)).toList();
+        log.info("菜单权限同步检测到类型差异, menuId={}, 移除企业类型={}, 新增企业类型={}",
+                menuId, JSON.toJSONString(removedEntTypes), JSON.toJSONString(addedEntTypes));
+
+        // 4. 先移除后新增,避免同一企业因类型调整产生脏数据
+        if (CollectionUtils.isNotEmpty(removedEntTypes)) {
+            log.info("菜单权限同步开始处理移除类型, menuId={}, 移除企业类型={}", menuId, JSON.toJSONString(removedEntTypes));
+            removeMenuRightsByEntTypes(menuId, removedEntTypes);
+        } else {
+            log.info("菜单权限同步无需移除权限, menuId={}", menuId);
+        }
+
+        if (CollectionUtils.isNotEmpty(addedEntTypes)) {
+            log.info("菜单权限同步开始处理新增类型, menuId={}, 新增企业类型={}", menuId, addedEntTypes);
+            initMenuRightsByEntTypes(menuId, addedEntTypes);
+        } else {
+            log.info("菜单权限同步无需新增权限, menuId={}", menuId);
+        }
+
+        log.info("菜单权限同步完成, menuId={}, 原usingRoles={}, 新usingRoles={}, 移除企业类型={}, 新增企业类型={}",
+                menuId, oldUsingRoles, newUsingRoles, removedEntTypes, addedEntTypes);
     }
     }
 
 
     /**
     /**
@@ -255,17 +524,21 @@ public class KwsMenuService {
 
 
 
 
     /**
     /**
+     * 更新菜单,并在 {@code usingRoles} 变更时同步菜单权限。
+     *
      * @param params 更新记录
      * @param params 更新记录
-     * @desc: 更新记录
-     * @author: czh
-     * @date: 2023/7/3
      */
      */
+    @Transactional(rollbackFor = Exception.class)
     public void update(KwsMenu params) {
     public void update(KwsMenu params) {
+        KwsMenu existingMenu = kwsMenuDao.selectByKey(params.getId());
+        if (Objects.isNull(existingMenu)) {
+            throw new SystemException(HttpStatus.QUERY_FAIL_CODE, HttpStatus.MENU_NOT_EXISTS);
+        }
         if (kwsMenuDao.update(params) <= 0) {
         if (kwsMenuDao.update(params) <= 0) {
             throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.UPDATE_FAIL);
             throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.UPDATE_FAIL);
         }
         }
+        syncMenuRightsOnUsingRolesChange(params.getId(), existingMenu.getUsingRoles(), params.getUsingRoles());
     }
     }
-
     /**
     /**
      * @param key 主键
      * @param key 主键
      * @return KwsMenu
      * @return KwsMenu
@@ -635,8 +908,8 @@ public class KwsMenuService {
         extracted(reqVo.getUserId(), reqVo.getClientType(), findMenuTreePojo);
         extracted(reqVo.getUserId(), reqVo.getClientType(), findMenuTreePojo);
         List<KwsMenuResVo> menuList = new ArrayList<>();
         List<KwsMenuResVo> menuList = new ArrayList<>();
         if (Objects.equals(LoginUserHolder.getSystemType(), SystemTypeEnum.COMPANY.getCode()) && org.apache.commons.lang3.StringUtils.equals(reqVo.getClientType(), String.valueOf(Global.NUMERICAL_THREE))) {
         if (Objects.equals(LoginUserHolder.getSystemType(), SystemTypeEnum.COMPANY.getCode()) && org.apache.commons.lang3.StringUtils.equals(reqVo.getClientType(), String.valueOf(Global.NUMERICAL_THREE))) {
-           // menuList = kwsMenuDao.findList1(findMenuTreePojo);
-            menuList = kwsMenuDao.findList2(findMenuTreePojo);
+            menuList = kwsMenuDao.findList1(findMenuTreePojo);
+           // menuList = kwsMenuDao.findList2(findMenuTreePojo);
         }else {
         }else {
             menuList = kwsMenuDao.findList(findMenuTreePojo);
             menuList = kwsMenuDao.findList(findMenuTreePojo);
         }
         }

+ 18 - 0
sckw-modules/sckw-system/src/main/resources/mapper/KwsEnterpriseDao.xml

@@ -944,4 +944,22 @@
         select *
         select *
         from ent_tree;
         from ent_tree;
     </select>
     </select>
+
+    <select id="queryCteByEntIds" resultType="com.sckw.system.model.vo.res.EntBaseInfo">
+        with RECURSIVE ent_tree as
+                           (select id, pid, firm_name
+                            from kws_enterprise
+                            where del_flag = 0
+                              and id in
+                            <foreach collection="list" item="item" open="(" close=")" separator=",">
+                                #{item}
+                            </foreach>
+                            union all
+                            select ke.id, ke.pid, ke.firm_name
+                            from kws_enterprise ke
+                                     inner join ent_tree et on et.id = ke.pid
+                            where ke.del_flag = 0)
+        select distinct id, pid, firm_name
+        from ent_tree;
+    </select>
 </mapper>
 </mapper>

+ 6 - 4
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/ParkingChangeStrategyService.java

@@ -268,11 +268,13 @@ public class ParkingChangeStrategyService {
         if (Objects.equals(param.getStatus(), Global.YES) && param.getDefaultFee() == null) {
         if (Objects.equals(param.getStatus(), Global.YES) && param.getDefaultFee() == null) {
             throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "开关打开后,默认费用不能为空");
             throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "开关打开后,默认费用不能为空");
         }
         }
-        strategySwitch.setStatus(param.getStatus());
-        strategySwitch.setDefaultFee(param.getDefaultFee());
-        parkingStrategySwitchRepository.updateById(strategySwitch);
+        KwtParkingStrategySwitch kwtParkingStrategySwitch = new KwtParkingStrategySwitch();
+        kwtParkingStrategySwitch.setId(strategySwitch.getId());
+        kwtParkingStrategySwitch.setStatus(param.getStatus());
+        kwtParkingStrategySwitch.setDefaultFee(param.getDefaultFee());
+        parkingStrategySwitchRepository.updateById(kwtParkingStrategySwitch);
         log.info("收费策略开关修改完成,id:{}, status:{}, defaultFee:{}",
         log.info("收费策略开关修改完成,id:{}, status:{}, defaultFee:{}",
-                strategySwitch.getId(), strategySwitch.getStatus(), strategySwitch.getDefaultFee());
+                kwtParkingStrategySwitch.getId(), kwtParkingStrategySwitch.getStatus(), kwtParkingStrategySwitch.getDefaultFee());
     }
     }
 
 
     /**
     /**