Bläddra i källkod

新增快捷解绑、查询接口

fanghuisheng 2 dagar sedan
förälder
incheckning
a641598b76

+ 29 - 0
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/controller/web/SysMenuController.java

@@ -17,6 +17,10 @@ import com.usky.system.domain.constants.UserConstants;
 import com.usky.system.model.LoginUser;
 import com.usky.system.service.ISysMenuService;
 import com.usky.system.service.ISysUserService;
+import com.usky.system.service.SysMenuCascadeService;
+import com.usky.system.service.vo.MenuBindingsResult;
+import com.usky.system.service.vo.MenuUnbindRequest;
+import com.usky.system.service.vo.MenuUnbindResult;
 import com.usky.system.service.vo.PlatformRouterVo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -46,6 +50,9 @@ public class SysMenuController extends BaseController
     @Autowired
     private TokenService tokenService;
 
+    @Autowired
+    private SysMenuCascadeService sysMenuCascadeService;
+
     /**
      * 获取菜单列表
      */
@@ -230,4 +237,26 @@ public class SysMenuController extends BaseController
         menuService.updateTenantMenu(tenantMenu);
         return ApiResult.success();
     }
+
+    /**
+     * 查询菜单关联绑定(子菜单、角色、租户、平台、帮助中心栏目)
+     * <p>示例:GET /menu/bindings/1607</p>
+     */
+    @GetMapping("/bindings/{menuId}")
+    public ApiResult<MenuBindingsResult> getBindings(@PathVariable("menuId") Long menuId)
+    {
+        return ApiResult.success(sysMenuCascadeService.getMenuBindings(menuId));
+    }
+
+    /**
+     * 精确解绑菜单关联(不删除菜单本身)
+     * <p>顺序:角色 → 租户 → 应用(平台);必须指定要解绑的ID,禁止整菜单全量清空</p>
+     * <p>示例:POST /menu/unbind</p>
+     */
+    @Log(title = "解绑-菜单关联", businessType = BusinessType.DELETE)
+    @PostMapping("/unbind")
+    public ApiResult<MenuUnbindResult> unbind(@RequestBody MenuUnbindRequest request)
+    {
+        return ApiResult.success(sysMenuCascadeService.unbindMenu(request));
+    }
 }

+ 57 - 0
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/mapper/SysMenuCascadeMapper.java

@@ -0,0 +1,57 @@
+package com.usky.system.mapper;
+
+import com.usky.system.service.vo.MenuBindingsResult;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * 菜单绑定查询 / 解绑专用 Mapper
+ */
+@Repository
+public interface SysMenuCascadeMapper {
+
+    /**
+     * 查询直接子菜单明细
+     */
+    List<MenuBindingsResult.ChildMenuItem> selectChildMenus(@Param("parentId") Long parentId);
+
+    /**
+     * 查询角色-菜单绑定
+     */
+    List<MenuBindingsResult.RoleBindItem> selectRoleBinds(@Param("menuId") Long menuId);
+
+    /**
+     * 查询租户-菜单绑定
+     */
+    List<MenuBindingsResult.TenantBindItem> selectTenantBinds(@Param("menuId") Long menuId);
+
+    /**
+     * 查询平台-菜单绑定
+     */
+    List<MenuBindingsResult.PlatformBindItem> selectPlatformBinds(@Param("menuId") Long menuId);
+
+    /**
+     * 查询帮助中心栏目绑定
+     */
+    List<MenuBindingsResult.HceCategoryBindItem> selectHceCategoryBinds(@Param("menuId") Long menuId);
+
+    /**
+     * 按菜单 + 角色精确删除角色-菜单关联
+     */
+    int deleteRoleMenuByMenuIdAndRoleIds(@Param("menuId") Long menuId,
+                                         @Param("roleIds") List<Long> roleIds);
+
+    /**
+     * 按菜单 + 租户精确删除租户-菜单关联
+     */
+    int deleteTenantMenuByMenuIdAndTenantIds(@Param("menuId") Long menuId,
+                                             @Param("tenantIds") List<Long> tenantIds);
+
+    /**
+     * 按菜单 + 平台精确删除平台-菜单关联
+     */
+    int deletePlatformMenuByMenuIdAndPlatformIds(@Param("menuId") Long menuId,
+                                                 @Param("platformIds") List<Long> platformIds);
+}

+ 28 - 0
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/SysMenuCascadeService.java

