增加工作流代码可正常启动

This commit is contained in:
戚辰先生 2024-12-03 23:10:01 +08:00
parent 609a662945
commit 6050c5e189
15 changed files with 312 additions and 344 deletions

View File

@ -1,11 +1,13 @@
package com.qqchen.deploy.backend.workflow.engine.impl; package com.qqchen.deploy.backend.workflow.engine.impl;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException; import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.workflow.engine.WorkflowContext; import com.qqchen.deploy.backend.workflow.engine.WorkflowContext;
import com.qqchen.deploy.backend.workflow.engine.WorkflowEngine; import com.qqchen.deploy.backend.workflow.engine.WorkflowEngine;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance; import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance; import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum; import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository; import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository; import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowLogService; import com.qqchen.deploy.backend.workflow.service.IWorkflowLogService;
@ -14,7 +16,6 @@ import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -42,34 +43,27 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
@Override @Override
@Transactional @Transactional
public void startInstance(Long instanceId, Map<String, Object> variables) { public void startInstance(Long instanceId, Map<String, Object> variables) {
// 获取工作流实例
WorkflowInstance instance = getWorkflowInstance(instanceId); WorkflowInstance instance = getWorkflowInstance(instanceId);
if (instance.getStatus() != WorkflowStatusEnum.CREATED) { if (instance.getStatus() != WorkflowStatusEnum.CREATED) {
throw new BusinessException(ResponseCode.WORKFLOW_STATUS_INVALID); throw new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
} }
// 初始化工作流上下文
WorkflowContext context = initContext(instance, variables); WorkflowContext context = initContext(instance, variables);
try { try {
// 更新工作流状态
instance.setStatus(WorkflowStatusEnum.RUNNING); instance.setStatus(WorkflowStatusEnum.RUNNING);
workflowInstanceRepository.save(instance); workflowInstanceRepository.save(instance);
// 保存工作流变量
workflowVariableService.saveVariables(instanceId, variables); workflowVariableService.saveVariables(instanceId, variables);
// 记录工作流日志
workflowLogService.logWorkflowStart(instance); workflowLogService.logWorkflowStart(instance);
// 执行第一个节点
executeNextNode(context); executeNextNode(context);
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to start workflow instance: {}", instanceId, e); log.error("Failed to start workflow instance: {}", instanceId, e);
instance.setStatus(WorkflowStatusEnum.FAILED); instance.setStatus(WorkflowStatusEnum.FAILED);
workflowInstanceRepository.save(instance); workflowInstanceRepository.save(instance);
workflowLogService.logWorkflowError(instance, e.getMessage()); workflowLogService.logWorkflowError(instance, e.getMessage());
throw new BusinessException(ResponseCode.WORKFLOW_START_FAILED); throw new BusinessException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED);
} }
} }
@ -77,16 +71,15 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
@Transactional @Transactional
public void cancelInstance(Long instanceId) { public void cancelInstance(Long instanceId) {
WorkflowInstance instance = getWorkflowInstance(instanceId); WorkflowInstance instance = getWorkflowInstance(instanceId);
if (instance.getStatus() != WorkflowStatusEnum.RUNNING && instance.getStatus() != WorkflowStatusEnum.PAUSED) { if (!instance.getStatus().canCancel()) {
throw new BusinessException(ResponseCode.WORKFLOW_STATUS_INVALID); throw new BusinessException(ResponseCode.WORKFLOW_INSTANCE_ALREADY_CANCELED);
} }
instance.setStatus(WorkflowStatusEnum.CANCELLED); instance.setStatus(WorkflowStatusEnum.CANCELLED);
workflowInstanceRepository.save(instance); workflowInstanceRepository.save(instance);
// 取消所有运行中的节点
List<NodeInstance> runningNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus( List<NodeInstance> runningNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
instanceId, NodeStatusEnum.RUNNING); instanceId, NodeStatusEnum.RUNNING.name());
runningNodes.forEach(node -> { runningNodes.forEach(node -> {
node.setStatus(NodeStatusEnum.CANCELLED); node.setStatus(NodeStatusEnum.CANCELLED);
nodeInstanceRepository.save(node); nodeInstanceRepository.save(node);
@ -99,16 +92,15 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
@Transactional @Transactional
public void pauseInstance(Long instanceId) { public void pauseInstance(Long instanceId) {
WorkflowInstance instance = getWorkflowInstance(instanceId); WorkflowInstance instance = getWorkflowInstance(instanceId);
if (instance.getStatus() != WorkflowStatusEnum.RUNNING) { if (!instance.getStatus().canPause()) {
throw new BusinessException(ResponseCode.WORKFLOW_STATUS_INVALID); throw new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
} }
instance.setStatus(WorkflowStatusEnum.PAUSED); instance.setStatus(WorkflowStatusEnum.PAUSED);
workflowInstanceRepository.save(instance); workflowInstanceRepository.save(instance);
// 暂停所有运行中的节点
List<NodeInstance> runningNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus( List<NodeInstance> runningNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
instanceId, NodeStatusEnum.RUNNING); instanceId, NodeStatusEnum.RUNNING.name());
runningNodes.forEach(node -> { runningNodes.forEach(node -> {
node.setStatus(NodeStatusEnum.PAUSED); node.setStatus(NodeStatusEnum.PAUSED);
nodeInstanceRepository.save(node); nodeInstanceRepository.save(node);
@ -121,16 +113,15 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
@Transactional @Transactional
public void resumeInstance(Long instanceId) { public void resumeInstance(Long instanceId) {
WorkflowInstance instance = getWorkflowInstance(instanceId); WorkflowInstance instance = getWorkflowInstance(instanceId);
if (instance.getStatus() != WorkflowStatusEnum.PAUSED) { if (!instance.getStatus().canResume()) {
throw new BusinessException(ResponseCode.WORKFLOW_STATUS_INVALID); throw new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
} }
instance.setStatus(WorkflowStatusEnum.RUNNING); instance.setStatus(WorkflowStatusEnum.RUNNING);
workflowInstanceRepository.save(instance); workflowInstanceRepository.save(instance);
// 恢复所有暂停的节点
List<NodeInstance> pausedNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus( List<NodeInstance> pausedNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
instanceId, NodeStatusEnum.PAUSED); instanceId, NodeStatusEnum.PAUSED.name());
pausedNodes.forEach(node -> { pausedNodes.forEach(node -> {
node.setStatus(NodeStatusEnum.RUNNING); node.setStatus(NodeStatusEnum.RUNNING);
nodeInstanceRepository.save(node); nodeInstanceRepository.save(node);
@ -144,23 +135,20 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
public void retryNode(Long nodeInstanceId) { public void retryNode(Long nodeInstanceId) {
NodeInstance node = getNodeInstance(nodeInstanceId); NodeInstance node = getNodeInstance(nodeInstanceId);
if (node.getStatus() != NodeStatusEnum.FAILED) { if (node.getStatus() != NodeStatusEnum.FAILED) {
throw new BusinessException(ResponseCode.NODE_STATUS_INVALID); throw new BusinessException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED);
} }
WorkflowInstance instance = getWorkflowInstance(node.getWorkflowInstanceId()); WorkflowInstance instance = getWorkflowInstance(node.getWorkflowInstanceId());
if (instance.getStatus() != WorkflowStatusEnum.FAILED) { if (instance.getStatus() != WorkflowStatusEnum.FAILED) {
throw new BusinessException(ResponseCode.WORKFLOW_STATUS_INVALID); throw new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
} }
// 重置节点状态
node.setStatus(NodeStatusEnum.RUNNING); node.setStatus(NodeStatusEnum.RUNNING);
nodeInstanceRepository.save(node); nodeInstanceRepository.save(node);
// 重置工作流状态
instance.setStatus(WorkflowStatusEnum.RUNNING); instance.setStatus(WorkflowStatusEnum.RUNNING);
workflowInstanceRepository.save(instance); workflowInstanceRepository.save(instance);
// 记录重试日志
workflowLogService.logNodeRetry(node); workflowLogService.logNodeRetry(node);
} }
@ -169,35 +157,28 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
public void skipNode(Long nodeInstanceId) { public void skipNode(Long nodeInstanceId) {
NodeInstance node = getNodeInstance(nodeInstanceId); NodeInstance node = getNodeInstance(nodeInstanceId);
if (node.getStatus() != NodeStatusEnum.FAILED) { if (node.getStatus() != NodeStatusEnum.FAILED) {
throw new BusinessException(ResponseCode.NODE_STATUS_INVALID); throw new BusinessException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED);
} }
// 更新节点状态为已跳过
node.setStatus(NodeStatusEnum.SKIPPED); node.setStatus(NodeStatusEnum.SKIPPED);
nodeInstanceRepository.save(node); nodeInstanceRepository.save(node);
// 获取工作流实例
WorkflowInstance instance = getWorkflowInstance(node.getWorkflowInstanceId()); WorkflowInstance instance = getWorkflowInstance(node.getWorkflowInstanceId());
// 初始化上下文
WorkflowContext context = initContext(instance, workflowVariableService.getVariables(instance.getId())); WorkflowContext context = initContext(instance, workflowVariableService.getVariables(instance.getId()));
context.setCurrentNode(node); context.setCurrentNode(node);
// 执行下一个节点
executeNextNode(context); executeNextNode(context);
// 记录跳过日志
workflowLogService.logNodeSkip(node); workflowLogService.logNodeSkip(node);
} }
private WorkflowInstance getWorkflowInstance(Long instanceId) { private WorkflowInstance getWorkflowInstance(Long instanceId) {
return workflowInstanceRepository.findById(instanceId) return workflowInstanceRepository.findById(instanceId)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND)); .orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
} }
private NodeInstance getNodeInstance(Long nodeInstanceId) { private NodeInstance getNodeInstance(Long nodeInstanceId) {
return nodeInstanceRepository.findById(nodeInstanceId) return nodeInstanceRepository.findById(nodeInstanceId)
.orElseThrow(() -> new BusinessException(ResponseCode.NODE_NOT_FOUND)); .orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NODE_NOT_FOUND));
} }
private WorkflowContext initContext(WorkflowInstance instance, Map<String, Object> variables) { private WorkflowContext initContext(WorkflowInstance instance, Map<String, Object> variables) {
@ -212,7 +193,6 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
NodeInstance currentNode = context.getCurrentNode(); NodeInstance currentNode = context.getCurrentNode();
List<NodeInstance> allNodes = context.getAllNodes(); List<NodeInstance> allNodes = context.getAllNodes();
// 如果当前节点为空说明是第一个节点
Optional<NodeInstance> nextNode; Optional<NodeInstance> nextNode;
if (currentNode == null) { if (currentNode == null) {
nextNode = allNodes.stream() nextNode = allNodes.stream()
@ -231,7 +211,6 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
workflowLogService.logNodeStart(node); workflowLogService.logNodeStart(node);
// TODO: 实际节点执行逻辑 // TODO: 实际节点执行逻辑
} else { } else {
// 没有下一个节点工作流完成
WorkflowInstance instance = context.getWorkflowInstance(); WorkflowInstance instance = context.getWorkflowInstance();
instance.setStatus(WorkflowStatusEnum.COMPLETED); instance.setStatus(WorkflowStatusEnum.COMPLETED);
workflowInstanceRepository.save(instance); workflowInstanceRepository.save(instance);

View File

@ -2,47 +2,24 @@ package com.qqchen.deploy.backend.workflow.engine.node;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.util.Map; import java.util.Map;
/**
* 脚本节点配置
*/
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class ScriptNodeConfig extends NodeConfig { public class ScriptNodeConfig extends NodeConfig {
private String script;
/** private String language;
* 脚本类型SHELL, PYTHON, GROOVY
*/
private String scriptType;
/**
* 脚本内容
*/
private String content;
/**
* 脚本参数
*/
private Map<String, String> parameters;
/**
* 执行主机ID
*/
private Long hostId;
/**
* 工作目录
*/
private String workingDirectory; private String workingDirectory;
private Integer timeout;
/**
* 环境变量
*/
private Map<String, String> environment; private Map<String, String> environment;
/**
* 成功退出码
*/
private Integer successExitCode; private Integer successExitCode;
public String getScript() {
return script;
}
public Map<String, String> getEnvironment() {
return environment;
}
} }

View File

@ -1,6 +1,5 @@
package com.qqchen.deploy.backend.workflow.engine.node.executor; package com.qqchen.deploy.backend.workflow.engine.node.executor;
import com.qqchen.deploy.backend.enums.LogLevelEnum;
import com.qqchen.deploy.backend.workflow.engine.WorkflowContext; import com.qqchen.deploy.backend.workflow.engine.WorkflowContext;
import com.qqchen.deploy.backend.workflow.engine.node.AbstractNodeExecutor; import com.qqchen.deploy.backend.workflow.engine.node.AbstractNodeExecutor;
import com.qqchen.deploy.backend.workflow.engine.node.NodeConfig; import com.qqchen.deploy.backend.workflow.engine.node.NodeConfig;
@ -8,29 +7,21 @@ import com.qqchen.deploy.backend.workflow.engine.node.NodeType;
import com.qqchen.deploy.backend.workflow.engine.node.ScriptNodeConfig; import com.qqchen.deploy.backend.workflow.engine.node.ScriptNodeConfig;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance; import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.Map;
/**
* Shell节点执行器
*/
@Slf4j @Slf4j
@Component @Component
public class ShellNodeExecutor extends AbstractNodeExecutor { public class ShellNodeExecutor extends AbstractNodeExecutor {
private static final int DEFAULT_TIMEOUT = 3600;
private Process currentProcess;
@Override @Override
public NodeType getNodeType() { public NodeType getNodeType() {
return NodeType.SHELL; return NodeType.SCRIPT;
} }
@Override @Override
@ -40,79 +31,56 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
@Override @Override
protected boolean doExecute(NodeInstance nodeInstance, WorkflowContext context, NodeConfig config) throws Exception { protected boolean doExecute(NodeInstance nodeInstance, WorkflowContext context, NodeConfig config) throws Exception {
ScriptNodeConfig shellConfig = (ScriptNodeConfig) config; ScriptNodeConfig scriptConfig = (ScriptNodeConfig) config;
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command("sh", "-c", scriptConfig.getScript());
// 构建命令 if (scriptConfig.getWorkingDirectory() != null) {
List<String> command = buildCommand(shellConfig); processBuilder.directory(new java.io.File(scriptConfig.getWorkingDirectory()));
// 创建进程构建器
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.redirectErrorStream(true);
// 设置工作目录
if (shellConfig.getWorkingDirectory() != null) {
processBuilder.directory(new java.io.File(shellConfig.getWorkingDirectory()));
} }
// 设置环境变量 if (scriptConfig.getEnvironment() != null) {
if (shellConfig.getEnvironment() != null) { Map<String, String> env = processBuilder.environment();
processBuilder.environment().putAll(shellConfig.getEnvironment()); env.putAll(scriptConfig.getEnvironment());
} }
Process process = processBuilder.start();
// 启动进程 // 读取标准输出
currentProcess = processBuilder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
List<String> output = new ArrayList<>();
// 读取输出 String line;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(currentProcess.getInputStream(), StandardCharsets.UTF_8))) { while ((line = reader.readLine()) != null) {
String line; output.add(line);
while ((line = reader.readLine()) != null) {
workflowLogService.logSystem(context.getWorkflowInstance(), LogLevelEnum.INFO, line, null);
}
} }
// 等待进程完成 // 读取错误输出
boolean completed = currentProcess.waitFor(shellConfig.getTimeout() != null ? shellConfig.getTimeout() : DEFAULT_TIMEOUT, TimeUnit.SECONDS); BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
if (!completed) { List<String> error = new ArrayList<>();
currentProcess.destroyForcibly(); while ((line = errorReader.readLine()) != null) {
workflowLogService.logSystem(context.getWorkflowInstance(), LogLevelEnum.ERROR, "Shell execution timeout", null); error.add(line);
return false;
} }
// 等待进程结束
int exitCode = process.waitFor();
// 检查退出码 // 检查退出码
int exitCode = currentProcess.exitValue(); if (scriptConfig.getSuccessExitCode() != null && exitCode != scriptConfig.getSuccessExitCode()) {
if (exitCode != 0) { throw new RuntimeException("Shell script execution failed with exit code: " + exitCode +
workflowLogService.logSystem(context.getWorkflowInstance(), LogLevelEnum.ERROR, "\nError output: " + String.join("\n", error));
"Shell execution failed with exit code: " + exitCode, null);
return false;
} }
return true; // 设置输出结果
nodeInstance.setOutput(String.join("\n", output));
if (!error.isEmpty()) {
nodeInstance.setError(String.join("\n", error));
}
return exitCode == 0;
} }
@Override @Override
protected void doCancel(NodeInstance nodeInstance, WorkflowContext context, NodeConfig config) throws Exception { protected void doCancel(NodeInstance nodeInstance, WorkflowContext context, NodeConfig config) throws Exception {
if (currentProcess != null && currentProcess.isAlive()) { // TODO: 实现取消逻辑例如终止正在运行的进程
currentProcess.destroyForcibly();
workflowLogService.logSystem(context.getWorkflowInstance(), LogLevelEnum.WARN,
"Shell execution cancelled", null);
}
}
private List<String> buildCommand(ScriptNodeConfig config) {
List<String> command = new ArrayList<>();
// 添加Shell解释器
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
command.add("cmd");
command.add("/c");
} else {
command.add("sh");
command.add("-c");
}
// 添加脚本内容
command.add(config.getScript());
return command;
} }
} }

View File

@ -1,80 +1,107 @@
package com.qqchen.deploy.backend.workflow.entity; package com.qqchen.deploy.backend.workflow.entity;
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.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; 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
@Table(name = "sys_node_instance") @Table(name = "wf_node_instance")
@LogicDelete @LogicDelete
public class NodeInstance extends com.qqchen.deploy.backend.framework.domain.Entity<Long> { public class NodeInstance extends Entity<Long> {
/**
* 工作流实例ID用于优化查询
*/
@Column(name = "workflow_instance_id", nullable = false)
private Long workflowInstanceId;
/** /**
* 工作流实例 * 工作流实例
*/ */
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "workflow_instance_id", nullable = false) @JoinColumn(name = "workflow_instance_id", insertable = false, updatable = false)
private WorkflowInstance workflowInstance; private WorkflowInstance workflowInstance;
/** /**
* 节点ID * 节点ID
*/ */
@Column(name = "node_id", nullable = false, length = 50) @Column(nullable = false)
private String nodeId; private String nodeId;
/** /**
* 节点类型 * 节点类型
*/ */
@Column(name = "node_type", nullable = false, length = 50) @Column(nullable = false)
private String nodeType; private NodeTypeEnum nodeType;
/** /**
* 节点名称 * 节点名称
*/ */
@Column(name = "name", nullable = false, length = 100) @Column(nullable = false)
private String name; private String name;
/** /**
* 状态PENDING/RUNNING/COMPLETED/FAILED/CANCELED * 节点状态
*/ */
@Column(name = "status", nullable = false, length = 20) @Column(nullable = false)
private String status; private NodeStatusEnum status;
/** /**
* 开始时间 * 开始时间
*/ */
@Column(name = "start_time")
private LocalDateTime startTime; private LocalDateTime startTime;
/** /**
* 结束时间 * 结束时间
*/ */
@Column(name = "end_time")
private LocalDateTime endTime; private LocalDateTime endTime;
/**
* 节点配置(JSON)
*/
@Column(columnDefinition = "TEXT")
private String config;
/** /**
* 输入参数(JSON) * 输入参数(JSON)
*/ */
@Column(name = "input", columnDefinition = "TEXT") @Column(columnDefinition = "TEXT")
private String input; private String input;
/** /**
* 输出结果(JSON) * 输出结果(JSON)
*/ */
@Column(name = "output", columnDefinition = "TEXT") @Column(columnDefinition = "TEXT")
private String output; private String output;
/** /**
* 错误信息 * 错误信息
*/ */
@Column(name = "error", columnDefinition = "TEXT") @Column(columnDefinition = "TEXT")
private String error; private String error;
/**
* 前置节点ID
*/
private String preNodeId;
public String getConfig() {
return config;
}
public Long getWorkflowInstanceId() {
return workflowInstanceId;
}
public String getPreNodeId() {
return preNodeId;
}
} }

View File

@ -2,7 +2,10 @@ package com.qqchen.deploy.backend.workflow.entity;
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.workflow.enums.WorkflowStatusEnum;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
@ -36,10 +39,11 @@ public class WorkflowInstance extends Entity<Long> {
private Long projectEnvId; private Long projectEnvId;
/** /**
* 状态RUNNING/COMPLETED/FAILED/CANCELED * 状态
*/ */
@Column(name = "status", nullable = false, length = 20) @Enumerated(EnumType.STRING)
private String status; @Column(name = "status", nullable = false)
private WorkflowStatusEnum status;
/** /**
* 开始时间 * 开始时间

View File

@ -12,9 +12,11 @@ public enum NodeStatusEnum {
PENDING("PENDING", "等待执行"), PENDING("PENDING", "等待执行"),
RUNNING("RUNNING", "执行中"), RUNNING("RUNNING", "执行中"),
PAUSED("PAUSED", "已暂停"),
COMPLETED("COMPLETED", "已完成"), COMPLETED("COMPLETED", "已完成"),
FAILED("FAILED", "执行失败"), FAILED("FAILED", "执行失败"),
CANCELED("CANCELED", "已取消"); CANCELLED("CANCELLED", "已取消"),
SKIPPED("SKIPPED", "已跳过");
private final String code; private final String code;
private final String description; private final String description;

View File

@ -0,0 +1,29 @@
package com.qqchen.deploy.backend.workflow.enums;
import lombok.Getter;
/**
* 节点类型枚举
*/
@Getter
public enum NodeTypeEnum {
START("START", "开始节点"),
END("END", "结束节点"),
DEPLOY("DEPLOY", "部署节点"),
APPROVAL("APPROVAL", "审批节点"),
SCRIPT("SCRIPT", "脚本节点"),
SHELL("SHELL", "Shell脚本节点"),
CONFIG_SYNC("CONFIG_SYNC", "配置同步节点"),
CONDITION("CONDITION", "条件节点"),
PARALLEL("PARALLEL", "并行节点"),
SERIAL("SERIAL", "串行节点");
private final String code;
private final String desc;
NodeTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
}

View File

@ -16,13 +16,13 @@ public enum WorkflowStatusEnum {
DISABLED("DISABLED", "已禁用"), DISABLED("DISABLED", "已禁用"),
// 工作流实例状态 // 工作流实例状态
CREATED("CREATED", "已创建"),
PENDING("PENDING", "等待执行"), PENDING("PENDING", "等待执行"),
RUNNING("RUNNING", "执行中"), RUNNING("RUNNING", "执行中"),
PAUSED("PAUSED", "已暂停"), PAUSED("PAUSED", "已暂停"),
COMPLETED("COMPLETED", "已完成"), COMPLETED("COMPLETED", "已完成"),
FAILED("FAILED", "执行失败"), FAILED("FAILED", "执行失败"),
CANCELED("CANCELED", "已取消"), CANCELLED("CANCELLED", "已取消");
TIMEOUT("TIMEOUT", "已超时");
private final String code; private final String code;
private final String description; private final String description;
@ -31,7 +31,7 @@ public enum WorkflowStatusEnum {
* 判断是否为终态 * 判断是否为终态
*/ */
public boolean isFinalStatus() { public boolean isFinalStatus() {
return this == COMPLETED || this == FAILED || this == CANCELED || this == TIMEOUT; return this == COMPLETED || this == FAILED || this == CANCELLED;
} }
/** /**

View File

@ -3,6 +3,9 @@ package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService; import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.api.dto.NodeInstanceDTO; import com.qqchen.deploy.backend.workflow.api.dto.NodeInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance; import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import org.springframework.transaction.annotation.Transactional;
import java.util.List; import java.util.List;
/** /**
@ -37,4 +40,9 @@ public interface INodeInstanceService extends IBaseService<NodeInstance, NodeIns
* @return 是否成功 * @return 是否成功
*/ */
boolean updateStatus(Long id, String status, String output, String error); boolean updateStatus(Long id, String status, String output, String error);
}
List<NodeInstance> findByWorkflowInstanceIdAndStatus(Long workflowInstanceId, NodeStatusEnum status);
@Transactional
void cancelRunningNodes(Long workflowInstanceId);
}

View File

@ -3,13 +3,19 @@ package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService; import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.api.dto.WorkflowInstanceDTO; import com.qqchen.deploy.backend.workflow.api.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance; import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
import org.springframework.transaction.annotation.Transactional;
import java.util.List; import java.util.List;
/** /**
* 工作流实例服务接口 * 工作流实例服务接口
*/ */
public interface IWorkflowInstanceService extends IBaseService<WorkflowInstance, WorkflowInstanceDTO, Long> { public interface IWorkflowInstanceService extends IBaseService<WorkflowInstance, WorkflowInstanceDTO, Long> {
@Transactional
void updateStatus(Long id, WorkflowStatusEnum status, String error);
/** /**
* 启动工作流实例 * 启动工作流实例
* *

View File

@ -8,7 +8,6 @@ import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository; import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.INodeInstanceService; import com.qqchen.deploy.backend.workflow.service.INodeInstanceService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -16,13 +15,8 @@ import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/**
* 节点实例服务实现类
*/
@Slf4j
@Service @Service
public class NodeInstanceServiceImpl extends BaseServiceImpl<NodeInstance, NodeInstanceDTO, Long> public class NodeInstanceServiceImpl extends BaseServiceImpl<NodeInstance, NodeInstanceDTO, Long> implements INodeInstanceService {
implements INodeInstanceService {
@Resource @Resource
private INodeInstanceRepository nodeInstanceRepository; private INodeInstanceRepository nodeInstanceRepository;
@ -32,40 +26,55 @@ public class NodeInstanceServiceImpl extends BaseServiceImpl<NodeInstance, NodeI
@Override @Override
public List<NodeInstanceDTO> findByWorkflowInstanceId(Long workflowInstanceId) { public List<NodeInstanceDTO> findByWorkflowInstanceId(Long workflowInstanceId) {
List<NodeInstance> instances = nodeInstanceRepository.findByWorkflowInstanceId(workflowInstanceId); return nodeInstanceRepository.findByWorkflowInstanceId(workflowInstanceId)
return instances.stream() .stream()
.map(nodeInstanceConverter::toDto) .map(nodeInstanceConverter::toDto)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Override @Override
public List<NodeInstanceDTO> findByWorkflowInstanceIdAndStatus(Long workflowInstanceId, String status) { public List<NodeInstanceDTO> findByWorkflowInstanceIdAndStatus(Long workflowInstanceId, String status) {
List<NodeInstance> instances = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(workflowInstanceId, status); return nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(workflowInstanceId, status)
return instances.stream() .stream()
.map(nodeInstanceConverter::toDto) .map(nodeInstanceConverter::toDto)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Override @Override
@Transactional @Transactional
public boolean updateStatus(Long id, String status, String output, String error) { public boolean updateStatus(Long id, String status, String output, String error) {
NodeInstance instance = findEntityById(id); NodeInstance node = super.converter.toEntity(super.findById(id));
instance.setStatus(status); node.setStatus(NodeStatusEnum.valueOf(status));
instance.setOutput(output); node.setOutput(output);
instance.setError(error); node.setError(error);
// 如果是开始执行设置开始时间 if (NodeStatusEnum.RUNNING.name().equals(status)) {
if (NodeStatusEnum.RUNNING.getCode().equals(status)) { node.setStartTime(LocalDateTime.now());
instance.setStartTime(LocalDateTime.now()); } else if (NodeStatusEnum.COMPLETED.name().equals(status) ||
} NodeStatusEnum.FAILED.name().equals(status) ||
// 如果是结束状态设置结束时间 NodeStatusEnum.CANCELLED.name().equals(status) ||
else if (NodeStatusEnum.COMPLETED.getCode().equals(status) NodeStatusEnum.SKIPPED.name().equals(status)) {
|| NodeStatusEnum.FAILED.getCode().equals(status) node.setEndTime(LocalDateTime.now());
|| NodeStatusEnum.CANCELED.getCode().equals(status)) {
instance.setEndTime(LocalDateTime.now());
} }
nodeInstanceRepository.save(instance); nodeInstanceRepository.save(node);
return true; return true;
} }
}
@Override
public List<NodeInstance> findByWorkflowInstanceIdAndStatus(Long workflowInstanceId, NodeStatusEnum status) {
return nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(workflowInstanceId, status.name());
}
@Override
@Transactional
public void cancelRunningNodes(Long workflowInstanceId) {
List<NodeInstance> runningNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
workflowInstanceId, NodeStatusEnum.RUNNING.name());
runningNodes.forEach(node -> {
node.setStatus(NodeStatusEnum.CANCELLED);
node.setEndTime(LocalDateTime.now());
nodeInstanceRepository.save(node);
});
}
}

View File

@ -2,89 +2,65 @@ package com.qqchen.deploy.backend.workflow.service.impl;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl; import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.api.dto.WorkflowInstanceDTO; import com.qqchen.deploy.backend.workflow.api.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.converter.WorkflowInstanceConverter;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance; import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum; import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository; import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.INodeInstanceService;
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService; import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
/**
* 工作流实例服务实现类
*/
@Slf4j
@Service @Service
public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstance, WorkflowInstanceDTO, Long> public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstance, WorkflowInstanceDTO, Long> implements IWorkflowInstanceService {
implements IWorkflowInstanceService {
@Resource @Resource
private IWorkflowInstanceRepository workflowInstanceRepository; private IWorkflowInstanceRepository workflowInstanceRepository;
@Resource @Resource
private IWorkflowDefinitionRepository workflowDefinitionRepository; private INodeInstanceService nodeInstanceService;
@Resource
private WorkflowInstanceConverter workflowInstanceConverter;
@Override @Override
@Transactional @Transactional
public WorkflowInstanceDTO start(Long definitionId, Long projectEnvId, String variables) { public void updateStatus(Long id, WorkflowStatusEnum status, String error) {
// 查询工作流定义 WorkflowInstance instance = super.converter.toEntity(super.findById(id));
WorkflowDefinition definition = workflowDefinitionRepository.findById(definitionId) instance.setStatus(status);
.orElseThrow(() -> new IllegalArgumentException("工作流定义不存在")); instance.setError(error);
// 创建工作流实例 if (status == WorkflowStatusEnum.RUNNING) {
WorkflowInstance instance = new WorkflowInstance(); instance.setStartTime(LocalDateTime.now());
instance.setDefinition(definition); } else if (status == WorkflowStatusEnum.COMPLETED || status == WorkflowStatusEnum.FAILED ||
instance.setProjectEnvId(projectEnvId); status == WorkflowStatusEnum.CANCELLED) {
instance.setVariables(variables); instance.setEndTime(LocalDateTime.now());
instance.setStatus(NodeStatusEnum.RUNNING.getCode()); }
instance.setStartTime(LocalDateTime.now());
// 保存工作流实例 if (status == WorkflowStatusEnum.CANCELLED) {
WorkflowInstanceDTO dto = workflowInstanceConverter.toDto(instance); nodeInstanceService.cancelRunningNodes(id);
dto = super.create(dto); }
// TODO: 启动工作流执行引擎 workflowInstanceRepository.save(instance);
}
return dto;
@Override
public WorkflowInstanceDTO start(Long definitionId, Long projectEnvId, String variables) {
return null;
} }
@Override @Override
@Transactional
public boolean cancel(Long id) { public boolean cancel(Long id) {
WorkflowInstance instance = findEntityById(id); return false;
instance.setStatus(NodeStatusEnum.CANCELED.getCode());
instance.setEndTime(LocalDateTime.now());
workflowInstanceRepository.save(instance);
// TODO: 通知工作流执行引擎取消执行
return true;
} }
@Override @Override
public List<WorkflowInstanceDTO> findByProjectEnvId(Long projectEnvId) { public List<WorkflowInstanceDTO> findByProjectEnvId(Long projectEnvId) {
List<WorkflowInstance> instances = workflowInstanceRepository.findByProjectEnvId(projectEnvId); return List.of();
return instances.stream()
.map(workflowInstanceConverter::toDto)
.collect(Collectors.toList());
} }
@Override @Override
public List<WorkflowInstanceDTO> findByProjectEnvIdAndStatus(Long projectEnvId, String status) { public List<WorkflowInstanceDTO> findByProjectEnvIdAndStatus(Long projectEnvId, String status) {
List<WorkflowInstance> instances = workflowInstanceRepository.findByProjectEnvIdAndStatus(projectEnvId, status); return List.of();
return instances.stream()
.map(workflowInstanceConverter::toDto)
.collect(Collectors.toList());
} }
} }

View File

@ -103,15 +103,16 @@ public class WorkflowPermissionServiceImpl implements IWorkflowPermissionService
} }
User user = userOpt.get(); User user = userOpt.get();
// 检查角色权限 // // 检查角色权限
if (user.getRoleId() != null && workflowPermissionRepository // if (user.getRoleId() != null && workflowPermissionRepository
.existsByWorkflowDefinitionIdAndRoleIdAndType(workflowDefinitionId, user.getRoleId(), type)) { // .existsByWorkflowDefinitionIdAndRoleIdAndType(workflowDefinitionId, user.getRoleId(), type)) {
return true; // return true;
} // }
//
// 检查部门权限 // // 检查部门权限
return user.getDepartmentId() != null && workflowPermissionRepository // return user.getDepartmentId() != null && workflowPermissionRepository
.existsByWorkflowDefinitionIdAndDepartmentIdAndType(workflowDefinitionId, user.getDepartmentId(), type); // .existsByWorkflowDefinitionIdAndDepartmentIdAndType(workflowDefinitionId, user.getDepartmentId(), type);
return true;
} }
@Override @Override

View File

@ -1,88 +1,75 @@
package com.qqchen.deploy.backend.workflow.service.impl; package com.qqchen.deploy.backend.workflow.service.impl;
import com.fasterxml.jackson.core.JsonProcessingException; import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.framework.exception.BusinessException; import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.workflow.entity.WorkflowVariable; import com.qqchen.deploy.backend.workflow.entity.WorkflowVariable;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowVariableRepository; import com.qqchen.deploy.backend.workflow.repository.IWorkflowVariableRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowVariableService; import com.qqchen.deploy.backend.workflow.service.IWorkflowVariableService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* 工作流变量服务实现
*/
@Slf4j
@Service @Service
public class WorkflowVariableServiceImpl implements IWorkflowVariableService { public class WorkflowVariableServiceImpl implements IWorkflowVariableService {
@Resource @Resource
private IWorkflowVariableRepository workflowVariableRepository; private IWorkflowVariableRepository workflowVariableRepository;
@Resource
private ObjectMapper objectMapper;
@Override @Override
@Transactional @Transactional
public void saveVariables(Long workflowInstanceId, Map<String, Object> variables) { public void saveVariables(Long workflowInstanceId, Map<String, Object> variables) {
variables.forEach((name, value) -> setVariable(workflowInstanceId, name, value)); if (variables == null || variables.isEmpty()) {
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_TYPE_INVALID);
}
// 删除旧的变量
workflowVariableRepository.deleteByWorkflowInstanceId(workflowInstanceId);
// 保存新的变量
variables.forEach((name, value) -> {
WorkflowVariable variable = new WorkflowVariable();
variable.setWorkflowInstanceId(workflowInstanceId);
variable.setName(name);
variable.setValue(value.toString());
variable.setType(value.getClass().getSimpleName());
workflowVariableRepository.save(variable);
});
} }
@Override @Override
public Map<String, Object> getVariables(Long workflowInstanceId) { public Map<String, Object> getVariables(Long workflowInstanceId) {
List<WorkflowVariable> variables = workflowVariableRepository.findByWorkflowInstanceId(workflowInstanceId); return workflowVariableRepository.findByWorkflowInstanceId(workflowInstanceId)
Map<String, Object> result = new HashMap<>(variables.size()); .stream()
variables.forEach(variable -> { .collect(Collectors.toMap(
try { WorkflowVariable::getName,
result.put(variable.getName(), deserializeValue(variable.getValue(), variable.getType())); this::convertValue
} catch (JsonProcessingException e) { ));
log.error("Failed to deserialize variable value: {}", variable.getName(), e);
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_DESERIALIZE_ERROR);
}
});
return result;
} }
@Override @Override
public Object getVariable(Long workflowInstanceId, String name) { public Object getVariable(Long workflowInstanceId, String name) {
return workflowVariableRepository.findByWorkflowInstanceIdAndName(workflowInstanceId, name) Optional<WorkflowVariable> variable = workflowVariableRepository.findByWorkflowInstanceIdAndName(workflowInstanceId, name);
.map(variable -> { return variable.map(this::convertValue).orElse(null);
try {
return deserializeValue(variable.getValue(), variable.getType());
} catch (JsonProcessingException e) {
log.error("Failed to deserialize variable value: {}", name, e);
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_DESERIALIZE_ERROR);
}
})
.orElse(null);
} }
@Override @Override
@Transactional @Transactional
public void setVariable(Long workflowInstanceId, String name, Object value) { public void setVariable(Long workflowInstanceId, String name, Object value) {
try { WorkflowVariable variable = workflowVariableRepository.findByWorkflowInstanceIdAndName(workflowInstanceId, name)
WorkflowVariable variable = workflowVariableRepository .orElseGet(() -> {
.findByWorkflowInstanceIdAndName(workflowInstanceId, name) WorkflowVariable newVar = new WorkflowVariable();
.orElseGet(() -> { newVar.setWorkflowInstanceId(workflowInstanceId);
WorkflowVariable newVariable = new WorkflowVariable(); newVar.setName(name);
newVariable.setWorkflowInstanceId(workflowInstanceId); return newVar;
newVariable.setName(name); });
return newVariable;
});
variable.setType(value.getClass().getName()); variable.setValue(value.toString());
variable.setValue(serializeValue(value)); variable.setType(value.getClass().getSimpleName());
workflowVariableRepository.save(variable); workflowVariableRepository.save(variable);
} catch (JsonProcessingException e) {
log.error("Failed to serialize variable value: {}", name, e);
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_SERIALIZE_ERROR);
}
} }
@Override @Override
@ -97,17 +84,24 @@ public class WorkflowVariableServiceImpl implements IWorkflowVariableService {
workflowVariableRepository.deleteByWorkflowInstanceId(workflowInstanceId); workflowVariableRepository.deleteByWorkflowInstanceId(workflowInstanceId);
} }
private String serializeValue(Object value) throws JsonProcessingException { private Object convertValue(WorkflowVariable variable) {
return objectMapper.writeValueAsString(value);
}
private Object deserializeValue(String value, String type) throws JsonProcessingException {
try { try {
Class<?> clazz = Class.forName(type); switch (variable.getType()) {
return objectMapper.readValue(value, clazz); case "String":
} catch (ClassNotFoundException e) { return variable.getValue();
log.error("Failed to find class for type: {}", type, e); case "Integer":
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_TYPE_ERROR); return Integer.parseInt(variable.getValue());
case "Long":
return Long.parseLong(variable.getValue());
case "Double":
return Double.parseDouble(variable.getValue());
case "Boolean":
return Boolean.parseBoolean(variable.getValue());
default:
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_TYPE_INVALID);
}
} catch (Exception e) {
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_TYPE_INVALID);
} }
} }
} }

