一、功能设计目的

        实现一个五层的菜单

        在最初的前台设计当中,我们将菜单和模块权限分别做了一个简单的实现。菜单具有两级,是平级的列表,包含当前用户的功能权限具体描述和标识。

        但是随着数据中菜单、导航的不断拓展,层级不断加深,我不得不再次重构整个后端逻辑代码。显得很头疼,现在提出这样一个临时的解决方案供大家,供自己以后参考。,如果有哪里不正确,敬请指出。

        因为代码可能不完整,本文仅提供一个解决思路和一个工具类。

        现在说明一下设计的基本思路:

        1、修改数据库,将原来的菜单(menu)表和权限(permission)表合并,通过具有层级的树状菜单结构,实现对用户的权限标识。

        2、编写sql语句

        3、创建工具类,将平级查询结果加工为层级菜单结构,一并返回

二、数据库设计

1、基本实现是基于权限五表

 

java 实现二级菜单 java实现多级菜单_工具类

2、其中的菜单权限表(permission)

java 实现二级菜单 java实现多级菜单_数据库_02


3、字段说明

id:主键

name:权限名\菜单名

pid:父级id

value:权限唯一标识,例如 api:front:basic:distribution 标识前端主页的农作物分布功能

server_type:权限类型,1表示前台权限,2表示前后台都用到,3表示后台权限

comment:备注

level:实现菜单层级标识

三、导航菜单查询

--sql
select x.id,name,pid,value,server_type,level,comment from 
(
select p.id,name,pid,value,server_type,level,comment,role_id 
from permission p inner join role_permission rp 
on p.id=rp.permission_id 
where server_type <3
ORDER BY p.id
)as x inner join user_role ur on ur.role_id=x.role_id
where user_id = 1 and pid>=0 ORDER BY value;

做一个简单的分析

java 实现二级菜单 java实现多级菜单_工具类_03

 

 记得在mapper文件中将id=1改为#{id}

java 实现二级菜单 java实现多级菜单_数据库_04

四、导航菜单分层、合并

创建工具类,将平级查询结果加工为层级菜单结构

注意:其中类型基本都是Object,涉及到的permissionVo也是严格的数据库格式。

package com.tx.agriculture.util;

import com.tx.agriculture.domain.vo.*;

import java.util.ArrayList;
import java.util.List;

/**
 * 列表工具类
 *
 * @author jhj
 * @date 2023/01/28
 */
public class ListUtil {

    /**
     * 将平级的菜单列表转为层级的列表
     *
     * @param list 列表
     * @return {@link List}<{@link Object}>
     */
    public static List<Object> indexTrsformation(List<Object> list) {
        //todo 暂定为五级层级
        List<PermissionVo> list1  = new ArrayList<>();
        List<PermissionVo> list2  = new ArrayList<>();
        List<PermissionVo> list3  = new ArrayList<>();
        List<PermissionVo> list4  = new ArrayList<>();
        List<PermissionVo> list5  = new ArrayList<>();
        Integer level=0;
        //分类
        for(Object item:list){
            PermissionVo tempItem=(PermissionVo)item;
            level= tempItem.getLevel();
            switch (level){
                case 1:list1.add(tempItem);break;
                case 2:list2.add(tempItem);break;
                case 3:list3.add(tempItem);break;
                case 4:list4.add(tempItem);break;
                case 5:list5.add(tempItem);break;
                default:;
            }
        }

        //合并菜单
        list4 = permissionCombination(list4, list5);
        list3 = permissionCombination(list3, list4);
        list2 = permissionCombination(list2, list3);
        list1 = permissionCombination(list1, list2);

        return ListUtil.PermissionVo2Object(list1);

    }

    /**
     * 根据 父子id匹配实现 菜单列表按层级追加,实现合并
     *
     * @param parent 父
     * @param child  孩子
     * @return {@link List}<{@link Object}>
     */
    public static List<PermissionVo> permissionCombination(List<PermissionVo> parent, List<PermissionVo> child){
        for(PermissionVo p:parent){
            List<PermissionVo> childList = new ArrayList<>();
            Integer pid = p.getId();
            //遍历5级菜单
            for (PermissionVo c:child){
                //如果是父子关系
                if (c.getPid().equals(pid)){
                    childList.add(c);
                }
            }
            p.setChildList(childList);
        }
        
        return parent;
    }

    /**
     * PermissionVo 转为 Object
     *
     * @param list 列表
     * @return {@link List}<{@link Object}>
     */
    public static List<Object> PermissionVo2Object(List<PermissionVo> list) {
        List<Object> Restult=new ArrayList<>();
        for (Object item : list) {
            Restult.add(item);
        }
        return Restult;
    }


}

权限类Value Object:PermissionVo

/**
 * 权限返回值
 *
 * @author jhj
 * @date 2023/01/30
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PermissionVo {

    /**
     * 权限数据id
     */
    Integer id;
    /**
     * 权限名
     */
    String name;
    /**
     * 父级id
     */
    Integer pid;
    /**
     * 父级路由名字
     */
    String pName;
    /**
     * 权限值(标识令牌)
     */
    String value;
    /**
     * 层级
     */
    Integer level;
    /**
     * 路由链接
     */
    String linkUrl;
    /**
     * 服务类型
     * 1:前台权限 2:前台和后台权限 3:后台权限
     */
    Integer serverType;
    /**
     * 子菜单
     */
    List<PermissionVo> childList;
    /**
     * 描述
     */
    String Comment;
}

五、接口测试

目前是可以返回五级菜单的

java 实现二级菜单 java实现多级菜单_解决方案_05

 

六、总结

现在对当前解决方案做出三点总结:

1、不足之处在于层级数(五层)属于硬编码,如果数据库存在更多级别(level>5)的数据,将不会被查询到。需要改进

2、虽然层级固定,但是需要更多的层级,使用现有的工具类和方法,进行拓展,工作量也大大减少了。

3、总的来说,解决方案有效,但是从长远角度来讲,需要对菜单层级进行一个可拓展的改进。