feat: 关系校验基本完成, runner入参确定
This commit is contained in:
@@ -30,7 +30,7 @@ public class ProcessDefinitionFacade {
|
||||
}
|
||||
|
||||
public App getByDeploymentId(Long deploymentId) {
|
||||
return appEngineService.getByDeploymentId(deploymentId);
|
||||
return appEngineService.getByWorkflowId(deploymentId);
|
||||
}
|
||||
|
||||
public void update(ProcessBo processBo) {
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
package com.metis.flow.domain.context;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.metis.flow.runner.FlowRunningContext;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class RunningContext {
|
||||
|
||||
/**
|
||||
@@ -16,4 +23,29 @@ public class RunningContext {
|
||||
*/
|
||||
private JSONObject custom;
|
||||
|
||||
/**
|
||||
* 节点运行上下文, 需要数据进行传递
|
||||
*/
|
||||
private Map<Long, JSONObject> nodeRunningContext;
|
||||
|
||||
/**
|
||||
* 下一个运行节点id集合, 可能是多个
|
||||
*/
|
||||
private Set<Long> nextRunNodeId;
|
||||
|
||||
|
||||
/**
|
||||
* 构建上下文
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return {@link RunningContext }
|
||||
*/
|
||||
public static RunningContext buildContext(SysContext sysContext, FlowRunningContext context) {
|
||||
return RunningContext.builder()
|
||||
.sys(sysContext)
|
||||
.custom(context.getCustom())
|
||||
.nodeRunningContext(new HashMap<>())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,19 +1,51 @@
|
||||
package com.metis.flow.domain.context;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class SysContext {
|
||||
|
||||
private List<String> file;
|
||||
/**
|
||||
* 文件列表
|
||||
*/
|
||||
private List<String> files;
|
||||
|
||||
/**
|
||||
* 沟通的内容
|
||||
*/
|
||||
private String query;
|
||||
|
||||
/**
|
||||
* 对话数
|
||||
*/
|
||||
private Integer dialogueCount;
|
||||
|
||||
/**
|
||||
* 沟通的id
|
||||
*/
|
||||
private String conversationId;
|
||||
|
||||
/**
|
||||
* 应用程序id
|
||||
*/
|
||||
private Long appId;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 工作流id
|
||||
*/
|
||||
private Long workflowId;
|
||||
|
||||
/**
|
||||
* 实例id
|
||||
*/
|
||||
private Long instanceId;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.metis.flow.domain.entity.base;
|
||||
|
||||
import com.metis.flow.enums.EdgeType;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -11,7 +10,7 @@ public class Edge {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@NotBlank(message = "唯一标识符不能为空")
|
||||
@NotNull(message = "唯一标识符不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
@@ -28,25 +27,25 @@ public class Edge {
|
||||
/**
|
||||
* 源节点ID,对应节点id
|
||||
*/
|
||||
@NotBlank(message = "源节点ID不能为空")
|
||||
private String source;
|
||||
@NotNull(message = "源节点ID不能为空")
|
||||
private Long source;
|
||||
|
||||
/**
|
||||
* 目标节点ID,对应节点id
|
||||
*/
|
||||
@NotBlank(message = "目标节点ID不能为空")
|
||||
private String target;
|
||||
@NotNull(message = "目标节点ID不能为空")
|
||||
private Long target;
|
||||
/**
|
||||
* 源句柄id
|
||||
*/
|
||||
@NotBlank(message = "源句柄ID不能为空")
|
||||
private String sourceHandle;
|
||||
@NotNull(message = "源句柄ID不能为空")
|
||||
private Long sourceHandle;
|
||||
|
||||
/**
|
||||
* 目标句柄id
|
||||
*/
|
||||
@NotBlank(message = "目标句柄ID不能为空")
|
||||
private String targetHandle;
|
||||
@NotNull(message = "目标句柄ID不能为空")
|
||||
private Long targetHandle;
|
||||
|
||||
/**
|
||||
* 边是否动画true/false
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package com.metis.flow.domain.entity.base;
|
||||
|
||||
import com.metis.flow.enums.PositionType;
|
||||
import com.metis.flow.enums.HandleType;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import com.metis.flow.enums.PositionType;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -14,8 +13,8 @@ public class Handle {
|
||||
/**
|
||||
* 句柄id
|
||||
*/
|
||||
@NotBlank(message = "句柄id不能为空")
|
||||
private String id;
|
||||
@NotNull(message = "句柄id不能为空")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 句柄类型
|
||||
|
||||
@@ -5,7 +5,6 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.metis.flow.enums.NodeType;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -18,8 +17,8 @@ public class Node {
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@NotBlank(message = "节点id不能为空")
|
||||
private String id;
|
||||
@NotNull(message = "节点id不能为空")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
@@ -43,7 +42,7 @@ public class Node {
|
||||
|
||||
|
||||
@JsonIgnore
|
||||
public Map<String, Handle> getHandleMap() {
|
||||
public Map<Long, Handle> getHandleMap() {
|
||||
if (CollUtil.isEmpty(data.getHandles())) {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.metis.flow.engine;
|
||||
|
||||
public interface AppEngineRunnerService {
|
||||
|
||||
|
||||
}
|
||||
@@ -26,6 +26,14 @@ public interface AppEngineService {
|
||||
List<App> list(AppQuery query);
|
||||
|
||||
|
||||
/**
|
||||
* 通过应用id获取
|
||||
*
|
||||
* @param appId 应用程序id
|
||||
* @return {@link App }
|
||||
*/
|
||||
App getByAppId(Long appId);
|
||||
|
||||
/**
|
||||
* 按身份证领取
|
||||
*
|
||||
@@ -37,10 +45,10 @@ public interface AppEngineService {
|
||||
/**
|
||||
* 通过部署id获取
|
||||
*
|
||||
* @param deploymentId 部署id
|
||||
* @param workflowId 部署id
|
||||
* @return {@link App }
|
||||
*/
|
||||
App getByDeploymentId(Long deploymentId);
|
||||
App getByWorkflowId(Long workflowId);
|
||||
|
||||
/**
|
||||
* 按id列表
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.metis.flow.engine;
|
||||
|
||||
import com.metis.flow.runner.FlowRunningContext;
|
||||
import com.metis.flow.runner.RunnerResult;
|
||||
|
||||
/**
|
||||
* 应用引擎运行器服务
|
||||
*
|
||||
* @author clay
|
||||
* @date 2025/04/07
|
||||
*/
|
||||
public interface AppFlowEngineRunnerService {
|
||||
|
||||
|
||||
/**
|
||||
* 运行
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return {@link RunnerResult }
|
||||
*/
|
||||
RunnerResult running(FlowRunningContext context);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -42,6 +42,12 @@ public class AppEngineServiceImpl implements AppEngineService {
|
||||
return BaseAppConvert.INSTANCE.toApps(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public App getByAppId(Long appId) {
|
||||
BaseApp baseApp = baseAppService.getByAppId(appId);
|
||||
return BaseAppConvert.INSTANCE.toApp(baseApp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public App getByAppId(Long appId, Integer version) {
|
||||
BaseApp baseApp = baseAppService.getByAppIdAndVersion(appId, version);
|
||||
@@ -49,8 +55,8 @@ public class AppEngineServiceImpl implements AppEngineService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public App getByDeploymentId(Long deploymentId) {
|
||||
BaseApp baseApp = baseAppService.getById(deploymentId);
|
||||
public App getByWorkflowId(Long workflowId) {
|
||||
BaseApp baseApp = baseAppService.getById(workflowId);
|
||||
return BaseAppConvert.INSTANCE.toApp(baseApp);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.metis.flow.engine.impl;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.metis.flow.domain.context.RunningContext;
|
||||
import com.metis.flow.domain.context.SysContext;
|
||||
import com.metis.flow.domain.entity.App;
|
||||
import com.metis.flow.engine.AppEngineService;
|
||||
import com.metis.flow.engine.AppFlowEngineRunnerService;
|
||||
import com.metis.flow.runner.FlowRunningContext;
|
||||
import com.metis.flow.runner.RunnerResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AppFlowEngineRunnerServiceImpl implements AppFlowEngineRunnerService {
|
||||
|
||||
private final AppEngineService appEngineService;
|
||||
|
||||
|
||||
@Override
|
||||
public RunnerResult running(FlowRunningContext context) {
|
||||
App app = getApp(context);
|
||||
// todo 构建运行实例, 并将运行实例放入上下文
|
||||
|
||||
Long instanceId = IdUtil.getSnowflakeNextId();
|
||||
// 构建系统上下文信息
|
||||
SysContext sysContext = SysContext.builder()
|
||||
.files(context.getFiles())
|
||||
.appId(app.getId())
|
||||
.workflowId(app.getWorkflowId())
|
||||
.instanceId(instanceId)
|
||||
.build();
|
||||
// 构建运行中上下文
|
||||
RunningContext runningContext = RunningContext.buildContext(sysContext, context);
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取到应用程序信息
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return {@link App }
|
||||
*/
|
||||
private App getApp(FlowRunningContext context) {
|
||||
if (ObjectUtil.isNull(context.getWorkflowId())) {
|
||||
return appEngineService.getByWorkflowId(context.getWorkflowId());
|
||||
}
|
||||
return appEngineService.getByAppId(context.getAppId());
|
||||
}
|
||||
}
|
||||
45
src/main/java/com/metis/flow/runner/FlowRunningContext.java
Normal file
45
src/main/java/com/metis/flow/runner/FlowRunningContext.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package com.metis.flow.runner;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 运行上下文
|
||||
*
|
||||
* @author clay
|
||||
* @date 2025/04/07
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class FlowRunningContext {
|
||||
|
||||
/**
|
||||
* 文件列表
|
||||
*/
|
||||
private List<String> files;
|
||||
|
||||
/**
|
||||
* 应用程序id
|
||||
*/
|
||||
private Long appId;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 工作流id
|
||||
*/
|
||||
private Long workflowId;
|
||||
|
||||
/**
|
||||
* 自定义
|
||||
*/
|
||||
private JSONObject custom;
|
||||
|
||||
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.metis.flow.runner;
|
||||
|
||||
import com.metis.flow.domain.context.RunningContext;
|
||||
import com.metis.flow.domain.entity.base.Edge;
|
||||
import com.metis.flow.domain.entity.base.Node;
|
||||
import com.metis.flow.enums.NodeType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface NodeRunner {
|
||||
|
||||
|
||||
@@ -12,9 +15,10 @@ public interface NodeRunner {
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param node 节点配置信息
|
||||
* @param edges
|
||||
* @return {@link RunningContext }
|
||||
*/
|
||||
RunningContext run(RunningContext context, Node node);
|
||||
RunningContext run(RunningContext context, Node node, List<Edge> edges);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,12 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* runner初始化
|
||||
*
|
||||
* @author clay
|
||||
* @date 2025/04/07
|
||||
*/
|
||||
@Service
|
||||
public class RunnerInitialize implements ApplicationContextAware {
|
||||
|
||||
|
||||
27
src/main/java/com/metis/flow/runner/RunnerResult.java
Normal file
27
src/main/java/com/metis/flow/runner/RunnerResult.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.metis.flow.runner;
|
||||
|
||||
|
||||
import com.metis.flow.domain.context.SysContext;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 运行结果
|
||||
*
|
||||
* @author clay
|
||||
* @date 2025/04/07
|
||||
*/
|
||||
@Data
|
||||
public class RunnerResult {
|
||||
|
||||
/**
|
||||
* 运行内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 上下文
|
||||
*/
|
||||
private SysContext context;
|
||||
|
||||
|
||||
}
|
||||
@@ -2,18 +2,21 @@ package com.metis.flow.runner.impl;
|
||||
|
||||
|
||||
import com.metis.flow.domain.context.RunningContext;
|
||||
import com.metis.flow.domain.entity.base.Edge;
|
||||
import com.metis.flow.domain.entity.base.Node;
|
||||
import com.metis.flow.enums.NodeType;
|
||||
import com.metis.flow.runner.NodeRunner;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class EndNodeRunner implements NodeRunner {
|
||||
|
||||
@Override
|
||||
public RunningContext run(RunningContext context, Node node) {
|
||||
public RunningContext run(RunningContext context, Node node, List<Edge> edges) {
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
package com.metis.flow.runner.impl;
|
||||
|
||||
import com.metis.flow.domain.context.RunningContext;
|
||||
import com.metis.flow.domain.entity.base.Edge;
|
||||
import com.metis.flow.domain.entity.base.Node;
|
||||
import com.metis.flow.enums.NodeType;
|
||||
import com.metis.flow.runner.NodeRunner;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class StartNodeRunner implements NodeRunner {
|
||||
|
||||
@Override
|
||||
public RunningContext run(RunningContext context, Node node) {
|
||||
public RunningContext run(RunningContext context, Node node, List<Edge> edges) {
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.metis.flow.validator;
|
||||
|
||||
import com.metis.flow.domain.entity.base.Edge;
|
||||
import com.metis.flow.domain.entity.base.Node;
|
||||
import com.metis.flow.enums.NodeType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface NodeValidator {
|
||||
|
||||
|
||||
@@ -12,7 +15,18 @@ public interface NodeValidator {
|
||||
* @param node 节点
|
||||
* @return {@link ValidatorResult }
|
||||
*/
|
||||
ValidatorResult validate(Node node);
|
||||
ValidatorResult validateValue(Node node);
|
||||
|
||||
|
||||
/**
|
||||
* 验证关系
|
||||
*
|
||||
* @param node 节点
|
||||
* @param sources 来源
|
||||
* @param targets 目标
|
||||
* @return {@link ValidatorResult }
|
||||
*/
|
||||
ValidatorResult validateRelation(Node node, List<Edge> sources, List<Edge> targets);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,7 +53,7 @@ public class ValidatorServiceImpl implements ValidatorService {
|
||||
NodeValidator validator = NodeValidatorFactory.get(type);
|
||||
// 节点校验器
|
||||
Assert.isTrue(ObjectUtil.isNotNull(validator), "无:{}类型的节点校验器", type.getName());
|
||||
ValidatorResult result = validator.validate(node);
|
||||
ValidatorResult result = validator.validateValue(node);
|
||||
// 返回值检查
|
||||
Assert.isTrue(ObjectUtil.isNotNull(result), "类型:{} 的校验器无返回值", validator.getType().getName());
|
||||
if (!result.getValid()) {
|
||||
@@ -94,6 +94,9 @@ public class ValidatorServiceImpl implements ValidatorService {
|
||||
* @param edges 边缘
|
||||
*/
|
||||
private void validateRelation(List<Node> nodes, List<Edge> edges) {
|
||||
// 0. 检查是否只有一个起始节点
|
||||
validateSingleStartNode(nodes);
|
||||
|
||||
// 1. 检查线是否连接有效节点
|
||||
validateEdgeConnections(nodes, edges);
|
||||
|
||||
@@ -102,16 +105,39 @@ public class ValidatorServiceImpl implements ValidatorService {
|
||||
|
||||
// 3. 检查是否存在孤立节点
|
||||
validateIsolatedNodes(nodes, edges);
|
||||
|
||||
// 4. 检查两个节点间是否有多根连线
|
||||
validateMultipleEdgesBetweenNodes(edges);
|
||||
|
||||
// 5. 检查节点自己的关系信息
|
||||
validateNodeRelations(nodes, edges);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否只有一个起始节点
|
||||
*
|
||||
* @param nodes 节点
|
||||
*/
|
||||
private void validateSingleStartNode(List<Node> nodes) {
|
||||
Long startNodeCount = nodes.stream()
|
||||
.filter(node -> NodeType.START.equals(node.getType()))
|
||||
.count();
|
||||
Assert.isTrue(startNodeCount.equals(1L), "图中必须且只能有一个起始节点,当前起始节点数量: {}", startNodeCount);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查线是否连接有效节点
|
||||
*
|
||||
* @param nodes 节点
|
||||
* @param edges 边缘
|
||||
*/
|
||||
private void validateEdgeConnections(List<Node> nodes, List<Edge> edges) {
|
||||
Map<String, Node> nodeMap = nodes.stream().collect(Collectors.toMap(Node::getId, Function.identity()));
|
||||
Map<Long, Node> nodeMap = nodes.stream().collect(Collectors.toMap(Node::getId, Function.identity()));
|
||||
for (Edge edge : edges) {
|
||||
String source = edge.getSource();
|
||||
String target = edge.getTarget();
|
||||
Long source = edge.getSource();
|
||||
Long target = edge.getTarget();
|
||||
Assert.isTrue(nodeMap.containsKey(source), "边 {} 的源节点 {} 不存在", edge.getLabel(), source);
|
||||
Assert.isTrue(nodeMap.containsKey(target), "边 {} 的目标节点 {} 不存在", edge.getLabel(), target);
|
||||
}
|
||||
@@ -119,9 +145,12 @@ public class ValidatorServiceImpl implements ValidatorService {
|
||||
|
||||
/**
|
||||
* 检查是否存在环结构
|
||||
*
|
||||
* @param nodes 节点
|
||||
* @param edges 边缘
|
||||
*/
|
||||
private void validateCycle(List<Node> nodes, List<Edge> edges) {
|
||||
Map<String, List<String>> adjacencyList = buildAdjacencyList(edges);
|
||||
Map<Long, List<Long>> adjacencyList = buildAdjacencyList(edges);
|
||||
for (Node node : nodes) {
|
||||
if (hasCycle(node.getId(), adjacencyList, new HashSet<>())) {
|
||||
throw new IllegalArgumentException("图中存在环结构,起始节点: " + node.getData().getLabel());
|
||||
@@ -131,9 +160,12 @@ public class ValidatorServiceImpl implements ValidatorService {
|
||||
|
||||
/**
|
||||
* 检查是否存在孤立节点
|
||||
*
|
||||
* @param nodes 节点
|
||||
* @param edges 边缘
|
||||
*/
|
||||
private void validateIsolatedNodes(List<Node> nodes, List<Edge> edges) {
|
||||
Set<String> connectedNodes = new HashSet<>();
|
||||
Set<Long> connectedNodes = new HashSet<>();
|
||||
for (Edge edge : edges) {
|
||||
connectedNodes.add(edge.getSource());
|
||||
connectedNodes.add(edge.getTarget());
|
||||
@@ -145,9 +177,12 @@ public class ValidatorServiceImpl implements ValidatorService {
|
||||
|
||||
/**
|
||||
* 构建邻接表
|
||||
*
|
||||
* @param edges 边缘
|
||||
* @return {@link Map }<{@link Long }, {@link List }<{@link Long }>>
|
||||
*/
|
||||
private Map<String, List<String>> buildAdjacencyList(List<Edge> edges) {
|
||||
Map<String, List<String>> adjacencyList = new HashMap<>();
|
||||
private Map<Long, List<Long>> buildAdjacencyList(List<Edge> edges) {
|
||||
Map<Long, List<Long>> adjacencyList = new HashMap<>();
|
||||
for (Edge edge : edges) {
|
||||
adjacencyList.computeIfAbsent(edge.getSource(), k -> new ArrayList<>()).add(edge.getTarget());
|
||||
}
|
||||
@@ -156,13 +191,18 @@ public class ValidatorServiceImpl implements ValidatorService {
|
||||
|
||||
/**
|
||||
* 深度优先搜索(DFS)检查环
|
||||
*
|
||||
* @param nodeId 节点id
|
||||
* @param adjacencyList 邻接表
|
||||
* @param visited 是否已经访问过了
|
||||
* @return boolean
|
||||
*/
|
||||
private boolean hasCycle(String nodeId, Map<String, List<String>> adjacencyList, Set<String> visited) {
|
||||
private boolean hasCycle(Long nodeId, Map<Long, List<Long>> adjacencyList, Set<Long> visited) {
|
||||
if (visited.contains(nodeId)) {
|
||||
return true; // 发现环
|
||||
}
|
||||
visited.add(nodeId);
|
||||
for (String neighbor : adjacencyList.getOrDefault(nodeId, new ArrayList<>())) {
|
||||
for (Long neighbor : adjacencyList.getOrDefault(nodeId, new ArrayList<>())) {
|
||||
if (hasCycle(neighbor, adjacencyList, visited)) {
|
||||
return true;
|
||||
}
|
||||
@@ -171,4 +211,40 @@ public class ValidatorServiceImpl implements ValidatorService {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查两个节点间是否有多根连线
|
||||
*
|
||||
* @param edges 边缘
|
||||
*/
|
||||
private void validateMultipleEdgesBetweenNodes(List<Edge> edges) {
|
||||
Map<String, Integer> edgeCountMap = new HashMap<>();
|
||||
for (Edge edge : edges) {
|
||||
String key = edge.getSource() + "-" + edge.getTarget(); // 使用 source 和 target 作为唯一标识
|
||||
edgeCountMap.put(key, edgeCountMap.getOrDefault(key, 0) + 1);
|
||||
if (edgeCountMap.get(key) > 1) {
|
||||
throw new IllegalArgumentException("节点 " + edge.getSource() + " 和节点 " + edge.getTarget() + " 之间存在多根连线");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查节点的关系信息
|
||||
*/
|
||||
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));
|
||||
|
||||
for (Node node : nodes) {
|
||||
NodeValidator validator = NodeValidatorFactory.get(node.getType());
|
||||
// 获取当前节点的 source 和 target
|
||||
List<Edge> sources = targetMap.getOrDefault(node.getId(), new ArrayList<>());
|
||||
List<Edge> targets = sourceMap.getOrDefault(node.getId(), new ArrayList<>());
|
||||
// 检查节点关系
|
||||
ValidatorResult result = validator.validateRelation(node, sources, targets);
|
||||
Assert.isTrue(result.getValid(), result.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.metis.flow.validator.impl.node;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import com.metis.flow.domain.entity.base.Edge;
|
||||
import com.metis.flow.domain.entity.base.Node;
|
||||
import com.metis.flow.domain.entity.config.node.DocumentExtractorNodeConfig;
|
||||
import com.metis.flow.enums.NodeType;
|
||||
@@ -10,6 +12,8 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@@ -17,13 +21,21 @@ public class DocumentExtractorNodeValidator implements NodeValidator {
|
||||
private final ValidatorCodeService validatorCodeService;
|
||||
|
||||
@Override
|
||||
public ValidatorResult validate(Node node) {
|
||||
public ValidatorResult validateValue(Node node) {
|
||||
DocumentExtractorNodeConfig config = node.getConfig();
|
||||
validatorCodeService.validateThrow(config);
|
||||
// 业务检查未通过
|
||||
return ValidatorResult.invalid("业务报错");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidatorResult validateRelation(Node node, List<Edge> sources, List<Edge> targets) {
|
||||
// 1. 检查 targets 数量是否等于 1(只允许一个出)
|
||||
Assert.isTrue(targets.size() == 1, "节点 {} 的出连接数必须为 1,当前数量: {}", node.getId(), targets.size());
|
||||
|
||||
return ValidatorResult.valid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeType getType() {
|
||||
return NodeType.DOCUMENT_EXTRACTOR;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.metis.flow.validator.impl.node;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import com.metis.flow.domain.entity.base.Edge;
|
||||
import com.metis.flow.domain.entity.base.Node;
|
||||
import com.metis.flow.enums.NodeType;
|
||||
import com.metis.flow.validator.NodeValidator;
|
||||
@@ -8,6 +10,8 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@@ -16,7 +20,19 @@ public class EndNodeValidator implements NodeValidator {
|
||||
|
||||
|
||||
@Override
|
||||
public ValidatorResult validate(Node node) {
|
||||
public ValidatorResult validateValue(Node node) {
|
||||
return ValidatorResult.valid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidatorResult validateRelation(Node node, List<Edge> sources, List<Edge> targets) {
|
||||
// 1. 结束节点不允许有目标连接
|
||||
Assert.isTrue(targets.isEmpty(), "结束节点 {} 不允许有目标连接", node.getId());
|
||||
|
||||
// 2. 检查 sources 数量是否小于 handles 数量
|
||||
int handleCount = node.getData().getHandles().size();
|
||||
Assert.isTrue(sources.size() <= handleCount, "结束节点 {} 的源连接数超过 handles 数量", node.getId());
|
||||
|
||||
return ValidatorResult.valid();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.metis.flow.validator.impl.node;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import com.metis.flow.domain.entity.base.Edge;
|
||||
import com.metis.flow.domain.entity.base.Node;
|
||||
import com.metis.flow.enums.NodeType;
|
||||
import com.metis.flow.validator.NodeValidator;
|
||||
@@ -7,12 +9,26 @@ import com.metis.flow.validator.ValidatorResult;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class StartNodeValidator implements NodeValidator {
|
||||
|
||||
@Override
|
||||
public ValidatorResult validate(Node node) {
|
||||
public ValidatorResult validateValue(Node node) {
|
||||
return ValidatorResult.valid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidatorResult validateRelation(Node node, List<Edge> sources, List<Edge> targets) {
|
||||
// 1. 开始节点不允许有源连接
|
||||
Assert.isTrue(sources.isEmpty(), "开始节点 {} 不允许有源连接", node.getId());
|
||||
|
||||
// 2. 检查 targets 数量是否小于 handles 数量
|
||||
int handleCount = node.getData().getHandles().size();
|
||||
Assert.isTrue(targets.size() <= handleCount, "开始节点 {} 的目标连接数超过 handles 数量", node.getId());
|
||||
|
||||
return ValidatorResult.valid();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user