From efc9b33f8c122ba50babce6ccc5a941ea3c83a4e Mon Sep 17 00:00:00 2001 From: xiaolong <19953373312@163.com> Date: Wed, 12 Nov 2025 15:52:07 +0800 Subject: [PATCH] =?UTF-8?q?[fix]=20=E4=BF=AE=E5=A4=8D=E2=80=9C=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E7=B1=BB=E5=88=AB=E2=80=9D=E6=A0=91=E5=BD=A2=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E6=9E=84=E5=BB=BA=E6=97=B6=E4=B8=8D=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=80=9A=E8=BF=87setChildren()=E6=96=B9=E6=B3=95=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E5=AD=90=E8=8A=82=E7=82=B9=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../warm/flow/ui/service/WarmFlowService.java | 3 +- .../warm/flow/ui/utils/TreeBuildUtil.java | 226 ++++++++++++++++++ 2 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 warm-flow-plugin/warm-flow-plugin-ui/warm-flow-plugin-ui-core/src/main/java/org/dromara/warm/flow/ui/utils/TreeBuildUtil.java diff --git a/warm-flow-plugin/warm-flow-plugin-ui/warm-flow-plugin-ui-core/src/main/java/org/dromara/warm/flow/ui/service/WarmFlowService.java b/warm-flow-plugin/warm-flow-plugin-ui/warm-flow-plugin-ui-core/src/main/java/org/dromara/warm/flow/ui/service/WarmFlowService.java index 74cfed47..0a78242a 100644 --- a/warm-flow-plugin/warm-flow-plugin-ui/warm-flow-plugin-ui-core/src/main/java/org/dromara/warm/flow/ui/service/WarmFlowService.java +++ b/warm-flow-plugin/warm-flow-plugin-ui/warm-flow-plugin-ui-core/src/main/java/org/dromara/warm/flow/ui/service/WarmFlowService.java @@ -31,6 +31,7 @@ import org.dromara.warm.flow.core.utils.StreamUtils; import org.dromara.warm.flow.core.utils.StringUtils; import org.dromara.warm.flow.ui.dto.HandlerFeedBackDto; import org.dromara.warm.flow.ui.dto.HandlerQuery; +import org.dromara.warm.flow.ui.utils.TreeBuildUtil; import org.dromara.warm.flow.ui.utils.TreeUtil; import org.dromara.warm.flow.ui.vo.*; @@ -102,7 +103,7 @@ public class WarmFlowService { CategoryService categoryService = FrameInvoker.getBean(CategoryService.class); if (categoryService != null) { List treeList = categoryService.queryCategory(); - defJson.setCategoryList(TreeUtil.buildTree(treeList)); + defJson.setCategoryList(TreeBuildUtil.buildTree(treeList)); } FormPathService formPathService = FrameInvoker.getBean(FormPathService.class); if (formPathService != null) { diff --git a/warm-flow-plugin/warm-flow-plugin-ui/warm-flow-plugin-ui-core/src/main/java/org/dromara/warm/flow/ui/utils/TreeBuildUtil.java b/warm-flow-plugin/warm-flow-plugin-ui/warm-flow-plugin-ui-core/src/main/java/org/dromara/warm/flow/ui/utils/TreeBuildUtil.java new file mode 100644 index 00000000..bb172fd9 --- /dev/null +++ b/warm-flow-plugin/warm-flow-plugin-ui/warm-flow-plugin-ui-core/src/main/java/org/dromara/warm/flow/ui/utils/TreeBuildUtil.java @@ -0,0 +1,226 @@ +package org.dromara.warm.flow.ui.utils; + +import org.dromara.warm.flow.core.dto.Tree; +import org.dromara.warm.flow.core.utils.StringUtils; + +import java.util.*; +import java.util.stream.Collectors; + +public class TreeBuildUtil { + private TreeBuildUtil() { + } + + /** + * 构建所需要树结构(支持混合输入:平铺节点和预构建树) + * + * @param trees 节点列表(可能包含平铺节点和预构建树) + * @return 完整的树结构列表 + */ + public static List buildTree(List trees) { + if (trees == null || trees.isEmpty()) { + return new ArrayList<>(); + } + + // 0. 创建节点副本以避免修改原始数据 + List nodeCopies = deepCopyNodes(trees); + + // 1. 收集所有节点ID(包括子节点的ID) + Set allNodeIds = collectAllNodeIds(nodeCopies); + + // 2. 处理平铺节点 + List flatNodes = extractFlatNodes(nodeCopies); + List builtTree = buildTreeFromFlatNodes(flatNodes, allNodeIds); + + // 3. 合并预构建的树结构 + List preBuiltTrees = extractPreBuiltTrees(trees); + if (!preBuiltTrees.isEmpty()) { + builtTree.addAll(preBuiltTrees); + } + + // 4. 再次构建确保所有节点正确挂载 + return finalizeTreeStructure(builtTree, allNodeIds); + } + + /** + * 创建节点的深拷贝 + */ + private static List deepCopyNodes(List nodes) { + return nodes.stream().map(TreeBuildUtil::copyNode).collect(Collectors.toList()); + } + + /** + * 复制单个节点(浅拷贝,因为Tree对象本身是简单的POJO) + * 如果Tree对象包含复杂属性,需要实现深拷贝 + */ + private static Tree copyNode(Tree original) { + Tree copy = new Tree(); + copy.setId(original.getId()); + copy.setName(original.getName()); + copy.setParentId(original.getParentId()); + // 如果有其他属性也需要复制 + + // 递归复制子节点 + if (original.getChildren() != null) { + List childrenCopies = original.getChildren().stream() + .map(TreeBuildUtil::copyNode) + .collect(Collectors.toList()); + copy.setChildren(childrenCopies); + } + + return copy; + } + + /** + * 收集所有节点ID(包括子节点的ID) + */ + private static Set collectAllNodeIds(List trees) { + Set ids = new HashSet<>(); + for (Tree tree : trees) { + collectNodeIds(tree, ids); + } + return ids; + } + + /** + * 递归收集节点ID + */ + private static void collectNodeIds(Tree node, Set ids) { + if (node == null || node.getId() == null) { + return; + } + ids.add(node.getId()); + if (node.getChildren() != null) { + for (Tree child : node.getChildren()) { + collectNodeIds(child, ids); + } + } + } + + /** + * 提取平铺节点(没有子节点的节点) + */ + private static List extractFlatNodes(List trees) { + return trees.stream() + .filter(node -> Objects.isNull(node.getChildren()) || node.getChildren().isEmpty()) + .collect(Collectors.toList()); + } + + /** + * 提取预构建的树结构(有子节点的节点) + */ + private static List extractPreBuiltTrees(List trees) { + return trees.stream() + .filter(node -> Objects.nonNull(node.getChildren()) && !node.getChildren().isEmpty()) + .collect(Collectors.toList()); + } + + /** + * 从平铺节点构建树 + */ + private static List buildTreeFromFlatNodes(List flatNodes, Set allNodeIds) { + List returnList = new ArrayList<>(); + for (Tree node : flatNodes) { + // 如果是顶级节点或父节点不存在于任何地方 + if (!allNodeIds.contains(node.getParentId())) { + recursionFn(flatNodes, node); + returnList.add(node); + } + } + return returnList; + } + + /** + * 最终处理树结构,确保所有节点正确挂载 + */ + private static List finalizeTreeStructure(List trees, Set allNodeIds) { + // 1. 收集所有未挂载的节点 + List allNodes = new ArrayList<>(); + collectAllNodes(trees, allNodes); + + // 2. 找出所有未挂载的节点 + List unAttachedNodes = allNodes.stream() + .filter(node -> node.getParentId() != null && allNodeIds.contains(node.getParentId())) + .filter(node -> { + // 检查是否已经挂载到父节点 + Tree parent = findParent(allNodes, node.getParentId()); + return parent == null || !parent.getChildren().contains(node); + }) + .collect(Collectors.toList()); + + // 3. 将这些节点挂载到正确的位置 + for (Tree node : unAttachedNodes) { + Tree parent = findParent(allNodes, node.getParentId()); + if (parent != null) { + if (parent.getChildren() == null) { + parent.setChildren(new ArrayList<>()); + } + if (!parent.getChildren().contains(node)) { + parent.getChildren().add(node); + } + } + } + + // 4. 返回顶级节点 + return trees.stream() + .filter(node -> node.getParentId() == null || !allNodeIds.contains(node.getParentId())) + .collect(Collectors.toList()); + } + + /** + * 递归收集所有节点 + */ + private static void collectAllNodes(List trees, List collector) { + for (Tree tree : trees) { + collector.add(tree); + if (tree.getChildren() != null) { + collectAllNodes(tree.getChildren(), collector); + } + } + } + + /** + * 查找父节点 + */ + private static Tree findParent(List allNodes, String parentId) { + if (parentId == null) { + return null; + } + return allNodes.stream() + .filter(node -> parentId.equals(node.getId())) + .findFirst() + .orElse(null); + } + + /** + * 递归列表 + */ + private static void recursionFn(List list, Tree t) { + List childList = getChildList(list, t); + t.setChildren(childList); + for (Tree tChild : childList) { + if (hasChild(list, tChild)) { + recursionFn(list, tChild); + } + } + } + + /** + * 判断是否有子节点 + */ + private static boolean hasChild(List list, Tree t) { + return !getChildList(list, t).isEmpty(); + } + + /** + * 得到子节点列表 + */ + private static List getChildList(List list, Tree t) { + List tlist = new ArrayList<>(); + for (Tree n : list) { + if (StringUtils.isNotEmpty(n.getParentId()) && n.getParentId().equals(t.getId())) { + tlist.add(n); + } + } + return tlist; + } +} -- Gitee