feat: 开始节点成功运行
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
package com.metis.controller;
|
package com.metis.controller;
|
||||||
|
|
||||||
import com.metis.flow.domain.bo.BuildApp;
|
import com.metis.flow.domain.bo.BuildApp;
|
||||||
|
import com.metis.flow.engine.AppFlowEngineRunnerService;
|
||||||
|
import com.metis.flow.runner.FlowRunningContext;
|
||||||
import com.metis.flow.validator.ValidatorService;
|
import com.metis.flow.validator.ValidatorService;
|
||||||
import com.metis.result.Result;
|
import com.metis.result.Result;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -16,6 +18,7 @@ public class TestController {
|
|||||||
|
|
||||||
|
|
||||||
private final ValidatorService validatorService;
|
private final ValidatorService validatorService;
|
||||||
|
private final AppFlowEngineRunnerService engineRunnerService;
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public Result<String> test(@RequestBody BuildApp app) {
|
public Result<String> test(@RequestBody BuildApp app) {
|
||||||
@@ -23,4 +26,12 @@ public class TestController {
|
|||||||
return Result.ok("测试成功");
|
return Result.ok("测试成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/run")
|
||||||
|
public Result<String> run(@RequestBody FlowRunningContext context) {
|
||||||
|
engineRunnerService.running(context);
|
||||||
|
return Result.ok("测试成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ package com.metis.flow.domain.context;
|
|||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.metis.flow.runner.FlowRunningContext;
|
import com.metis.flow.runner.FlowRunningContext;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@Data
|
@Getter
|
||||||
@Builder
|
@Builder
|
||||||
public class RunningContext {
|
public class RunningContext {
|
||||||
|
|
||||||
@@ -29,11 +29,21 @@ public class RunningContext {
|
|||||||
private Map<Long, JSONObject> nodeRunningContext;
|
private Map<Long, JSONObject> nodeRunningContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下一个运行节点id集合, 可能是多个
|
* 下一个运行节点id集合, 可能是多个, 执行器每一次清空该节点
|
||||||
*/
|
*/
|
||||||
private Set<Long> nextRunNodeId;
|
private Set<Long> nextRunNodeId;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加节点运行环境
|
||||||
|
*
|
||||||
|
* @param nodeId 节点id
|
||||||
|
* @param nodeRunningContext 节点运行背景信息
|
||||||
|
*/
|
||||||
|
public void addNodeRunningContext(Long nodeId, JSONObject nodeRunningContext) {
|
||||||
|
this.nodeRunningContext.put(nodeId, nodeRunningContext);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建上下文
|
* 构建上下文
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.metis.flow.domain.context;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class RunningResult {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点上下文
|
||||||
|
*/
|
||||||
|
private JSONObject nodeContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下一个运行节点id, 一些特殊节点需要, 必须条件节点满足后, 才会运行下一个节点
|
||||||
|
*/
|
||||||
|
private Set<Long> nextRunNodeId;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建结果
|
||||||
|
*
|
||||||
|
* @param nodeContext 节点上下文
|
||||||
|
* @param nextRunNodeId 下一个运行节点id
|
||||||
|
* @return {@link RunningResult }
|
||||||
|
*/
|
||||||
|
public static RunningResult buildResult(JSONObject nodeContext, Set<Long> nextRunNodeId) {
|
||||||
|
return RunningResult.builder()
|
||||||
|
.nodeContext(nodeContext)
|
||||||
|
.nextRunNodeId(nextRunNodeId)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建结果
|
||||||
|
*
|
||||||
|
* @param nodeContext 节点上下文
|
||||||
|
* @return {@link RunningResult }
|
||||||
|
*/
|
||||||
|
public static RunningResult buildResult(JSONObject nodeContext) {
|
||||||
|
return RunningResult.builder()
|
||||||
|
.nodeContext(nodeContext)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RunningResult buildResult() {
|
||||||
|
return RunningResult.builder()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -40,6 +40,23 @@ public class Node {
|
|||||||
@NotNull(message = "节点业务数据不能为空")
|
@NotNull(message = "节点业务数据不能为空")
|
||||||
private NodeData data;
|
private NodeData data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 宽度
|
||||||
|
*/
|
||||||
|
// @NotNull(message = "节点宽度不能为空")
|
||||||
|
private Integer width;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高度
|
||||||
|
*/
|
||||||
|
// @NotNull(message = "节点高度不能为空")
|
||||||
|
private Integer height;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点是否选中
|
||||||
|
*/
|
||||||
|
private Boolean selected;
|
||||||
|
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public Map<Long, Handle> getHandleMap() {
|
public Map<Long, Handle> getHandleMap() {
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ public class NodeData {
|
|||||||
*/
|
*/
|
||||||
private PositionType toolbarPosition;
|
private PositionType toolbarPosition;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置
|
* 配置
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package com.metis.flow.domain.entity.base;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.metis.flow.enums.FileUploadType;
|
||||||
|
import com.metis.flow.enums.NodeVariableType;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class NodeVariable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数字段
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "参数字段不能为空")
|
||||||
|
private String variable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签
|
||||||
|
*/
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大长度
|
||||||
|
*/
|
||||||
|
private Integer maxLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
@NotNull(message = "类型不能为空")
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否必填
|
||||||
|
*/
|
||||||
|
@NotNull(message = "是否必填不能为空")
|
||||||
|
private Boolean required;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选项
|
||||||
|
*/
|
||||||
|
@Valid
|
||||||
|
private List<VariableOption> options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 允许上传方式
|
||||||
|
*/
|
||||||
|
private List<FileUploadType> allowedFileUploadMethods;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 允许文件类型
|
||||||
|
*/
|
||||||
|
private List<String> allowedFileTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 允许文件扩展名
|
||||||
|
*/
|
||||||
|
private List<String> allowedFileExtensions;
|
||||||
|
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public Object getValue(JSONObject custom) {
|
||||||
|
Object serializable = getSerializable(custom);
|
||||||
|
Assert.isTrue(!(ObjectUtil.isNull(serializable) && ObjectUtil.isNotNull(required) && required), "参数字段 {} 的值不能为空", variable);
|
||||||
|
return serializable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getSerializable(JSONObject custom) {
|
||||||
|
switch (getVariableType()) {
|
||||||
|
case TEXT_INPUT, PARAGRAPH, SELECT, FILE -> {
|
||||||
|
return custom.getString(variable);
|
||||||
|
}
|
||||||
|
case NUMBER -> {
|
||||||
|
return custom.getInteger(variable);
|
||||||
|
}
|
||||||
|
case FILE_LIST -> {
|
||||||
|
return custom.getList(variable, String.class);
|
||||||
|
}
|
||||||
|
default -> throw new RuntimeException("不支持的类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public NodeVariableType getVariableType() {
|
||||||
|
return NodeVariableType.get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.metis.flow.domain.entity.base;
|
||||||
|
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class VariableOption {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签
|
||||||
|
*/
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 值
|
||||||
|
*/
|
||||||
|
@NotNull(message = "值不能为空")
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.metis.flow.domain.entity.config.node;
|
||||||
|
|
||||||
|
import com.metis.flow.domain.entity.base.NodeVariable;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class StartNodeConfig {
|
||||||
|
|
||||||
|
|
||||||
|
@Valid
|
||||||
|
private List<NodeVariable> variables;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,18 +1,31 @@
|
|||||||
package com.metis.flow.engine.impl;
|
package com.metis.flow.engine.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.metis.flow.domain.bo.Graph;
|
||||||
import com.metis.flow.domain.context.RunningContext;
|
import com.metis.flow.domain.context.RunningContext;
|
||||||
|
import com.metis.flow.domain.context.RunningResult;
|
||||||
import com.metis.flow.domain.context.SysContext;
|
import com.metis.flow.domain.context.SysContext;
|
||||||
import com.metis.flow.domain.entity.App;
|
import com.metis.flow.domain.entity.App;
|
||||||
|
import com.metis.flow.domain.entity.base.Edge;
|
||||||
|
import com.metis.flow.domain.entity.base.Node;
|
||||||
import com.metis.flow.engine.AppEngineService;
|
import com.metis.flow.engine.AppEngineService;
|
||||||
import com.metis.flow.engine.AppFlowEngineRunnerService;
|
import com.metis.flow.engine.AppFlowEngineRunnerService;
|
||||||
|
import com.metis.flow.enums.NodeType;
|
||||||
import com.metis.flow.runner.FlowRunningContext;
|
import com.metis.flow.runner.FlowRunningContext;
|
||||||
|
import com.metis.flow.runner.NodeRunner;
|
||||||
import com.metis.flow.runner.RunnerResult;
|
import com.metis.flow.runner.RunnerResult;
|
||||||
|
import com.metis.flow.runner.factory.RunnerFactory;
|
||||||
import lombok.RequiredArgsConstructor;
|
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;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -36,9 +49,33 @@ public class AppFlowEngineRunnerServiceImpl implements AppFlowEngineRunnerServic
|
|||||||
.build();
|
.build();
|
||||||
// 构建运行中上下文
|
// 构建运行中上下文
|
||||||
RunningContext runningContext = RunningContext.buildContext(sysContext, context);
|
RunningContext runningContext = RunningContext.buildContext(sysContext, context);
|
||||||
|
// 构建节点映射对象
|
||||||
|
Graph graph = app.getGraph();
|
||||||
|
Map<Long, Node> nodeMap = graph.getNodes().stream()
|
||||||
|
.collect(Collectors.toMap(Node::getId, Function.identity()));
|
||||||
|
Map<Long, List<Edge>> edgeMap = graph.getEdges().stream()
|
||||||
|
.collect(Collectors.groupingBy(Edge::getSource));
|
||||||
|
// 获取到开始节点
|
||||||
|
Node runningNode = graph.getNodes().stream().filter(node -> node.getType() == NodeType.START)
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
// 开始节点为空,则表示数据存在异常
|
||||||
|
Assert.isTrue(ObjectUtil.isNotNull(runningNode), "流程图不存在开始节点");
|
||||||
|
while (ObjectUtil.isNotNull(runningNode)) {
|
||||||
|
NodeRunner nodeRunner = RunnerFactory.get(runningNode.getType());
|
||||||
|
// 获取到返回结果
|
||||||
|
RunningResult result = nodeRunner.run(runningContext, runningNode, edgeMap.get(runningNode.getId()));
|
||||||
|
if (ObjectUtil.isNotNull(result.getNodeContext())) {
|
||||||
|
runningContext.addNodeRunningContext(runningNode.getId(), result.getNodeContext());
|
||||||
|
}
|
||||||
|
runningNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return null;
|
return RunnerResult.builder()
|
||||||
|
.content("你他妈的!")
|
||||||
|
.context(sysContext)
|
||||||
|
.build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -49,7 +86,7 @@ public class AppFlowEngineRunnerServiceImpl implements AppFlowEngineRunnerServic
|
|||||||
* @return {@link App }
|
* @return {@link App }
|
||||||
*/
|
*/
|
||||||
private App getApp(FlowRunningContext context) {
|
private App getApp(FlowRunningContext context) {
|
||||||
if (ObjectUtil.isNull(context.getWorkflowId())) {
|
if (ObjectUtil.isNotNull(context.getWorkflowId())) {
|
||||||
return appEngineService.getByWorkflowId(context.getWorkflowId());
|
return appEngineService.getByWorkflowId(context.getWorkflowId());
|
||||||
}
|
}
|
||||||
return appEngineService.getByAppId(context.getAppId());
|
return appEngineService.getByAppId(context.getAppId());
|
||||||
|
|||||||
45
src/main/java/com/metis/flow/enums/FileUploadType.java
Normal file
45
src/main/java/com/metis/flow/enums/FileUploadType.java
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package com.metis.flow.enums;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传类型
|
||||||
|
*
|
||||||
|
* @author clay
|
||||||
|
* @date 2025/04/08
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum FileUploadType {
|
||||||
|
LOCAL_FILE(1, "localFile", "本地文件"),
|
||||||
|
REMOTE_URL(2, "remoteUrl", "远程URL");
|
||||||
|
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
|
||||||
|
@JsonValue
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 枚举序列化器(前端传code时自动转换为对应枚举)
|
||||||
|
*
|
||||||
|
* @param value 值
|
||||||
|
* @return 枚举
|
||||||
|
*/
|
||||||
|
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
|
||||||
|
public static FileUploadType get(String value) {
|
||||||
|
return Arrays.stream(FileUploadType.class.getEnumConstants())
|
||||||
|
.filter(e -> e.getValue().equals(value))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package com.metis.flow.enums;
|
|||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
import com.fasterxml.jackson.annotation.JsonValue;
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
import com.metis.flow.domain.entity.config.node.DocumentExtractorNodeConfig;
|
import com.metis.flow.domain.entity.config.node.DocumentExtractorNodeConfig;
|
||||||
|
import com.metis.flow.domain.entity.config.node.StartNodeConfig;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
@@ -12,11 +13,9 @@ import java.util.Arrays;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public enum NodeType {
|
public enum NodeType {
|
||||||
|
|
||||||
START(1, "start", "开始", Object.class),
|
START(1, "start", "开始", StartNodeConfig.class),
|
||||||
END(2, "end", "结束", Object.class),
|
END(2, "end", "结束", Object.class),
|
||||||
DOCUMENT_EXTRACTOR(3, "document-extractor", "文档提取器", DocumentExtractorNodeConfig.class)
|
DOCUMENT_EXTRACTOR(3, "document-extractor", "文档提取器", DocumentExtractorNodeConfig.class);
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
private final Integer code;
|
private final Integer code;
|
||||||
|
|||||||
50
src/main/java/com/metis/flow/enums/NodeVariableType.java
Normal file
50
src/main/java/com/metis/flow/enums/NodeVariableType.java
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package com.metis.flow.enums;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点变量类型
|
||||||
|
*
|
||||||
|
* @author clay
|
||||||
|
* @date 2025/04/08
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum NodeVariableType {
|
||||||
|
TEXT_INPUT(1, "text-input", "文本"),
|
||||||
|
PARAGRAPH(2, "paragraph", "段落"),
|
||||||
|
SELECT(3, "select", "下拉框"),
|
||||||
|
NUMBER(4, "number", "数字"),
|
||||||
|
FILE(5, "file", "文件"),
|
||||||
|
FILE_LIST(6, "file-list", "文件列表")
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
|
||||||
|
@JsonValue
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 枚举序列化器(前端传code时自动转换为对应枚举)
|
||||||
|
*
|
||||||
|
* @param value 值
|
||||||
|
* @return 枚举
|
||||||
|
*/
|
||||||
|
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
|
||||||
|
public static NodeVariableType get(String value) {
|
||||||
|
return Arrays.stream(NodeVariableType.class.getEnumConstants())
|
||||||
|
.filter(e -> e.getValue().equals(value))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
package com.metis.flow.runner;
|
package com.metis.flow.runner;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -14,6 +16,8 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
public class FlowRunningContext {
|
public class FlowRunningContext {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
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.context.RunningResult;
|
||||||
import com.metis.flow.domain.entity.base.Edge;
|
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;
|
||||||
@@ -18,7 +19,7 @@ public interface NodeRunner {
|
|||||||
* @param edges
|
* @param edges
|
||||||
* @return {@link RunningContext }
|
* @return {@link RunningContext }
|
||||||
*/
|
*/
|
||||||
RunningContext run(RunningContext context, Node node, List<Edge> edges);
|
RunningResult run(RunningContext context, Node node, List<Edge> edges);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.metis.flow.runner;
|
|||||||
|
|
||||||
|
|
||||||
import com.metis.flow.domain.context.SysContext;
|
import com.metis.flow.domain.context.SysContext;
|
||||||
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,6 +12,7 @@ import lombok.Data;
|
|||||||
* @date 2025/04/07
|
* @date 2025/04/07
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
|
@Builder
|
||||||
public class RunnerResult {
|
public class RunnerResult {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ 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.context.RunningResult;
|
||||||
import com.metis.flow.domain.entity.base.Edge;
|
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;
|
||||||
@@ -16,8 +17,8 @@ import java.util.List;
|
|||||||
public class EndNodeRunner implements NodeRunner {
|
public class EndNodeRunner implements NodeRunner {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RunningContext run(RunningContext context, Node node, List<Edge> edges) {
|
public RunningResult run(RunningContext context, Node node, List<Edge> edges) {
|
||||||
return context;
|
return RunningResult.buildResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
package com.metis.flow.runner.impl;
|
package com.metis.flow.runner.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.metis.flow.domain.context.RunningContext;
|
import com.metis.flow.domain.context.RunningContext;
|
||||||
|
import com.metis.flow.domain.context.RunningResult;
|
||||||
import com.metis.flow.domain.entity.base.Edge;
|
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.base.NodeVariable;
|
||||||
|
import com.metis.flow.domain.entity.config.node.StartNodeConfig;
|
||||||
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;
|
||||||
@@ -10,15 +15,37 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动节点运行器, 开始节点功能主要为将用户传入的参数进行校验, 并放入到上下文中
|
||||||
|
*
|
||||||
|
* @author clay
|
||||||
|
* @date 2025/04/08
|
||||||
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class StartNodeRunner implements NodeRunner {
|
public class StartNodeRunner implements NodeRunner {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RunningContext run(RunningContext context, Node node, List<Edge> edges) {
|
public RunningResult run(RunningContext context, Node node, List<Edge> edges) {
|
||||||
return context;
|
log.info("开始节点{}, 节点id: {} 运行", node.getData().getLabel(), node.getId());
|
||||||
|
StartNodeConfig config = node.getConfig();
|
||||||
|
// 获取到节点的自定义参数
|
||||||
|
List<NodeVariable> variables = config.getVariables();
|
||||||
|
// 如果没有自定义参数, 则直接返回
|
||||||
|
if (CollUtil.isEmpty(variables)) {
|
||||||
|
return RunningResult.buildResult();
|
||||||
|
}
|
||||||
|
// 获取用户自定义参数
|
||||||
|
JSONObject custom = context.getCustom();
|
||||||
|
JSONObject contextNodeValue = new JSONObject();
|
||||||
|
for (NodeVariable variable : variables) {
|
||||||
|
Object value = variable.getValue(custom);
|
||||||
|
contextNodeValue.put(variable.getVariable(), value);
|
||||||
|
}
|
||||||
|
return RunningResult.buildResult(contextNodeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NodeType getType() {
|
public NodeType getType() {
|
||||||
return NodeType.START;
|
return NodeType.START;
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
package com.metis.flow.validator.impl.node;
|
package com.metis.flow.validator.impl.node;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.metis.flow.domain.entity.base.Edge;
|
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.base.NodeVariable;
|
||||||
|
import com.metis.flow.domain.entity.config.node.StartNodeConfig;
|
||||||
import com.metis.flow.enums.NodeType;
|
import com.metis.flow.enums.NodeType;
|
||||||
import com.metis.flow.validator.NodeValidator;
|
import com.metis.flow.validator.NodeValidator;
|
||||||
|
import com.metis.flow.validator.ValidatorCodeService;
|
||||||
import com.metis.flow.validator.ValidatorResult;
|
import com.metis.flow.validator.ValidatorResult;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -13,13 +19,65 @@ import java.util.List;
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class StartNodeValidator implements NodeValidator {
|
public class StartNodeValidator implements NodeValidator {
|
||||||
|
|
||||||
|
private final ValidatorCodeService validatorCodeService;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidatorResult validateValue(Node node) {
|
public ValidatorResult validateValue(Node node) {
|
||||||
|
StartNodeConfig config = node.getConfig();
|
||||||
|
validatorCodeService.validateThrow(config);
|
||||||
|
|
||||||
|
List<NodeVariable> variables = config.getVariables();
|
||||||
|
if (CollUtil.isEmpty(variables)) {
|
||||||
|
return ValidatorResult.valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (NodeVariable variable : variables) {
|
||||||
|
switch (variable.getVariableType()) {
|
||||||
|
// 文本类型校验
|
||||||
|
case TEXT_INPUT, PARAGRAPH -> textVariableValidator(variable);
|
||||||
|
// 下拉框变量校验
|
||||||
|
case SELECT -> selectVariableValidator(variable);
|
||||||
|
// 文件变量校验
|
||||||
|
case FILE, FILE_LIST -> fileVariableValidator(variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
return ValidatorResult.valid();
|
return ValidatorResult.valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本变量验证器
|
||||||
|
*
|
||||||
|
* @param variable 变量
|
||||||
|
*/
|
||||||
|
private void textVariableValidator(NodeVariable variable) {
|
||||||
|
Assert.isTrue(ObjectUtil.isNotNull(variable.getMaxLength()), "文本变量 {} {} 的最大长度不能为空", variable.getLabel(), variable.getLabel(), variable.getVariable());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择变量验证器
|
||||||
|
*
|
||||||
|
* @param variable 变量
|
||||||
|
*/
|
||||||
|
private void selectVariableValidator(NodeVariable variable) {
|
||||||
|
Assert.isTrue(CollUtil.isNotEmpty(variable.getOptions()), "选择变量 {} {} 的选项不能为空", variable.getLabel(), variable.getLabel(), variable.getVariable());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件变量验证器
|
||||||
|
*
|
||||||
|
* @param variable 变量
|
||||||
|
*/
|
||||||
|
private void fileVariableValidator(NodeVariable variable) {
|
||||||
|
Assert.isTrue(ObjectUtil.isNotNull(variable.getMaxLength()), "文本变量 {} {} 的最大长度不能为空", variable.getLabel(), variable.getVariable());
|
||||||
|
Assert.isTrue(CollUtil.isNotEmpty(variable.getAllowedFileUploadMethods()), "文本变量 {} {} 的允许上传方式不能为空", variable.getLabel(), variable.getVariable());
|
||||||
|
Assert.isTrue(CollUtil.isNotEmpty(variable.getAllowedFileTypes()) || CollUtil.isNotEmpty(variable.getAllowedFileExtensions()), "文本变量 {} 的允许上传文件类型不能为空", variable.getLabel(), variable.getVariable());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidatorResult validateRelation(Node node, List<Edge> sources, List<Edge> targets) {
|
public ValidatorResult validateRelation(Node node, List<Edge> sources, List<Edge> targets) {
|
||||||
// 1. 开始节点不允许有源连接
|
// 1. 开始节点不允许有源连接
|
||||||
|
|||||||
7
src/main/resources/json/run.json
Normal file
7
src/main/resources/json/run.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"appId": "1909636986931470336",
|
||||||
|
"userId": 1,
|
||||||
|
"custom": {
|
||||||
|
"context": "测试内容!"
|
||||||
|
}
|
||||||
|
}
|
||||||
152
src/main/resources/json/test.json
Normal file
152
src/main/resources/json/test.json
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "测试流程",
|
||||||
|
"description": "测试流程",
|
||||||
|
"graph": {
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "5",
|
||||||
|
"type": "start",
|
||||||
|
"dimensions": {
|
||||||
|
"width": 300,
|
||||||
|
"height": 300
|
||||||
|
},
|
||||||
|
"draggable": true,
|
||||||
|
"resizing": false,
|
||||||
|
"selected": true,
|
||||||
|
"data": {
|
||||||
|
"label": "开始",
|
||||||
|
"toolbarPosition": "right",
|
||||||
|
"config": {
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"variable": "context",
|
||||||
|
"label": "段落",
|
||||||
|
"type": "paragraph",
|
||||||
|
"maxLength": 48,
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"variable": "text",
|
||||||
|
"label": "文本",
|
||||||
|
"type": "text-input",
|
||||||
|
"maxLength": 48,
|
||||||
|
"required": true,
|
||||||
|
"options": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"variable": "select",
|
||||||
|
"label": "下拉",
|
||||||
|
"type": "select",
|
||||||
|
"maxLength": 48,
|
||||||
|
"required": true,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "选型1",
|
||||||
|
"value": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "选型2",
|
||||||
|
"value": "2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"variable": "number",
|
||||||
|
"label": "数字",
|
||||||
|
"type": "number",
|
||||||
|
"maxLength": 48,
|
||||||
|
"required": true,
|
||||||
|
"options": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"variable": "singlefile",
|
||||||
|
"label": "singlefile单文件",
|
||||||
|
"type": "file",
|
||||||
|
"maxLength": 48,
|
||||||
|
"required": true,
|
||||||
|
"options": [],
|
||||||
|
"allowedFileUploadMethods": [
|
||||||
|
"local_file",
|
||||||
|
"remote_url"
|
||||||
|
],
|
||||||
|
"allowedFileTypes": [
|
||||||
|
"image",
|
||||||
|
"document",
|
||||||
|
"audio",
|
||||||
|
"video"
|
||||||
|
],
|
||||||
|
"allowedFileExtensions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"variable": "mufile",
|
||||||
|
"label": "多文件",
|
||||||
|
"type": "file-list",
|
||||||
|
"maxLength": 5,
|
||||||
|
"required": true,
|
||||||
|
"options": [],
|
||||||
|
"allowedFileUploadMethods": [
|
||||||
|
"local_file"
|
||||||
|
],
|
||||||
|
"allowedFileTypes": [
|
||||||
|
"custom"
|
||||||
|
],
|
||||||
|
"allowedFileExtensions": [
|
||||||
|
"docx",
|
||||||
|
"aaa"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parent": "234"
|
||||||
|
},
|
||||||
|
"handles": [
|
||||||
|
{
|
||||||
|
"id": "7",
|
||||||
|
"position": "right",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"position": { "x": 0, "y": 300 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6",
|
||||||
|
"type": "end",
|
||||||
|
"selected": false,
|
||||||
|
"data": {
|
||||||
|
"label": "结束",
|
||||||
|
"toolbarPosition": "right",
|
||||||
|
"handles": [
|
||||||
|
{
|
||||||
|
"id": "8",
|
||||||
|
"position": "left",
|
||||||
|
"type": "target",
|
||||||
|
"connectable": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"position": { "x": 500, "y": 300 }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"id": "6",
|
||||||
|
"type": "default",
|
||||||
|
"source": "5",
|
||||||
|
"target": "6",
|
||||||
|
"sourceHandle": "7",
|
||||||
|
"animated": true,
|
||||||
|
"targetHandle": "8",
|
||||||
|
"label": "线标签",
|
||||||
|
"markerEnd": "none",
|
||||||
|
"markerStart": "none",
|
||||||
|
"updatable": true,
|
||||||
|
"selectable": true,
|
||||||
|
"deletable": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user