Java8 流式递归树形菜单

1. 说明

树形菜单在web后台管理系统, 权限管理中基本上很容易见到。如:csdn的个人后台管理系统。

只不过这个目录只有两层。三层的如下:

甚至可以套n层。


为什么要递归?

因为数据库表的设计,菜单会用一张表设计。菜单表的两个关键列,主键id和其父级id。使用父级id引用主键id 来作为父级菜单。即主键自关联。

2. 表设计

create table acl_permission
(
    id               char(19)            default '' not null comment '编号' primary key,
    pid              char(19)            default '' not null comment '所属上级',
    name             varchar(20)         default '' not null comment '名称',
    type             tinyint(3)          default 0  not null comment '类型(1:菜单,2:按钮)',
    permission_value varchar(50)         null comment '权限值',
    path             varchar(100)        null comment '访问路径',
    component        varchar(100)        null comment '组件路径',
    icon             varchar(50)         null comment '图标',
    status           tinyint             null comment '状态(0:禁止,1:正常)',
    is_deleted       tinyint(1) unsigned default 0  not null comment '逻辑删除 1(true)已删除, 0(false)未删除',
    gmt_create       datetime            null comment '创建时间',
    gmt_modified     datetime            null comment '更新时间'
)
    comment '权限';


实体类

package top.bitqian.rye.acl.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import lombok.Data;

/**
 * 权限
 * 
 * @author echo lovely
 * @date 2021-01-21 19:31:47
 */
@Data
@TableName("acl_permission")
public class PermissionEntity implements Serializable {
    
    
	private static final long serialVersionUID = 1L;

	/**
	 * 编号
	 */
	@TableId
	private String id;
	/**
	 * 所属上级
	 */
	private String pid;
	/**
	 * 名称
	 */
	private String name;
	/**
	 * 类型(1:菜单,2:按钮)
	 */
	private Integer type;
	/**
	 * 权限值
	 */
	private String permissionValue;
	/**
	 * 访问路径
	 */
	private String path;
	/**
	 * 组件路径
	 */
	private String component;
	/**
	 * 图标
	 */
	private String icon;
	/**
	 * 状态(0:禁止,1:正常)
	 */
	private Integer status;
	/**
	 * 逻辑删除 1(true)已删除, 0(false)未删除
	 */
	@TableField(fill = FieldFill.INSERT)
	private Integer isDeleted;
	/**
	 * 创建时间
	 */
	@TableField(fill = FieldFill.INSERT)
	private Date gmtCreate;
	/**
	 * 更新时间
	 */
	@TableField(fill = FieldFill.INSERT_UPDATE)
	private Date gmtModified;

	/**
	 * 等级 根节点为0
	 */
	@TableField(exist = false)
	private String level;

	/**
	 * 子级结点
	 */
	@TableField(exist = false)
	private List<PermissionEntity> children;

}

3. 普通递归

   @Test
    void contextLoad1() {
    
    

		// 表中所有数据
        List<PermissionEntity> list = permissionService.list();

        List<PermissionEntity> nodeList = new ArrayList<>();

        for (PermissionEntity item : list) {
    
    

            if ("1".equals(item.getPid())) {
    
    
                // 一级父节点
                nodeList.add(item);

                // 为每个父结点设置 children
                item.setChildren(collectTreeData(item, list));
            }

        }

        nodeList.forEach(System.out::println);

    }

    /**
     *
     * @param permission 每个父级结点
     * @param list 所有数据
     * @return 封装好的数据 子节点
     */
    private List<PermissionEntity> collectTreeData(PermissionEntity permission, List<PermissionEntity> list) {
    
    

        List<PermissionEntity> tmpList = new ArrayList<>();

        // 遍历所有元素
        for (PermissionEntity item : list) {
    
    

            // 找到子节点
            if (item.getPid().equals(permission.getId())) {
    
    
                // 将儿子收集
                tmpList.add(item);

                // 继续递归
                collectTreeData(item, list);
            }

        }

        // 设置父级结点的儿子
        permission.setChildren(tmpList);

        return tmpList;
    }

4. 流式递归语法糖

    public List<PermissionEntity> getMenuTree() {
    
    

        // 所有树形菜单
        List<PermissionEntity> list = this.list();

        // pid = 1 的根节点
        List<PermissionEntity> nodeList = list.stream().
                filter(r -> "1".equals(r.getPid())).
                collect(Collectors.toList());


        // 为 pid=1 设置子节点。递归。
        return nodeList.stream().peek(r -> {
    
    

            r.setLevel("0");

            // 为每个根结点 设置 children
            List<PermissionEntity> dataList = collectTreeData(r, list);

            r.setChildren(dataList);

        }).collect(Collectors.toList());

    }

    private List<PermissionEntity> collectTreeData(PermissionEntity permission, List<PermissionEntity> list) {
    
    

        return list.stream().
                filter(r -> r.getPid().equals(permission.getId())).peek(r -> {
    
    
            // set value..
            r.setChildren(collectTreeData(r, list));
        }).collect(Collectors.toList());

    }

猜你喜欢

转载自blog.csdn.net/qq_44783283/article/details/113732936