遇到的情况是:在做导航菜单那块时,发现数据库里将权限表设计成一张了,此时再改数据库会牵一发而动全身,如果直接用递归的方式将权限封装成树,那么会将增删查之类的权限也变成导航菜单的一部分,也就是说查出来的“删除用户”、“添加角色”等权限也会出现在children里。
解决方法:在递归时,将查出来的所有权限结果过滤,将增删改等子功能放在一个单独的list里(下文中的permitFunc中)。
具体做法:
- 给数据库的权限表增加字段isMenu,用来区别是否是导航菜单(1表示是导航菜单,0表示不能出现在导航菜单的权限功能),还是增删改等功能,如下(请忽略父子关系的不合理):
2. 写sql语句,一个是用来查询某用户的导航菜单(即isMenu是1),另一个是用来查该用户的其他权限(即isMenu是0),如下:
3. 将用户的导航菜单全部查出来,并排序:
目前的menuList如下:
4. 按照前端所需的树结构存放,并将将查出来的所有权限结果过滤,将增删改等子功能放在一个单独的list(permitFunc)里:
findTree里是这样的:
/**
* 将数据库中查询到的菜单list转为菜单树list
*
* @param allMenu
* @return
*/
private Map<String, Object> findTree(List<Permission> allMenu,String userName) {
Map<String, Object> data = new HashMap<String, Object>();
try {//查询所有菜单
//根节点
List<NavigatMenuVo> rootMenu = new ArrayList<NavigatMenuVo>();
for (Permission nav : allMenu) {
NavigatMenuVo navigatMenuVo = new NavigatMenuVo();
Integer ss = nav.getPid();
if (nav.getPid() == 0) {//父节点是0的,为根节点。
navigatMenuVo.setChildren(nav.getChildren());
navigatMenuVo.setId(nav.getId());
navigatMenuVo.setLevel(nav.getLevel());
navigatMenuVo.setPermTag(nav.getPermTag());
navigatMenuVo.setTitle(nav.getPermName());
navigatMenuVo.setUrl(nav.getUrl());
rootMenu.add(navigatMenuVo);
break;
}
}
//为根菜单设置子菜单,getClild是递归调用的
for (NavigatMenuVo navigatMenuVo2 : rootMenu) {
/* 获取根节点下的所有子节点 使用getChild方法*/
List<NavigatMenuVo> childList = getChild(navigatMenuVo2.getId(), allMenu,userName);
// 将查出来的所有权限结果过滤,将增删改等子功能放在一个单独的list里(permitFunc中)
List<NavigatMenuVo> permitFuncs = userMapper.findFuncPermissionByUsername(navigatMenuVo2.getId(),userName);
navigatMenuVo2.setPermitFunc(permitFuncs);
navigatMenuVo2.setChildren(childList);//给根节点设置子节点
}
/**
* 输出构建好的菜单数据。
*
*/
data.put("success", "true");
data.put("list", rootMenu);
return data;
} catch (Exception e) {
data.put("success", "false");
data.put("list", new ArrayList());
return data;
}
}
private List<NavigatMenuVo> getChild(Integer id, List<Permission> allMenu,String userName) {
//子菜单
List<NavigatMenuVo> childList = new ArrayList<NavigatMenuVo>();
List<NavigatMenuVo> permitList = new ArrayList<>();
for (Permission nav : allMenu) {
NavigatMenuVo navigatMenuVo = new NavigatMenuVo();
// 遍历所有节点,将所有菜单的父id与传过来的根节点的id比较
//相等说明:为该根节点的子节点。
if (nav.getPid() == id) {
navigatMenuVo.setChildren(nav.getChildren());
navigatMenuVo.setId(nav.getId());
navigatMenuVo.setLevel(nav.getLevel());
navigatMenuVo.setPermTag(nav.getPermTag());
navigatMenuVo.setTitle(nav.getPermName());
navigatMenuVo.setUrl(nav.getUrl());
// 下面注释掉的这行其实等同于再下面的这行
// List<Permission> permitFuncs2 = allMenu.stream().filter(c->c.getIsMenu().equals(0) && c.getPid().equals(navigatMenuVo.getId())).collect(Collectors.toList());
// 将查出来的所有权限结果过滤,将增删改等子功能放在一个单独的list里(permitFunc中)
List<NavigatMenuVo> permitFuncs = userMapper.findFuncPermissionByUsername(navigatMenuVo.getId(),userName);
navigatMenuVo.setPermitFunc(permitFuncs);
childList.add(navigatMenuVo);
}
}
//递归
for (NavigatMenuVo navigatMenuVo : childList) {
navigatMenuVo.setChildren(getChild(navigatMenuVo.getId(), allMenu,userName));
}
//如果节点下没有子节点,返回一个空List(递归退出)
if (childList.size() == 0) {
return new ArrayList<NavigatMenuVo>();
}
return childList;
}
最后得到的结果如下:
{
"rtnCode": 200,
"msg": "success",
"data": [
{
"id": 1,
"title": "权限管理",
"level": 0,
"permitFunc": [
{
"id": 19,
"title": "添加计划信息",
"level": 1,
"permitFunc": null,
"children": null
},
{
"id": 20,
"title": "计划信息excel文件上传",
"level": 1,
"permitFunc": null,
"children": null
},
{
"id": 21,
"title": "根据token获取用户信息",
"level": 1,
"permitFunc": null,
"children": null
},
{
"id": 22,
"title": "根据token获取任务统计信息",
"level": 1,
"permitFunc": null,
"children": null
},
{
"id": 23,
"title": "添加日程日志",
"level": 1,
"permitFunc": null,
"children": null
},
{
"id": 24,
"title": "更新日程退出时间",
"level": 1,
"permitFunc": null,
"children": null
},
{
"id": 25,
"title": "查询日程日志",
"level": 1,
"permitFunc": null,
"children": null
}
],
"children": [
{
"id": 2,
"title": "用户管理",
"level": 1,
"permitFunc": [
{
"id": 6,
"title": "添加用户",
"level": 2,
"permitFunc": null,
"children": null
},
{
"id": 7,
"title": "修改用户",
"level": 2,
"permitFunc": null,
"children": null
},
{
"id": 8,
"title": "删除用户",
"level": 2,
"permitFunc": null,
"children": null
}
],
"children": []
},
{
"id": 3,
"title": "角色管理",
"level": 1,
"permitFunc": [
{
"id": 10,
"title": "添加角色",
"level": 2,
"permitFunc": null,
"children": null
},
{
"id": 11,
"title": "修改角色",
"level": 2,
"permitFunc": null,
"children": null
},
{
"id": 12,
"title": "删除角色",
"level": 2,
"permitFunc": null,
"children": null
}
],
"children": []
},
{
"id": 4,
"title": "菜单管理",
"level": 1,
"permitFunc": [
{
"id": 14,
"title": "添加菜单",
"level": 2,
"permitFunc": null,
"children": null
},
{
"id": 15,
"title": "修改菜单",
"level": 2,
"permitFunc": null,
"children": null
},
{
"id": 16,
"title": "删除菜单",
"level": 2,
"permitFunc": null,
"children": null
}
],
"children": []
}
]
}
]
}
总结:其实这种解决方法还是很笨拙,可以进一步优化,比如用sql查询时就是直接封装好,再比如只用写一个sql,然后在利用
stream().filter()过滤查询结果。不过总的来说,在设计数据库表的时候就应该考虑到这点,将权限表拆分成两张表,一张存放导航里要显示的权限,另一张来存放其他权限。编程之路很辛苦,还请各位多多指教!