前言
数据库里面有子父级的数据,查询出来是一个平级list集合,想要展示树形的数据结构,需要重新封装一下,所以提供一个通用的方法方便灵活
一、数据准备
CREATE TABLE `content_file_category` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL COMMENT '名称',
`parent_id` bigint(20) DEFAULT NULL COMMENT '所属分类',
`order_num` int(11) DEFAULT NULL COMMENT '排序序号',
`level` int(11) DEFAULT NULL COMMENT '第几分类,从1开始',
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modify` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最近更新时间',
PRIMARY KEY (`id`),
KEY `level_idx` (`level`),
KEY `parent_id_idx` (`parent_id`),
KEY `name_idx` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=68 DEFAULT CHARSET=utf8 COMMENT='文件分类管理'
方案一:
一般情况下,我们自己写递归代码的方式
(1)获取树形结构数据接口
/**
* 获取文件分类树形结构
*
* @return
*/
@Override
public FileCategoryListResponse queryFileCategoryTree(Integer levelDeep) {
//查询一级分类
List<FileCategoryDO> fileCategoryLevel1 = this.list(new LambdaQueryWrapper<FileCategoryDO>()
.eq(FileCategoryDO::getLevel, FileCategoryLevelEnum.LEVEL_1.getCode())
.orderByAsc(FileCategoryDO::getSort));
//最终响应实体
FileCategoryListResponse response = new FileCategoryListResponse();
List<FileCategoryResponse> fileCategoryResponseList = new ArrayList<FileCategoryResponse>();
if (!CollectionUtils.isEmpty(fileCategoryLevel1)) {
for (FileCategoryDO fileCategoryDO : fileCategoryLevel1) {
FileCategoryResponse fileCategoryResponse = new FileCategoryResponse().setChildren(new ArrayList<>());
BeanUtils.copyProperties(fileCategoryDO, fileCategoryResponse);
//循环遍历一级分类并进行递归操作,组装子分类
getChildrenFileCategory(fileCategoryResponse, levelDeep);
fileCategoryResponseList.add(fileCategoryResponse);
}
}
response.setList(fileCategoryResponseList);
return response;
}
/**
* 递归获取下层级的文件分类
*
* @param fileCategoryResponse
*/
private void getChildrenFileCategory(FileCategoryResponse fileCategoryResponse, Integer levelDeep) {
//获取id
if (fileCategoryResponse != null) {
Long id = fileCategoryResponse.getId();
List<FileCategoryDO> list = this.list(new LambdaQueryWrapper<FileCategoryDO>()
.eq(FileCategoryDO::getPid, id).le(FileCategoryDO::getLevel, levelDeep)
.orderByAsc(FileCategoryDO::getSort));
if (!CollectionUtils.isEmpty(list)) {
List<FileCategoryResponse> childrens = list.stream().map(x -> {
FileCategoryResponse dto = new FileCategoryResponse().setChildren(new ArrayList<>());
BeanUtils.copyProperties(x, dto);
return dto;
}).collect(Collectors.toList());
fileCategoryResponse.getChildren().addAll(childrens);
for (FileCategoryResponse categoryResponse : childrens) {
getChildrenFileCategory(categoryResponse, levelDeep);
}
}
}
}
(2)根据父级id 递归获取子节点id集合
/**
* 根据文件分类id获取最下级文件分类id
*
* @param id
* @return
*/
@Override
public List<Long> recurQueryCatehoryId(Long id) {
List<FileCategoryResponse> categoryResponses = new CopyOnWriteArrayList<>();
FileCategoryDO fileCategory = this.getById(id);
if (ObjectUtil.isNull(fileCategory)) {
return null;
}
FileCategoryResponse fileCategoryResponse = new FileCategoryResponse().setChildren(new ArrayList<>());
BeanUtils.copyProperties(fileCategory, fileCategoryResponse);
categoryResponses.add(fileCategoryResponse);
//递归获取子节点
getChildrenFileCategory(fileCategoryResponse, FileCategoryLevelEnum.LEVEL_3.getCode());
//将子节点加入到list
categoryResponses = joinChildrenCategoryId(fileCategoryResponse, categoryResponses);
//获取当前文件分类的非1级分类id
List<Long> idList = categoryResponses.stream().filter(x -> x.getLevel() != null && Integer.parseInt(x.getLevel()) > FileCategoryLevelEnum.LEVEL_1.getCode())
.map(FileCategoryResponse::getId).collect(Collectors.toList());
// //根据层级进行分组,key-》层级,value-》每层级的文件分类列表
// Map<String, List<FileCategoryResponse>> collect = categoryResponses.stream().collect(
// Collectors.groupingBy(FileCategoryResponse::getLevel));
// //根据层级key进行倒序,找到key值最大的层级(即最下级分类)
// List<Long> idList = collect.entrySet().stream().sorted((o1, o2) -> o2.getKey().compareTo(o1.getKey())).
// map(x -> x.getValue().stream().map(FileCategoryResponse::getId).collect(Collectors.toList()))
// .findFirst().orElseGet(ArrayList::new);
return idList;
}
private List<FileCategoryResponse> joinChildrenCategoryId(FileCategoryResponse fileCategoryResponse, List<FileCategoryResponse> categoryResponses) {
List<FileCategoryResponse> childrens = fileCategoryResponse.getChildren();
if (!CollectionUtils.isEmpty(childrens)) {
categoryResponses.addAll(childrens);
for (FileCategoryResponse children : childrens) {
joinChildrenCategoryId(children, categoryResponses);
}
}
return categoryResponses;
}
方案二:
使用 Hutool的 TreeUtil 工具类
public static void main(String[] args) {
//模拟的从数据库查询出来的数据
List<FileCategoryDO> fileCategoryDOS = new ArrayList<>();
// 构建node列表
List<TreeNode<String>> nodeList = CollUtil.newArrayList();
List<TreeNode<Long>> collect = fileCategoryDOS.stream().map(fileCategoryDO -> {
Map<String, Object> map = new HashMap<>();
map.put("level", "2");
map.put("gmt_create", LocalDateTime.now());
map.put("gmt_modifier", LocalDateTime.now());
TreeNode<Long> treeNode = new TreeNode<Long>().setId(fileCategoryDO.getId())
.setName(fileCategoryDO.getCategoryName())
.setParentId(Long.parseLong(fileCategoryDO.getPid()))
.setWeight(fileCategoryDO.getSort())
.setExtra(map);
return treeNode;
}).collect(Collectors.toList());
//配置
TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
// 自定义属性名 都要默认值的
treeNodeConfig.setWeightKey("orderNum");
treeNodeConfig.setIdKey("id");
treeNodeConfig.setChildrenKey("childrenNode");
// 最大递归深度
treeNodeConfig.setDeep(3);
//转换器
List<Tree<String>> treeNodes = TreeUtil.build(nodeList, "0", treeNodeConfig,
(treeNode, tree) -> {
tree.setId(treeNode.getId());
tree.setParentId(treeNode.getParentId());
tree.setWeight(treeNode.getWeight());
tree.setName(treeNode.getName());
// 扩展属性 ...
tree.putExtra("level", treeNode.getExtra().getOrDefault("level", 2));
tree.putExtra("gmt_create", treeNode.getExtra().getOrDefault("gmt_create", null));
});
System.out.println(JSON.toJSONString(treeNodes));
}