在开发中很多同学都遇到过遍历 查询所有子节点的业务,在这里给大家分析 使用java8的语法 很简单的递归来实现
我就拿一个最普遍的 菜单遍历案例分享给大家
其中我添加了一点数据来测试
可以看到我所有菜单的父级都是 系统管理(自己也可以添加不同的父子级关系)
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="SysMenu对象", description="")
public class SysMenu implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "ID")
@TableId(value = "menu_id", type = IdType.AUTO)
private Integer menuId;
@ApiModelProperty(value = "父ID")
private Integer parentId;
@ApiModelProperty(value = "名称")
private String name;
@ApiModelProperty(value = "路径")
private String component;
@ApiModelProperty(value = "路由")
private String path;
@ApiModelProperty(value = "等级")
private Integer level;
@ApiModelProperty(value = "类型 0:目录,1:菜单, 2:按钮")
private Integer type;
@ApiModelProperty(value = "图标")
private String icon;
@ApiModelProperty(value = "排序")
private Integer sort;
@ApiModelProperty(value = "状态 0:失效 1:启用")
private Integer status;
@ApiModelProperty(value = "shiro权限标识")
private String permit;
@ApiModelProperty(value = "父名称")
@TableField(exist = false)
private String pname;
}
/**
* 菜单的VO
*/
@Data
public class MenuVO {
private String path;
private String component;
private boolean alwaysShow;
private String name;
private MetaVO meta;
private List children;
}
@Override
public List getListMenuVO(List roleId) {
//查询出所有菜单,这里过滤了一个状态条件(mybatis-puls的写法,不了解的同学可以去看下mybatis-puls)
QueryWrapper sysMenuQueryWrapper = new QueryWrapper<>();
sysMenuQueryWrapper.eq("status",1);
sysMenuQueryWrapper.orderByAsc("sort");
List sysMenus = list(sysMenuQueryWrapper);
//第一个参数必须是当前最高目录的parentId的值,这里写0也就是一级目录的parentId的值
return recursionForMenu(0,sysMenus);
}
/**
* 左侧菜单通过递归算法实现树
* @param parentId 父Id
* @param menuList 当前所有菜单
* @return
*/
private List recursionForMenu(int parentId,List menuList){
List list = new ArrayList<>();
/**
* Optional.ofNullable(menuList).orElse(new ArrayList<>()) 如果menuList是空的则返回一个new ArrayList<>()
* .stream() 返回List中的流
* .filter(menu -> menu.getParentId().equals(parentId)) 筛选List,返回只有条件成立的元素(当前元素的parentId必须等于父id)
* .forEach 遍历这个list
*/
Optional.ofNullable(menuList).orElse(new ArrayList<>())
.stream()
.filter(menu -> menu.getParentId().equals(parentId))
.forEach(menu -> {
MenuVO menuVO = new MenuVO();
menuVO.setName(menu.getName());
//是否是目录
menuVO.setAlwaysShow(menu.getLevel().equals(1)?true:false);
menuVO.setPath(menu.getPath());
menuVO.setComponent(StringUtils.isNotEmpty(menu.getComponent())?menu.getComponent():"Layout");
menuVO.setMeta(new MetaVO(menu.getName(),menu.getIcon(),new ArrayList<>(Arrays.asList(1))));
List children=recursionForMenu(menu.getMenuId(),menuList);
menuVO.setChildren(children);
list.add(menuVO);
});
return list;
}
如果有了解 stream、filter的同学应该很容易看懂,代码快上给大家写了简单的注释来解释了下
@Autowired
private SysMenuService sysMenuService;
@ApiOperation(value = "获取菜单树")
@GetMapping("/getMenuList")
public ResponseEntity getMenuList(HttpServletRequest request) {
ResponseInfo ri = new ResponseInfo();
List menuVOS = sysMenuService.getListMenuVO(null);
ri.setData(menuVOS);
return ResponseEntity.ok().body(ri.ok());
}
"data": [
{
"path": "/permission",
"component": "Layout",
"alwaysShow": true,
"name": "系统管理",
"meta": {
"title": "系统管理",
"icon": "el-icon-s-tools",
"roles": [
1
]
},
"children": [
{
"path": "organization",
"component": "user/organization/organization-index",
"alwaysShow": false,
"name": "企业管理",
"meta": {
"title": "企业管理",
"icon": "el-icon-office-building",
"roles": [
1
]
},
"children": []
},
{
"path": "page",
"component": "permission/directive",
"alwaysShow": false,
"name": "角色管理",
"meta": {
"title": "角色管理",
"icon": "el-icon-s-custom",
"roles": [
1
]
},
"children": []
},
{
"path": "role",
"component": "permission/role",
"alwaysShow": false,
"name": "部门管理",
"meta": {
"title": "部门管理",
"icon": "el-icon-s-home",
"roles": [
1
]
},
"children": []
},
{
"path": "menu",
"component": "user/menu/menu-index",
"alwaysShow": false,
"name": "菜单管理",
"meta": {
"title": "菜单管理",
"icon": "el-icon-s-order",
"roles": [
1
]
},
"children": []
}
]
}
]