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 74cfed470b3529f4dafdbf24f60dc02c984e5eac..0a78242abc5a56976ce8802ceeb325c018bdfad6 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 0000000000000000000000000000000000000000..bb172fd96e3e58d63756050b14c620b6bb17e162 --- /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; + } +}