소스 검색

Merge branch 'develop' of uskycloud/usky-cloud into master

hanzhengyi 2 주 전
부모
커밋
d4678b8fd9
12개의 변경된 파일507개의 추가작업 그리고 4개의 파일을 삭제
  1. 33 4
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/controller/web/SysMenuController.java
  2. 57 0
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/mapper/SysMenuCascadeMapper.java
  3. 10 0
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/mapper/SysMenuMapper.java
  4. 8 0
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/ISysMenuService.java
  5. 28 0
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/SysMenuCascadeService.java
  6. 128 0
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/impl/SysMenuCascadeServiceImpl.java
  7. 14 0
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/impl/SysMenuServiceImpl.java
  8. 94 0
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/vo/MenuBindingsResult.java
  9. 24 0
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/vo/MenuUnbindRequest.java
  10. 25 0
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/vo/MenuUnbindResult.java
  11. 81 0
      base-modules/service-system/service-system-biz/src/main/resources/mapper/system/SysMenuCascadeMapper.xml
  12. 5 0
      base-modules/service-system/service-system-biz/src/main/resources/mapper/system/SysMenuMapper.xml

+ 33 - 4
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;
+
     /**
      * 获取菜单列表
      */
@@ -113,9 +120,9 @@ public class SysMenuController extends BaseController
     @PostMapping
     public ApiResult add(@Validated @RequestBody SysMenu menu)
     {
-        if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu)))
+        if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuPathUnique(menu)))
         {
-            return ApiResult.error(BusinessErrorCode.BIZ_BUSINESS_ERROR.getCode(), "新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
+            return ApiResult.error(BusinessErrorCode.BIZ_BUSINESS_ERROR.getCode(), "新增菜单'" + menu.getMenuName() + "'失败,路由地址已存在");
         }
         else if (UserConstants.YES_FRAME.equals(menu.getIsFrame())
         && !StringUtils.startsWithAny(menu.getPath(), CommonConst.HTTP_PREFIX, CommonConst.HTTPS_PREFIX))
@@ -133,9 +140,9 @@ public class SysMenuController extends BaseController
     @PutMapping
     public ApiResult edit(@Validated @RequestBody SysMenu menu)
     {
-        if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu)))
+        if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuPathUnique(menu)))
         {
-            return ApiResult.error(BusinessErrorCode.BIZ_BUSINESS_ERROR.getCode(), "修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
+            return ApiResult.error(BusinessErrorCode.BIZ_BUSINESS_ERROR.getCode(), "修改菜单'" + menu.getMenuName() + "'失败,路由地址已存在");
         }
         else if (UserConstants.YES_FRAME.equals(menu.getIsFrame())
         && !StringUtils.startsWithAny(menu.getPath(), CommonConst.HTTP_PREFIX, CommonConst.HTTPS_PREFIX))
@@ -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);
+}

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

@@ -154,6 +154,16 @@ public interface SysMenuMapper extends CrudMapper<SysMenu> {
     public SysMenu checkMenuNameUnique(@Param("menuName") String menuName,
                                        @Param("parentId") Long parentId);
 
+    /**
+     * 校验路由地址是否唯一(同一父菜单下)
+     *
+     * @param path     路由地址
+     * @param parentId 父菜单ID
+     * @return 结果
+     */
+    public SysMenu checkMenuPathUnique(@Param("path") String path,
+                                       @Param("parentId") Long parentId);
+
     public List<SysMenu> getSysMenuList(String menuType);
 
     public List<SysMenu> getTenantPlatformMenuList(@Param("tenantId") Integer tenantId,@Param("platformId") Integer platformId);

+ 8 - 0
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/ISysMenuService.java

@@ -193,6 +193,14 @@ public interface ISysMenuService extends CrudService<SysMenu>
      */
     public String checkMenuNameUnique(SysMenu menu);
 
+    /**
+     * 校验路由地址是否唯一(同一父菜单下;path 为空时视为唯一)
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public String checkMenuPathUnique(SysMenu menu);
+
     /**
      * 根据角色ID查询应用菜单树信息
      * @return 选中菜单列表

+ 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;
+    }
+}

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

@@ -614,6 +614,20 @@ public class SysMenuServiceImpl extends AbstractCrudService<SysMenuMapper, SysMe
         return UserConstants.UNIQUE;
     }
 
+    @Override
+    public String checkMenuPathUnique(SysMenu menu) {
+        // 按钮等场景 path 常为空,不做重复校验
+        if (StringUtils.isEmpty(menu.getPath())) {
+            return UserConstants.UNIQUE;
+        }
+        Long menuId = Objects.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
+        SysMenu info = menuMapper.checkMenuPathUnique(menu.getPath(), menu.getParentId());
+        if (Objects.nonNull(info) && info.getMenuId().longValue() != menuId.longValue()) {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
     /**
      * 获取路由名称
      *

+ 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>

+ 5 - 0
base-modules/service-system/service-system-biz/src/main/resources/mapper/system/SysMenuMapper.xml

@@ -367,6 +367,11 @@
         where menu_name=#{menuName} and parent_id = #{parentId} limit 1
     </select>
 
+    <select id="checkMenuPathUnique" resultMap="SysMenuResult">
+        <include refid="selectMenuVo"/>
+        where path = #{path} and parent_id = #{parentId} limit 1
+    </select>
+
     <update id="updateMenu" parameterType="com.usky.system.domain.SysMenu">
         update sys_menu
         <set>