feat: node 和 edge id切换为String类型
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 句柄类型
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 句柄类型
|
||||
|
||||
@@ -13,7 +13,7 @@ public class Node {
|
||||
* id
|
||||
*/
|
||||
@NotNull(message = "节点id不能为空")
|
||||
private Long id;
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user