@@ -0,0 +1,28 @@
+package com.usky.system.service;
+
+import com.usky.system.service.vo.MenuBindingsResult;
+import com.usky.system.service.vo.MenuUnbindRequest;
+import com.usky.system.service.vo.MenuUnbindResult;
+
+/**
+ * 菜单绑定查询 / 解绑(独立于原有单条删除逻辑)
+ */
+public interface SysMenuCascadeService {
+
+    /**
+     * 查询菜单的关联绑定(子菜单、角色、租户、平台、帮助中心栏目)
+     *
+     * @param menuId 菜单ID
+     * @return 绑定详情
+     */
+    MenuBindingsResult getMenuBindings(Long menuId);
+
+    /**
+     * 精确解绑菜单关联:角色 → 租户 → 应用(平台)
+     * <p>必须指定 roleIds / tenantIds / platformIds 中至少一类,避免清空该菜单全部绑定</p>
+     *
+     * @param request 解绑请求
+     * @return 解绑统计
+     */
+    MenuUnbindResult unbindMenu(MenuUnbindRequest request);
+}

+ 128 - 0
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/impl/SysMenuCascadeServiceImpl.java

@@ -0,0 +1,128 @@
+package com.usky.system.service.impl;
+
+import com.usky.common.core.exception.BusinessException;
+import com.usky.system.domain.SysMenu;
+import com.usky.system.mapper.SysMenuCascadeMapper;
+import com.usky.system.mapper.SysMenuMapper;
+import com.usky.system.service.SysMenuCascadeService;
+import com.usky.system.service.vo.MenuBindingsResult;
+import com.usky.system.service.vo.MenuUnbindRequest;
+import com.usky.system.service.vo.MenuUnbindResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 菜单绑定查询 / 解绑实现
+ */
+@Service
+public class SysMenuCascadeServiceImpl implements SysMenuCascadeService {
+
+    @Autowired
+    private SysMenuMapper sysMenuMapper;
+
+    @Autowired
+    private SysMenuCascadeMapper sysMenuCascadeMapper;
+
+    @Override
+    public MenuBindingsResult getMenuBindings(Long menuId) {
+        if (menuId == null) {
+            throw new BusinessException("lack necessary param:menuId is required!");
+        }
+        SysMenu menu = sysMenuMapper.selectMenuById(menuId);
+        if (menu == null) {
+            throw new BusinessException("菜单不存在,menuId=" + menuId);
+        }
+
+        MenuBindingsResult result = new MenuBindingsResult();
+        result.setMenuId(menu.getMenuId());
+        result.setMenuName(menu.getMenuName());
+        result.setPath(menu.getPath());
+
+        List<MenuBindingsResult.ChildMenuItem> childMenus = sysMenuCascadeMapper.selectChildMenus(menuId);
+        if (childMenus == null) {
+            childMenus = Collections.emptyList();
+        }
+        result.setChildMenus(childMenus);
+        result.setChildMenuCount(childMenus.size());
+
+        List<MenuBindingsResult.RoleBindItem> roleBinds = sysMenuCascadeMapper.selectRoleBinds(menuId);
+        if (roleBinds == null) {
+            roleBinds = Collections.emptyList();
+        }
+        result.setRoleBinds(roleBinds);
+        result.setRoleBindCount(roleBinds.size());
+
+        List<MenuBindingsResult.TenantBindItem> tenantBinds = sysMenuCascadeMapper.selectTenantBinds(menuId);
+        if (tenantBinds == null) {
+            tenantBinds = Collections.emptyList();
+        }
+        result.setTenantBinds(tenantBinds);
+        result.setTenantBindCount(tenantBinds.size());
+
+        List<MenuBindingsResult.PlatformBindItem> platformBinds = sysMenuCascadeMapper.selectPlatformBinds(menuId);
+        if (platformBinds == null) {
+            platformBinds = Collections.emptyList();
+        }
+        result.setPlatformBinds(platformBinds);
+        result.setPlatformBindCount(platformBinds.size());
+
+        List<MenuBindingsResult.HceCategoryBindItem> hceCategories = sysMenuCascadeMapper.selectHceCategoryBinds(menuId);
+        if (hceCategories == null) {
+            hceCategories = Collections.emptyList();
+        }
+        result.setHceCategories(hceCategories);
+        result.setHceCategoryCount(hceCategories.size());
+
+        result.setHasBinding(
+                result.getChildMenuCount() > 0
+                        || result.getRoleBindCount() > 0
+                        || result.getTenantBindCount() > 0
+                        || result.getPlatformBindCount() > 0
+                        || result.getHceCategoryCount() > 0
+        );
+        return result;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public MenuUnbindResult unbindMenu(MenuUnbindRequest request) {
+        if (request == null || request.getMenuId() == null) {
+            throw new BusinessException("lack necessary param:menuId is required!");
+        }
+        boolean hasRole = !CollectionUtils.isEmpty(request.getRoleIds());
+        boolean hasTenant = !CollectionUtils.isEmpty(request.getTenantIds());
+        boolean hasPlatform = !CollectionUtils.isEmpty(request.getPlatformIds());
+        if (!hasRole && !hasTenant && !hasPlatform) {
+            throw new BusinessException("请指定要解绑的 roleIds / tenantIds / platformIds,禁止整菜单全量解绑");
+        }
+
+        SysMenu menu = sysMenuMapper.selectMenuById(request.getMenuId());
+        if (menu == null) {
+            throw new BusinessException("菜单不存在,menuId=" + request.getMenuId());
+        }
+
+        MenuUnbindResult result = new MenuUnbindResult();
+        result.setMenuId(menu.getMenuId());
+        result.setMenuName(menu.getMenuName());
+
+        // 解绑顺序:角色 → 租户 → 应用(平台)
+        if (hasRole) {
+            result.setRoleUnbindCount(Math.max(
+                    sysMenuCascadeMapper.deleteRoleMenuByMenuIdAndRoleIds(request.getMenuId(), request.getRoleIds()), 0));
+        }
+        if (hasTenant) {
+            result.setTenantUnbindCount(Math.max(
+                    sysMenuCascadeMapper.deleteTenantMenuByMenuIdAndTenantIds(request.getMenuId(), request.getTenantIds()), 0));
+        }
+        if (hasPlatform) {
+            result.setPlatformUnbindCount(Math.max(
+                    sysMenuCascadeMapper.deletePlatformMenuByMenuIdAndPlatformIds(request.getMenuId(), request.getPlatformIds()), 0));
+        }
+        return result;
+    }
+}

+ 94 - 0
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/vo/MenuBindingsResult.java

@@ -0,0 +1,94 @@
+package com.usky.system.service.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 菜单关联绑定查询结果
+ */
+@Data
+public class MenuBindingsResult {
+
+    /** 当前菜单ID */
+    private Long menuId;
+
+    /** 当前菜单名称 */
+    private String menuName;
+
+    /** 当前路由地址 */
+    private String path;
+
+    /** 是否存在任意绑定(含子菜单) */
+    private boolean hasBinding;
+
+    /** 直接子菜单数量 */
+    private int childMenuCount;
+
+    /** 直接子菜单列表 */
+    private List<ChildMenuItem> childMenus = new ArrayList<>();
+
+    /** 角色绑定数量 */
+    private int roleBindCount;
+
+    /** 角色绑定列表 */
+    private List<RoleBindItem> roleBinds = new ArrayList<>();
+
+    /** 租户绑定数量 */
+    private int tenantBindCount;
+
+    /** 租户绑定列表 */
+    private List<TenantBindItem> tenantBinds = new ArrayList<>();
+
+    /** 平台绑定数量 */
+    private int platformBindCount;
+
+    /** 平台绑定列表 */
+    private List<PlatformBindItem> platformBinds = new ArrayList<>();
+
+    /** 帮助中心栏目绑定数量 */
+    private int hceCategoryCount;
+
+    /** 帮助中心栏目列表 */
+    private List<HceCategoryBindItem> hceCategories = new ArrayList<>();
+
+    @Data
+    public static class ChildMenuItem {
+        private Long menuId;
+        private String menuName;
+        private String path;
+        private String menuType;
+    }
+
+    @Data
+    public static class RoleBindItem {
+        private Long roleId;
+        private String roleName;
+        private String menuAliasName;
+        /** 角色所属租户ID */
+        private Integer tenantId;
+        /** 角色所属租户名称 */
+        private String tenantName;
+    }
+
+    @Data
+    public static class TenantBindItem {
+        private Long tenantId;
+        private String tenantName;
+        private String menuAliasName;
+    }
+
+    @Data
+    public static class PlatformBindItem {
+        private Long platformId;
+        private String platformName;
+    }
+
+    @Data
+    public static class HceCategoryBindItem {
+        private Integer id;
+        private String categoryName;
+        private Integer menuId;
+    }
+}

+ 24 - 0
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/vo/MenuUnbindRequest.java

@@ -0,0 +1,24 @@
+package com.usky.system.service.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 菜单精确解绑请求:必须指定要解绑的角色/租户/应用,避免一次清空全部绑定
+ */
+@Data
+public class MenuUnbindRequest {
+
+    /** 菜单ID(必填) */
+    private Long menuId;
+
+    /** 要解绑的角色ID列表(可选) */
+    private List<Long> roleIds;
+
+    /** 要解绑的租户ID列表(可选) */
+    private List<Long> tenantIds;
+
+    /** 要解绑的应用(平台)ID列表(可选) */
+    private List<Long> platformIds;
+}

+ 25 - 0
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/vo/MenuUnbindResult.java

@@ -0,0 +1,25 @@
+package com.usky.system.service.vo;
+
+import lombok.Data;
+
+/**
+ * 菜单解绑结果(角色 → 租户 → 应用)
+ */
+@Data
+public class MenuUnbindResult {
+
+    /** 菜单ID */
+    private Long menuId;
+
+    /** 菜单名称 */
+    private String menuName;
+
+    /** 解绑的角色-菜单关联数量 */
+    private int roleUnbindCount;
+
+    /** 解绑的租户-菜单关联数量 */
+    private int tenantUnbindCount;
+
+    /** 解绑的应用(平台)-菜单关联数量 */
+    private int platformUnbindCount;
+}

+ 81 - 0
base-modules/service-system/service-system-biz/src/main/resources/mapper/system/SysMenuCascadeMapper.xml

@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.usky.system.mapper.SysMenuCascadeMapper">
+
+    <select id="selectChildMenus" resultType="com.usky.system.service.vo.MenuBindingsResult$ChildMenuItem">
+        select menu_id   as menuId,
+               menu_name as menuName,
+               path,
+               menu_type as menuType
+        from sys_menu
+        where parent_id = #{parentId}
+        order by order_num, menu_id
+    </select>
+
+    <select id="selectRoleBinds" resultType="com.usky.system.service.vo.MenuBindingsResult$RoleBindItem">
+        select rm.role_id         as roleId,
+               r.role_name        as roleName,
+               rm.menu_alias_name as menuAliasName,
+               r.tenant_id        as tenantId,
+               t.tenant_name      as tenantName
+        from sys_role_menu rm
+                 left join sys_role r on rm.role_id = r.role_id
+                 left join sys_tenant t on r.tenant_id = t.id
+        where rm.menu_id = #{menuId}
+    </select>
+
+    <select id="selectTenantBinds" resultType="com.usky.system.service.vo.MenuBindingsResult$TenantBindItem">
+        select tm.tenant_id       as tenantId,
+               t.tenant_name      as tenantName,
+               tm.menu_alias_name as menuAliasName
+        from sys_tenant_menu tm
+                 left join sys_tenant t on tm.tenant_id = t.id
+        where tm.menu_id = #{menuId}
+    </select>
+
+    <select id="selectPlatformBinds" resultType="com.usky.system.service.vo.MenuBindingsResult$PlatformBindItem">
+        select pm.platform_id  as platformId,
+               p.platform_name as platformName
+        from sys_platform_menu pm
+                 left join sys_platform p on pm.platform_id = p.id
+        where pm.menu_id = #{menuId}
+    </select>
+
+    <select id="selectHceCategoryBinds" resultType="com.usky.system.service.vo.MenuBindingsResult$HceCategoryBindItem">
+        select id,
+               category_name as categoryName,
+               menu_id       as menuId
+        from hce_category
+        where menu_id = #{menuId}
+    </select>
+
+    <delete id="deleteRoleMenuByMenuIdAndRoleIds">
+        delete from sys_role_menu
+        where menu_id = #{menuId}
+          and role_id in
+        <foreach collection="roleIds" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <delete id="deleteTenantMenuByMenuIdAndTenantIds">
+        delete from sys_tenant_menu
+        where menu_id = #{menuId}
+          and tenant_id in
+        <foreach collection="tenantIds" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <delete id="deletePlatformMenuByMenuIdAndPlatformIds">
+        delete from sys_platform_menu
+        where menu_id = #{menuId}
+          and platform_id in
+        <foreach collection="platformIds" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+</mapper>