Ver Fonte

支持菜单保存在SQLite数据库中

liyan há 2 anos atrás
pai
commit
fe3342d8d9

BIN
menu/sys_menu.db


+ 7 - 0
pom.xml

@@ -186,6 +186,12 @@
                 <version>${mybatis-plus.version}</version>
                 <version>${mybatis-plus.version}</version>
             </dependency>
             </dependency>
 
 
+            <dependency>
+                <groupId>com.ruoyi</groupId>
+                <artifactId>ruoyi-menu-extend</artifactId>
+                <version>${ruoyi.version}</version>
+            </dependency>
+
         </dependencies>
         </dependencies>
     </dependencyManagement>
     </dependencyManagement>
 
 
@@ -196,6 +202,7 @@
         <module>ruoyi-quartz</module>
         <module>ruoyi-quartz</module>
         <module>ruoyi-generator</module>
         <module>ruoyi-generator</module>
         <module>ruoyi-common</module>
         <module>ruoyi-common</module>
+        <module>ruoyi-menu-extend</module>
     </modules>
     </modules>
     <packaging>pom</packaging>
     <packaging>pom</packaging>
 
 

+ 13 - 7
ruoyi-admin/pom.xml

@@ -61,6 +61,12 @@
             <artifactId>ruoyi-generator</artifactId>
             <artifactId>ruoyi-generator</artifactId>
         </dependency>
         </dependency>
 
 
+        <!-- SQLite菜单保存扩展,引入依赖使用SQLite导入-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-menu-extend</artifactId>
+        </dependency>
+
     </dependencies>
     </dependencies>
 
 
     <build>
     <build>
@@ -80,17 +86,17 @@
                     </execution>
                     </execution>
                 </executions>
                 </executions>
             </plugin>
             </plugin>
-            <plugin>   
-                <groupId>org.apache.maven.plugins</groupId>   
-                <artifactId>maven-war-plugin</artifactId>   
-                <version>3.1.0</version>   
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <version>3.1.0</version>
                 <configuration>
                 <configuration>
                     <failOnMissingWebXml>false</failOnMissingWebXml>
                     <failOnMissingWebXml>false</failOnMissingWebXml>
                     <warName>${project.artifactId}</warName>
                     <warName>${project.artifactId}</warName>
-                </configuration>   
-           </plugin>   
+                </configuration>
+           </plugin>
         </plugins>
         </plugins>
         <finalName>${project.artifactId}</finalName>
         <finalName>${project.artifactId}</finalName>
     </build>
     </build>
 
 
-</project>
+</project>

+ 73 - 0
ruoyi-admin/src/main/resources/application-db.yml

@@ -0,0 +1,73 @@
+# 开发环境配置
+spring:
+  autoconfigure:
+    exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
+  datasource:
+    dynamic:
+      druid:
+        initial-size: 10
+        # 初始化大小,最小,最大
+        min-idle: 20
+        maxActive: 500
+        # 配置获取连接等待超时的时间
+        maxWait: 60000
+        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+        timeBetweenEvictionRunsMillis: 60000
+        # 配置一个连接在池中最小生存的时间,单位是毫秒
+        minEvictableIdleTimeMillis: 300000
+        testWhileIdle: true
+        testOnBorrow: false
+        validation-query: SELECT 1
+        testOnReturn: false
+        # 打开PSCache,并且指定每个连接上PSCache的大小
+        # 使用sqlLite会出现问题
+#        poolPreparedStatements: true
+#        maxPoolPreparedStatementPerConnectionSize: 20
+        filters: stat,wall
+        filter:
+          wall:
+            config:
+              multi-statement-allow: true
+              none-base-statement-allow: true
+            enabled: true
+          stat:
+            enabled: true
+            # 慢SQL记录
+            log-slow-sql: true
+            slow-sql-millis: 1000
+            merge-sql: true
+        # 配置DruidStatFilter
+        web-stat-filter:
+          enabled: true
+          url-pattern: "/*"
+          exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
+        # 配置DruidStatViewServlet
+        stat-view-servlet:
+          enabled: true
+          url-pattern: "/druid/*"
+          # IP白名单(没有配置或者为空,则允许所有访问)
+          allow:
+          # IP黑名单 (存在共同时,deny优先于allow)
+          deny:
+          #  禁用HTML页面上的“Reset All”功能
+          reset-enable: false
+          # 登录名
+          login-username: ruoyi
+          # 登录密码
+          login-password: 123456
+        query-timeout: 36000
+      primary: master
+      strict: false
+      datasource:
+        master:
+          url: jdbc:mysql://localhost:3306/ry-admin?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+          username: root
+          password: 123456
+          driver-class-name: com.mysql.cj.jdbc.Driver
+        menu:
+          url: jdbc:sqlite:menu/sys_menu.db
+          username:
+          password:
+          driver-class-name: org.sqlite.JDBC
+          # 数据源懒加载
+          lazy: true

