可执行一个简单的工作流。
This commit is contained in:
parent
4b4d35fccd
commit
f5fb0a2dae
@ -124,7 +124,7 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
|
|||||||
startNode.setNodeType(startNodeConfig.getType());
|
startNode.setNodeType(startNodeConfig.getType());
|
||||||
startNode.setName(startNodeConfig.getName());
|
startNode.setName(startNodeConfig.getName());
|
||||||
startNode.setConfig(objectMapper.writeValueAsString(startNodeConfig.getConfig()));
|
startNode.setConfig(objectMapper.writeValueAsString(startNodeConfig.getConfig()));
|
||||||
startNode.setStatus(NodeStatusEnum.PENDING);
|
startNode.setStatus(NodeStatusEnum.RUNNING);
|
||||||
startNode.setCreateTime(LocalDateTime.now());
|
startNode.setCreateTime(LocalDateTime.now());
|
||||||
nodeInstanceRepository.save(startNode);
|
nodeInstanceRepository.save(startNode);
|
||||||
|
|
||||||
@ -203,7 +203,6 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
|
|||||||
.findByWorkflowInstanceAndStatusNot(instance, NodeStatusEnum.COMPLETED);
|
.findByWorkflowInstanceAndStatusNot(instance, NodeStatusEnum.COMPLETED);
|
||||||
|
|
||||||
if (uncompletedNodes.isEmpty()) {
|
if (uncompletedNodes.isEmpty()) {
|
||||||
instance.complete();
|
|
||||||
workflowInstanceRepository.save(instance);
|
workflowInstanceRepository.save(instance);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -223,21 +222,18 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void createAndExecuteNextNode(WorkflowInstance instance, String nextNodeId, NodeConfig nodeConfig) {
|
private void createAndExecuteNextNode(WorkflowInstance instance, String nextNodeId, NodeConfig nodeConfig) {
|
||||||
try {
|
NodeInstance nextNode = new NodeInstance();
|
||||||
NodeInstance nextNode = new NodeInstance();
|
nextNode.setNodeId(nextNodeId);
|
||||||
nextNode.setNodeId(nextNodeId);
|
nextNode.setWorkflowInstance(instance);
|
||||||
nextNode.setWorkflowInstance(instance);
|
nextNode.setNodeType(nodeConfig.getType());
|
||||||
nextNode.setNodeType(nodeConfig.getType());
|
nextNode.setName(nodeConfig.getName());
|
||||||
nextNode.setName(nodeConfig.getName());
|
// nextNode.setConfigObject(nodeExecuteConfigConverter.toNodeExecutorConfig(nodeConfig));
|
||||||
nextNode.setConfig(objectMapper.writeValueAsString(nodeConfig.getConfig()));
|
// nextNode.setConfigObject(nodeConfig);
|
||||||
nextNode.setStatus(NodeStatusEnum.PENDING);
|
nextNode.setStatus(NodeStatusEnum.PENDING);
|
||||||
nodeInstanceRepository.save(nextNode);
|
nodeInstanceRepository.save(nextNode);
|
||||||
|
|
||||||
// 递归执行后续节点
|
// 递归执行后续节点
|
||||||
executeNode(nextNode.getId());
|
executeNode(nextNode.getId());
|
||||||
} catch (JsonProcessingException e) {
|
|
||||||
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_INVALID, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -15,6 +15,5 @@ public class WorkflowEngineException extends BusinessException {
|
|||||||
|
|
||||||
public WorkflowEngineException(ResponseCode code, Throwable cause) {
|
public WorkflowEngineException(ResponseCode code, Throwable cause) {
|
||||||
super(code, new Object[]{cause.getMessage()});
|
super(code, new Object[]{cause.getMessage()});
|
||||||
initCause(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5,9 +5,10 @@ import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
|||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
|
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
|
import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.executor.node.config.ScriptNodeConfig;
|
import com.qqchen.deploy.backend.workflow.engine.executor.node.config.ScriptNodeExecutorConfig;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
|
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
|
||||||
|
import com.qqchen.deploy.backend.workflow.enums.ScriptLanguageEnum;
|
||||||
import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
|
import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -51,7 +52,7 @@ public class ScriptNodeExecutor extends AbstractNodeExecutor {
|
|||||||
@Override
|
@Override
|
||||||
public void validate(String config) {
|
public void validate(String config) {
|
||||||
try {
|
try {
|
||||||
ScriptNodeConfig scriptConfig = objectMapper.readValue(config, ScriptNodeConfig.class);
|
ScriptNodeExecutorConfig scriptConfig = objectMapper.readValue(config, ScriptNodeExecutorConfig.class);
|
||||||
// 验证脚本内容
|
// 验证脚本内容
|
||||||
if (scriptConfig.getScript() == null || scriptConfig.getScript().trim().isEmpty()) {
|
if (scriptConfig.getScript() == null || scriptConfig.getScript().trim().isEmpty()) {
|
||||||
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR, "Script content cannot be empty");
|
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR, "Script content cannot be empty");
|
||||||
@ -78,40 +79,44 @@ public class ScriptNodeExecutor extends AbstractNodeExecutor {
|
|||||||
@Override
|
@Override
|
||||||
protected void doExecute(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
protected void doExecute(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
try {
|
try {
|
||||||
String configJson = nodeInstance.getConfig();
|
// String configJson = nodeInstance.getConfig();
|
||||||
ScriptNodeConfig config = objectMapper.readValue(configJson, ScriptNodeConfig.class);
|
// ScriptNodeExecutorConfig config = objectMapper.readValue(configJson, ScriptNodeExecutorConfig.class);
|
||||||
|
//
|
||||||
// 设置重试次数和间隔
|
// // 设置重试次数和间隔
|
||||||
int maxAttempts = config.getRetryTimes() != null ? config.getRetryTimes() : 1;
|
// int maxAttempts = config.getRetryTimes() != null ? config.getRetryTimes() : 1;
|
||||||
long retryInterval = config.getRetryInterval() != null ? config.getRetryInterval() : 0;
|
// long retryInterval = config.getRetryInterval() != null ? config.getRetryInterval() : 0;
|
||||||
|
//
|
||||||
Exception lastException = null;
|
// Exception lastException = null;
|
||||||
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
|
// for (int attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||||
try {
|
// try {
|
||||||
// 执行脚本
|
// // 执行脚本
|
||||||
|
ScriptNodeExecutorConfig config = new ScriptNodeExecutorConfig();
|
||||||
|
config.setLanguage(ScriptLanguageEnum.SHELL);
|
||||||
|
config.setInterpreter("/bin/bash");
|
||||||
|
config.setScript("ls -a");
|
||||||
executeScript(config, nodeInstance, context);
|
executeScript(config, nodeInstance, context);
|
||||||
return; // 执行成功,直接返回
|
// return; // 执行成功,直接返回
|
||||||
} catch (Exception e) {
|
// } catch (Exception e) {
|
||||||
lastException = e;
|
// lastException = e;
|
||||||
if (attempt < maxAttempts) {
|
// if (attempt < maxAttempts) {
|
||||||
context.log(String.format("Script execution failed (attempt %d/%d), retrying in %d seconds",
|
// context.log(String.format("Script execution failed (attempt %d/%d), retrying in %d seconds",
|
||||||
attempt, maxAttempts, retryInterval), LogLevelEnum.WARN);
|
// attempt, maxAttempts, retryInterval), LogLevelEnum.WARN);
|
||||||
Thread.sleep(retryInterval * 1000L);
|
// Thread.sleep(retryInterval * 1000L);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// 如果所有重试都失败,抛出最后一个异常
|
// // 如果所有重试都失败,抛出最后一个异常
|
||||||
if (lastException != null) {
|
// if (lastException != null) {
|
||||||
throw lastException;
|
// throw lastException;
|
||||||
}
|
// }
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED, e);
|
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeScript(ScriptNodeConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) throws Exception {
|
private void executeScript(ScriptNodeExecutorConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) throws Exception {
|
||||||
ProcessBuilder processBuilder = new ProcessBuilder();
|
ProcessBuilder processBuilder = new ProcessBuilder();
|
||||||
|
|
||||||
// 获取命令实现并构建命令
|
// 获取命令实现并构建命令
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
|||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
|
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
|
import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.executor.node.config.ShellNodeConfig;
|
import com.qqchen.deploy.backend.workflow.engine.executor.node.config.ShellNodeExecutorConfig;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
|
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
|
||||||
import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
|
import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
|
||||||
@ -46,7 +46,7 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
|
|||||||
@Override
|
@Override
|
||||||
public void validate(String config) {
|
public void validate(String config) {
|
||||||
try {
|
try {
|
||||||
ShellNodeConfig shellConfig = objectMapper.readValue(config, ShellNodeConfig.class);
|
ShellNodeExecutorConfig shellConfig = objectMapper.readValue(config, ShellNodeExecutorConfig.class);
|
||||||
// 验证执行器类型
|
// 验证执行器类型
|
||||||
if (!"SHELL".equals(shellConfig.getExecutor())) {
|
if (!"SHELL".equals(shellConfig.getExecutor())) {
|
||||||
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR);
|
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR);
|
||||||
@ -74,7 +74,7 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
|
|||||||
protected void doExecute(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
protected void doExecute(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
try {
|
try {
|
||||||
String configJson = nodeInstance.getConfig();
|
String configJson = nodeInstance.getConfig();
|
||||||
ShellNodeConfig config = objectMapper.readValue(configJson, ShellNodeConfig.class);
|
ShellNodeExecutorConfig config = objectMapper.readValue(configJson, ShellNodeExecutorConfig.class);
|
||||||
|
|
||||||
// 验证执行器类型
|
// 验证执行器类型
|
||||||
if (!"SHELL".equals(config.getExecutor())) {
|
if (!"SHELL".equals(config.getExecutor())) {
|
||||||
@ -110,7 +110,7 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeShellCommand(ShellNodeConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) throws Exception {
|
private void executeShellCommand(ShellNodeExecutorConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) throws Exception {
|
||||||
ProcessBuilder processBuilder = new ProcessBuilder();
|
ProcessBuilder processBuilder = new ProcessBuilder();
|
||||||
processBuilder.command("sh", "-c", config.getScript());
|
processBuilder.command("sh", "-c", config.getScript());
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class ApprovalNodeConfig extends NodeConfig {
|
public class ApprovalNodeExecutorConfig extends NodeExecutorConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 审批人类型:USER-指定用户,ROLE-指定角色,LEADER-直属领导
|
* 审批人类型:USER-指定用户,ROLE-指定角色,LEADER-直属领导
|
||||||
@ -9,7 +9,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class ConditionNodeConfig extends NodeConfig {
|
public class ConditionNodeExecutorConfig extends NodeExecutorConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 条件表达式(使用SpEL表达式)
|
* 条件表达式(使用SpEL表达式)
|
||||||
@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class GitNodeConfig extends NodeConfig {
|
public class GitNodeExecutorConfig extends NodeExecutorConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Git仓库URL
|
* Git仓库URL
|
||||||
@ -9,7 +9,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class HttpNodeConfig extends NodeConfig {
|
public class HttpNodeExecutorConfig extends NodeExecutorConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求URL
|
* 请求URL
|
||||||
@ -9,7 +9,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class JenkinsNodeConfig extends NodeConfig {
|
public class JenkinsNodeExecutorConfig extends NodeExecutorConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jenkins服务器ID
|
* Jenkins服务器ID
|
||||||
@ -9,7 +9,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class NacosNodeConfig extends NodeConfig {
|
public class NacosNodeExecutorConfig extends NodeExecutorConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nacos服务器ID
|
* Nacos服务器ID
|
||||||
@ -1,66 +0,0 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.engine.executor.node.config;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
|
||||||
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 节点配置基类
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
|
||||||
@JsonSubTypes({
|
|
||||||
@JsonSubTypes.Type(value = ApprovalNodeConfig.class, name = "APPROVAL"),
|
|
||||||
@JsonSubTypes.Type(value = ScriptNodeConfig.class, name = "SCRIPT"),
|
|
||||||
@JsonSubTypes.Type(value = ShellNodeConfig.class, name = "SHELL"),
|
|
||||||
@JsonSubTypes.Type(value = JenkinsNodeConfig.class, name = "JENKINS"),
|
|
||||||
@JsonSubTypes.Type(value = GitNodeConfig.class, name = "GIT"),
|
|
||||||
@JsonSubTypes.Type(value = ConditionNodeConfig.class, name = "CONDITION"),
|
|
||||||
@JsonSubTypes.Type(value = ParallelNodeConfig.class, name = "PARALLEL"),
|
|
||||||
@JsonSubTypes.Type(value = NacosNodeConfig.class, name = "NACOS"),
|
|
||||||
@JsonSubTypes.Type(value = HttpNodeConfig.class, name = "HTTP"),
|
|
||||||
@JsonSubTypes.Type(value = NotifyNodeConfig.class, name = "NOTIFY")
|
|
||||||
})
|
|
||||||
public abstract class NodeConfig {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 节点ID
|
|
||||||
*/
|
|
||||||
private String id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 节点名称
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 节点类型
|
|
||||||
*/
|
|
||||||
private NodeTypeEnum type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 超时时间(分钟)
|
|
||||||
*/
|
|
||||||
private Integer timeout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 重试次数
|
|
||||||
*/
|
|
||||||
private Integer retryCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 重试间隔(秒)
|
|
||||||
*/
|
|
||||||
private Integer retryInterval;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 失败是否继续
|
|
||||||
*/
|
|
||||||
private Boolean continueOnFailed;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 描述
|
|
||||||
*/
|
|
||||||
private String description;
|
|
||||||
}
|
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.engine.executor.node.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点配置基类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true) // 忽略未知字段
|
||||||
|
@JsonSubTypes({
|
||||||
|
@JsonSubTypes.Type(value = ApprovalNodeExecutorConfig.class, name = "APPROVAL"),
|
||||||
|
@JsonSubTypes.Type(value = ScriptNodeExecutorConfig.class, name = "SCRIPT"),
|
||||||
|
@JsonSubTypes.Type(value = ShellNodeExecutorConfig.class, name = "SHELL"),
|
||||||
|
@JsonSubTypes.Type(value = JenkinsNodeExecutorConfig.class, name = "JENKINS"),
|
||||||
|
@JsonSubTypes.Type(value = GitNodeExecutorConfig.class, name = "GIT"),
|
||||||
|
@JsonSubTypes.Type(value = ConditionNodeExecutorConfig.class, name = "CONDITION"),
|
||||||
|
@JsonSubTypes.Type(value = ParallelNodeExecutorConfig.class, name = "PARALLEL"),
|
||||||
|
@JsonSubTypes.Type(value = NacosNodeExecutorConfig.class, name = "NACOS"),
|
||||||
|
@JsonSubTypes.Type(value = HttpNodeExecutorConfig.class, name = "HTTP"),
|
||||||
|
@JsonSubTypes.Type(value = NotifyNodeExecutorConfig.class, name = "NOTIFY")
|
||||||
|
})
|
||||||
|
public class NodeExecutorConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点ID
|
||||||
|
*/
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点类型
|
||||||
|
*/
|
||||||
|
private NodeTypeEnum type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
}
|
||||||
@ -10,7 +10,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class NotifyNodeConfig extends NodeConfig {
|
public class NotifyNodeExecutorConfig extends NodeExecutorConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通知类型:EMAIL-邮件,SMS-短信,DINGTALK-钉钉,WEIXIN-企业微信
|
* 通知类型:EMAIL-邮件,SMS-短信,DINGTALK-钉钉,WEIXIN-企业微信
|
||||||
@ -9,7 +9,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class ParallelNodeConfig extends NodeConfig {
|
public class ParallelNodeExecutorConfig extends NodeExecutorConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 并行分支列表
|
* 并行分支列表
|
||||||
@ -12,7 +12,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class ScriptNodeConfig extends NodeConfig {
|
public class ScriptNodeExecutorConfig extends NodeExecutorConfig {
|
||||||
/**
|
/**
|
||||||
* 脚本内容
|
* 脚本内容
|
||||||
*/
|
*/
|
||||||
@ -39,16 +39,6 @@ public class ScriptNodeConfig extends NodeConfig {
|
|||||||
*/
|
*/
|
||||||
private Integer timeout;
|
private Integer timeout;
|
||||||
|
|
||||||
/**
|
|
||||||
* 环境变量
|
|
||||||
*/
|
|
||||||
private Map<String, String> environment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 成功退出码
|
|
||||||
*/
|
|
||||||
private Integer successExitCode = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重试次数
|
* 重试次数
|
||||||
*/
|
*/
|
||||||
@ -60,15 +50,17 @@ public class ScriptNodeConfig extends NodeConfig {
|
|||||||
private Integer retryInterval;
|
private Integer retryInterval;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 脚本参数
|
* 环境变量
|
||||||
*/
|
*/
|
||||||
private Map<String, String> arguments;
|
private Map<String, String> environment;
|
||||||
|
|
||||||
public String getScript() {
|
/**
|
||||||
return script;
|
* 成功退出码
|
||||||
}
|
*/
|
||||||
|
private Integer successExitCode = 0;
|
||||||
|
|
||||||
public Map<String, String> getEnvironment() {
|
/**
|
||||||
return environment;
|
* 失败是否继续
|
||||||
}
|
*/
|
||||||
|
private Boolean continueOnFailed;
|
||||||
}
|
}
|
||||||
@ -7,12 +7,12 @@ import java.util.Map;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Shell节点配置
|
* Shell节点配置
|
||||||
* @deprecated 请使用 {@link ScriptNodeConfig} 替代,设置 language="shell"
|
* @deprecated 请使用 {@link ScriptNodeExecutorConfig} 替代,设置 language="shell"
|
||||||
*/
|
*/
|
||||||
@Deprecated(since = "1.0", forRemoval = true)
|
@Deprecated(since = "1.0", forRemoval = true)
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class ShellNodeConfig extends NodeConfig {
|
public class ShellNodeExecutorConfig extends NodeExecutorConfig {
|
||||||
/**
|
/**
|
||||||
* 执行器类型,固定为 SHELL
|
* 执行器类型,固定为 SHELL
|
||||||
*/
|
*/
|
||||||
@ -1,6 +1,6 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.engine.executor.node.script.command;
|
package com.qqchen.deploy.backend.workflow.engine.executor.node.script.command;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.workflow.engine.executor.node.config.ScriptNodeConfig;
|
import com.qqchen.deploy.backend.workflow.engine.executor.node.config.ScriptNodeExecutorConfig;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -12,5 +12,5 @@ public interface ScriptCommand {
|
|||||||
* @param config 脚本配置
|
* @param config 脚本配置
|
||||||
* @return 命令行参数列表
|
* @return 命令行参数列表
|
||||||
*/
|
*/
|
||||||
List<String> buildCommand(ScriptNodeConfig config);
|
List<String> buildCommand(ScriptNodeExecutorConfig config);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ package com.qqchen.deploy.backend.workflow.engine.executor.node.script.command.i
|
|||||||
|
|
||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
|
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.executor.node.config.ScriptNodeConfig;
|
import com.qqchen.deploy.backend.workflow.engine.executor.node.config.ScriptNodeExecutorConfig;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.executor.node.script.command.ScriptCommand;
|
import com.qqchen.deploy.backend.workflow.engine.executor.node.script.command.ScriptCommand;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.executor.node.script.command.ScriptLanguageSupport;
|
import com.qqchen.deploy.backend.workflow.engine.executor.node.script.command.ScriptLanguageSupport;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.ScriptLanguageEnum;
|
import com.qqchen.deploy.backend.workflow.enums.ScriptLanguageEnum;
|
||||||
@ -17,7 +17,7 @@ import java.util.List;
|
|||||||
@ScriptLanguageSupport(ScriptLanguageEnum.PYTHON)
|
@ScriptLanguageSupport(ScriptLanguageEnum.PYTHON)
|
||||||
public class PythonScriptCommand implements ScriptCommand {
|
public class PythonScriptCommand implements ScriptCommand {
|
||||||
@Override
|
@Override
|
||||||
public List<String> buildCommand(ScriptNodeConfig config) {
|
public List<String> buildCommand(ScriptNodeExecutorConfig config) {
|
||||||
if (config.getInterpreter() == null || config.getInterpreter().trim().isEmpty()) {
|
if (config.getInterpreter() == null || config.getInterpreter().trim().isEmpty()) {
|
||||||
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR,
|
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR,
|
||||||
"Python interpreter path must be specified");
|
"Python interpreter path must be specified");
|
||||||
|
|||||||
@ -2,7 +2,7 @@ package com.qqchen.deploy.backend.workflow.engine.executor.node.script.command.i
|
|||||||
|
|
||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
|
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.executor.node.config.ScriptNodeConfig;
|
import com.qqchen.deploy.backend.workflow.engine.executor.node.config.ScriptNodeExecutorConfig;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.executor.node.script.command.ScriptCommand;
|
import com.qqchen.deploy.backend.workflow.engine.executor.node.script.command.ScriptCommand;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.executor.node.script.command.ScriptLanguageSupport;
|
import com.qqchen.deploy.backend.workflow.engine.executor.node.script.command.ScriptLanguageSupport;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.ScriptLanguageEnum;
|
import com.qqchen.deploy.backend.workflow.enums.ScriptLanguageEnum;
|
||||||
@ -17,7 +17,7 @@ import java.util.List;
|
|||||||
@ScriptLanguageSupport(ScriptLanguageEnum.SHELL)
|
@ScriptLanguageSupport(ScriptLanguageEnum.SHELL)
|
||||||
public class ShellScriptCommand implements ScriptCommand {
|
public class ShellScriptCommand implements ScriptCommand {
|
||||||
@Override
|
@Override
|
||||||
public List<String> buildCommand(ScriptNodeConfig config) {
|
public List<String> buildCommand(ScriptNodeExecutorConfig config) {
|
||||||
if (config.getInterpreter() == null || config.getInterpreter().trim().isEmpty()) {
|
if (config.getInterpreter() == null || config.getInterpreter().trim().isEmpty()) {
|
||||||
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR,
|
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR,
|
||||||
"Shell interpreter path must be specified");
|
"Shell interpreter path must be specified");
|
||||||
|
|||||||
@ -1,7 +1,12 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.entity;
|
package com.qqchen.deploy.backend.workflow.entity;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
|
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
|
||||||
import com.qqchen.deploy.backend.framework.domain.Entity;
|
import com.qqchen.deploy.backend.framework.domain.Entity;
|
||||||
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
|
import com.qqchen.deploy.backend.framework.utils.SpringUtils;
|
||||||
|
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
|
||||||
|
import com.qqchen.deploy.backend.workflow.engine.executor.node.config.NodeExecutorConfig;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
|
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
|
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
@ -10,6 +15,9 @@ import lombok.EqualsAndHashCode;
|
|||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@jakarta.persistence.Entity
|
@jakarta.persistence.Entity
|
||||||
@ -18,77 +26,84 @@ import java.time.LocalDateTime;
|
|||||||
public class NodeInstance extends Entity<Long> {
|
public class NodeInstance extends Entity<Long> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工作流实例
|
*
|
||||||
*/
|
*/
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "workflow_instance_id", nullable = false)
|
@JoinColumn(name = "workflow_instance_id", nullable = false)
|
||||||
private WorkflowInstance workflowInstance;
|
private WorkflowInstance workflowInstance;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 节点ID
|
* ID
|
||||||
*/
|
*/
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String nodeId;
|
private String nodeId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 节点类型
|
*
|
||||||
*/
|
*/
|
||||||
@Enumerated(EnumType.ORDINAL)
|
@Enumerated(EnumType.ORDINAL)
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private NodeTypeEnum nodeType;
|
private NodeTypeEnum nodeType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 节点名称
|
*
|
||||||
*/
|
*/
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 节点状态
|
*
|
||||||
*/
|
*/
|
||||||
@Enumerated(EnumType.ORDINAL)
|
@Enumerated(EnumType.ORDINAL)
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private NodeStatusEnum status;
|
private NodeStatusEnum status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始时间
|
*
|
||||||
*/
|
*/
|
||||||
private LocalDateTime startTime;
|
private LocalDateTime startTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 结束时间
|
*
|
||||||
*/
|
*/
|
||||||
private LocalDateTime endTime;
|
private LocalDateTime endTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 节点配置(JSON)
|
* (JSON)
|
||||||
*/
|
*/
|
||||||
@Column(columnDefinition = "TEXT")
|
@Column(columnDefinition = "TEXT")
|
||||||
private String config;
|
private String config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 输入参数(JSON)
|
* (JSON)
|
||||||
*/
|
*/
|
||||||
@Column(columnDefinition = "TEXT")
|
@Column(columnDefinition = "TEXT")
|
||||||
private String input;
|
private String input;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 输出结果(JSON)
|
* (JSON)
|
||||||
*/
|
*/
|
||||||
@Column(columnDefinition = "TEXT")
|
@Column(columnDefinition = "TEXT")
|
||||||
private String output;
|
private String output;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 错误信息
|
*
|
||||||
*/
|
*/
|
||||||
@Column(columnDefinition = "TEXT")
|
@Column(columnDefinition = "TEXT")
|
||||||
private String error;
|
private String error;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 前置节点ID
|
* ID
|
||||||
*/
|
*/
|
||||||
private String preNodeId;
|
private String preNodeId;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private NodeExecutorConfig configObject;
|
||||||
|
|
||||||
|
private static ObjectMapper getObjectMapper() {
|
||||||
|
return SpringUtils.getBean(ObjectMapper.class);
|
||||||
|
}
|
||||||
|
|
||||||
public String getConfig() {
|
public String getConfig() {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@ -96,4 +111,26 @@ public class NodeInstance extends Entity<Long> {
|
|||||||
public String getPreNodeId() {
|
public String getPreNodeId() {
|
||||||
return preNodeId;
|
return preNodeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T extends NodeExecutorConfig> T getConfigObject(Class<T> configClass) {
|
||||||
|
if (configObject == null && config != null) {
|
||||||
|
try {
|
||||||
|
configObject = getObjectMapper().readValue(this.config, configClass);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR,
|
||||||
|
String.format("Failed to parse config for node %s: %s", name, e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (T) configObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfigObject(NodeExecutorConfig config) {
|
||||||
|
this.configObject = config;
|
||||||
|
try {
|
||||||
|
this.config = getObjectMapper().writeValueAsString(config);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR,
|
||||||
|
String.format("Failed to serialize config for node %s: %s", name, e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -161,16 +161,6 @@ public class WorkflowInstance extends Entity<Long> {
|
|||||||
this.error = reason;
|
this.error = reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 完成工作流实例
|
|
||||||
*/
|
|
||||||
public void complete() {
|
|
||||||
if (!canComplete()) {
|
|
||||||
throw new IllegalStateException("Cannot complete workflow instance in " + status + " status");
|
|
||||||
}
|
|
||||||
this.status = WorkflowInstanceStatusEnum.COMPLETED;
|
|
||||||
this.endTime = LocalDateTime.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标记工作流实例为失败
|
* 标记工作流实例为失败
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user