feat:[REQ-3282] 增加通用树工具
This commit is contained in:
parent
729c1b0adc
commit
bafc1e0bb3
@ -23,6 +23,10 @@
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,35 @@
|
||||
package cn.axzo.orgmanax.common.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IBaseTree<T extends IBaseTree<T, O>, O> {
|
||||
|
||||
/**
|
||||
* 节点编码
|
||||
*
|
||||
* @return 节点编码
|
||||
*/
|
||||
O getNodeCode();
|
||||
|
||||
/**
|
||||
* 父节点编码
|
||||
*
|
||||
* @return 父节点编码
|
||||
*/
|
||||
O getParentNodeCode();
|
||||
|
||||
/**
|
||||
* 子节点
|
||||
*
|
||||
* @return 子节点
|
||||
*/
|
||||
List<T> getNodeChildren();
|
||||
|
||||
/**
|
||||
* 设置子节点
|
||||
*
|
||||
* @param nodeChildren
|
||||
*/
|
||||
void setNodeChildren(List<T> nodeChildren);
|
||||
|
||||
}
|
||||
@ -0,0 +1,246 @@
|
||||
package cn.axzo.orgmanax.common.util;
|
||||
|
||||
import cn.axzo.orgmanax.common.model.IBaseTree;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Pair;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TreeUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 合并两个树 极大合并<p/> 这里以新树为基础:<br/> 同层级树: <br/> 1).如果新树的当前节点在旧树中存在,则直接使用新树的当前节点,并保持当前节点的顺序不变<br/>
|
||||
* 2).如果新树的当前节点在旧树中存在,在 1)的基础上,去递归比较新树的子节点<br/> 3).如果新树的当前节点在旧树中不存在,直接使用新树的当前节点,并保持当前节点的顺序不变<br/>
|
||||
* 4).如果旧树的节点在新树中不存在,则直接附加到新树之后,并保持旧树的顺序<br/>
|
||||
* <p/>
|
||||
* snabbdom:https://github.com/snabbdom/snabbdom/blob/master/src/init.ts#L277
|
||||
*
|
||||
* @param oldTree
|
||||
* @param newTree
|
||||
* @return
|
||||
*/
|
||||
public static <T extends IBaseTree<T, O>, O> List<T> largeMergeTree(List<T> oldTree,
|
||||
List<T> newTree) {
|
||||
return largeMergeTree(oldTree, newTree, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并树
|
||||
*
|
||||
* @param oldTree
|
||||
* @param newTree
|
||||
* @param oldTreeConsumer 对需要附加到新树的节点进行操作
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T extends IBaseTree<T, O>, O> List<T> largeMergeTree(List<T> oldTree,
|
||||
List<T> newTree,
|
||||
Consumer<List<T>> oldTreeConsumer) {
|
||||
//为空直接返回
|
||||
if (CollectionUtils.isEmpty(oldTree)) {
|
||||
return newTree;
|
||||
}
|
||||
if (CollectionUtils.isEmpty(newTree)) {
|
||||
return oldTree;
|
||||
}
|
||||
//构造老树code与所在索引的map
|
||||
Map<O, Integer> oldTreeIndexMap = new HashMap<>(oldTree.size());
|
||||
for (int i = 0; i < oldTree.size(); i++) {
|
||||
oldTreeIndexMap.put(oldTree.get(i).getNodeCode(), i);
|
||||
}
|
||||
|
||||
//循环新树,找到新树中的当前节点在老树中的索引
|
||||
for (T t : newTree) {
|
||||
Integer oldTreeIndex = oldTreeIndexMap.get(t.getNodeCode());
|
||||
//找到了,则直接使用新树的当前节点,并保持当前节点的顺序不变
|
||||
if (oldTreeIndex != null) {
|
||||
t.setNodeChildren(
|
||||
largeMergeTree(oldTree.get(oldTreeIndex).getNodeChildren(), t.getNodeChildren(),
|
||||
oldTreeConsumer));
|
||||
oldTreeIndexMap.put(t.getNodeCode(), null);
|
||||
}
|
||||
}
|
||||
List<T> tempOldTree = null;
|
||||
if (oldTreeConsumer != null) {
|
||||
tempOldTree = new ArrayList<>();
|
||||
}
|
||||
List<T> finalResult = tempOldTree;
|
||||
//将所有老树中未找到的节点附加到新树之后,并保持旧树的顺序
|
||||
oldTreeIndexMap.values().stream().filter(Objects::nonNull).sorted().forEach(e -> {
|
||||
T t = oldTree.get(e);
|
||||
newTree.add(t);
|
||||
if (oldTreeConsumer != null) {
|
||||
finalResult.add(t);
|
||||
}
|
||||
});
|
||||
if (oldTreeConsumer != null) {
|
||||
oldTreeConsumer.accept(tempOldTree);
|
||||
}
|
||||
return newTree;
|
||||
}
|
||||
|
||||
|
||||
public static <T extends IBaseTree<T, O>, O> List<T> buildTree(List<T> treeList) {
|
||||
if (CollectionUtils.isEmpty(treeList)) {
|
||||
return treeList;
|
||||
}
|
||||
Set<O> codes = treeList.stream().map(IBaseTree::getNodeCode).collect(Collectors.toSet());
|
||||
List<T> rootList = treeList.stream().filter(e -> !codes.contains(e.getParentNodeCode()))
|
||||
.collect(Collectors.toList());
|
||||
List<T> children = treeList.stream().filter(e -> codes.contains(e.getParentNodeCode()))
|
||||
.collect(Collectors.toList());
|
||||
for (T t : rootList) {
|
||||
t.setNodeChildren(buildTree(children, t.getNodeCode()));
|
||||
}
|
||||
return rootList;
|
||||
}
|
||||
|
||||
public static <T extends IBaseTree<T, O>, O> List<T> buildTree(List<T> treeList, O rootCode) {
|
||||
if (CollectionUtils.isEmpty(treeList)) {
|
||||
return treeList;
|
||||
}
|
||||
//root级树
|
||||
List<T> rootList = treeList.stream()
|
||||
.filter(e -> Objects.equals(e.getParentNodeCode(), rootCode))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<T> childrenList = treeList.stream()
|
||||
.filter(e -> !Objects.equals(e.getParentNodeCode(), rootCode))
|
||||
.collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(childrenList)) {
|
||||
return CollectionUtils.isEmpty(rootList) ? null : rootList;
|
||||
}
|
||||
for (T t : rootList) {
|
||||
t.setNodeChildren(buildTree(childrenList, t.getNodeCode()));
|
||||
}
|
||||
return CollectionUtils.isEmpty(rootList) ? null : rootList;
|
||||
}
|
||||
|
||||
// public static <T extends IBaseTree<T, O>, O> List<T> buildFastTree(List<T> treeList, O rootCode) {
|
||||
// if (CollectionUtils.isEmpty(treeList)) {
|
||||
// return treeList;
|
||||
// }
|
||||
// //root级树
|
||||
// List<T> rootList = treeList.stream().filter(e -> Objects.equals(e.getParentNodeCode(), rootCode))
|
||||
// .collect(Collectors.toList());
|
||||
//
|
||||
// List<T> notRootList = treeList.stream().filter(e -> !Objects.equals(e.getParentNodeCode(), rootCode))
|
||||
// .collect(Collectors.toList());
|
||||
//
|
||||
// Map<O, List<T>> childrenMap = notRootList.stream()
|
||||
// .collect(Collectors.groupingBy(IBaseTree::getParentNodeCode));
|
||||
//
|
||||
// for (T t : rootList) {
|
||||
// List<T> children = childrenMap.get(t.getNodeCode());
|
||||
// if(children==null){
|
||||
// children=new ArrayList<>();
|
||||
// }
|
||||
// t.setNodeChildren(children);
|
||||
// }
|
||||
// return rootList;
|
||||
// }
|
||||
|
||||
/**
|
||||
* 树的扁平化操作
|
||||
*
|
||||
* @param trees 树列表
|
||||
* @param maxLevel 业务场景所允许的最大的树高
|
||||
*/
|
||||
public static <T extends IBaseTree<T, O>, O> List<T> flattening(List<T> trees, int maxLevel) {
|
||||
if (CollUtil.isEmpty(trees)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return trees.stream()
|
||||
.flatMap(e -> flattening(e, maxLevel).stream())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 树的扁平化操作
|
||||
*
|
||||
* @param tree 树
|
||||
* @param maxLevel 业务场景所允许的最大的树高
|
||||
*/
|
||||
public static <T extends IBaseTree<T, O>, O> List<T> flattening(T tree, int maxLevel) {
|
||||
if (Objects.isNull(tree)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<T> result = new ArrayList<>();
|
||||
// 根节点放入栈中
|
||||
LinkedList<Pair<Integer, T>> stack = new LinkedList<>();
|
||||
stack.push(Pair.of(1, tree));
|
||||
Pair<Integer, T> pointer;
|
||||
do {
|
||||
// 出栈
|
||||
pointer = stack.pop();
|
||||
// 校验当前遍历的层级是否过深
|
||||
Assert.isTrue(pointer.getKey() <= maxLevel, "层数过深,请检查数据的准确性");
|
||||
// 数据放入结果集
|
||||
result.add(pointer.getValue());
|
||||
if (CollUtil.isNotEmpty(pointer.getValue().getNodeChildren())) {
|
||||
// 子集层级自增并入栈
|
||||
int recursiveLevel = pointer.getKey() + 1;
|
||||
pointer.getValue().getNodeChildren().stream()
|
||||
.map(e -> Pair.of(recursiveLevel, e))
|
||||
.forEach(stack::push);
|
||||
}
|
||||
} while (CollUtil.isNotEmpty(stack));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 树的剪枝操作
|
||||
*
|
||||
* @param root 树
|
||||
* @param treeNodeCode 剪枝切入点
|
||||
*/
|
||||
public static <T extends IBaseTree<T, O>, O> T pruning(T root, O treeNodeCode) {
|
||||
if (Objects.isNull(root) || Objects.isNull(treeNodeCode)) {
|
||||
return root;
|
||||
}
|
||||
// 根节点放入栈中
|
||||
LinkedList<T> stack = new LinkedList<>();
|
||||
stack.push(root);
|
||||
T node;
|
||||
do {
|
||||
// 出栈
|
||||
node = stack.pop();
|
||||
if (Objects.equals(node.getNodeCode(), treeNodeCode)) {
|
||||
return node;
|
||||
}
|
||||
if (CollUtil.isNotEmpty(node.getNodeChildren())) {
|
||||
stack.addAll(node.getNodeChildren());
|
||||
}
|
||||
} while (CollUtil.isNotEmpty(stack));
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T extends IBaseTree<T, O>, O> List<T> buildTree(List<T> treeList, Function<T, Boolean> filter) {
|
||||
Objects.requireNonNull(filter);
|
||||
|
||||
if (CollectionUtils.isEmpty(treeList)) {
|
||||
return treeList;
|
||||
}
|
||||
|
||||
Set<O> codes = treeList.stream().map(IBaseTree::getNodeCode).collect(Collectors.toSet());
|
||||
List<T> rootList = treeList.stream()
|
||||
.filter(filter::apply)
|
||||
.filter(e -> !codes.contains(e.getParentNodeCode()))
|
||||
.collect(Collectors.toList());
|
||||
List<T> children = treeList.stream()
|
||||
.filter(filter::apply)
|
||||
.filter(e -> codes.contains(e.getParentNodeCode()))
|
||||
.collect(Collectors.toList());
|
||||
for (T t : rootList) {
|
||||
t.setNodeChildren(buildTree(children, t.getNodeCode()));
|
||||
}
|
||||
return rootList;
|
||||
}
|
||||
|
||||
}
|
||||
@ -11,6 +11,7 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.google.common.collect.Sets;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -28,7 +29,7 @@ import java.util.stream.Stream;
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class NodeQueryRepositoryImpl implements NodeQueryRepository {
|
||||
public class NodeQueryRepositoryImpl extends ServiceImpl implements NodeQueryRepository {
|
||||
|
||||
private final NodeDao nodeDao;
|
||||
|
||||
@ -58,9 +59,6 @@ public class NodeQueryRepositoryImpl implements NodeQueryRepository {
|
||||
if (CollUtil.isEmpty(records)) {
|
||||
return resp;
|
||||
}
|
||||
|
||||
// assemble parent node
|
||||
assembleParentNode(req, records);
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
||||
@ -166,6 +166,7 @@ public class CooperateShipServiceImpl implements CooperateShipService {
|
||||
resultNodeList.addAll(ancestorShipNodes);
|
||||
}
|
||||
|
||||
|
||||
// 是否查询父级节点
|
||||
if (BooleanUtil.isTrue(req.getIncludeAncestors())) {
|
||||
Set<Long> ancestorIds = cooperateShipFoundationService.extractAncestorIds(currentNodeList);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user