+ 1 - 1
ruoyi-admin/src/main/resources/application.yml

@@ -54,7 +54,7 @@ spring:
     # 国际化资源文件路径
     # 国际化资源文件路径
     basename: i18n/messages
     basename: i18n/messages
   profiles:
   profiles:
-    active: druid
+    active: db
   # 文件上传
   # 文件上传
   servlet:
   servlet:
      multipart:
      multipart:

+ 4 - 4
ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java

@@ -26,10 +26,10 @@ import com.ruoyi.framework.datasource.DynamicDataSource;
 
 
 /**
 /**
  * druid 配置多数据源
  * druid 配置多数据源
- * 
+ *
  * @author ruoyi
  * @author ruoyi
  */
  */
-@Configuration
+//@Configuration
 public class DruidConfig
 public class DruidConfig
 {
 {
     @Bean
     @Bean
@@ -58,10 +58,10 @@ public class DruidConfig
         setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
         setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
         return new DynamicDataSource(masterDataSource, targetDataSources);
         return new DynamicDataSource(masterDataSource, targetDataSources);
     }
     }
-    
+
     /**
     /**
      * 设置数据源
      * 设置数据源
-     * 
+     *
      * @param targetDataSources 备选数据源集合
      * @param targetDataSources 备选数据源集合
      * @param sourceName 数据源名称
      * @param sourceName 数据源名称
      * @param beanName bean名称
      * @param beanName bean名称

+ 2 - 2
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java

@@ -6,10 +6,10 @@ import com.alibaba.druid.pool.DruidDataSource;
 
 
 /**
 /**
  * druid 配置属性
  * druid 配置属性
- * 
+ *
  * @author ruoyi
  * @author ruoyi
  */
  */
