Selaa lähdekoodia

update 优化 添加菜单路由地址和名称的校验规则

疯狂的狮子Li 6 kuukautta sitten
vanhempi
commit
948eba6566

+ 4 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java

@@ -137,6 +137,8 @@ public class SysMenuController extends BaseController {
             return R.fail("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
         } else if (SystemConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
             return R.fail("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
+        } else if (!menuService.checkRouteConfigUnique(menu)) {
+            return R.fail("新增菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
         }
         return toAjax(menuService.insertMenu(menu));
     }
@@ -156,6 +158,8 @@ public class SysMenuController extends BaseController {
             return R.fail("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
         } else if (menu.getMenuId().equals(menu.getParentId())) {
             return R.fail("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
+        } else if (!menuService.checkRouteConfigUnique(menu)) {
+            return R.fail("新增菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
         }
         return toAjax(menuService.updateMenu(menu));
     }

+ 5 - 5
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMenu.java

@@ -130,11 +130,11 @@ public class SysMenu extends BaseEntity {
     public String getRouterPath() {
         String routerPath = this.path;
         // 内链打开外网方式
-        if (getParentId() != 0L && isInnerLink()) {
+        if (!Constants.TOP_PARENT_ID.equals(getParentId()) && isInnerLink()) {
             routerPath = innerLinkReplaceEach(routerPath);
         }
         // 非外链并且是一级目录(类型为目录)
-        if (0L == getParentId() && SystemConstants.TYPE_DIR.equals(getMenuType())
+        if (Constants.TOP_PARENT_ID.equals(getParentId()) && SystemConstants.TYPE_DIR.equals(getMenuType())
             && SystemConstants.NO_FRAME.equals(getIsFrame())) {
             routerPath = "/" + this.path;
         }
@@ -152,7 +152,7 @@ public class SysMenu extends BaseEntity {
         String component = SystemConstants.LAYOUT;
         if (StringUtils.isNotEmpty(this.component) && !isMenuFrame()) {
             component = this.component;
-        } else if (StringUtils.isEmpty(this.component) && getParentId() != 0L && isInnerLink()) {
+        } else if (StringUtils.isEmpty(this.component) && !Constants.TOP_PARENT_ID.equals(getParentId()) && isInnerLink()) {
             component = SystemConstants.INNER_LINK;
         } else if (StringUtils.isEmpty(this.component) && isParentView()) {
             component = SystemConstants.PARENT_VIEW;
@@ -164,7 +164,7 @@ public class SysMenu extends BaseEntity {
      * 是否为菜单内部跳转
      */
     public boolean isMenuFrame() {
-        return getParentId() == 0L && SystemConstants.TYPE_MENU.equals(menuType) && isFrame.equals(SystemConstants.NO_FRAME);
+        return Constants.TOP_PARENT_ID.equals(getParentId()) && SystemConstants.TYPE_MENU.equals(menuType) && isFrame.equals(SystemConstants.NO_FRAME);
     }
 
     /**
@@ -178,7 +178,7 @@ public class SysMenu extends BaseEntity {
      * 是否为parent_view组件
      */
     public boolean isParentView() {
-        return getParentId() != 0L && SystemConstants.TYPE_DIR.equals(menuType);
+        return !Constants.TOP_PARENT_ID.equals(getParentId()) && SystemConstants.TYPE_DIR.equals(menuType);
     }
 
     /**

+ 9 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java

@@ -160,4 +160,13 @@ public interface ISysMenuService {
      * @return 结果
      */
     boolean checkMenuNameUnique(SysMenuBo menu);
+
+    /**
+     * 校验路由组合是否唯一
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    boolean checkRouteConfigUnique(SysMenuBo menu);
+
 }

+ 46 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java

@@ -6,6 +6,7 @@ import cn.hutool.core.lang.tree.Tree;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.core.constant.Constants;
 import org.dromara.common.core.constant.SystemConstants;
 import org.dromara.common.core.utils.MapstructUtils;
@@ -29,13 +30,17 @@ import org.dromara.system.service.ISysMenuService;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
 
 /**
  * 菜单 业务层处理
  *
  * @author Lion Li
  */
+@Slf4j
 @RequiredArgsConstructor
 @Service
 public class SysMenuServiceImpl implements ISysMenuService {
@@ -353,6 +358,46 @@ public class SysMenuServiceImpl implements ISysMenuService {
         return !exist;
     }
 
+    /**
+     * 校验路由名称是否唯一
+     *
+     * @param menuBo 菜单信息
+     * @return 结果
+     */
+    @Override
+    public boolean checkRouteConfigUnique(SysMenuBo menuBo) {
+        SysMenu menu = MapstructUtils.convert(menuBo, SysMenu.class);
+        long menuId = ObjectUtil.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
+        Long parentId = menu.getParentId();
+        String path = menu.getPath();
+        String routeName = StringUtils.isEmpty(menu.getRouteName()) ? path : menu.getRouteName();
+        List<SysMenu> sysMenuList = baseMapper.selectList(
+            new LambdaQueryWrapper<SysMenu>()
+                .in(SysMenu::getMenuType, SystemConstants.TYPE_DIR, SystemConstants.TYPE_MENU)
+                .and(w ->
+                    w.eq(SysMenu::getPath, path)
+                        .or().eq(SysMenu::getRouteName, path)
+                        .or().eq(SysMenu::getRouteName, routeName)));
+        for (SysMenu sysMenu : sysMenuList) {
+            if (sysMenu.getMenuId() != menuId) {
+                Long dbParentId = sysMenu.getParentId();
+                String dbPath = sysMenu.getPath();
+                String dbRouteName = StringUtils.isEmpty(sysMenu.getRouteName()) ? dbPath : sysMenu.getRouteName();
+                if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.longValue() == dbParentId.longValue()) {
+                    log.warn("[同级路由冲突] 同级下已存在相同路由路径 '{}',冲突菜单:{}", dbPath, sysMenu.getMenuName());
+                    return false;
+                } else if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.longValue() == Constants.TOP_PARENT_ID) {
+                    log.warn("[根目录路由冲突] 根目录下路由 '{}' 必须唯一,已被菜单 '{}' 占用", path, sysMenu.getMenuName());
+                    return false;
+                } else if (StringUtils.equalsAnyIgnoreCase(routeName, dbRouteName)) {
+                    log.warn("[路由名称冲突] 路由名称 '{}' 需全局唯一,已被菜单 '{}' 使用", routeName, sysMenu.getMenuName());
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
     /**
      * 根据父节点的ID获取所有子节点
      *