目录
一、简介
在我们业务需求中,有些业务要实现List转化Tree或Tree转化List,如:菜单权限、产品分类、数字字典等等!
举个栗子
业务需求:我们菜单权限需要树形化显示菜单层级关系
表结构如下
CREATE TABLE "sys_permission" (
"id" bigint(20) NOT NULL AUTO_INCREMENT,
"pid" bigint(20) DEFAULT NULL COMMENT '父级权限id',
"name" varchar(100) DEFAULT NULL COMMENT '名称',
"permission_value" varchar(200) DEFAULT NULL COMMENT '权限值',
"icon" varchar(500) DEFAULT NULL COMMENT '图标',
"type" int(1) DEFAULT NULL COMMENT '权限类型:0->目录;1->菜单;2->按钮(接口绑定权限)',
"data_type" int(1) DEFAULT '0' COMMENT '权限数据类型:0->查看全部;1->查看自己数据权限',
"url" varchar(200) DEFAULT NULL COMMENT '前端资源路径',
"permission_status" int(1) NOT NULL DEFAULT '1' COMMENT '启用状态;0->启用;1->禁用',
"sort" int(4) DEFAULT NULL COMMENT '排序',
"create_time" datetime DEFAULT NULL COMMENT '创建时间',
"create_user" varchar(100) DEFAULT NULL COMMENT '创建用户',
"update_time" datetime DEFAULT NULL COMMENT '创建时间',
"update_user" varchar(100) DEFAULT NULL COMMENT '创建用户',
PRIMARY KEY ("id"),
KEY "idx_pid" ("pid")
) ENGINE=InnoDB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8mb4 COMMENT='后台用户权限表';
二、List转Tree
1.controller层
需求:根据角色ID获取权限树
@RestController
@AllArgsConstructor
@RequestMapping("/permission")
@Api(value = "permission", tags = "权限模块")
public class PermissionController {
private final PermissionService permissionService;
@GetMapping("getPermissionTreeList")
@ApiOperation(value = "获取树形权限列表", notes = "获取权限列表")
public Result<List<PermissionTreeResp>> getPermissionTreeList(
@RequestParam(value = "roleId") @ApiParam(name = "roleId", value = "角色ID") Long roleId
) {
List<PermissionTreeResp> permissionResp = permissionService.getPermissionTreeList(roleId);
return Result.success(permissionResp);
}
@PostMapping("savePermission")
@ApiOperation(value = "保存权限", notes = "保存权限")
public Result<List<PermissionTreeResp>> savePermission(@RequestBody @Validated PermissionTreeReq permissionTreeReq) {
permissionService.savePermission(permissionTreeReq);
return getPermissionTreeList(permissionTreeReq.getRoleId());
}
}
2. PermissionServiceImpl接口实现层
实现思路
- 查询正常的权限列表;
- 把DTO转换成Entity,(如果你返回对象也是Entity就不用转换对象);
- 查询一级目录权限列表;
- 遍历一级权限列表,把一级权限ID等于所有权限Pid(父ID);
- 递归遍历;
@Override
public List<PermissionTreeResp> getPermissionTreeList(Long roleId) {
log.info("getPermissionTreeList.req roleId={}", roleId);
Wrapper<Permission> wapper = new QueryWrapper<>(new Permission())
.eq("permission_status", StatusEnum.NORMAL.getValue());
List<Permission> permissionList = this.list(wapper);
List<PermissionTreeResp> permissionTreeResps = new LinkedList<>();
if (CollectionUtil.isNotEmpty(permissionList)) {
List<PermissionTreeResp> permissionTreeList = BeanToUtils.entityToList(permissionList, PermissionTreeResp.class);
// 查询一级目录
permissionTreeResps = permissionTreeList.stream().filter(permission -> permission.getPid() == 0).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(permissionTreeList)) {
for (PermissionTreeResp permissionTreeResp : permissionTreeResps) {
permissionTreeResp.setChildren(getChildrenTree(permissionTreeResp, permissionTreeList));
}
}
}
// 降序排序
permissionTreeResps = getPermissionSortedTree(permissionTreeResps);
return permissionTreeResps;
}
/**
* 递归遍历子树结构
*
* @param permissionTreeResp 父权限
* @param permissionTreeResps 所有权限
* @date: 2021/3/9 15:32
* @return: java.util.List<com.zlp.dto.PermissionTreeResp>
*/
private List<PermissionTreeResp> getChildrenTree(PermissionTreeResp permissionTreeResp, List<PermissionTreeResp> permissionTreeResps) {
List<PermissionTreeResp> treeRespList;
treeRespList = permissionTreeResps.stream().filter(permission -> permission.getPid().
equals(permissionTreeResp.getId()))
.collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(treeRespList)) {
for (PermissionTreeResp treeResp : treeRespList) {
treeResp.setChildren(getChildrenTree(treeResp, permissionTreeResps));
}
}
// 降序排序
treeRespList = getPermissionSortedTree(treeRespList);
return treeRespList;
}
接口返回的数据
{
"code": 200,
"message": "操作成功",
"data": [
{
"id": 1,
"pid": 0,
"name": "设备",
"permissionValue": null,
"type": 0,
"dataType": 0,
"sort": 1,
"isHaveFlag": false,
"children": [
{
"id": 5,
"pid": 1,
"name": "Co-Brain",
"permissionValue": null,
"type": 1,
"dataType": 0,
"sort": 1,
"isHaveFlag": false,
"children": [
{
"id": 12,
"pid": 5,
"name": "添加设备",
"permissionValue": "equipment:add",
"type": 2,
"dataType": 0,
"sort": 2,
"isHaveFlag": false,
"children": []
},
{
"id": 13,
"pid": 5,
"name": "删除设备",
"permissionValue": "equipment:del",
"type": 2,
"dataType": 0,
"sort": 3,
"isHaveFlag": false,
"children": []
},
{
"id": 15,
"pid": 5,
"name": "维修设置",
"permissionValue": "equipment:repair",
"type": 2,
"dataType": 0,
"sort": 5,
"isHaveFlag": false,
"children": []
},
{
"id": 16,
"pid": 5,
"name": "启用设备",
"permissionValue": "equipment:enable",
"type": 2,
"dataType": 0,
"sort": 6,
"isHaveFlag": false,
"children": []
}
]
}
]
},
{
"id": 2,
"pid": 0,
"name": "客户",
"permissionValue": null,
"type": 0,
"dataType": 0,
"sort": 2,
"isHaveFlag": false,
"children": [
{
"id": 6,
"pid": 2,
"name": "机构管理",
"permissionValue": null,
"type": 1,
"dataType": 0,
"sort": 1,
"isHaveFlag": false,
"children": [
{
"id": 19,
"pid": 6,
"name": "添加机构",
"permissionValue": "organ:add",
"type": 2,
"dataType": 0,
"sort": 2,
"isHaveFlag": false,
"children": []
},
{
"id": 53,
"pid": 6,
"name": "添加机构",
"permissionValue": "organ:add",
"type": 2,
"dataType": 1,
"sort": 2,
"isHaveFlag": false,
"children": []
},
{
"id": 21,
"pid": 6,
"name": "编辑资料",
"permissionValue": "organ:update",
"type": 2,
"dataType": 0,
"sort": 4,
"isHaveFlag": false,
"children": []
},
{
"id": 55,
"pid": 6,
"name": "编辑资料",
"permissionValue": "organ:update",
"type": 2,
"dataType": 1,
"sort": 4,
"isHaveFlag": false,
"children": []
},
{
"id": 22,
"pid": 6,
"name": "创建协议",
"permissionValue": "organ:createEquipment",
"type": 2,
"dataType": 0,
"sort": 5,
"isHaveFlag": false,
"children": []
},
{
"id": 56,
"pid": 6,
"name": "创建协议",
"permissionValue": "organ:createEquipment",
"type": 2,
"dataType": 1,
"sort": 5,
"isHaveFlag": false,
"children": []
},
{
"id": 24,
"pid": 6,
"name": "编辑协议",
"permissionValue": "organ:agreement:update",
"type": 2,
"dataType": 0,
"sort": 7,
"isHaveFlag": false,
"children": []
},
{
"id": 58,
"pid": 6,
"name": "编辑协议",
"permissionValue": "organ:agreement:update",
"type": 2,
"dataType": 1,
"sort": 7,
"isHaveFlag": false,
"children": []
}
]
},
{
"id": 7,
"pid": 2,
"name": "机构审核",
"permissionValue": null,
"type": 1,
"dataType": 0,
"sort": 2,
"isHaveFlag": false,
"children": []
}
]
},
{
"id": 3,
"pid": 0,
"name": "客户账号",
"permissionValue": null,
"type": 0,
"dataType": 0,
"sort": 3,
"isHaveFlag": false,
"children": [
{
"id": 8,
"pid": 3,
"name": "账号管理",
"permissionValue": null,
"type": 1,
"dataType": 0,
"sort": 1,
"isHaveFlag": false,
"children": []
}
]
}
]
}
三、List转化深林树
1. INode 接口
把共同方法抽离出来
public interface INode {
/**
* 主键
*
* @return
*/
Long getId();
/**
* 排序 1,2,3,4 (从小到大排序)
*
* @return
*/
Integer getSort();
/**
* 父主键
*
* @return
*/
Long getPid();
/**
* 子孙节点
*
* @return
*/
List<INode> getChildren();
}
2. ForestNodeManager
/**
* 森林管理类
*
*/
public class ForestNodeManager<T extends INode> {
/**
* 森林的所有节点
*/
private List<T> list;
/**
* 森林的父节点ID
*/
private List<Long> parentIds = new ArrayList<>();
public ForestNodeManager(List<T> items) {
list = items;
}
/**
* 根据节点ID获取一个节点
*
* @param id 节点ID
* @return 对应的节点对象
*/
public INode getTreeNodeAT(Long id) {
for (INode forestNode : list) {
if (forestNode.getId().longValue() == id.longValue()) {
return forestNode;
}
}
return null;
}
/**
* 增加父节点ID
*
* @param parentId
*/
public void addParentId(Long parentId) {
parentIds.add(parentId);
}
/**
* 获取树的根节点(一个森林对应多颗树)
*
* @return 树的根节点集合
*/
public List<T> getRoot() {
List<T> roots = new ArrayList<>();
for (T forestNode : list) {
if (forestNode.getPid() == 0 || parentIds.contains(forestNode.getId())) {
roots.add(forestNode);
}
}
return roots;
}
}
3. ForestNodeMerger
/**
* 森林节点归并类
*
*/
public class ForestNodeMerger {
/**
* 将节点数组归并为一个森林(多棵树)(填充节点的children域)
* 时间复杂度为O(n^2)
*
* @param items 节点域
* @return 多棵树的根节点集合
*/
public static <T extends INode> List<T> merge(List<T> items) {
ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items);
items.forEach(forestNode -> {
if (forestNode.getPid() != 0) {
// 获取父节点
INode node = forestNodeManager.getTreeNodeAT(forestNode.getPid());
if (node != null) {
node.getChildren().add(forestNode);
} else {
forestNodeManager.addParentId(forestNode.getId());
}
}
});
List<T> root = forestNodeManager.getRoot();
return root.stream().sorted(Comparator.comparing(T::getSort)).collect(Collectors.toList());
}
}
4. PermissionForestTree实体类
实现INode接口,实现getChildren方法
@Data
@ApiModel(value = "权限树形返回信息")
public class PermissionForestTree implements INode {
@ApiModelProperty(value = "ID")
private Long id;
@ApiModelProperty(value = "父级权限id")
private Long pid;
@ApiModelProperty(value = "权限名称")
private String name;
@ApiModelProperty(value = "按钮权限值")
private String permissionValue;
@ApiModelProperty(value = "权限类型:0->目录;1->菜单;2->按钮(接口绑定权限)")
private Integer type;
@ApiModelProperty(value = "权限操作类型 0->查看全部按钮权限值; 1->查看自己数据权限按钮权限值")
private Integer dataType;
@ApiModelProperty(value = "排序 1,2,3,4 (从小到大排序)")
private Integer sort;
@ApiModelProperty(value = "是否拥有权限标识:false->否 ; ture->是")
private Boolean isHaveFlag = false;
@ApiModelProperty(value = "子孙节点")
private List<INode> children ;
@Override
public List<INode> getChildren() {
if (this.children == null) {
this.children = new ArrayList<>();
}
return this.children;
}
}
5. 调用ForestNodeMerger.merge转化深林树
@GetMapping("getPermissionForestTreeList")
@ApiOperation(value = "获取深林树形权限列表", notes = "获取深林树形权限列表")
public Result<List<PermissionForestTree>> getPermissionForestTreeList(
@RequestParam(value = "roleId") @ApiParam(name = "roleId", value = "角色ID") Long roleId
) {
List<PermissionForestTree> permissionForestTrees = permissionService.getPermissionForestTreeList(roleId);
return Result.success(ForestNodeMerger.merge(permissionForestTrees));
}
四、Tree转List
需求:修改权限,跟前端妹子协商好参数,也是通过树形化传给后台
实现思路
- 获取前端传给的树形列表;
- 定义一个空的List用来添加权限列表
- 把一级目录权限添加都List集合
- 在判断有子孙节点是否有元素
- 递归遍历子孙节点添加List集合
@Override
public Boolean savePermission(PermissionTreeReq permissionTreeReq) {
log.info("savePermission.req permissionTreeReq={}", JSON.toJSONString(permissionTreeReq));
List<PermissionTreeResp> permissionRespList = new LinkedList<>();
List<PermissionTreeResp> permissionTreeList = permissionTreeReq.getPermissionTreeResps();
// 树转化成List
treeToList(permissionRespList, permissionTreeList);
log.info("treeToList={}",JSON.toJSONString(permissionRespList));
return Boolean.TRUE;
}
/**
* 数转换成List
* @param permissionRespList
* @param permissionTreeList
* @date: 2021/3/9 22:23
* @return: void
*/
private void treeToList(List<PermissionTreeResp> permissionRespList, List<PermissionTreeResp> permissionTreeList) {
for (PermissionTreeResp permissionTreeResp : permissionTreeList) {
permissionRespList.add(permissionTreeResp);
if (CollectionUtil.isNotEmpty(permissionTreeResp.getChildren())) {
treeToList(permissionRespList,permissionTreeResp.getChildren());
}
}
}
感谢大家看到最后,如文章有不足,欢迎大家在评论区支持,给予意见。如果觉得我的文章对你有帮助,那就给我一个赞同吧!