-@Configuration
+//@Configuration
 public class DruidProperties
 public class DruidProperties
 {
 {
     @Value("${spring.datasource.druid.initialSize}")
     @Value("${spring.datasource.druid.initialSize}")

+ 39 - 0
ruoyi-menu-extend/pom.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>com.ruoyi</groupId>
+        <artifactId>ruoyi</artifactId>
+        <version>3.8.5</version>
+    </parent>
+
+    <artifactId>ruoyi-menu-extend</artifactId>
+
+    <description>
+        基于SqlLite数据库的菜单服务扩展
+    </description>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-system</artifactId>
+        </dependency>
+
+        <!-- sqlite驱动包 -->
+        <dependency>
+            <groupId>org.xerial</groupId>
+            <artifactId>sqlite-jdbc</artifactId>
+            <version>3.40.0.0</version>
+        </dependency>
+
+    </dependencies>
+</project>

+ 128 - 0
ruoyi-menu-extend/src/main/java/com/ruoyi/menuex/mapper/SysMenuExMapper.java

@@ -0,0 +1,128 @@
+package com.ruoyi.menuex.mapper;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import com.ruoyi.common.core.domain.entity.SysMenu;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 菜单表 数据层
+ *
+ * @author ruoyi
+ */
+public interface SysMenuExMapper {
+    /**
+     * 查询系统菜单列表
+     *
+     * @param menu 菜单信息
+     * @return 菜单列表
+     */
+    @DS("menu")
+    public List<SysMenu> selectMenuList(SysMenu menu);
+
+    /**
+     * 根据用户所有权限
+     *
+     * @return 权限列表
+     */
+    @DS("menu")
+    public List<String> selectMenuPermsByMenuIdsAndStatus(@Param("menuIds") List<Long> menuIds, @Param("status") String status);
+
+    /**
+     * 查询指定角色ID绑定的菜单ID列表
+     *
+     * @param roleId 待查询的角色ID
+     * @return 绑定的菜单ID列表
+     */
+    public List<Long> selectMenuIdsByRoleId(Long roleId);
+
+    /**
+     * 根据用户ID查询菜单ID列表,且角色状态为启用状态
+     *
+     * @param userId 用户ID
+     * @return 菜单ID列表
+     */
+    public List<Long> selectMenuIdsByUserIdAndStatus(@Param("userId") Long userId, @Param("status") String status);
+
+    /**
+     * 根据用户ID查询菜单
+     *
+     * @return 菜单列表
+     */
+    @DS("menu")
+    public List<SysMenu> selectMenuTreeAll();
+
+    /**
+     * 根据菜单ID列表查询菜单树
+     *
+     * @param menuIds 菜单ID列表
+     * @return 菜单列表数据
+     */
+    @DS("menu")
+    public List<SysMenu> selectMenuTreeByMenuIds(@Param("menuIds") List<Long> menuIds);
+
+    /**
+     * 根据指定ID列表查询父级菜单列表
+     *
+     * @param menuIds 菜单ID列表
+     * @return 父级菜单ID列表
+     */
+    @DS("menu")
+    public List<Long> selectMenuParentIdsByMenuIds(@Param("menuIds") List<Long> menuIds);
+
+    /**
+     * 根据菜单ID查询信息
+     *
+     * @param menuId 菜单ID
+     * @return 菜单信息
+     */
+    @DS("menu")
+    public SysMenu selectMenuById(Long menuId);
+
+    /**
+     * 是否存在菜单子节点
+     *
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    @DS("menu")
+    public int hasChildByMenuId(Long menuId);
+
+    /**
+     * 新增菜单信息
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    @DS("menu")
+    public int insertMenu(SysMenu menu);
+
+    /**
+     * 修改菜单信息
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    @DS("menu")
+    public int updateMenu(SysMenu menu);
+
+    /**
+     * 删除菜单管理信息
+     *
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    @DS("menu")
+    public int deleteMenuById(Long menuId);
+
+    /**
+     * 校验菜单名称是否唯一
+     *
+     * @param menuName 菜单名称
+     * @param parentId 父菜单ID
+     * @return 结果
+     */
+    @DS("menu")
+    public SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId);
+}

+ 482 - 0
ruoyi-menu-extend/src/main/java/com/ruoyi/menuex/service/SysMenuExService.java

@@ -0,0 +1,482 @@
+package com.ruoyi.menuex.service;
+
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.core.domain.TreeSelect;
+import com.ruoyi.common.core.domain.entity.SysMenu;
+import com.ruoyi.common.core.domain.entity.SysRole;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.menuex.mapper.SysMenuExMapper;
+import com.ruoyi.system.domain.vo.MetaVo;
+import com.ruoyi.system.domain.vo.RouterVo;
+import com.ruoyi.system.mapper.SysRoleMapper;
+import com.ruoyi.system.mapper.SysRoleMenuMapper;
+import com.ruoyi.system.service.ISysMenuService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 菜单 业务层处理
+ *
+ * @author ruoyi
+ */
+@Service
+@Primary
+public class SysMenuExService implements ISysMenuService {
+
+    @Autowired
+    private SysMenuExMapper menuMapper;
+
+    @Autowired
+    private SysRoleMapper roleMapper;
+
+    @Autowired
+    private SysRoleMenuMapper roleMenuMapper;
+
+    /**
+     * 根据用户查询系统菜单列表
+     *
+     * @param userId 用户ID
+     * @return 菜单列表
+     */
+
+    public List<SysMenu> selectMenuList(Long userId) {
+        return selectMenuList(new SysMenu(), userId);
+    }
+
+    /**
+     * 查询系统菜单列表
+     *
+     * @param menu 菜单信息
+     * @return 菜单列表
+     */
+
+    public List<SysMenu> selectMenuList(SysMenu menu, Long userId) {
+        List<SysMenu> menuList = null;
+        // 管理员显示所有菜单信息
+        if (SysUser.isAdmin(userId)) {
+            menuList = menuMapper.selectMenuList(menu);
+        } else {
+            List<Long> menuIds = menuMapper.selectMenuIdsByUserIdAndStatus(userId, null);
+            menu.getParams().put("menuIds", menuIds);
+            menuList = menuMapper.selectMenuList(menu);
+        }
+        return menuList;
+    }
+
+    /**
+     * 根据用户ID查询权限
+     *
+     * @param userId 用户ID
+     * @return 权限列表
+     */
+
+    public Set<String> selectMenuPermsByUserId(Long userId) {
+        List<Long> menuIds = menuMapper.selectMenuIdsByUserIdAndStatus(userId, "0");
+        List<String> perms = menuMapper.selectMenuPermsByMenuIdsAndStatus(menuIds, "0");
+        // todo 修改权限查询
+        Set<String> permsSet = new HashSet<>();
+        for (String perm : perms) {
+            if (StringUtils.isNotEmpty(perm)) {
+                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
+            }
+        }
+        return permsSet;
+    }
+
+    /**
+     * 根据角色ID查询权限
+     *
+     * @param roleId 角色ID
+     * @return 权限列表
+     */
+
+    public Set<String> selectMenuPermsByRoleId(Long roleId) {
+        // todo 改造此方法
+        List<Long> menuIds = menuMapper.selectMenuIdsByRoleId(roleId);
+        List<String> perms = menuMapper.selectMenuPermsByMenuIdsAndStatus(menuIds, "0");
+        Set<String> permsSet = new HashSet<>();
+        for (String perm : perms) {
+            if (StringUtils.isNotEmpty(perm)) {
+                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
+            }
+        }
+        return permsSet;
+    }
+
+    /**
+     * 根据用户ID查询菜单
+     *
+     * @param userId 用户名称
+     * @return 菜单列表
+     */
+
+    public List<SysMenu> selectMenuTreeByUserId(Long userId) {
+        // todo 修改菜单查询
+        List<SysMenu> menus = null;
+        if (SecurityUtils.isAdmin(userId)) {
+            menus = menuMapper.selectMenuTreeAll();
+        } else {
+            List<Long> menuIds = menuMapper.selectMenuIdsByUserIdAndStatus(userId, "0");
+            menus = menuMapper.selectMenuTreeByMenuIds(menuIds);
+//            menus = menuMapper.selectMenuTreeByUserId(userId);
+        }
+        return getChildPerms(menus, 0);
+    }
+
+    /**
+     * 根据角色ID查询菜单树信息
+     *
+     * @param roleId 角色ID
+     * @return 选中菜单列表
+     */
+
+    public List<Long> selectMenuListByRoleId(Long roleId) {
+        // todo 修改菜单查询
+        SysRole role = roleMapper.selectRoleById(roleId);
+        List<Long> menuIds = menuMapper.selectMenuIdsByRoleId(roleId);
+        if (role.isMenuCheckStrictly()) {
+            List<Long> excludeIds = menuMapper.selectMenuParentIdsByMenuIds(menuIds);
+            menuIds.removeAll(excludeIds);
+        }
+        SysMenu menu = new SysMenu();
+        menu.getParams().put("menuIds", menuIds);
+        return menuMapper.selectMenuList(menu).stream().map(SysMenu::getMenuId).collect(Collectors.toList());
+//        return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly());
+    }
+
+    /**
+     * 构建前端路由所需要的菜单
+     *
+     * @param menus 菜单列表
+     * @return 路由列表
+     */
+
+    public List<RouterVo> buildMenus(List<SysMenu> menus) {
+        List<RouterVo> routers = new LinkedList<RouterVo>();
+        for (SysMenu menu : menus) {
+            RouterVo router = new RouterVo();
+            router.setHidden("1".equals(menu.getVisible()));
+            router.setName(getRouteName(menu));
+            router.setPath(getRouterPath(menu));
+            router.setComponent(getComponent(menu));
+            router.setQuery(menu.getQuery());
+            router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
+            List<SysMenu> cMenus = menu.getChildren();
+            if (!cMenus.isEmpty() && cMenus.size() > 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType())) {
+                router.setAlwaysShow(true);
+                router.setRedirect("noRedirect");
+                router.setChildren(buildMenus(cMenus));
+            } else if (isMenuFrame(menu)) {
+                router.setMeta(null);
+                List<RouterVo> childrenList = new ArrayList<RouterVo>();
+                RouterVo children = new RouterVo();
+                children.setPath(menu.getPath());
+                children.setComponent(menu.getComponent());
+                children.setName(StringUtils.capitalize(menu.getPath()));
+                children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
+                children.setQuery(menu.getQuery());
+                childrenList.add(children);
+                router.setChildren(childrenList);
+            } else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) {
+                router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
+                router.setPath("/");
+                List<RouterVo> childrenList = new ArrayList<RouterVo>();
+                RouterVo children = new RouterVo();
+                String routerPath = innerLinkReplaceEach(menu.getPath());
+                children.setPath(routerPath);
+                children.setComponent(UserConstants.INNER_LINK);
+                children.setName(StringUtils.capitalize(routerPath));
+                children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath()));
+                childrenList.add(children);
+                router.setChildren(childrenList);
+            }
+            routers.add(router);
+        }
+        return routers;
+    }
+
+    /**
+     * 构建前端所需要树结构
+     *
+     * @param menus 菜单列表
+     * @return 树结构列表
+     */
+
+    public List<SysMenu> buildMenuTree(List<SysMenu> menus) {
+        List<SysMenu> returnList = new ArrayList<SysMenu>();
+        List<Long> tempList = menus.stream().map(SysMenu::getMenuId).collect(Collectors.toList());
+        for (Iterator<SysMenu> iterator = menus.iterator(); iterator.hasNext(); ) {
+            SysMenu menu = (SysMenu) iterator.next();
+            // 如果是顶级节点, 遍历该父节点的所有子节点
+            if (!tempList.contains(menu.getParentId())) {
+                recursionFn(menus, menu);
+                returnList.add(menu);
+            }
+        }
+        if (returnList.isEmpty()) {
+            returnList = menus;
+        }
+        return returnList;
+    }
+
+    /**
+     * 构建前端所需要下拉树结构
+     *
+     * @param menus 菜单列表
+     * @return 下拉树结构列表
+     */
+
+    public List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus) {
+        List<SysMenu> menuTrees = buildMenuTree(menus);
+        return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
+    }
+
+    /**
+     * 根据菜单ID查询信息
+     *
+     * @param menuId 菜单ID
+     * @return 菜单信息
+     */
+
+    public SysMenu selectMenuById(Long menuId) {
+        return menuMapper.selectMenuById(menuId);
+    }
+
+    /**
+     * 是否存在菜单子节点
+     *
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+
+    public boolean hasChildByMenuId(Long menuId) {
+        int result = menuMapper.hasChildByMenuId(menuId);
+        return result > 0;
+    }
+
+    /**
+     * 查询菜单使用数量
+     *
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+
+    public boolean checkMenuExistRole(Long menuId) {
+        int result = roleMenuMapper.checkMenuExistRole(menuId);
+        return result > 0;
+    }
+
+    /**
+     * 新增保存菜单信息
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+
+    public int insertMenu(SysMenu menu) {
+        return menuMapper.insertMenu(menu);
+    }
+
+    /**
+     * 修改保存菜单信息
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+
+    public int updateMenu(SysMenu menu) {
+        return menuMapper.updateMenu(menu);
+    }
+
+    /**
+     * 删除菜单管理信息
+     *
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+
+    public int deleteMenuById(Long menuId) {
+        return menuMapper.deleteMenuById(menuId);
+    }
+
+    /**
+     * 校验菜单名称是否唯一
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+
+    public String checkMenuNameUnique(SysMenu menu) {
+        Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
+        SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId());
+        if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue()) {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    /**
+     * 获取路由名称
+     *
+     * @param menu 菜单信息
+     * @return 路由名称
+     */
+    public String getRouteName(SysMenu menu) {
+        String routerName = StringUtils.capitalize(menu.getPath());
+        // 非外链并且是一级目录(类型为目录)
+        if (isMenuFrame(menu)) {
+            routerName = StringUtils.EMPTY;
+        }
+        return routerName;
+    }
+
+    /**
+     * 获取路由地址
+     *
+     * @param menu 菜单信息
+     * @return 路由地址
+     */
+    public String getRouterPath(SysMenu menu) {
+        String routerPath = menu.getPath();
+        // 内链打开外网方式
+        if (menu.getParentId().intValue() != 0 && isInnerLink(menu)) {
+            routerPath = innerLinkReplaceEach(routerPath);
+        }
+        // 非外链并且是一级目录(类型为目录)
+        if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType())
+                && UserConstants.NO_FRAME.equals(menu.getIsFrame())) {
+            routerPath = "/" + menu.getPath();
+        }
+        // 非外链并且是一级目录(类型为菜单)
+        else if (isMenuFrame(menu)) {
+            routerPath = "/";
+        }
+        return routerPath;
+    }
+
+    /**
+     * 获取组件信息
+     *
+     * @param menu 菜单信息
+     * @return 组件信息
+     */
+    public String getComponent(SysMenu menu) {
+        String component = UserConstants.LAYOUT;
+        if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) {
+            component = menu.getComponent();
+        } else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) {
+            component = UserConstants.INNER_LINK;
+        } else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) {
+            component = UserConstants.PARENT_VIEW;
+        }
+        return component;
+    }
+
+    /**
+     * 是否为菜单内部跳转
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public boolean isMenuFrame(SysMenu menu) {
+        return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType())
+                && menu.getIsFrame().equals(UserConstants.NO_FRAME);
+    }
+
+    /**
+     * 是否为内链组件
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public boolean isInnerLink(SysMenu menu) {
+        return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath());
+    }
+
+    /**
+     * 是否为parent_view组件
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public boolean isParentView(SysMenu menu) {
+        return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType());
+    }
+
+    /**
+     * 根据父节点的ID获取所有子节点
+     *
+     * @param list     分类表
+     * @param parentId 传入的父节点ID
+     * @return String
+     */
+    public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId) {
+        List<SysMenu> returnList = new ArrayList<SysMenu>();
+        for (Iterator<SysMenu> iterator = list.iterator(); iterator.hasNext(); ) {
+            SysMenu t = (SysMenu) iterator.next();
+            // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
+            if (t.getParentId() == parentId) {
+                recursionFn(list, t);
+                returnList.add(t);
+            }
+        }
+        return returnList;
+    }
+
+    /**
+     * 递归列表
+     *
+     * @param list 分类表
+     * @param t    子节点
+     */
+    private void recursionFn(List<SysMenu> list, SysMenu t) {
+        // 得到子节点列表
+        List<SysMenu> childList = getChildList(list, t);
+        t.setChildren(childList);
+        for (SysMenu tChild : childList) {
+            if (hasChild(list, tChild)) {
+                recursionFn(list, tChild);
+            }
+        }
+    }
+
+    /**
+     * 得到子节点列表
+     */
+    private List<SysMenu> getChildList(List<SysMenu> list, SysMenu t) {
+        List<SysMenu> tlist = new ArrayList<SysMenu>();
+        Iterator<SysMenu> it = list.iterator();
+        while (it.hasNext()) {
+            SysMenu n = (SysMenu) it.next();
+            if (n.getParentId().longValue() == t.getMenuId().longValue()) {
+                tlist.add(n);
+            }
+        }
+        return tlist;
+    }
+
+    /**
+     * 判断是否有子节点
+     */
+    private boolean hasChild(List<SysMenu> list, SysMenu t) {
+        return getChildList(list, t).size() > 0;
+    }
+
+    /**
+     * 内链域名特殊字符替换
+     *
+     * @return 替换后的内链域名
+     */
+    public String innerLinkReplaceEach(String path) {
+        return StringUtils.replaceEach(path, new String[]{Constants.HTTP, Constants.HTTPS, Constants.WWW, "."},
+                new String[]{"", "", "", "/"});
+    }
+}