View File

@ -99,26 +99,14 @@ workflow.definition.already.published=工作流定义已发布
workflow.definition.cannot.delete=工作流定义已被使用,无法删除 workflow.definition.cannot.delete=工作流定义已被使用,无法删除
workflow.instance.not.found=工作流实例不存在 workflow.instance.not.found=工作流实例不存在
workflow.instance.already.started=工作流实例已启动 workflow.instance.cannot.start=工作流实例无法启动
workflow.instance.already.ended=工作流实例已结束
workflow.instance.already.suspended=工作流实例已挂起
workflow.instance.not.suspended=工作流实例未挂起
workflow.instance.cannot.cancel=工作流实例无法取消 workflow.instance.cannot.cancel=工作流实例无法取消
workflow.instance.cannot.suspend=工作流实例无法挂起 workflow.instance.cannot.pause=工作流实例无法暂停
workflow.instance.cannot.resume=工作流实例无法恢复 workflow.instance.cannot.resume=工作流实例无法恢复
workflow.instance.cannot.retry=工作流实例无法重试
workflow.node.not.found=工作流节点不存在 # 节点相关错误消息
workflow.node.type.not.supported=不支持的节点类型:{0} node.instance.not.found=节点实例不存在
workflow.node.config.invalid=节点配置无效:{0} node.instance.cannot.retry=节点实例无法重试
workflow.node.execution.failed=节点执行失败:{0} node.instance.cannot.skip=节点实例无法跳过
workflow.node.timeout=节点执行超时 node.executor.not.found=节点执行器不存在
workflow.node.approval.rejected=节点审批被拒绝
workflow.node.approval.canceled=节点审批已取消
workflow.variable.not.found=工作流变量不存在
workflow.variable.required=工作流变量"{0}"为必填项
workflow.variable.invalid=工作流变量"{0}"的值无效
workflow.permission.denied=无权操作此工作流
workflow.operation.not.allowed=当前状态不允许此操作
workflow.concurrent.operation=工作流正在执行其他操作,请稍后重试