feat: node 和 edge id切换为String类型

This commit is contained in:
2025-05-04 23:04:12 +08:00
parent 7a72358fc7
commit bef6849e45
14 changed files with 75 additions and 70 deletions

View File

@@ -2,6 +2,7 @@ package com.metis.domain.bo;
import com.metis.enums.EdgeType;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@@ -11,7 +12,7 @@ public class EdgeBO {
/**
* 唯一标识符
*/
@NotNull(message = "唯一标识符不能为空")
@NotBlank(message = "唯一标识符不能为空")
private String id;
/**
@@ -29,24 +30,24 @@ public class EdgeBO {
* 源节点ID,对应节点id
*/
@NotNull(message = "源节点ID不能为空")
private Long source;
private String source;
/**
* 目标节点ID,对应节点id
*/
@NotNull(message = "目标节点ID不能为空")
private Long target;
private String target;
/**
* 源句柄id
*/
@NotNull(message = "源句柄ID不能为空")
private Long sourceHandle;
private String sourceHandle;
/**
* 目标句柄id
*/
@NotNull(message = "目标句柄ID不能为空")
private Long targetHandle;
private String targetHandle;
/**
* 边是否动画true/false

View File

@@ -3,6 +3,7 @@ package com.metis.domain.bo;
import com.metis.enums.HandleType;
import com.metis.enums.PositionType;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@@ -14,9 +15,9 @@ public class HandleBO {
/**
* 句柄id
*/
@NotNull(message = "句柄id不能为空")
@NotBlank(message = "句柄id不能为空")
@Schema(description = "句柄id")
private Long id;
private String id;
/**
* 句柄类型

View File

@@ -3,6 +3,7 @@ package com.metis.domain.bo;
import com.metis.enums.NodeType;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@@ -12,9 +13,9 @@ public class NodeBO {
/**
* id
*/
@NotNull(message = "节点id不能为空")
@NotBlank(message = "节点id不能为空")
@Schema(description = "节点id")
private Long id;
private String id;
/**
* 类型

View File

@@ -36,12 +36,12 @@ public class RunningContext {
/**
* 节点运行上下文, 需要数据进行传递
*/
private Map<Long, JSONObject> nodeRunningContext;
private Map<String, JSONObject> nodeRunningContext;
/**
* 下一个运行节点id集合, 可能是多个, 执行器每一次清空该节点
*/
private Set<Long> nextRunNodeId;
private Set<String> nextRunNodeId;
/**
@@ -50,7 +50,7 @@ public class RunningContext {
* @param nodeId 节点id
* @param nodeRunningContext 节点运行背景信息
*/
public void addNodeRunningContext(Long nodeId, JSONObject nodeRunningContext) {
public void addNodeRunningContext(String nodeId, JSONObject nodeRunningContext) {
this.nodeRunningContext.put(nodeId, nodeRunningContext);
}
@@ -60,7 +60,7 @@ public class RunningContext {
* @param nodeId 节点id
* @return {@link JSONObject }
*/
public JSONObject getRunningContext(Long nodeId) {
public JSONObject getRunningContext(String nodeId) {
return this.nodeRunningContext.get(nodeId);
}
@@ -79,19 +79,19 @@ public class RunningContext {
return parser.parseExpression(key).getValue(context);
}
try {
// 解析 key 中的数字部分并转换为 Long 类型
String[] parts = key.split("\\.");
if (parts.length == 2 && StrUtil.isNumeric(parts[0])) {
Long nodeId = Long.valueOf(parts[0]);
JSONObject runningContext = getRunningContext(nodeId);
if (runningContext != null) {
return runningContext.get(parts[1]);
}
}
} catch (Exception e) {
log.error("数字类型获取动态参数失败: {}", key);
}
// try {
// // 解析 key 中的数字部分并转换为 Long 类型
// String[] parts = key.split("\\.");
// if (parts.length == 2 && StrUtil.isNumeric(parts[0])) {
// String nodeId = Long.valueOf(parts[0]);
// JSONObject runningContext = getRunningContext(nodeId);
// if (runningContext != null) {
// return runningContext.get(parts[1]);
// }
// }
// } catch (Exception e) {
// log.error("数字类型获取动态参数失败: {}", key);
// }
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext(this);
return parser.parseExpression(key).getValue(context);
@@ -105,7 +105,7 @@ public class RunningContext {
* @param key 关键
* @return {@link Object }
*/
public Object getValue(Long nodeId, String key) {
public Object getValue(String nodeId, String key) {
if (ObjectUtil.isNull(nodeId)) {
return null;
}

View File

@@ -18,7 +18,7 @@ public class RunningResult {
/**
* 下一个运行节点id, 一些特殊节点需要, 必须条件节点满足后, 才会运行下一个节点
*/
private Set<Long> nextRunNodeId;
private Set<String> nextRunNodeId;
/**
@@ -28,7 +28,7 @@ public class RunningResult {
* @param nextRunNodeId 下一个运行节点id
* @return {@link RunningResult }
*/
public static RunningResult buildResult(JSONObject nodeContext, Set<Long> nextRunNodeId) {
public static RunningResult buildResult(JSONObject nodeContext, Set<String> nextRunNodeId) {
return RunningResult.builder()
.nodeContext(nodeContext)
.nextRunNodeId(nextRunNodeId)
@@ -41,7 +41,7 @@ public class RunningResult {
* @param nextRunNodeId 下一个运行节点id
* @return {@link RunningResult }
*/
public static RunningResult buildResult(Set<Long> nextRunNodeId) {
public static RunningResult buildResult(Set<String> nextRunNodeId) {
return RunningResult.builder()
.nextRunNodeId(nextRunNodeId)
.build();

View File

@@ -12,13 +12,13 @@ import java.util.stream.Collectors;
public class GraphDto {
private final Map<Long, Node> nodeMap;
private final Map<String, Node> nodeMap;
private final Map<Long, Boolean> nodeReadyMap;
private final Map<String, Boolean> nodeReadyMap;
private final Map<Long, List<Edge>> edgeMap;
private final Map<String, List<Edge>> edgeMap;
private final Map<Long, List<Long>> adjacencyList = new HashMap<>();
private final Map<String, List<String>> adjacencyList = new HashMap<>();
private final List<Node> sortedNodes = new ArrayList<>();
@@ -27,7 +27,7 @@ public class GraphDto {
return new ArrayList<>(sortedNodes);
}
public List<Edge> getEdgeNodeId(Long nodeId) {
public List<Edge> getEdgeNodeId(String nodeId) {
return edgeMap.getOrDefault(nodeId, new ArrayList<>());
}
@@ -38,15 +38,15 @@ public class GraphDto {
.orElse(null);
}
public Node getNode(Long nodeId) {
public Node getNode(String nodeId) {
return nodeMap.get(nodeId);
}
public void updateNodeReadyMap(Long nodeId, Boolean ready) {
public void updateNodeReadyMap(String nodeId, Boolean ready) {
nodeReadyMap.put(nodeId, ready);
}
public Boolean isNodeReady(Long nodeId) {
public Boolean isNodeReady(String nodeId) {
return nodeReadyMap.get(nodeId);
}
@@ -69,7 +69,7 @@ public class GraphDto {
private void initAdjacencyList(List<Edge> edges) {
for (Edge edge : edges) {
List<Long> targetList = adjacencyList.getOrDefault(edge.getSource(), new ArrayList<>());
List<String> targetList = adjacencyList.getOrDefault(edge.getSource(), new ArrayList<>());
targetList.add(edge.getTarget());
adjacencyList.put(edge.getSource(), targetList);
}
@@ -83,9 +83,9 @@ public class GraphDto {
*/
private List<Node> topologicalSort() {
List<Node> sortedNodes = new ArrayList<>();
Set<Long> visited = new HashSet<>();
Set<Long> visiting = new HashSet<>();
for (Long nodeId : nodeMap.keySet()) {
Set<String> visited = new HashSet<>();
Set<String> visiting = new HashSet<>();
for (String nodeId : nodeMap.keySet()) {
if (!visited.contains(nodeId)) {
dfs(nodeId, visited, visiting, sortedNodes);
}
@@ -102,13 +102,13 @@ public class GraphDto {
* @param visiting 参观
* @param sortedNodes 排序节点
*/
private void dfs(Long nodeId, Set<Long> visited, Set<Long> visiting, List<Node> sortedNodes) {
private void dfs(String nodeId, Set<String> visited, Set<String> visiting, List<Node> sortedNodes) {
if (visiting.contains(nodeId)) {
throw new IllegalStateException("Cycle detected in the graph");
}
if (!visited.contains(nodeId)) {
visiting.add(nodeId);
for (Long neighbor : adjacencyList.getOrDefault(nodeId, new ArrayList<>())) {
for (String neighbor : adjacencyList.getOrDefault(nodeId, new ArrayList<>())) {
dfs(neighbor, visited, visiting, sortedNodes);
}
visiting.remove(nodeId);

View File

@@ -1,6 +1,7 @@
package com.metis.domain.entity.base;
import com.metis.enums.EdgeType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@@ -10,7 +11,7 @@ public class Edge {
/**
* 唯一标识符
*/
@NotNull(message = "唯一标识符不能为空")
@NotBlank(message = "唯一标识符不能为空")
private String id;
/**
@@ -28,24 +29,24 @@ public class Edge {
* 源节点ID,对应节点id
*/
@NotNull(message = "源节点ID不能为空")
private Long source;
private String source;
/**
* 目标节点ID,对应节点id
*/
@NotNull(message = "目标节点ID不能为空")
private Long target;
private String target;
/**
* 源句柄id
*/
@NotNull(message = "源句柄ID不能为空")
private Long sourceHandle;
private String sourceHandle;
/**
* 目标句柄id
*/
@NotNull(message = "目标句柄ID不能为空")
private Long targetHandle;
private String targetHandle;
/**
* 边是否动画true/false

View File

@@ -2,6 +2,7 @@ package com.metis.domain.entity.base;
import com.metis.enums.HandleType;
import com.metis.enums.PositionType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@@ -13,8 +14,8 @@ public class Handle {
/**
* 句柄id
*/
@NotNull(message = "句柄id不能为空")
private Long id;
@NotBlank(message = "句柄id不能为空")
private String id;
/**
* 句柄类型

View File

@@ -13,7 +13,7 @@ public class Node {
* id
*/
@NotNull(message = "节点id不能为空")
private Long id;
private String id;
/**
* 类型

View File

@@ -59,7 +59,7 @@ public class AppFlowEngineRunnerServiceImpl implements AppFlowEngineRunnerServic
// 开始节点为空,则表示数据存在异常
Assert.isTrue(ObjectUtil.isNotNull(readyRunningNode), "流程图不存在开始节点");
for (Node node : graph.getSortedNodes()) {
Long nodeId = node.getId();
String nodeId = node.getId();
if (!graph.isNodeReady(nodeId)) {
continue;
}
@@ -79,7 +79,7 @@ public class AppFlowEngineRunnerServiceImpl implements AppFlowEngineRunnerServic
}
// 下一个需要运行的节点id加入到可以运行的节点中
if (CollUtil.isNotEmpty(result.getNextRunNodeId())) {
for (Long nextNodeId : result.getNextRunNodeId()) {
for (String nextNodeId : result.getNextRunNodeId()) {
graph.updateNodeReadyMap(nextNodeId, true);
}
} else {

View File

@@ -46,7 +46,7 @@ public interface NodeRunner<T extends NodeConfig> {
* @param edges 边缘
* @return {@link Set }<{@link Long }>
*/
default Set<Long> getNextNodeIds(List<Edge> edges) {
default Set<String> getNextNodeIds(List<Edge> edges) {
if (CollUtil.isEmpty(edges)) {
return Set.of();
}

View File

@@ -21,11 +21,11 @@ public class QuestionClassifierRunner implements NodeRunner<QuestionClassifierCo
@Override
public RunningResult run(RunningContext context, Node node, List<Edge> edges) {
Set<Long> nextNodeIds = getNextNodeIds(edges);
Set<String> nextNodeIds = getNextNodeIds(edges);
// 生成随机索引
Random random = new Random();
int randomIndex = random.nextInt(nextNodeIds.size());
List<Long> nodeIds = new ArrayList<>(nextNodeIds);
List<String> nodeIds = new ArrayList<>(nextNodeIds);
return RunningResult.buildResult(Set.of(nodeIds.get(randomIndex)));
}

View File

@@ -35,14 +35,14 @@ public class RenderContext {
/**
* 节点运行上下文, 需要数据进行传递
*/
private Map<Long, JSONObject> nodeRunningContext;
private Map<String, JSONObject> nodeRunningContext;
public Map<String, Object> getContext() {
Map<String, Object> context = new HashMap<>();
context.put("context", this.context);
context.put("sys", this.sys);
for (Map.Entry<Long, JSONObject> entry : nodeRunningContext.entrySet()) {
for (Map.Entry<String, JSONObject> entry : nodeRunningContext.entrySet()) {
context.put(String.valueOf(entry.getKey()), entry.getValue());
}
return context;

View File

@@ -137,10 +137,10 @@ public class ValidatorServiceImpl implements ValidatorService {
* @param edges 边缘
*/
private void validateEdgeConnections(List<Node> nodes, List<Edge> edges) {
Map<Long, Node> nodeMap = nodes.stream().collect(Collectors.toMap(Node::getId, Function.identity()));
Map<String, Node> nodeMap = nodes.stream().collect(Collectors.toMap(Node::getId, Function.identity()));
for (Edge edge : edges) {
Long source = edge.getSource();
Long target = edge.getTarget();
String source = edge.getSource();
String target = edge.getTarget();
Assert.isTrue(nodeMap.containsKey(source), "边 {} 的源节点 {} 不存在", edge.getLabel(), source);
Assert.isTrue(nodeMap.containsKey(target), "边 {} 的目标节点 {} 不存在", edge.getLabel(), target);
}
@@ -153,7 +153,7 @@ public class ValidatorServiceImpl implements ValidatorService {
* @param edges 边缘
*/
private void validateCycle(List<Node> nodes, List<Edge> edges) {
Map<Long, List<Long>> adjacencyList = buildAdjacencyList(edges);
Map<String, List<String>> adjacencyList = buildAdjacencyList(edges);
for (Node node : nodes) {
if (hasCycle(node.getId(), adjacencyList, new HashSet<>())) {
throw new IllegalArgumentException("图中存在环结构,起始节点: " + node.getData().getLabel());
@@ -168,7 +168,7 @@ public class ValidatorServiceImpl implements ValidatorService {
* @param edges 边缘
*/
private void validateIsolatedNodes(List<Node> nodes, List<Edge> edges) {
Set<Long> connectedNodes = new HashSet<>();
Set<String> connectedNodes = new HashSet<>();
for (Edge edge : edges) {
connectedNodes.add(edge.getSource());
connectedNodes.add(edge.getTarget());
@@ -184,8 +184,8 @@ public class ValidatorServiceImpl implements ValidatorService {
* @param edges 边缘
* @return {@link Map }<{@link Long }, {@link List }<{@link Long }>>
*/
private Map<Long, List<Long>> buildAdjacencyList(List<Edge> edges) {
Map<Long, List<Long>> adjacencyList = new HashMap<>();
private Map<String, List<String>> buildAdjacencyList(List<Edge> edges) {
Map<String, List<String>> adjacencyList = new HashMap<>();
for (Edge edge : edges) {
adjacencyList.computeIfAbsent(edge.getSource(), k -> new ArrayList<>()).add(edge.getTarget());
}
@@ -200,12 +200,12 @@ public class ValidatorServiceImpl implements ValidatorService {
* @param visited 是否已经访问过了
* @return boolean
*/
private boolean hasCycle(Long nodeId, Map<Long, List<Long>> adjacencyList, Set<Long> visited) {
private boolean hasCycle(String nodeId, Map<String, List<String>> adjacencyList, Set<String> visited) {
if (visited.contains(nodeId)) {
return true; // 发现环
}
visited.add(nodeId);
for (Long neighbor : adjacencyList.getOrDefault(nodeId, new ArrayList<>())) {
for (String neighbor : adjacencyList.getOrDefault(nodeId, new ArrayList<>())) {
if (hasCycle(neighbor, adjacencyList, visited)) {
return true;
}
@@ -236,8 +236,8 @@ public class ValidatorServiceImpl implements ValidatorService {
*/
private void validateNodeRelations(List<Node> nodes, List<Edge> edges) {
// 构建 source 和 target 的映射
Map<Long, List<Edge>> sourceMap = edges.stream().collect(Collectors.groupingBy(Edge::getSource));
Map<Long, List<Edge>> targetMap = edges.stream().collect(Collectors.groupingBy(Edge::getTarget));
Map<String, List<Edge>> sourceMap = edges.stream().collect(Collectors.groupingBy(Edge::getSource));
Map<String, List<Edge>> targetMap = edges.stream().collect(Collectors.groupingBy(Edge::getTarget));
for (Node node : nodes) {
NodeValidator validator = getNodeValidator(node);