一、功能设计目的
实现一个五层的菜单
在最初的前台设计当中,我们将菜单和模块权限分别做了一个简单的实现。菜单具有两级,是平级的列表,包含当前用户的功能权限具体描述和标识。
但是随着数据中菜单、导航的不断拓展,层级不断加深,我不得不再次重构整个后端逻辑代码。显得很头疼,现在提出这样一个临时的解决方案供大家,供自己以后参考。,如果有哪里不正确,敬请指出。
因为代码可能不完整,本文仅提供一个解决思路和一个工具类。
现在说明一下设计的基本思路:
1、修改数据库,将原来的菜单(menu)表和权限(permission)表合并,通过具有层级的树状菜单结构,实现对用户的权限标识。
2、编写sql语句
3、创建工具类,将平级查询结果加工为层级菜单结构,一并返回
二、数据库设计
1、基本实现是基于权限五表
2、其中的菜单权限表(permission)
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;
做一个简单的分析
记得在mapper文件中将id=1改为#{id}
四、导航菜单分层、合并
创建工具类,将平级查询结果加工为层级菜单结构
注意:其中类型基本都是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;
}
五、接口测试
目前是可以返回五级菜单的
六、总结
现在对当前解决方案做出三点总结:
1、不足之处在于层级数(五层)属于硬编码,如果数据库存在更多级别(level>5)的数据,将不会被查询到。需要改进
2、虽然层级固定,但是需要更多的层级,使用现有的工具类和方法,进行拓展,工作量也大大减少了。
3、总的来说,解决方案有效,但是从长远角度来讲,需要对菜单层级进行一个可拓展的改进。