+ 254 - 0
ruoyi-menu-extend/src/main/resources/mapper/menuex/SysMenuExMapper.xml

@@ -0,0 +1,254 @@
+<?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.ruoyi.menuex.mapper.SysMenuExMapper">
+
+	<resultMap type="SysMenu" id="SysMenuResult">
+		<id property="menuId" column="menu_id"/>
+		<result property="menuName" column="menu_name"/>
+		<result property="parentName" column="parent_name"/>
+		<result property="parentId" column="parent_id"/>
+		<result property="orderNum" column="order_num"/>
+		<result property="path" column="path"/>
+		<result property="component" column="component"/>
+		<result property="query" column="query"/>
+		<result property="isFrame" column="is_frame"/>
+		<result property="isCache" column="is_cache"/>
+		<result property="menuType" column="menu_type"/>
+		<result property="visible" column="visible"/>
+		<result property="status" column="status"/>
+		<result property="perms" column="perms"/>
+		<result property="icon" column="icon"/>
+		<result property="createBy" column="create_by"/>
+		<result property="createTime" column="create_time"/>
+		<result property="updateTime" column="update_time"/>
+		<result property="updateBy" column="update_by"/>
+		<result property="remark" column="remark"/>
+	</resultMap>
+
+	<sql id="selectMenuVo">
+		select menu_id,
+			   menu_name,
+			   parent_id,
+			   order_num,
+			   path,
+			   component,
+			   `query`,
+			   is_frame,
+			   is_cache,
+			   menu_type,
+			   visible,
+			   status,
+			   ifnull(perms, '') as perms,
+			   icon,
+			   create_time
+		from sys_menu
+	</sql>
+
+	<select id="selectMenuList" parameterType="SysMenu" resultMap="SysMenuResult">
+		<include refid="selectMenuVo"/>
+		<where>
+			<if test="menuName != null and menuName != ''">
+				AND menu_name like concat('%', #{menuName}, '%')
+			</if>
+			<if test="visible != null and visible != ''">
+				AND visible = #{visible}
+			</if>
+			<if test="status != null and status != ''">
+				AND status = #{status}
+			</if>
+			<if test="params.menuIds != null">
+				AND menu_id in
+				<foreach collection="params.menuIds" item="menuId" open="(" separator="," close=")">
+					#{menuId}
+				</foreach>
+			</if>
+			<if test="params.menuTypes != null">
+				AND menu_type in
+				<foreach collection="params.menuTypes" item="type" open="(" separator="," close=")">
+					#{type}
+				</foreach>
+			</if>
+		</where>
+		order by parent_id, order_num
+	</select>
+
+	<select id="selectMenuTreeAll" resultMap="SysMenuResult">
+		select distinct m.menu_id,
+						m.parent_id,
+						m.menu_name,
+						m.path,
+						m.component,
+						m.`query`,
+						m.visible,
+						m.status,
+						ifnull(m.perms, '') as perms,
+						m.is_frame,
+						m.is_cache,
+						m.menu_type,
+						m.icon,
+						m.order_num,
+						m.create_time
+		from sys_menu m
+		where m.menu_type in ('M', 'C')
+		  and m.status = 0
+		order by m.parent_id, m.order_num
+	</select>
+
+	<select id="selectMenuTreeByMenuIds" resultMap="SysMenuResult">
+		select distinct menu_id,
+		parent_id,
+		menu_name,
+		path,
+		component,
+		`query`,
+		visible,
+		status,
+		ifnull(perms, '') as perms,
+		is_frame,
+		is_cache,
+		menu_type,
+		icon,
+		order_num,
+		create_time
+		from sys_menu
+		where menu_type in ('M', 'C')
+		AND status = 0
+		<if test="menuIds != null">
+			AND menu_id in
+			<foreach collection="menuIds" item="menuId" open="(" close=")" separator=",">
+				#{menuId}
+			</foreach>
+		</if>
+		order by parent_id, order_num
+	</select>
+
+	<select id="selectMenuParentIdsByMenuIds" resultType="long">
+		select distinct parent_id from sys_menu where 1 = 1
+		<if test="menuIds != null">
+			AND menu_id in
+			<foreach collection="menuIds" item="menuId" open="(" separator="," close=")">
+				#{menuId}
+			</foreach>
+		</if>
+	</select>
+
+	<select id="selectMenuPermsByMenuIdsAndStatus" resultType="String">
+		select distinct perms
+		from sys_menu
+		where 1 = 1
+		<if test="status != null and status != ''">
+			AND `status` = #{status}
+		</if>
+		<if test="menuIds != null">
+			AND menu_id in
+			<foreach collection="menuIds" item="menuId" open="(" close=")" separator=",">
+				#{menuId}
+			</foreach>
+		</if>
+	</select>
+
+	<select id="selectMenuIdsByUserIdAndStatus" parameterType="long" resultType="long">
+		select distinct rm.menu_id
+		from sys_role_menu rm
+		left join sys_user_role ur on rm.role_id = ur.role_id
+		left join sys_role r on r.role_id = ur.role_id
+		where ur.user_id = #{userId}
+		<if test="status != null and status != ''">
+			AND r.status = #{status}
+		</if>
+	</select>
+
+	<select id="selectMenuIdsByRoleId" parameterType="Long" resultType="Long">
+		select distinct menu_id
+		from sys_role_menu
+		where role_id = #{roleId}
+	</select>
+
+	<select id="selectMenuById" parameterType="Long" resultMap="SysMenuResult">
+		<include refid="selectMenuVo"/>
+		where menu_id = #{menuId}
+	</select>
+
+	<select id="hasChildByMenuId" resultType="Integer">
+		select count(1)
+		from sys_menu
+		where parent_id = #{menuId}
+	</select>
+
+	<select id="checkMenuNameUnique" parameterType="SysMenu" resultMap="SysMenuResult">
+		<include refid="selectMenuVo"/>
+		where menu_name=#{menuName} and parent_id = #{parentId} limit 1
+	</select>
+
+	<update id="updateMenu" parameterType="SysMenu">
+		update sys_menu
+		<set>
+			<if test="menuName != null and menuName != ''">menu_name = #{menuName},</if>
+			<if test="parentId != null">parent_id = #{parentId},</if>
+			<if test="orderNum != null">order_num = #{orderNum},</if>
+			<if test="path != null and path != ''">path = #{path},</if>
+			<if test="component != null">component = #{component},</if>
+			<if test="query != null">`query` = #{query},</if>
+			<if test="isFrame != null and isFrame != ''">is_frame = #{isFrame},</if>
+			<if test="isCache != null and isCache != ''">is_cache = #{isCache},</if>
+			<if test="menuType != null and menuType != ''">menu_type = #{menuType},</if>
+			<if test="visible != null">visible = #{visible},</if>
+			<if test="status != null">status = #{status},</if>
+			<if test="perms !=null">perms = #{perms},</if>
+			<if test="icon !=null and icon != ''">icon = #{icon},</if>
+			<if test="remark != null and remark != ''">remark = #{remark},</if>
+			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+			update_time = strftime('%Y-%m-%d %H:%M:%f','now','localtime')
+		</set>
+		where menu_id = #{menuId}
+	</update>
+
+	<insert id="insertMenu" parameterType="SysMenu">
+		insert into sys_menu(
+		<if test="menuId != null and menuId != 0">menu_id,</if>
+		<if test="parentId != null and parentId != 0">parent_id,</if>
+		<if test="menuName != null and menuName != ''">menu_name,</if>
+		<if test="orderNum != null">order_num,</if>
+		<if test="path != null and path != ''">path,</if>
+		<if test="component != null and component != ''">component,</if>
+		<if test="query != null and query != ''">`query`,</if>
+		<if test="isFrame != null and isFrame != ''">is_frame,</if>
+		<if test="isCache != null and isCache != ''">is_cache,</if>
+		<if test="menuType != null and menuType != ''">menu_type,</if>
+		<if test="visible != null">visible,</if>
+		<if test="status != null">status,</if>
+		<if test="perms !=null and perms != ''">perms,</if>
+		<if test="icon != null and icon != ''">icon,</if>
+		<if test="remark != null and remark != ''">remark,</if>
+		<if test="createBy != null and createBy != ''">create_by,</if>
+		create_time
+		)values(
+		<if test="menuId != null and menuId != 0">#{menuId},</if>
+		<if test="parentId != null and parentId != 0">#{parentId},</if>
+		<if test="menuName != null and menuName != ''">#{menuName},</if>
+		<if test="orderNum != null">#{orderNum},</if>
+		<if test="path != null and path != ''">#{path},</if>
+		<if test="component != null and component != ''">#{component},</if>
+		<if test="query != null and query != ''">#{query},</if>
+		<if test="isFrame != null and isFrame != ''">#{isFrame},</if>
+		<if test="isCache != null and isCache != ''">#{isCache},</if>
+		<if test="menuType != null and menuType != ''">#{menuType},</if>
+		<if test="visible != null">#{visible},</if>
+		<if test="status != null">#{status},</if>
+		<if test="perms !=null and perms != ''">#{perms},</if>
+		<if test="icon != null and icon != ''">#{icon},</if>
+		<if test="remark != null and remark != ''">#{remark},</if>
+		<if test="createBy != null and createBy != ''">#{createBy},</if>
+		strftime('%Y-%m-%d %H:%M:%f','now','localtime')
+		)
+	</insert>
+
+	<delete id="deleteMenuById" parameterType="Long">
+		delete
+		from sys_menu
+		where menu_id = #{menuId}
+	</delete>
+
+</mapper>