From bafc1e0bb364a880fbcbf0c3b5e2fe72a2a90f1a Mon Sep 17 00:00:00 2001 From: liuyang Date: Tue, 24 Dec 2024 13:47:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:[REQ-3282]=20=E5=A2=9E=E5=8A=A0=E9=80=9A?= =?UTF-8?q?=E7=94=A8=E6=A0=91=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- orgmanax-common/pom.xml | 4 + .../axzo/orgmanax/common/model/IBaseTree.java | 35 +++ .../axzo/orgmanax/common/util/TreeUtil.java | 246 ++++++++++++++++++ .../impl/NodeQueryRepositoryImpl.java | 6 +- .../impl/CooperateShipServiceImpl.java | 1 + 5 files changed, 288 insertions(+), 4 deletions(-) create mode 100644 orgmanax-common/src/main/java/cn/axzo/orgmanax/common/model/IBaseTree.java create mode 100644 orgmanax-common/src/main/java/cn/axzo/orgmanax/common/util/TreeUtil.java diff --git a/orgmanax-common/pom.xml b/orgmanax-common/pom.xml index 100d46a..2ff5b46 100644 --- a/orgmanax-common/pom.xml +++ b/orgmanax-common/pom.xml @@ -23,6 +23,10 @@ 2.0.0-SNAPSHOT compile + + cn.hutool + hutool-all + \ No newline at end of file diff --git a/orgmanax-common/src/main/java/cn/axzo/orgmanax/common/model/IBaseTree.java b/orgmanax-common/src/main/java/cn/axzo/orgmanax/common/model/IBaseTree.java new file mode 100644 index 0000000..54aa3cd --- /dev/null +++ b/orgmanax-common/src/main/java/cn/axzo/orgmanax/common/model/IBaseTree.java @@ -0,0 +1,35 @@ +package cn.axzo.orgmanax.common.model; + +import java.util.List; + +public interface IBaseTree, O> { + + /** + * 节点编码 + * + * @return 节点编码 + */ + O getNodeCode(); + + /** + * 父节点编码 + * + * @return 父节点编码 + */ + O getParentNodeCode(); + + /** + * 子节点 + * + * @return 子节点 + */ + List getNodeChildren(); + + /** + * 设置子节点 + * + * @param nodeChildren + */ + void setNodeChildren(List nodeChildren); + +} diff --git a/orgmanax-common/src/main/java/cn/axzo/orgmanax/common/util/TreeUtil.java b/orgmanax-common/src/main/java/cn/axzo/orgmanax/common/util/TreeUtil.java new file mode 100644 index 0000000..54ae6df --- /dev/null +++ b/orgmanax-common/src/main/java/cn/axzo/orgmanax/common/util/TreeUtil.java @@ -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 { + + + /** + * 合并两个树 极大合并

这里以新树为基础:
同层级树:
1).如果新树的当前节点在旧树中存在,则直接使用新树的当前节点,并保持当前节点的顺序不变
+ * 2).如果新树的当前节点在旧树中存在,在 1)的基础上,去递归比较新树的子节点
3).如果新树的当前节点在旧树中不存在,直接使用新树的当前节点,并保持当前节点的顺序不变
+ * 4).如果旧树的节点在新树中不存在,则直接附加到新树之后,并保持旧树的顺序
+ *

+ * snabbdom:https://github.com/snabbdom/snabbdom/blob/master/src/init.ts#L277 + * + * @param oldTree + * @param newTree + * @return + */ + public static , O> List largeMergeTree(List oldTree, + List newTree) { + return largeMergeTree(oldTree, newTree, null); + } + + /** + * 合并树 + * + * @param oldTree + * @param newTree + * @param oldTreeConsumer 对需要附加到新树的节点进行操作 + * @param + * @return + */ + public static , O> List largeMergeTree(List oldTree, + List newTree, + Consumer> oldTreeConsumer) { + //为空直接返回 + if (CollectionUtils.isEmpty(oldTree)) { + return newTree; + } + if (CollectionUtils.isEmpty(newTree)) { + return oldTree; + } + //构造老树code与所在索引的map + Map 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 tempOldTree = null; + if (oldTreeConsumer != null) { + tempOldTree = new ArrayList<>(); + } + List 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 , O> List buildTree(List treeList) { + if (CollectionUtils.isEmpty(treeList)) { + return treeList; + } + Set codes = treeList.stream().map(IBaseTree::getNodeCode).collect(Collectors.toSet()); + List rootList = treeList.stream().filter(e -> !codes.contains(e.getParentNodeCode())) + .collect(Collectors.toList()); + List 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 , O> List buildTree(List treeList, O rootCode) { + if (CollectionUtils.isEmpty(treeList)) { + return treeList; + } + //root级树 + List rootList = treeList.stream() + .filter(e -> Objects.equals(e.getParentNodeCode(), rootCode)) + .collect(Collectors.toList()); + + List 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 , O> List buildFastTree(List treeList, O rootCode) { +// if (CollectionUtils.isEmpty(treeList)) { +// return treeList; +// } +// //root级树 +// List rootList = treeList.stream().filter(e -> Objects.equals(e.getParentNodeCode(), rootCode)) +// .collect(Collectors.toList()); +// +// List notRootList = treeList.stream().filter(e -> !Objects.equals(e.getParentNodeCode(), rootCode)) +// .collect(Collectors.toList()); +// +// Map> childrenMap = notRootList.stream() +// .collect(Collectors.groupingBy(IBaseTree::getParentNodeCode)); +// +// for (T t : rootList) { +// List children = childrenMap.get(t.getNodeCode()); +// if(children==null){ +// children=new ArrayList<>(); +// } +// t.setNodeChildren(children); +// } +// return rootList; +// } + + /** + * 树的扁平化操作 + * + * @param trees 树列表 + * @param maxLevel 业务场景所允许的最大的树高 + */ + public static , O> List flattening(List 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 , O> List flattening(T tree, int maxLevel) { + if (Objects.isNull(tree)) { + return Collections.emptyList(); + } + List result = new ArrayList<>(); + // 根节点放入栈中 + LinkedList> stack = new LinkedList<>(); + stack.push(Pair.of(1, tree)); + Pair 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 , O> T pruning(T root, O treeNodeCode) { + if (Objects.isNull(root) || Objects.isNull(treeNodeCode)) { + return root; + } + // 根节点放入栈中 + LinkedList 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 , O> List buildTree(List treeList, Function filter) { + Objects.requireNonNull(filter); + + if (CollectionUtils.isEmpty(treeList)) { + return treeList; + } + + Set codes = treeList.stream().map(IBaseTree::getNodeCode).collect(Collectors.toSet()); + List rootList = treeList.stream() + .filter(filter::apply) + .filter(e -> !codes.contains(e.getParentNodeCode())) + .collect(Collectors.toList()); + List 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; + } + +} diff --git a/orgmanax-infra/src/main/java/cn/axzo/orgmanax/infra/dao/node/repository/impl/NodeQueryRepositoryImpl.java b/orgmanax-infra/src/main/java/cn/axzo/orgmanax/infra/dao/node/repository/impl/NodeQueryRepositoryImpl.java index f8ec562..794e168 100644 --- a/orgmanax-infra/src/main/java/cn/axzo/orgmanax/infra/dao/node/repository/impl/NodeQueryRepositoryImpl.java +++ b/orgmanax-infra/src/main/java/cn/axzo/orgmanax/infra/dao/node/repository/impl/NodeQueryRepositoryImpl.java @@ -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; } diff --git a/orgmanax-server/src/main/java/cn/axzo/orgmanax/server/cooperateship/service/impl/CooperateShipServiceImpl.java b/orgmanax-server/src/main/java/cn/axzo/orgmanax/server/cooperateship/service/impl/CooperateShipServiceImpl.java index 1745409..ef6ee89 100644 --- a/orgmanax-server/src/main/java/cn/axzo/orgmanax/server/cooperateship/service/impl/CooperateShipServiceImpl.java +++ b/orgmanax-server/src/main/java/cn/axzo/orgmanax/server/cooperateship/service/impl/CooperateShipServiceImpl.java @@ -166,6 +166,7 @@ public class CooperateShipServiceImpl implements CooperateShipService { resultNodeList.addAll(ancestorShipNodes); } + // 是否查询父级节点 if (BooleanUtil.isTrue(req.getIncludeAncestors())) { Set ancestorIds = cooperateShipFoundationService.extractAncestorIds(currentNodeList);