feat: 关系校验基本完成, runner入参确定
This commit is contained in:
@@ -30,7 +30,7 @@ public class ProcessDefinitionFacade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public App getByDeploymentId(Long deploymentId) {
|
public App getByDeploymentId(Long deploymentId) {
|
||||||
return appEngineService.getByDeploymentId(deploymentId);
|
return appEngineService.getByWorkflowId(deploymentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(ProcessBo processBo) {
|
public void update(ProcessBo processBo) {
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
package com.metis.flow.domain.context;
|
package com.metis.flow.domain.context;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.metis.flow.runner.FlowRunningContext;
|
||||||
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@Builder
|
||||||
public class RunningContext {
|
public class RunningContext {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,4 +23,29 @@ public class RunningContext {
|
|||||||
*/
|
*/
|
||||||
private JSONObject custom;
|
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;
|
package com.metis.flow.domain.context;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@Builder
|
||||||
public class SysContext {
|
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;
|
private Long appId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户id
|
||||||
|
*/
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作流id
|
||||||
|
*/
|
||||||
private Long workflowId;
|
private Long workflowId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实例id
|
||||||
|
*/
|
||||||
private Long instanceId;
|
private Long instanceId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.metis.flow.domain.entity.base;
|
package com.metis.flow.domain.entity.base;
|
||||||
|
|
||||||
import com.metis.flow.enums.EdgeType;
|
import com.metis.flow.enums.EdgeType;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -11,7 +10,7 @@ public class Edge {
|
|||||||
/**
|
/**
|
||||||
* 唯一标识符
|
* 唯一标识符
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "唯一标识符不能为空")
|
@NotNull(message = "唯一标识符不能为空")
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,25 +27,25 @@ public class Edge {
|
|||||||
/**
|
/**
|
||||||
* 源节点ID,对应节点id
|
* 源节点ID,对应节点id
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "源节点ID不能为空")
|
@NotNull(message = "源节点ID不能为空")
|
||||||
private String source;
|
private Long source;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 目标节点ID,对应节点id
|
* 目标节点ID,对应节点id
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "目标节点ID不能为空")
|
@NotNull(message = "目标节点ID不能为空")
|
||||||
private String target;
|
private Long target;
|
||||||
/**
|
/**
|
||||||
* 源句柄id
|
* 源句柄id
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "源句柄ID不能为空")
|
@NotNull(message = "源句柄ID不能为空")
|
||||||
private String sourceHandle;
|
private Long sourceHandle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 目标句柄id
|
* 目标句柄id
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "目标句柄ID不能为空")
|
@NotNull(message = "目标句柄ID不能为空")
|
||||||
private String targetHandle;
|
private Long targetHandle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 边是否动画true/false
|
* 边是否动画true/false
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package com.metis.flow.domain.entity.base;
|
package com.metis.flow.domain.entity.base;
|
||||||
|
|
||||||
import com.metis.flow.enums.PositionType;
|
|
||||||
import com.metis.flow.enums.HandleType;
|
import com.metis.flow.enums.HandleType;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import com.metis.flow.enums.PositionType;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -14,8 +13,8 @@ public class Handle {
|
|||||||
/**
|
/**
|
||||||
* 句柄id
|
* 句柄id
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "句柄id不能为空")
|
@NotNull(message = "句柄id不能为空")
|
||||||
private String id;
|
private Long id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 句柄类型
|
* 句柄类型
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import cn.hutool.core.util.ObjectUtil;
|
|||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.metis.flow.enums.NodeType;
|
import com.metis.flow.enums.NodeType;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -18,8 +17,8 @@ public class Node {
|
|||||||
/**
|
/**
|
||||||
* id
|
* id
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "节点id不能为空")
|
@NotNull(message = "节点id不能为空")
|
||||||
private String id;
|
private Long id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类型
|
* 类型
|
||||||
@@ -43,7 +42,7 @@ public class Node {
|
|||||||
|
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public Map<String, Handle> getHandleMap() {
|
public Map<Long, Handle> getHandleMap() {
|
||||||
if (CollUtil.isEmpty(data.getHandles())) {
|
if (CollUtil.isEmpty(data.getHandles())) {
|
||||||
return Map.of();
|
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);
|
List<App> list(AppQuery query);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过应用id获取
|
||||||
|
*
|
||||||
|
* @param appId 应用程序id
|
||||||
|
* @return {@link App }
|
||||||
|
*/
|
||||||
|
App getByAppId(Long appId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按身份证领取
|
* 按身份证领取
|
||||||
*
|
*
|
||||||
@@ -37,10 +45,10 @@ public interface AppEngineService {
|
|||||||
/**
|
/**
|
||||||
* 通过部署id获取
|
* 通过部署id获取
|
||||||
*
|
*
|
||||||
* @param deploymentId 部署id
|
* @param workflowId 部署id
|
||||||
* @return {@link App }
|
* @return {@link App }
|
||||||
*/
|
*/
|
||||||
App getByDeploymentId(Long deploymentId);
|
App getByWorkflowId(Long workflowId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按id列表
|
* 按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);
|
return BaseAppConvert.INSTANCE.toApps(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public App getByAppId(Long appId) {
|
||||||
|
BaseApp baseApp = baseAppService.getByAppId(appId);
|
||||||
|
return BaseAppConvert.INSTANCE.toApp(baseApp);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public App getByAppId(Long appId, Integer version) {
|
public App getByAppId(Long appId, Integer version) {
|
||||||
BaseApp baseApp = baseAppService.getByAppIdAndVersion(appId, version);
|
BaseApp baseApp = baseAppService.getByAppIdAndVersion(appId, version);
|
||||||
@@ -49,8 +55,8 @@ public class AppEngineServiceImpl implements AppEngineService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public App getByDeploymentId(Long deploymentId) {
|
public App getByWorkflowId(Long workflowId) {
|
||||||
BaseApp baseApp = baseAppService.getById(deploymentId);
|
BaseApp baseApp = baseAppService.getById(workflowId);
|
||||||
return BaseAppConvert.INSTANCE.toApp(baseApp);
|
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;
|
package com.metis.flow.runner;
|
||||||
|
|
||||||
import com.metis.flow.domain.context.RunningContext;
|
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.domain.entity.base.Node;
|
||||||
import com.metis.flow.enums.NodeType;
|
import com.metis.flow.enums.NodeType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface NodeRunner {
|
public interface NodeRunner {
|
||||||
|
|
||||||
|
|
||||||
@@ -12,9 +15,10 @@ public interface NodeRunner {
|
|||||||
*
|
*
|
||||||
* @param context 上下文
|
* @param context 上下文
|
||||||
* @param node 节点配置信息
|
* @param node 节点配置信息
|
||||||
|
* @param edges
|
||||||
* @return {@link RunningContext }
|
* @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;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* runner初始化
|
||||||
|
*
|
||||||
|
* @author clay
|
||||||
|
* @date 2025/04/07
|
||||||
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class RunnerInitialize implements ApplicationContextAware {
|
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.context.RunningContext;
|
||||||
|
import com.metis.flow.domain.entity.base.Edge;
|
||||||
import com.metis.flow.domain.entity.base.Node;
|
import com.metis.flow.domain.entity.base.Node;
|
||||||
import com.metis.flow.enums.NodeType;
|
import com.metis.flow.enums.NodeType;
|
||||||
import com.metis.flow.runner.NodeRunner;
|
import com.metis.flow.runner.NodeRunner;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class EndNodeRunner implements NodeRunner {
|
public class EndNodeRunner implements NodeRunner {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RunningContext run(RunningContext context, Node node) {
|
public RunningContext run(RunningContext context, Node node, List<Edge> edges) {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
package com.metis.flow.runner.impl;
|
package com.metis.flow.runner.impl;
|
||||||
|
|
||||||
import com.metis.flow.domain.context.RunningContext;
|
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.domain.entity.base.Node;
|
||||||
import com.metis.flow.enums.NodeType;
|
import com.metis.flow.enums.NodeType;
|
||||||
import com.metis.flow.runner.NodeRunner;
|
import com.metis.flow.runner.NodeRunner;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class StartNodeRunner implements NodeRunner {
|
public class StartNodeRunner implements NodeRunner {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RunningContext run(RunningContext context, Node node) {
|
public RunningContext run(RunningContext context, Node node, List<Edge> edges) {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package com.metis.flow.validator;
|
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.domain.entity.base.Node;
|
||||||
import com.metis.flow.enums.NodeType;
|
import com.metis.flow.enums.NodeType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface NodeValidator {
|
public interface NodeValidator {
|
||||||
|
|
||||||
|
|
||||||
@@ -12,7 +15,18 @@ public interface NodeValidator {
|
|||||||
* @param node 节点
|
* @param node 节点
|
||||||
* @return {@link ValidatorResult }
|
* @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);
|
NodeValidator validator = NodeValidatorFactory.get(type);
|
||||||
// 节点校验器
|
// 节点校验器
|
||||||
Assert.isTrue(ObjectUtil.isNotNull(validator), "无:{}类型的节点校验器", type.getName());
|
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());
|
Assert.isTrue(ObjectUtil.isNotNull(result), "类型:{} 的校验器无返回值", validator.getType().getName());
|
||||||
if (!result.getValid()) {
|
if (!result.getValid()) {
|
||||||
@@ -94,6 +94,9 @@ public class ValidatorServiceImpl implements ValidatorService {
|
|||||||
* @param edges 边缘
|
* @param edges 边缘
|
||||||
*/
|
*/
|
||||||
private void validateRelation(List<Node> nodes, List<Edge> edges) {
|
private void validateRelation(List<Node> nodes, List<Edge> edges) {
|
||||||
|
// 0. 检查是否只有一个起始节点
|
||||||
|
validateSingleStartNode(nodes);
|
||||||
|
|
||||||
// 1. 检查线是否连接有效节点
|
// 1. 检查线是否连接有效节点
|
||||||
validateEdgeConnections(nodes, edges);
|
validateEdgeConnections(nodes, edges);
|
||||||
|
|
||||||
@@ -102,16 +105,39 @@ public class ValidatorServiceImpl implements ValidatorService {
|
|||||||
|
|
||||||
// 3. 检查是否存在孤立节点
|
// 3. 检查是否存在孤立节点
|
||||||
validateIsolatedNodes(nodes, edges);
|
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) {
|
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) {
|
for (Edge edge : edges) {
|
||||||
String source = edge.getSource();
|
Long source = edge.getSource();
|
||||||
String target = edge.getTarget();
|
Long target = edge.getTarget();
|
||||||
Assert.isTrue(nodeMap.containsKey(source), "边 {} 的源节点 {} 不存在", edge.getLabel(), source);
|
Assert.isTrue(nodeMap.containsKey(source), "边 {} 的源节点 {} 不存在", edge.getLabel(), source);
|
||||||
Assert.isTrue(nodeMap.containsKey(target), "边 {} 的目标节点 {} 不存在", edge.getLabel(), target);
|
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) {
|
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) {
|
for (Node node : nodes) {
|
||||||
if (hasCycle(node.getId(), adjacencyList, new HashSet<>())) {
|
if (hasCycle(node.getId(), adjacencyList, new HashSet<>())) {
|
||||||
throw new IllegalArgumentException("图中存在环结构,起始节点: " + node.getData().getLabel());
|
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) {
|
private void validateIsolatedNodes(List<Node> nodes, List<Edge> edges) {
|
||||||
Set<String> connectedNodes = new HashSet<>();
|
Set<Long> connectedNodes = new HashSet<>();
|
||||||
for (Edge edge : edges) {
|
for (Edge edge : edges) {
|
||||||
connectedNodes.add(edge.getSource());
|
connectedNodes.add(edge.getSource());
|
||||||
connectedNodes.add(edge.getTarget());
|
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) {
|
private Map<Long, List<Long>> buildAdjacencyList(List<Edge> edges) {
|
||||||
Map<String, List<String>> adjacencyList = new HashMap<>();
|
Map<Long, List<Long>> adjacencyList = new HashMap<>();
|
||||||
for (Edge edge : edges) {
|
for (Edge edge : edges) {
|
||||||
adjacencyList.computeIfAbsent(edge.getSource(), k -> new ArrayList<>()).add(edge.getTarget());
|
adjacencyList.computeIfAbsent(edge.getSource(), k -> new ArrayList<>()).add(edge.getTarget());
|
||||||
}
|
}
|
||||||
@@ -156,13 +191,18 @@ public class ValidatorServiceImpl implements ValidatorService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 深度优先搜索(DFS)检查环
|
* 深度优先搜索(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)) {
|
if (visited.contains(nodeId)) {
|
||||||
return true; // 发现环
|
return true; // 发现环
|
||||||
}
|
}
|
||||||
visited.add(nodeId);
|
visited.add(nodeId);
|
||||||
for (String neighbor : adjacencyList.getOrDefault(nodeId, new ArrayList<>())) {
|
for (Long neighbor : adjacencyList.getOrDefault(nodeId, new ArrayList<>())) {
|
||||||
if (hasCycle(neighbor, adjacencyList, visited)) {
|
if (hasCycle(neighbor, adjacencyList, visited)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -171,4 +211,40 @@ public class ValidatorServiceImpl implements ValidatorService {
|
|||||||
return false;
|
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;
|
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.base.Node;
|
||||||
import com.metis.flow.domain.entity.config.node.DocumentExtractorNodeConfig;
|
import com.metis.flow.domain.entity.config.node.DocumentExtractorNodeConfig;
|
||||||
import com.metis.flow.enums.NodeType;
|
import com.metis.flow.enums.NodeType;
|
||||||
@@ -10,6 +12,8 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -17,13 +21,21 @@ public class DocumentExtractorNodeValidator implements NodeValidator {
|
|||||||
private final ValidatorCodeService validatorCodeService;
|
private final ValidatorCodeService validatorCodeService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidatorResult validate(Node node) {
|
public ValidatorResult validateValue(Node node) {
|
||||||
DocumentExtractorNodeConfig config = node.getConfig();
|
DocumentExtractorNodeConfig config = node.getConfig();
|
||||||
validatorCodeService.validateThrow(config);
|
validatorCodeService.validateThrow(config);
|
||||||
// 业务检查未通过
|
// 业务检查未通过
|
||||||
return ValidatorResult.invalid("业务报错");
|
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
|
@Override
|
||||||
public NodeType getType() {
|
public NodeType getType() {
|
||||||
return NodeType.DOCUMENT_EXTRACTOR;
|
return NodeType.DOCUMENT_EXTRACTOR;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.metis.flow.validator.impl.node;
|
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.base.Node;
|
||||||
import com.metis.flow.enums.NodeType;
|
import com.metis.flow.enums.NodeType;
|
||||||
import com.metis.flow.validator.NodeValidator;
|
import com.metis.flow.validator.NodeValidator;
|
||||||
@@ -8,6 +10,8 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -16,7 +20,19 @@ public class EndNodeValidator implements NodeValidator {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@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();
|
return ValidatorResult.valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.metis.flow.validator.impl.node;
|
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.base.Node;
|
||||||
import com.metis.flow.enums.NodeType;
|
import com.metis.flow.enums.NodeType;
|
||||||
import com.metis.flow.validator.NodeValidator;
|
import com.metis.flow.validator.NodeValidator;
|
||||||
@@ -7,12 +9,26 @@ import com.metis.flow.validator.ValidatorResult;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class StartNodeValidator implements NodeValidator {
|
public class StartNodeValidator implements NodeValidator {
|
||||||
|
|
||||||
@Override
|
@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();
|
return ValidatorResult.valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user