diff --git a/backend/pom.xml b/backend/pom.xml
index 0c60cf85..c3af167a 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -196,6 +196,11 @@
commons-exec
1.3
+
+ com.github.ben-manes.caffeine
+ caffeine
+ 3.1.8
+
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/enums/ResponseCode.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/enums/ResponseCode.java
index c38e88ec..1f96cfa6 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/framework/enums/ResponseCode.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/enums/ResponseCode.java
@@ -137,7 +137,18 @@ public enum ResponseCode {
WORKFLOW_NODE_TYPE_DISABLED(2201, "workflow.node.type.disabled"),
WORKFLOW_NODE_TYPE_CODE_EXISTS(2202, "workflow.node.type.code.exists"),
WORKFLOW_NODE_TYPE_INVALID_CATEGORY(2203, "workflow.node.type.invalid.category"),
- WORKFLOW_NODE_TYPE_INVALID_EXECUTOR(2204, "workflow.node.type.invalid.executor");
+ WORKFLOW_NODE_TYPE_INVALID_EXECUTOR(2204, "workflow.node.type.invalid.executor"),
+ WORKFLOW_NODE_EXECUTOR_NOT_FOUND(2205, "workflow.node.executor.not.found"),
+
+ /**
+ * 工作流变量序列化错误
+ */
+ WORKFLOW_VARIABLE_SERIALIZE_ERROR(50301, "workflow.variable.serialize.error"),
+
+ /**
+ * 工作流变量反序列化错误
+ */
+ WORKFLOW_VARIABLE_DESERIALIZE_ERROR(50302, "workflow.variable.deserialize.error");
private final int code;
private final String messageKey; // 国际化消息key
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/api/WorkflowLogApiController.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/api/WorkflowLogApiController.java
index 6a949d37..79d4d6b4 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/api/WorkflowLogApiController.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/api/WorkflowLogApiController.java
@@ -45,14 +45,14 @@ public class WorkflowLogApiController extends BaseController log(
+ public Response recordLog(
@Parameter(description = "工作流实例ID", required = true) @RequestParam Long workflowInstanceId,
@Parameter(description = "节点ID") @RequestParam(required = false) String nodeId,
@Parameter(description = "日志内容", required = true) @RequestParam String message,
@Parameter(description = "日志级别", required = true) @RequestParam LogLevelEnum level,
@Parameter(description = "详细信息") @RequestParam(required = false) String detail
) {
- workflowLogService.log(workflowInstanceId, nodeId, message, level, detail);
+ workflowLogService.recordLog(workflowInstanceId, nodeId, message, level, detail);
return Response.success();
}
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/converter/NodeInstanceConverter.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/converter/NodeInstanceConverter.java
index 52dd9f63..243302f7 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/converter/NodeInstanceConverter.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/converter/NodeInstanceConverter.java
@@ -13,7 +13,6 @@ import org.mapstruct.Mapping;
public interface NodeInstanceConverter extends BaseConverter {
@Override
- @Mapping(target = "workflowInstanceId", source = "workflowInstanceId")
@Mapping(target = "nodeId", source = "nodeId")
@Mapping(target = "nodeType", source = "nodeType")
@Mapping(target = "name", source = "name")
@@ -28,7 +27,7 @@ public interface NodeInstanceConverter extends BaseConverter {
-
- @Override
- @Mapping(target = "nodes", source = "nodes")
- WorkflowDefinitionDTO toDto(WorkflowDefinition entity);
-
- @Override
- @Mapping(target = "nodes", source = "nodes")
- WorkflowDefinition toEntity(WorkflowDefinitionDTO dto);
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/WorkflowDefinitionDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/WorkflowDefinitionDTO.java
index fb957235..ab2e537d 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/WorkflowDefinitionDTO.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/WorkflowDefinitionDTO.java
@@ -7,8 +7,6 @@ import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
-import java.util.List;
-
/**
* 工作流定义DTO
*/
@@ -36,37 +34,30 @@ public class WorkflowDefinitionDTO extends BaseDTO {
/**
* 工作流状态
*/
- @NotNull(message = "工作流状态不能为空")
private WorkflowDefinitionStatusEnum status;
/**
* 版本号
*/
- @NotNull(message = "版本号不能为空")
private Integer version;
/**
- * 节点定义列表
- */
- private List nodes;
-
- /**
- * 节点配置
+ * 节点配置(JSON)
*/
private String nodeConfig;
/**
- * 流转配置
+ * 流转配置(JSON)
*/
private String transitionConfig;
/**
- * 表单定义
+ * 表单定义(JSON)
*/
private String formDefinition;
/**
- * 图形信息
+ * 图形信息(JSON)
*/
private String graphDefinition;
@@ -80,4 +71,4 @@ public class WorkflowDefinitionDTO extends BaseDTO {
* 备注
*/
private String remark;
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/WorkflowLogDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/WorkflowLogDTO.java
index e28effc3..be4b9454 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/WorkflowLogDTO.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/WorkflowLogDTO.java
@@ -1,41 +1,46 @@
package com.qqchen.deploy.backend.workflow.dto;
-import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
+import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
+import java.time.LocalDateTime;
+
+/**
+ * 工作流日志DTO
+ */
@Data
@EqualsAndHashCode(callSuper = true)
public class WorkflowLogDTO extends BaseDTO {
- /**
- * 工作流实例ID
- */
- @NotNull(message = "工作流实例ID不能为空")
- private Long workflowInstanceId;
-
/**
* 节点ID
*/
private String nodeId;
- /**
- * 日志内容
- */
- @NotBlank(message = "日志内容不能为空")
- private String message;
-
/**
* 日志级别
*/
- @NotNull(message = "日志级别不能为空")
private LogLevelEnum level;
+ /**
+ * 日志内容
+ */
+ private String message;
+
/**
* 详细信息
*/
private String detail;
-}
\ No newline at end of file
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 创建人
+ */
+ private String createBy;
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/DefaultWorkflowEngine.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/DefaultWorkflowEngine.java
index 022aae3b..e9414b61 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/DefaultWorkflowEngine.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/DefaultWorkflowEngine.java
@@ -2,7 +2,7 @@ package com.qqchen.deploy.backend.workflow.engine;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.workflow.engine.context.DefaultWorkflowContext;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.engine.executor.NodeExecutor;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
@@ -15,6 +15,7 @@ import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
+import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -41,7 +42,7 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
private Map nodeExecutors;
@Resource
- private DefaultWorkflowContext.Factory workflowContextFactory;
+ private WorkflowVariableOperations variableOperations;
@Override
@Transactional
@@ -59,19 +60,19 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
// 2. 创建工作流实例
WorkflowInstance instance = new WorkflowInstance();
- instance.setDefinition(definition);
+ instance.setWorkflowDefinition(definition);
+ instance.setBusinessKey(businessKey);
// 设置工作流实例初始状态为 PENDING
instance.setStatus(WorkflowInstanceStatusEnum.PENDING);
workflowInstanceRepository.save(instance);
- // 3. 创建工作流上下文
- WorkflowContext context = workflowContextFactory.create(instance);
- if (variables != null) {
- variables.forEach((key, value) -> context.setVariable(key, value));
+ // 3. 设置工作流变量
+ if (variables != null && !variables.isEmpty()) {
+ variableOperations.setVariables(instance.getId(), variables);
}
// 4. 创建开始节点实例并启动工作流
- NodeInstance startNode = createStartNode(definition, instance.getId());
+ NodeInstance startNode = createStartNode(definition, instance);
instance.start();
workflowInstanceRepository.save(instance);
executeNode(startNode.getId());
@@ -90,43 +91,33 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
}
- try {
- // 1. 获取节点执行器
- NodeExecutor executor = nodeExecutors.get(nodeInstance.getNodeType());
- if (executor == null) {
- throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_TYPE_NOT_SUPPORTED);
- }
+ // 获取节点执行器
+ NodeExecutor executor = nodeExecutors.get(nodeInstance.getNodeType());
+ if (executor == null) {
+ throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTOR_NOT_FOUND);
+ }
- // 2. 执行节点
- WorkflowContext context = workflowContextFactory.create(instance);
- executor.execute(nodeInstance, context);
+ // 创建工作流上下文
+ WorkflowContextOperations context = DefaultWorkflowContext.builder()
+ .workflowInstance(instance)
+ .variableOperations(variableOperations)
+ .build();
+
+ // 执行节点
+ executor.execute(nodeInstance, context);
- // 3. 更新节点状态
- nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
- nodeInstance.setEndTime(LocalDateTime.now());
- nodeInstanceRepository.save(nodeInstance);
+ // 3. 更新节点状态
+ nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
+ nodeInstance.setEndTime(LocalDateTime.now());
+ nodeInstanceRepository.save(nodeInstance);
- // 4. 检查是否所有节点都已完成
- List uncompletedNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatusNot(
- instance.getId(), NodeStatusEnum.COMPLETED);
-
- if (uncompletedNodes.isEmpty()) {
- instance.complete();
- workflowInstanceRepository.save(instance);
- }
-
- } catch (Exception e) {
- // 更新节点状态为失败
- nodeInstance.setStatus(NodeStatusEnum.FAILED);
- nodeInstance.setEndTime(LocalDateTime.now());
- nodeInstance.setError(e.getMessage());
- nodeInstanceRepository.save(nodeInstance);
-
- // 更新工作流实例状态
- instance.updateError(e.getMessage());
+ // 4. 检查是否所有节点都已完成
+ List uncompletedNodes = nodeInstanceRepository.findByWorkflowInstanceAndStatusNot(
+ instance, NodeStatusEnum.COMPLETED);
+
+ if (uncompletedNodes.isEmpty()) {
+ instance.complete();
workflowInstanceRepository.save(instance);
-
- throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED, e);
}
}
@@ -136,15 +127,11 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
NodeInstance nodeInstance = nodeInstanceRepository.findById(nodeInstanceId)
.orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_NOT_FOUND));
- if (nodeInstance.getStatus() != NodeStatusEnum.RUNNING) {
- throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED,
- "Node is not in running status");
- }
-
WorkflowInstance instance = nodeInstance.getWorkflowInstance();
- WorkflowContext context = workflowContextFactory.create(instance);
- if (variables != null) {
- variables.forEach((key, value) -> context.setVariable(key, value));
+
+ // 设置节点输出变量
+ if (variables != null && !variables.isEmpty()) {
+ variableOperations.setVariables(instance.getId(), variables);
}
nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
@@ -157,23 +144,12 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
public void terminateWorkflow(Long instanceId, String reason) {
WorkflowInstance instance = workflowInstanceRepository.findById(instanceId)
.orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
-
- // 检查工作流实例状态
- if (!instance.canTerminate()) {
- throw new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
- }
-
- // 终止所有运行中的节点
- List runningNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
- instanceId, NodeStatusEnum.RUNNING);
- for (NodeInstance node : runningNodes) {
- node.setStatus(NodeStatusEnum.TERMINATED);
- node.setEndTime(LocalDateTime.now());
- nodeInstanceRepository.save(node);
- }
-
+
instance.terminate(reason);
workflowInstanceRepository.save(instance);
+
+ // 清理上下文缓存
+ variableOperations.clearVariables(instance.getId());
}
@Override
@@ -219,8 +195,8 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
workflowInstanceRepository.save(instance);
// 获取失败的节点
- List failedNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
- instanceId, NodeStatusEnum.FAILED);
+ List failedNodes = nodeInstanceRepository.findByWorkflowInstanceAndStatus(
+ instance, NodeStatusEnum.FAILED);
// 重试失败的节点
for (NodeInstance node : failedNodes) {
@@ -243,9 +219,9 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
}
}
- private NodeInstance createStartNode(WorkflowDefinition definition, Long instanceId) {
+ private NodeInstance createStartNode(WorkflowDefinition definition, WorkflowInstance instance) {
NodeInstance startNode = new NodeInstance();
- startNode.setWorkflowInstanceId(instanceId);
+ startNode.setWorkflowInstance(instance);
startNode.setNodeId("start");
startNode.setNodeType(NodeTypeEnum.START);
startNode.setName("开始节点");
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/WorkflowContext.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/WorkflowContext.java
index 4d213bc5..766d8681 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/WorkflowContext.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/WorkflowContext.java
@@ -2,7 +2,9 @@ package com.qqchen.deploy.backend.workflow.engine;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
+import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
import lombok.Data;
+import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.Map;
@@ -10,14 +12,15 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* 工作流上下文
+ * 负责管理工作流执行过程中的运行时状态
*/
@Data
public class WorkflowContext {
/**
- * 工作流实例
+ * 工作流实例ID
*/
- private WorkflowInstance workflowInstance;
+ private final Long instanceId;
/**
* 当前节点实例
@@ -30,32 +33,57 @@ public class WorkflowContext {
private List allNodes;
/**
- * 工作流变量
+ * 临时变量(节点间传递,不持久化)
*/
- private Map variables;
+ private final Map tempVariables;
/**
- * 临时变量(节点间传递)
+ * 变量操作服务
*/
- private Map tempVariables;
+ private final WorkflowVariableOperations variableOperations;
- public WorkflowContext() {
- this.variables = new ConcurrentHashMap<>();
+ public WorkflowContext(Long instanceId, WorkflowVariableOperations variableOperations) {
+ this.instanceId = instanceId;
this.tempVariables = new ConcurrentHashMap<>();
+ this.variableOperations = variableOperations;
}
/**
- * 获取变量值
+ * 获取变量值(委托给 WorkflowVariableOperations)
+ * @deprecated 建议直接使用 WorkflowVariableOperations
*/
+ @Deprecated
public Object getVariable(String key) {
- return variables.get(key);
+ return variableOperations.getVariables(instanceId).get(key);
}
/**
- * 设置变量值
+ * 设置变量值(委托给 WorkflowVariableOperations)
+ * @deprecated 建议直接使用 WorkflowVariableOperations
*/
+ @Deprecated
public void setVariable(String key, Object value) {
- variables.put(key, value);
+ Map vars = new ConcurrentHashMap<>();
+ vars.put(key, value);
+ variableOperations.setVariables(instanceId, vars);
+ }
+
+ /**
+ * 获取所有变量(委托给 WorkflowVariableOperations)
+ * @deprecated 建议直接使用 WorkflowVariableOperations
+ */
+ @Deprecated
+ public Map getVariables() {
+ return variableOperations.getVariables(instanceId);
+ }
+
+ /**
+ * 设置多个变量(委托给 WorkflowVariableOperations)
+ * @deprecated 建议直接使用 WorkflowVariableOperations
+ */
+ @Deprecated
+ public void setVariables(Map variables) {
+ variableOperations.setVariables(instanceId, variables);
}
/**
@@ -72,10 +100,17 @@ public class WorkflowContext {
tempVariables.put(key, value);
}
+ /**
+ * 获取所有临时变量
+ */
+ public Map getTempVariables() {
+ return new ConcurrentHashMap<>(tempVariables);
+ }
+
/**
* 清除临时变量
*/
public void clearTempVariables() {
tempVariables.clear();
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/context/DefaultWorkflowContext.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/context/DefaultWorkflowContext.java
index c986c0ce..91fa8495 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/context/DefaultWorkflowContext.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/context/DefaultWorkflowContext.java
@@ -2,73 +2,96 @@ package com.qqchen.deploy.backend.workflow.engine.context;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
-import com.qqchen.deploy.backend.workflow.service.IWorkflowLogService;
-import com.qqchen.deploy.backend.workflow.service.IWorkflowVariableService;
+import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
import lombok.Getter;
-import org.springframework.stereotype.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import java.util.HashMap;
import java.util.Map;
-public class DefaultWorkflowContext implements WorkflowContext {
-
+/**
+ * 默认工作流上下文实现
+ */
+public class DefaultWorkflowContext implements WorkflowContextOperations {
+
+ private static final Logger logger = LoggerFactory.getLogger(DefaultWorkflowContext.class);
+
@Getter
- private final WorkflowInstance instance;
-
- private final Map variables;
-
- private final IWorkflowVariableService variableService;
-
- private final IWorkflowLogService logService;
-
- private DefaultWorkflowContext(WorkflowInstance instance, IWorkflowVariableService variableService, IWorkflowLogService logService) {
- this.instance = instance;
- this.variables = new HashMap<>();
- this.variableService = variableService;
- this.logService = logService;
+ private final WorkflowInstance workflowInstance;
+
+ private final WorkflowVariableOperations variableOperations;
+
+ public DefaultWorkflowContext(WorkflowInstance workflowInstance, WorkflowVariableOperations variableOperations) {
+ this.workflowInstance = workflowInstance;
+ this.variableOperations = variableOperations;
}
-
+
+ @Override
+ public WorkflowInstance getInstance() {
+ return workflowInstance;
+ }
+
+ @Override
+ public Object getVariable(String key) {
+ return variableOperations.getVariable(workflowInstance.getId(), key);
+ }
+
+ @Override
+ public void setVariable(String key, Object value) {
+ variableOperations.setVariable(workflowInstance.getId(), key, value);
+ }
+
+ @Override
+ public void setVariables(Map variables) {
+ variableOperations.setVariables(workflowInstance.getId(), variables);
+ }
+
@Override
public Map getVariables() {
- return new HashMap<>(variables);
+ return variableOperations.getVariables(workflowInstance.getId());
}
-
- @Override
- public Object getVariable(String name) {
- return variables.get(name);
- }
-
- @Override
- public void setVariable(String name, Object value) {
- variables.put(name, value);
- variableService.setVariable(instance.getId(), name, value);
- }
-
+
@Override
public void log(String message, LogLevelEnum level) {
- log(message, null, level);
+ switch (level) {
+ case DEBUG -> logger.debug("[Workflow:{}] {}", workflowInstance.getId(), message);
+ case INFO -> logger.info("[Workflow:{}] {}", workflowInstance.getId(), message);
+ case WARN -> logger.warn("[Workflow:{}] {}", workflowInstance.getId(), message);
+ case ERROR -> logger.error("[Workflow:{}] {}", workflowInstance.getId(), message);
+ }
}
-
+
@Override
public void log(String message, String detail, LogLevelEnum level) {
- logService.log(instance.getId(), null, message, level, detail);
- }
-
- /**
- * 工作流上下文工厂类
- */
- @Component
- public static class Factory {
- private final IWorkflowVariableService variableService;
- private final IWorkflowLogService logService;
-
- public Factory(IWorkflowVariableService variableService, IWorkflowLogService logService) {
- this.variableService = variableService;
- this.logService = logService;
- }
-
- public DefaultWorkflowContext create(WorkflowInstance instance) {
- return new DefaultWorkflowContext(instance, variableService, logService);
+ switch (level) {
+ case DEBUG -> logger.debug("[Workflow:{}] {} - Detail: {}", workflowInstance.getId(), message, detail);
+ case INFO -> logger.info("[Workflow:{}] {} - Detail: {}", workflowInstance.getId(), message, detail);
+ case WARN -> logger.warn("[Workflow:{}] {} - Detail: {}", workflowInstance.getId(), message, detail);
+ case ERROR -> logger.error("[Workflow:{}] {} - Detail: {}", workflowInstance.getId(), message, detail);
}
}
-}
\ No newline at end of file
+
+ public static class Builder {
+ private WorkflowInstance workflowInstance;
+
+ private WorkflowVariableOperations variableOperations;
+
+ public Builder workflowInstance(WorkflowInstance workflowInstance) {
+ this.workflowInstance = workflowInstance;
+ return this;
+ }
+
+ public Builder variableOperations(WorkflowVariableOperations variableOperations) {
+ this.variableOperations = variableOperations;
+ return this;
+ }
+
+ public DefaultWorkflowContext build() {
+ return new DefaultWorkflowContext(workflowInstance, variableOperations);
+ }
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/context/WorkflowContext.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/context/WorkflowContextOperations.java
similarity index 64%
rename from backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/context/WorkflowContext.java
rename to backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/context/WorkflowContextOperations.java
index 0530d774..3e7232ea 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/context/WorkflowContext.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/context/WorkflowContextOperations.java
@@ -6,28 +6,37 @@ import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import java.util.Map;
/**
- * 工作流上下文
+ * 工作流上下文操作接口
+ * 定义工作流执行过程中的上下文操作能力
*/
-public interface WorkflowContext {
+public interface WorkflowContextOperations {
/**
* 获取工作流实例
*/
WorkflowInstance getInstance();
-
+
+ void setVariables(Map variables);
+
/**
* 获取所有变量
+ * @deprecated 使用 WorkflowVariableOperations 替代
*/
+ @Deprecated
Map getVariables();
/**
* 获取变量
+ * @deprecated 使用 WorkflowVariableOperations 替代
*/
+ @Deprecated
Object getVariable(String name);
/**
* 设置变量
+ * @deprecated 使用 WorkflowVariableOperations 替代
*/
+ @Deprecated
void setVariable(String name, Object value);
/**
@@ -39,4 +48,4 @@ public interface WorkflowContext {
* 记录日志(带详情)
*/
void log(String message, String detail, LogLevelEnum level);
-}
\ No newline at end of file
+}
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/EndNodeExecutor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/EndNodeExecutor.java
index 6b09a340..ec6f63bc 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/EndNodeExecutor.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/EndNodeExecutor.java
@@ -3,7 +3,7 @@ package com.qqchen.deploy.backend.workflow.engine.executor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
@@ -34,7 +34,7 @@ public class EndNodeExecutor implements NodeExecutor {
}
@Override
- public void execute(NodeInstance nodeInstance, WorkflowContext context) {
+ public void execute(NodeInstance nodeInstance, WorkflowContextOperations context) {
// 1. 完成结束节点
nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
nodeInstance.setEndTime(LocalDateTime.now());
@@ -59,7 +59,7 @@ public class EndNodeExecutor implements NodeExecutor {
}
@Override
- public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
+ public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
// 结束节点无需终止操作
}
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/GatewayNodeExecutor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/GatewayNodeExecutor.java
index 541b2462..ac8d4bec 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/GatewayNodeExecutor.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/GatewayNodeExecutor.java
@@ -2,7 +2,7 @@ package com.qqchen.deploy.backend.workflow.engine.executor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
@@ -31,7 +31,7 @@ public class GatewayNodeExecutor implements NodeExecutor {
}
@Override
- public void execute(NodeInstance nodeInstance, WorkflowContext context) {
+ public void execute(NodeInstance nodeInstance, WorkflowContextOperations context) {
try {
GatewayConfig config = objectMapper.readValue(nodeInstance.getConfig(), GatewayConfig.class);
@@ -57,7 +57,7 @@ public class GatewayNodeExecutor implements NodeExecutor {
}
}
- private void handleExclusiveGateway(NodeInstance nodeInstance, WorkflowContext context, GatewayConfig config) {
+ private void handleExclusiveGateway(NodeInstance nodeInstance, WorkflowContextOperations context, GatewayConfig config) {
try {
for (GatewayCondition condition : config.getConditions()) {
if (evaluateCondition(condition.getExpression(), context)) {
@@ -73,7 +73,7 @@ public class GatewayNodeExecutor implements NodeExecutor {
}
}
- private void handleParallelGateway(NodeInstance nodeInstance, WorkflowContext context, GatewayConfig config) {
+ private void handleParallelGateway(NodeInstance nodeInstance, WorkflowContextOperations context, GatewayConfig config) {
try {
List nextNodeIds = new ArrayList<>();
for (GatewayCondition condition : config.getConditions()) {
@@ -87,7 +87,7 @@ public class GatewayNodeExecutor implements NodeExecutor {
}
}
- private void handleInclusiveGateway(NodeInstance nodeInstance, WorkflowContext context, GatewayConfig config) {
+ private void handleInclusiveGateway(NodeInstance nodeInstance, WorkflowContextOperations context, GatewayConfig config) {
try {
List nextNodeIds = new ArrayList<>();
for (GatewayCondition condition : config.getConditions()) {
@@ -106,7 +106,7 @@ public class GatewayNodeExecutor implements NodeExecutor {
}
}
- private boolean evaluateCondition(String expression, WorkflowContext context) {
+ private boolean evaluateCondition(String expression, WorkflowContextOperations context) {
try {
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
evaluationContext.setVariables(context.getVariables());
@@ -140,7 +140,7 @@ public class GatewayNodeExecutor implements NodeExecutor {
}
@Override
- public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
+ public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
// Gateway nodes are instant operations, no need to terminate
}
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/NodeExecutor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/NodeExecutor.java
index ef062058..b769fc67 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/NodeExecutor.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/NodeExecutor.java
@@ -1,6 +1,6 @@
package com.qqchen.deploy.backend.workflow.engine.executor;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
@@ -16,8 +16,11 @@ public interface NodeExecutor {
/**
* 执行节点
+ *
+ * @param nodeInstance 节点实例
+ * @param context 工作流上下文
*/
- void execute(NodeInstance nodeInstance, WorkflowContext context);
+ void execute(NodeInstance nodeInstance, WorkflowContextOperations context);
/**
* 验证节点配置
@@ -26,6 +29,9 @@ public interface NodeExecutor {
/**
* 终止节点执行
+ *
+ * @param nodeInstance 节点实例
+ * @param context 工作流上下文
*/
- void terminate(NodeInstance nodeInstance, WorkflowContext context);
+ void terminate(NodeInstance nodeInstance, WorkflowContextOperations context);
}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/StartNodeExecutor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/StartNodeExecutor.java
index 9a262d0b..c131da70 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/StartNodeExecutor.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/StartNodeExecutor.java
@@ -3,7 +3,7 @@ package com.qqchen.deploy.backend.workflow.engine.executor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
@@ -26,7 +26,7 @@ public class StartNodeExecutor implements NodeExecutor {
}
@Override
- public void execute(NodeInstance nodeInstance, WorkflowContext context) {
+ public void execute(NodeInstance nodeInstance, WorkflowContextOperations context) {
// 开始节点直接完成
nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
context.log("开始节点执行完成", LogLevelEnum.INFO);
@@ -43,7 +43,7 @@ public class StartNodeExecutor implements NodeExecutor {
}
@Override
- public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
+ public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
// 开始节点无需终止操作
}
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/TaskNodeExecutor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/TaskNodeExecutor.java
index c893286e..c43e0796 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/TaskNodeExecutor.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/TaskNodeExecutor.java
@@ -3,7 +3,7 @@ package com.qqchen.deploy.backend.workflow.engine.executor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
@@ -25,7 +25,7 @@ public class TaskNodeExecutor implements NodeExecutor {
}
@Override
- public void execute(NodeInstance nodeInstance, WorkflowContext context) {
+ public void execute(NodeInstance nodeInstance, WorkflowContextOperations context) {
try {
// 1. 解析任务配置
TaskConfig config = parseConfig(nodeInstance.getConfig());
@@ -57,7 +57,7 @@ public class TaskNodeExecutor implements NodeExecutor {
}
@Override
- public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
+ public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
// 终止任务执行
TaskConfig config = parseConfig(nodeInstance.getConfig());
terminateTask(config, nodeInstance, context);
@@ -71,7 +71,7 @@ public class TaskNodeExecutor implements NodeExecutor {
}
}
- private void executeTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContext context) {
+ private void executeTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) {
switch (config.getType()) {
case HTTP:
executeHttpTask(config, nodeInstance, context);
@@ -84,7 +84,7 @@ public class TaskNodeExecutor implements NodeExecutor {
}
}
- private void terminateTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContext context) {
+ private void terminateTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) {
// 根据任务类型执行终止操作
switch (config.getType()) {
case HTTP:
@@ -96,19 +96,19 @@ public class TaskNodeExecutor implements NodeExecutor {
}
}
- private void executeHttpTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContext context) {
+ private void executeHttpTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) {
// TODO: 实现HTTP请求执行
}
- private void executeJavaTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContext context) {
+ private void executeJavaTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) {
// TODO: 实现Java方法调用
}
- private void terminateHttpTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContext context) {
+ private void terminateHttpTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) {
// TODO: 实现HTTP请求终止
}
- private void terminateJavaTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContext context) {
+ private void terminateJavaTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) {
// TODO: 实现Java方法终止
}
}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/HttpTaskExecutor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/HttpTaskExecutor.java
index 1902abb5..4b3eb611 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/HttpTaskExecutor.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/HttpTaskExecutor.java
@@ -3,7 +3,7 @@ package com.qqchen.deploy.backend.workflow.engine.executor.task;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import lombok.extern.slf4j.Slf4j;
@@ -21,7 +21,7 @@ public class HttpTaskExecutor implements TaskExecutor {
private final RestTemplate restTemplate = new RestTemplate();
@Override
- public void execute(NodeInstance nodeInstance, WorkflowContext context, Map parameters) {
+ public void execute(NodeInstance nodeInstance, WorkflowContextOperations context, Map parameters) {
String url = (String) parameters.get("url");
HttpMethod method = HttpMethod.valueOf((String) parameters.getOrDefault("method", "GET"));
Object body = parameters.get("body");
@@ -55,7 +55,7 @@ public class HttpTaskExecutor implements TaskExecutor {
}
@Override
- public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
+ public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
// HTTP请求无需终止操作
}
}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/JavaTaskExecutor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/JavaTaskExecutor.java
index 48552890..b9b69392 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/JavaTaskExecutor.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/JavaTaskExecutor.java
@@ -3,7 +3,7 @@ package com.qqchen.deploy.backend.workflow.engine.executor.task;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import lombok.extern.slf4j.Slf4j;
@@ -24,14 +24,14 @@ public class JavaTaskExecutor implements TaskExecutor {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
- public void execute(NodeInstance nodeInstance, WorkflowContext context, Map parameters) {
+ public void execute(NodeInstance nodeInstance, WorkflowContextOperations context, Map parameters) {
String className = parameters.get("className").toString();
String methodName = parameters.get("methodName").toString();
try {
Class> clazz = Class.forName(className);
Object instance = applicationContext.getBean(clazz);
- Method method = clazz.getMethod(methodName, NodeInstance.class, WorkflowContext.class, Map.class);
+ Method method = clazz.getMethod(methodName, NodeInstance.class, WorkflowContextOperations.class, Map.class);
method.invoke(instance, nodeInstance, context, parameters);
} catch (ClassNotFoundException e) {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR, "Class not found: " + className);
@@ -43,7 +43,7 @@ public class JavaTaskExecutor implements TaskExecutor {
}
@Override
- public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
+ public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
// Java任务无法中断,记录日志
context.log("Java task cannot be terminated: " + nodeInstance.getNodeId(), LogLevelEnum.WARN);
}
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/ShellTaskExecutor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/ShellTaskExecutor.java
index 36231e1c..96ee25f8 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/ShellTaskExecutor.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/ShellTaskExecutor.java
@@ -3,7 +3,7 @@ package com.qqchen.deploy.backend.workflow.engine.executor.task;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import lombok.extern.slf4j.Slf4j;
@@ -21,7 +21,7 @@ public class ShellTaskExecutor implements TaskExecutor {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
- public void execute(NodeInstance nodeInstance, WorkflowContext context, Map parameters) {
+ public void execute(NodeInstance nodeInstance, WorkflowContextOperations context, Map parameters) {
String command = (String) parameters.get("command");
Integer timeout = (Integer) parameters.getOrDefault("timeout", 300);
@@ -60,7 +60,7 @@ public class ShellTaskExecutor implements TaskExecutor {
}
@Override
- public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
+ public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
// TODO: 实现Shell命令终止逻辑
}
}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/TaskExecutor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/TaskExecutor.java
index fd7c6df6..8805a9d4 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/TaskExecutor.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/executor/task/TaskExecutor.java
@@ -1,6 +1,6 @@
package com.qqchen.deploy.backend.workflow.engine.executor.task;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import java.util.Map;
@@ -13,10 +13,10 @@ public interface TaskExecutor {
/**
* 执行任务
*/
- void execute(NodeInstance nodeInstance, WorkflowContext context, Map parameters);
+ void execute(NodeInstance nodeInstance, WorkflowContextOperations context, Map parameters);
/**
* 终止任务
*/
- void terminate(NodeInstance nodeInstance, WorkflowContext context);
+ void terminate(NodeInstance nodeInstance, WorkflowContextOperations context);
}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/node/AbstractNodeExecutor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/node/AbstractNodeExecutor.java
index 93325a2d..cc3fa813 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/node/AbstractNodeExecutor.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/node/AbstractNodeExecutor.java
@@ -1,88 +1,134 @@
package com.qqchen.deploy.backend.workflow.engine.node;
+import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
+import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.engine.executor.NodeExecutor;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
-import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
+import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowLogService;
+import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
+import java.time.LocalDateTime;
+
/**
* 抽象节点执行器
*/
@Slf4j
public abstract class AbstractNodeExecutor implements NodeExecutor {
+ @Resource
+ protected WorkflowVariableOperations variableOperations;
+
+ @Resource
+ protected INodeInstanceRepository nodeInstanceRepository;
+
@Resource
protected IWorkflowLogService workflowLogService;
@Override
- public void execute(NodeInstance nodeInstance, WorkflowContext context) {
+ public void execute(NodeInstance nodeInstance, WorkflowContextOperations context) {
try {
- // 1. 记录系统日志
- logSystem(context.getInstance(), LogLevelEnum.INFO,
- String.format("开始执行节点: %s[%s]", nodeInstance.getName(), nodeInstance.getNodeId()),
- null);
+ // 1. 前置处理
+ beforeExecute(nodeInstance, context);
- // 2. 记录节点开始日志
- logNodeStart(nodeInstance);
-
- // 3. 执行节点
+ // 2. 执行节点逻辑
doExecute(nodeInstance, context);
- // 4. 记录节点完成日志
- logNodeComplete(nodeInstance);
+ // 3. 后置处理
+ afterExecute(nodeInstance, context);
+
+ // 4. 更新节点状态
+ updateNodeStatus(nodeInstance, true);
} catch (Exception e) {
- // 5. 记录错误日志
- logSystem(context.getInstance(), LogLevelEnum.ERROR,
- String.format("节点执行失败: %s[%s]", nodeInstance.getName(), nodeInstance.getNodeId()),
- e.getMessage());
- logNodeError(nodeInstance, e.getMessage());
- throw e;
+ // 5. 异常处理
+ handleExecutionError(nodeInstance, context, e);
+ throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED, e);
}
}
/**
- * 执行节点
+ * 执行节点逻辑
*/
- protected abstract void doExecute(NodeInstance nodeInstance, WorkflowContext context);
+ protected abstract void doExecute(NodeInstance nodeInstance, WorkflowContextOperations context);
+
+ /**
+ * 前置处理
+ */
+ protected void beforeExecute(NodeInstance nodeInstance, WorkflowContextOperations context) {
+ context.log("Starting node execution: " + nodeInstance.getName(), LogLevelEnum.INFO);
+ logNodeStart(nodeInstance);
+ }
+
+ /**
+ * 后置处理
+ */
+ protected void afterExecute(NodeInstance nodeInstance, WorkflowContextOperations context) {
+ context.log("Node execution completed: " + nodeInstance.getName(), LogLevelEnum.INFO);
+ logNodeComplete(nodeInstance);
+ }
+
+ /**
+ * 处理执行异常
+ */
+ protected void handleExecutionError(NodeInstance nodeInstance, WorkflowContextOperations context, Exception e) {
+ log.error("Node execution failed. nodeInstance: {}, error: {}", nodeInstance.getId(), e.getMessage(), e);
+ context.log("Node execution failed: " + e.getMessage(), LogLevelEnum.ERROR);
+ logSystem(nodeInstance, LogLevelEnum.ERROR,
+ String.format("节点执行失败: %s[%s]", nodeInstance.getName(), nodeInstance.getNodeId()),
+ e.getMessage());
+ logNodeError(nodeInstance, e.getMessage());
+ updateNodeStatus(nodeInstance, false);
+ }
+
+ /**
+ * 更新节点状态
+ */
+ private void updateNodeStatus(NodeInstance nodeInstance, boolean success) {
+ nodeInstance.setStatus(success ? NodeStatusEnum.COMPLETED : NodeStatusEnum.FAILED);
+ if (success) {
+ nodeInstance.setEndTime(LocalDateTime.now());
+ }
+ nodeInstanceRepository.save(nodeInstance);
+ }
/**
* 记录系统日志
*/
- protected void logSystem(WorkflowInstance instance, LogLevelEnum level, String message, String detail) {
- workflowLogService.log(instance.getId(), null, message, level, detail);
+ protected void logSystem(NodeInstance nodeInstance, LogLevelEnum level, String message, String detail) {
+ workflowLogService.log(nodeInstance.getWorkflowInstance(), nodeInstance.getNodeId(), level, message, detail);
}
/**
* 记录节点开始日志
*/
protected void logNodeStart(NodeInstance nodeInstance) {
- workflowLogService.log(nodeInstance.getWorkflowInstanceId(), nodeInstance.getNodeId(),
- String.format("节点开始执行: %s", nodeInstance.getName()), LogLevelEnum.INFO, null);
+ workflowLogService.log(nodeInstance.getWorkflowInstance(), nodeInstance.getNodeId(),
+ LogLevelEnum.INFO, String.format("节点开始执行: %s", nodeInstance.getName()), null);
nodeInstance.setStatus(NodeStatusEnum.RUNNING);
+ nodeInstance.setStartTime(LocalDateTime.now());
+ nodeInstanceRepository.save(nodeInstance);
}
/**
* 记录节点完成日志
*/
protected void logNodeComplete(NodeInstance nodeInstance) {
- workflowLogService.log(nodeInstance.getWorkflowInstanceId(), nodeInstance.getNodeId(),
- String.format("节点执行完成: %s", nodeInstance.getName()), LogLevelEnum.INFO, null);
- nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
+ workflowLogService.log(nodeInstance.getWorkflowInstance(), nodeInstance.getNodeId(),
+ LogLevelEnum.INFO, String.format("节点执行完成: %s", nodeInstance.getName()), null);
}
/**
* 记录节点错误日志
*/
protected void logNodeError(NodeInstance nodeInstance, String error) {
- workflowLogService.log(nodeInstance.getWorkflowInstanceId(), nodeInstance.getNodeId(),
- String.format("节点执行失败: %s", nodeInstance.getName()), LogLevelEnum.ERROR, error);
- nodeInstance.setStatus(NodeStatusEnum.FAILED);
+ workflowLogService.log(nodeInstance.getWorkflowInstance(), nodeInstance.getNodeId(),
+ LogLevelEnum.ERROR, String.format("节点执行失败: %s", nodeInstance.getName()), error);
nodeInstance.setError(error);
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/node/NodeExecutor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/node/NodeExecutor.java
deleted file mode 100644
index 5d51e4c6..00000000
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/node/NodeExecutor.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.qqchen.deploy.backend.workflow.engine.node;
-
-import com.qqchen.deploy.backend.workflow.engine.WorkflowContext;
-import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
-
-/**
- * 节点执行器接口
- */
-public interface NodeExecutor {
-
- /**
- * 执行节点
- *
- * @param nodeInstance 节点实例
- * @param context 工作流上下文
- * @return 执行结果
- */
- boolean execute(NodeInstance nodeInstance, WorkflowContext context);
-
- /**
- * 取消节点执行
- *
- * @param nodeInstance 节点实例
- * @param context 工作流上下文
- */
- void cancel(NodeInstance nodeInstance, WorkflowContext context);
-
- /**
- * 获取支持的节点类型
- *
- * @return 节点类型
- */
- NodeType getNodeType();
-}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/node/executor/ShellNodeExecutor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/node/executor/ShellNodeExecutor.java
index 3f20d868..c8109ecc 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/node/executor/ShellNodeExecutor.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/node/executor/ShellNodeExecutor.java
@@ -1,13 +1,14 @@
package com.qqchen.deploy.backend.workflow.engine.node.executor;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
-import com.qqchen.deploy.backend.workflow.engine.node.AbstractNodeExecutor;
-import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
-import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
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.node.AbstractNodeExecutor;
+import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
+import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
+import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -16,6 +17,7 @@ import jakarta.annotation.Resource;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
@@ -27,6 +29,9 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
@Resource
private ObjectMapper objectMapper;
+ @Resource
+ private WorkflowVariableOperations variableOperations;
+
private final ExecutorService executorService = Executors.newCachedThreadPool();
@Override
@@ -62,7 +67,7 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
}
@Override
- protected void doExecute(NodeInstance nodeInstance, WorkflowContext context) {
+ protected void doExecute(NodeInstance nodeInstance, WorkflowContextOperations context) {
try {
String configJson = nodeInstance.getConfig();
ShellConfig config = objectMapper.readValue(configJson, ShellConfig.class);
@@ -85,9 +90,7 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
} catch (Exception e) {
lastException = e;
if (attempt < maxAttempts) {
- // 记录重试日志
- context.log(String.format("Shell execution failed (attempt %d/%d), retrying in %d seconds",
- attempt, maxAttempts, retryInterval), LogLevelEnum.WARN);
+ context.log(String.format("Shell execution failed (attempt %d/%d), retrying in %d seconds", attempt, maxAttempts, retryInterval), LogLevelEnum.WARN);
Thread.sleep(retryInterval * 1000L);
}
}
@@ -103,7 +106,7 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
}
}
- private void executeShellCommand(ShellConfig config, NodeInstance nodeInstance, WorkflowContext context) throws Exception {
+ private void executeShellCommand(ShellConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) throws Exception {
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command("sh", "-c", config.getScript());
@@ -149,12 +152,12 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
exitCode, String.join("\n", error)));
}
- // 设置输出结果
- nodeInstance.setOutput(String.join("\n", output));
- if (!error.isEmpty()) {
- nodeInstance.setError(String.join("\n", error));
- }
-
+ // 设置输出变量
+ Map outputVariables = new HashMap<>();
+ outputVariables.put("shellOutput", String.join("\n", output));
+ outputVariables.put("exitCode", exitCode);
+ variableOperations.setVariables(nodeInstance.getWorkflowInstance().getId(), outputVariables);
+
// 记录执行日志
context.log(String.format("Shell script executed successfully with exit code: %d", exitCode), LogLevelEnum.INFO);
}
@@ -171,7 +174,7 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
}
@Override
- public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
+ public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
// TODO: 实现终止Shell进程的逻辑
context.log("Shell node termination is not implemented yet", LogLevelEnum.WARN);
}
@@ -187,4 +190,4 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
private Map environment; // 环境变量
private Integer successExitCode; // 成功退出码
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/transition/TransitionExecutor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/transition/TransitionExecutor.java
index 5d9196b7..67c802c1 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/transition/TransitionExecutor.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/transition/TransitionExecutor.java
@@ -2,7 +2,7 @@ package com.qqchen.deploy.backend.workflow.engine.transition;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.entity.NodeDefinition;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
@@ -33,7 +33,7 @@ public class TransitionExecutor {
/**
* 执行节点流转
*/
- public void executeTransition(NodeInstance currentNode, WorkflowDefinition definition, WorkflowContext context) {
+ public void executeTransition(NodeInstance currentNode, WorkflowDefinition definition, WorkflowContextOperations context) {
// 1. 获取下一个节点ID列表
List nextNodeIds = transitionRuleEngine.getNextNodeIds(currentNode, definition, context);
@@ -46,7 +46,7 @@ public class TransitionExecutor {
}
NodeInstance nextNode = new NodeInstance();
- nextNode.setWorkflowInstanceId(currentNode.getWorkflowInstanceId());
+ nextNode.setWorkflowInstance(currentNode.getWorkflowInstance());
nextNode.setNodeId(nodeId);
nextNode.setNodeType(nextNodeDef.getType());
nextNode.setName(nextNodeDef.getName());
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/transition/TransitionRuleEngine.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/transition/TransitionRuleEngine.java
index 1df4624f..2a5ee74f 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/transition/TransitionRuleEngine.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/engine/transition/TransitionRuleEngine.java
@@ -3,7 +3,7 @@ package com.qqchen.deploy.backend.workflow.engine.transition;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
-import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
@@ -28,7 +28,7 @@ public class TransitionRuleEngine {
/**
* 获取下一个节点ID列表
*/
- public List getNextNodeIds(NodeInstance currentNode, WorkflowDefinition definition, WorkflowContext context) {
+ public List getNextNodeIds(NodeInstance currentNode, WorkflowDefinition definition, WorkflowContextOperations context) {
try {
// 解析流转规则
List rules = parseTransitionRules(definition.getTransitionConfig());
@@ -62,7 +62,7 @@ public class TransitionRuleEngine {
}
}
- private boolean evaluateCondition(String condition, WorkflowContext context) {
+ private boolean evaluateCondition(String condition, WorkflowContextOperations context) {
if (condition == null || condition.trim().isEmpty()) {
return true;
}
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/NodeInstance.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/NodeInstance.java
index 41794d77..9098a3c0 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/NodeInstance.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/NodeInstance.java
@@ -17,17 +17,11 @@ import java.time.LocalDateTime;
@LogicDelete
public class NodeInstance extends Entity {
- /**
- * 工作流实例ID,用于优化查询
- */
- @Column(name = "workflow_instance_id", nullable = false)
- private Long workflowInstanceId;
-
/**
* 工作流实例
*/
@ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "workflow_instance_id", insertable = false, updatable = false)
+ @JoinColumn(name = "workflow_instance_id", nullable = false)
private WorkflowInstance workflowInstance;
/**
@@ -99,10 +93,6 @@ public class NodeInstance extends Entity {
return config;
}
- public Long getWorkflowInstanceId() {
- return workflowInstanceId;
- }
-
public String getPreNodeId() {
return preNodeId;
}
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowDefinition.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowDefinition.java
index 441f9047..cd9dd771 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowDefinition.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowDefinition.java
@@ -75,12 +75,6 @@ public class WorkflowDefinition extends Entity {
@Column(name = "graph_definition", columnDefinition = "TEXT")
private String graphDefinition;
- /**
- * 节点定义列表
- */
- @OneToMany(mappedBy = "workflowDefinition", cascade = CascadeType.ALL, orphanRemoval = true)
- private List nodes = new ArrayList<>();
-
/**
* 版本号
*/
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowInstance.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowInstance.java
index 3abe893d..34a5afc9 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowInstance.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowInstance.java
@@ -40,7 +40,7 @@ public class WorkflowInstance extends Entity {
/**
* 业务标识
*/
- @Column(name = "business_key")
+ @Column(name = "business_key", nullable = false)
private String businessKey;
/**
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowLog.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowLog.java
index 0076bb95..510f7d23 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowLog.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowLog.java
@@ -1,9 +1,12 @@
package com.qqchen.deploy.backend.workflow.entity;
-import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity;
+import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import jakarta.persistence.Column;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -13,38 +16,27 @@ import lombok.EqualsAndHashCode;
*/
@Data
@EqualsAndHashCode(callSuper = true)
-@jakarta.persistence.Entity
@Table(name = "wf_log")
@LogicDelete
+@jakarta.persistence.Entity
public class WorkflowLog extends Entity {
- /**
- * 工作流实例ID
- */
- @Column(name = "workflow_instance_id", nullable = false)
- private Long workflowInstanceId;
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "workflow_instance_id", nullable = false)
+ private WorkflowInstance workflowInstance;
- /**
- * 节点ID
- */
@Column(name = "node_id")
private String nodeId;
- /**
- * 日志内容
- */
- @Column(nullable = false)
- private String content;
-
/**
* 日志级别
*/
- @Column(nullable = false)
+ @Column(name = "level")
private LogLevelEnum level;
- /**
- * 详细信息
- */
- @Column(columnDefinition = "TEXT")
+ @Column(name = "message", nullable = false, columnDefinition = "TEXT")
+ private String message;
+
+ @Column(name = "detail", columnDefinition = "TEXT")
private String detail;
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowVariable.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowVariable.java
index 1f253ac8..b86a08dc 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowVariable.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/entity/WorkflowVariable.java
@@ -18,10 +18,11 @@ import lombok.EqualsAndHashCode;
public class WorkflowVariable extends Entity {
/**
- * 工作流实例ID
+ * 工作流实例
*/
- @Column(name = "workflow_instance_id", nullable = false)
- private Long workflowInstanceId;
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "workflow_instance_id", nullable = false)
+ private WorkflowInstance workflowInstance;
/**
* 变量名称
@@ -52,4 +53,4 @@ public class WorkflowVariable extends Entity {
*/
@Column(name = "node_id")
private String nodeId;
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/monitor/WorkflowContextMonitor.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/monitor/WorkflowContextMonitor.java
new file mode 100644
index 00000000..65682897
--- /dev/null
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/monitor/WorkflowContextMonitor.java
@@ -0,0 +1,149 @@
+package com.qqchen.deploy.backend.workflow.monitor;
+
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Timer;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.DoubleAdder;
+
+/**
+ * 工作流上下文监控
+ * 负责收集和记录工作流变量操作的各项指标
+ */
+@Slf4j
+@Component
+public class WorkflowContextMonitor {
+
+ private final MeterRegistry meterRegistry;
+ private final Counter variableSetCounter;
+ private final Counter variableGetCounter;
+ private final Counter cacheHitCounter;
+ private final Counter cacheMissCounter;
+ private final Timer variableOperationTimer;
+ private final Timer transactionTimer;
+ private final Gauge contextCacheSize;
+ private final DoubleAdder cacheSize = new DoubleAdder();
+
+ public WorkflowContextMonitor(MeterRegistry registry) {
+ this.meterRegistry = registry;
+ // 变量操作计数器
+ this.variableSetCounter = Counter.builder("workflow.variable.operations")
+ .tag("type", "set")
+ .description("Number of variable set operations")
+ .register(registry);
+
+ this.variableGetCounter = Counter.builder("workflow.variable.operations")
+ .tag("type", "get")
+ .description("Number of variable get operations")
+ .register(registry);
+
+ // 缓存命中计数器
+ this.cacheHitCounter = Counter.builder("workflow.context.cache")
+ .tag("result", "hit")
+ .description("Number of context cache hits")
+ .register(registry);
+
+ this.cacheMissCounter = Counter.builder("workflow.context.cache")
+ .tag("result", "miss")
+ .description("Number of context cache misses")
+ .register(registry);
+
+ // 操作耗时计时器
+ this.variableOperationTimer = Timer.builder("workflow.variable.operation.duration")
+ .description("Time taken for variable operations")
+ .publishPercentiles(0.5, 0.95, 0.99)
+ .register(registry);
+
+ this.transactionTimer = Timer.builder("workflow.variable.transaction.duration")
+ .description("Time taken for variable transactions")
+ .publishPercentiles(0.5, 0.95, 0.99)
+ .register(registry);
+
+ // 缓存大小测量
+ this.contextCacheSize = Gauge.builder("workflow.context.cache.size",
+ cacheSize::doubleValue) // 使用 DoubleAdder 来存储和获取值
+ .description("Current size of workflow context cache")
+ .register(registry);
+ }
+
+ /**
+ * 记录变量设置操作
+ */
+ public void recordVariableSet() {
+ variableSetCounter.increment();
+ }
+
+ /**
+ * 记录变量获取操作
+ */
+ public void recordVariableGet() {
+ variableGetCounter.increment();
+ }
+
+ /**
+ * 记录缓存命中
+ */
+ public void recordCacheHit() {
+ cacheHitCounter.increment();
+ }
+
+ /**
+ * 记录缓存未命中
+ */
+ public void recordCacheMiss() {
+ cacheMissCounter.increment();
+ }
+
+ /**
+ * 记录操作耗时
+ */
+ public Timer.Sample startOperation() {
+ return Timer.start();
+ }
+
+ /**
+ * 停止操作计时
+ */
+ public void stopOperation(Timer.Sample sample) {
+ sample.stop(variableOperationTimer);
+ }
+
+ /**
+ * 记录事务耗时
+ */
+ public Timer.Sample startTransaction() {
+ return Timer.start();
+ }
+
+ /**
+ * 停止事务计时
+ */
+ public void stopTransaction(Timer.Sample sample) {
+ sample.stop(transactionTimer);
+ }
+
+ /**
+ * 更新缓存大小
+ */
+ public void updateCacheSize(long size) {
+ cacheSize.reset();
+ cacheSize.add(size);
+ }
+
+ /**
+ * 记录错误
+ */
+ public void recordError(String operation, Exception e) {
+ log.error("Workflow variable operation error. Operation: {}, Error: {}",
+ operation, e.getMessage(), e);
+ Counter.builder("workflow.variable.errors")
+ .tag("operation", operation)
+ .tag("error", e.getClass().getSimpleName())
+ .register(meterRegistry)
+ .increment();
+ }
+}
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/repository/INodeInstanceRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/repository/INodeInstanceRepository.java
index ea0ecb2d..4119b1c2 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/repository/INodeInstanceRepository.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/repository/INodeInstanceRepository.java
@@ -2,6 +2,7 @@ package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
+import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@@ -15,22 +16,36 @@ public interface INodeInstanceRepository extends IBaseRepository findByWorkflowInstanceIdOrderByCreateTime(@Param("instanceId") Long instanceId);
+ @Query("SELECT n FROM NodeInstance n WHERE n.workflowInstance = :instance AND n.deleted = false ORDER BY n.createTime")
+ List findByWorkflowInstanceOrderByCreateTime(@Param("instance") WorkflowInstance instance);
+
+ /**
+ * 根据工作流实例ID查询所有节点
+ */
+ @Query("SELECT n FROM NodeInstance n WHERE n.workflowInstance.id = :workflowInstanceId AND n.deleted = false ORDER BY n.createTime")
+ List findByWorkflowInstanceId(@Param("workflowInstanceId") Long workflowInstanceId);
+
+ /**
+ * 根据工作流实例ID和状态查询节点
+ */
+ @Query("SELECT n FROM NodeInstance n WHERE n.workflowInstance.id = :workflowInstanceId AND n.status = :status AND n.deleted = false ORDER BY n.createTime")
+ List findByWorkflowInstanceIdAndStatus(
+ @Param("workflowInstanceId") Long workflowInstanceId,
+ @Param("status") NodeStatusEnum status);
/**
* 查询指定状态的节点实例
*/
- List findByWorkflowInstanceIdAndStatusAndDeletedFalse(Long workflowInstanceId, NodeStatusEnum status);
+ List findByWorkflowInstanceAndStatusAndDeletedFalse(WorkflowInstance instance, NodeStatusEnum status);
- List findByWorkflowInstanceIdAndStatus(Long workflowInstanceId, NodeStatusEnum status);
+ List findByWorkflowInstanceAndStatus(WorkflowInstance instance, NodeStatusEnum status);
- List findByWorkflowInstanceId(Long workflowInstanceId);
+ List findByWorkflowInstance(WorkflowInstance instance);
- void deleteByWorkflowInstanceId(Long workflowInstanceId);
+ void deleteByWorkflowInstance(WorkflowInstance instance);
/**
* 查询不是指定状态的节点实例
*/
- List findByWorkflowInstanceIdAndStatusNot(Long workflowInstanceId, NodeStatusEnum status);
-}
\ No newline at end of file
+ List findByWorkflowInstanceAndStatusNot(WorkflowInstance instance, NodeStatusEnum status);
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/repository/IWorkflowLogRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/repository/IWorkflowLogRepository.java
index 531c1771..6556658c 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/repository/IWorkflowLogRepository.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/repository/IWorkflowLogRepository.java
@@ -1,6 +1,7 @@
package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
+import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@@ -17,17 +18,17 @@ public interface IWorkflowLogRepository extends IBaseRepository findByWorkflowInstanceId(@Param("instanceId") Long instanceId);
/**
* 查询节点实例的所有日志
*/
- @Query("SELECT l FROM WorkflowLog l WHERE l.workflowInstanceId = :instanceId AND l.nodeId = :nodeId AND l.deleted = false ORDER BY l.createTime")
+ @Query("SELECT l FROM WorkflowLog l WHERE l.workflowInstance.id = :instanceId AND l.nodeId = :nodeId AND l.deleted = false ORDER BY l.createTime")
List findByWorkflowInstanceIdAndNodeId(@Param("instanceId") Long instanceId, @Param("nodeId") String nodeId);
/**
* 删除工作流实例的所有日志
*/
- void deleteByWorkflowInstanceId(Long instanceId);
-}
\ No newline at end of file
+ void deleteByWorkflowInstance(WorkflowInstance instance);
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/repository/IWorkflowVariableRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/repository/IWorkflowVariableRepository.java
index f25791b5..e9f59002 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/repository/IWorkflowVariableRepository.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/repository/IWorkflowVariableRepository.java
@@ -1,7 +1,9 @@
package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
+import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowVariable;
+import com.qqchen.deploy.backend.workflow.enums.VariableScopeEnum;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@@ -18,40 +20,69 @@ public interface IWorkflowVariableRepository extends IBaseRepository findByWorkflowInstanceId(@Param("instanceId") Long instanceId);
+ @Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance = :instance AND v.deleted = false")
+ List findByWorkflowInstance(@Param("instance") WorkflowInstance instance);
/**
* 查询工作流实例的指定作用域的变量
*/
- @Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstanceId = :instanceId AND v.scope = :scope AND v.deleted = false")
- List findByWorkflowInstanceIdAndScope(@Param("instanceId") Long instanceId, @Param("scope") String scope);
+ @Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance = :instance AND v.scope = :scope AND v.deleted = false")
+ List findByWorkflowInstanceAndScope(@Param("instance") WorkflowInstance instance, @Param("scope") String scope);
/**
* 查询工作流实例的指定变量
*/
- Optional findByWorkflowInstanceIdAndName(Long workflowInstanceId, String name);
+ Optional findByWorkflowInstanceAndName(WorkflowInstance instance, String name);
/**
* 删除工作流实例的所有变量
*/
- void deleteByWorkflowInstanceId(Long instanceId);
+ void deleteByWorkflowInstance(WorkflowInstance instance);
/**
* 查询工作流实例的指定作用域和节点的变量
*/
- @Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstanceId = :instanceId AND v.scope = :scope AND v.nodeId = :nodeId AND v.deleted = false")
- List findByWorkflowInstanceIdAndScopeAndNodeId(
- @Param("instanceId") Long instanceId,
+ @Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance = :instance AND v.scope = :scope AND v.nodeId = :nodeId AND v.deleted = false")
+ List findByWorkflowInstanceAndScopeAndNodeId(
+ @Param("instance") WorkflowInstance instance,
@Param("scope") String scope,
@Param("nodeId") String nodeId);
/**
* 查询工作流实例的指定名称和作用域的变量
*/
- @Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstanceId = :instanceId AND v.name = :name AND v.scope = :scope AND v.deleted = false")
- Optional findByWorkflowInstanceIdAndNameAndScope(
- @Param("instanceId") Long instanceId,
+ @Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance = :instance AND v.name = :name AND v.scope = :scope AND v.deleted = false")
+ Optional findByWorkflowInstanceAndNameAndScope(
+ @Param("instance") WorkflowInstance instance,
@Param("name") String name,
@Param("scope") String scope);
-}
\ No newline at end of file
+
+ /**
+ * 根据工作流实例ID查询所有变量
+ *
+ * @param workflowInstanceId 工作流实例ID
+ * @return 变量列表
+ */
+ @Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance.id = :workflowInstanceId AND v.deleted = false")
+ List findByWorkflowInstanceId(@Param("workflowInstanceId") Long workflowInstanceId);
+
+ /**
+ * 根据工作流实例ID和作用域查询变量列表
+ *
+ * @param workflowInstanceId 工作流实例ID
+ * @param scope 变量作用域
+ * @return 变量列表
+ */
+ @Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance.id = :workflowInstanceId AND v.scope = :scope AND v.deleted = false")
+ List findByWorkflowInstanceIdAndScope(
+ @Param("workflowInstanceId") Long workflowInstanceId,
+ @Param("scope") VariableScopeEnum scope);
+
+ /**
+ * 根据工作流实例ID删除所有变量(软删除)
+ *
+ * @param workflowInstanceId 工作流实例ID
+ */
+ @Query("UPDATE WorkflowVariable v SET v.deleted = true, v.updateTime = CURRENT_TIMESTAMP WHERE v.workflowInstance.id = :workflowInstanceId AND v.deleted = false")
+ void deleteByWorkflowInstanceId(@Param("workflowInstanceId") Long workflowInstanceId);
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/IWorkflowLogService.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/IWorkflowLogService.java
index 67932776..a5d903c1 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/IWorkflowLogService.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/IWorkflowLogService.java
@@ -1,8 +1,9 @@
package com.qqchen.deploy.backend.workflow.service;
-import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.framework.service.IBaseService;
+import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.workflow.dto.WorkflowLogDTO;
+import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
import java.util.List;
@@ -13,22 +14,49 @@ import java.util.List;
public interface IWorkflowLogService extends IBaseService {
/**
- * 记录日志
+ * 记录日志 (内部使用,由工作流引擎调用)
+ *
+ * @param instance 工作流实例
+ * @param nodeId 节点ID
+ * @param level 日志级别
+ * @param message 日志内容
+ * @param detail 详细信息
+ * @return 工作流日志
*/
- void log(Long workflowInstanceId, String nodeId, String message, LogLevelEnum level, String detail);
+ WorkflowLog log(WorkflowInstance instance, String nodeId, LogLevelEnum level, String message, String detail);
+
+ /**
+ * 记录日志 (外部接口使用)
+ *
+ * @param workflowInstanceId 工作流实例ID
+ * @param nodeId 节点ID
+ * @param message 日志内容
+ * @param level 日志级别
+ * @param detail 详细信息
+ */
+ void recordLog(Long workflowInstanceId, String nodeId, String message, LogLevelEnum level, String detail);
/**
* 查询工作流实例的所有日志
+ *
+ * @param workflowInstanceId 工作流实例ID
+ * @return 日志列表
*/
List getLogs(Long workflowInstanceId);
/**
* 查询节点实例的所有日志
+ *
+ * @param workflowInstanceId 工作流实例ID
+ * @param nodeId 节点ID
+ * @return 日志列表
*/
List getNodeLogs(Long workflowInstanceId, String nodeId);
/**
* 删除工作流实例的所有日志
+ *
+ * @param workflowInstance 工作流实例
*/
- void deleteLogs(Long workflowInstanceId);
-}
\ No newline at end of file
+ void deleteLogs(WorkflowInstance workflowInstance);
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/IWorkflowVariableService.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/IWorkflowVariableService.java
index 8cdabefe..adc6447e 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/IWorkflowVariableService.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/IWorkflowVariableService.java
@@ -2,6 +2,7 @@ package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.dto.WorkflowVariableDTO;
+import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowVariable;
import com.qqchen.deploy.backend.workflow.enums.VariableScopeEnum;
@@ -14,21 +15,29 @@ public interface IWorkflowVariableService extends IBaseService variables);
/**
* 获取所有变量
+ *
+ * @return 变量映射
*/
Map getVariables(Long workflowInstanceId);
/**
* 获取指定作用域的变量
+ *
+ * @param scope 变量作用域
+ * @return 变量映射
*/
Map getVariablesByScope(Long workflowInstanceId, VariableScopeEnum scope);
/**
- * 删除所有变量
+ * 清除实例的所有变量
+ *
*/
- void deleteVariables(Long workflowInstanceId);
-}
\ No newline at end of file
+ void clearVariables(Long workflowInstanceId);
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/WorkflowVariableOperations.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/WorkflowVariableOperations.java
new file mode 100644
index 00000000..b4ce69c1
--- /dev/null
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/WorkflowVariableOperations.java
@@ -0,0 +1,52 @@
+package com.qqchen.deploy.backend.workflow.service;
+
+import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
+
+import java.util.Map;
+
+/**
+ * 工作流变量操作接口
+ */
+public interface WorkflowVariableOperations {
+
+ /**
+ * 获取工作流实例的所有变量
+ *
+ * @param workflowInstanceId 工作流实例ID
+ * @return 变量Map
+ */
+ Map getVariables(Long workflowInstanceId);
+
+ /**
+ * 设置工作流实例的变量
+ *
+ * @param workflowInstanceId 工作流实例ID
+ * @param variables 变量Map
+ */
+ void setVariables(Long workflowInstanceId, Map variables);
+
+ /**
+ * 获取工作流实例的指定变量
+ *
+ * @param workflowInstanceId 工作流实例ID
+ * @param key 变量键
+ * @return 变量值
+ */
+ Object getVariable(Long workflowInstanceId, String key);
+
+ /**
+ * 设置工作流实例的指定变量
+ *
+ * @param workflowInstanceId 工作流实例ID
+ * @param key 变量键
+ * @param value 变量值
+ */
+ void setVariable(Long workflowInstanceId, String key, Object value);
+
+ /**
+ * 清除实例的所有变量
+ *
+ * @param workflowInstanceId 工作流实例ID
+ */
+ void clearVariables(Long workflowInstanceId);
+}
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/impl/ConcurrentWorkflowVariableOperations.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/impl/ConcurrentWorkflowVariableOperations.java
new file mode 100644
index 00000000..0434a95f
--- /dev/null
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/impl/ConcurrentWorkflowVariableOperations.java
@@ -0,0 +1,70 @@
+package com.qqchen.deploy.backend.workflow.service.impl;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
+import com.qqchen.deploy.backend.workflow.service.IWorkflowVariableService;
+import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.support.TransactionTemplate;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 并发安全的工作流变量操作实现
+ */
+@Slf4j
+@Service
+public class ConcurrentWorkflowVariableOperations implements WorkflowVariableOperations {
+
+ @Resource
+ private IWorkflowVariableService variableService;
+
+ private final Cache> variableCache;
+
+ public ConcurrentWorkflowVariableOperations() {
+ this.variableCache = Caffeine.newBuilder()
+ .expireAfterWrite(30, TimeUnit.MINUTES)
+ .maximumSize(10000)
+ .build();
+ }
+
+ @Override
+ public Map getVariables(Long workflowInstanceId) {
+ return variableCache.get(workflowInstanceId,
+ id -> variableService.getVariables(id));
+ }
+
+ @Override
+ public void setVariables(Long workflowInstanceId, Map variables) {
+ if (variables == null || variables.isEmpty()) {
+ return;
+ }
+ variableService.setVariables(workflowInstanceId, variables);
+ variableCache.put(workflowInstanceId, variables);
+ }
+
+ @Override
+ public Object getVariable(Long workflowInstanceId, String key) {
+ Map variables = getVariables(workflowInstanceId);
+ return variables != null ? variables.get(key) : null;
+ }
+
+ @Override
+ public void setVariable(Long workflowInstanceId, String key, Object value) {
+ Map variables = getVariables(workflowInstanceId);
+ if (variables != null) {
+ variables.put(key, value);
+ setVariables(workflowInstanceId, variables);
+ }
+ }
+
+ @Override
+ public void clearVariables(Long workflowInstanceId) {
+ variableService.clearVariables(workflowInstanceId);
+ variableCache.invalidate(workflowInstanceId);
+ }
+}
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/impl/WorkflowInstanceServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/impl/WorkflowInstanceServiceImpl.java
index e5325b56..82b030f3 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/impl/WorkflowInstanceServiceImpl.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/impl/WorkflowInstanceServiceImpl.java
@@ -11,7 +11,7 @@ import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
-import com.qqchen.deploy.backend.workflow.service.IWorkflowVariableService;
+import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -34,7 +34,7 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl workflowVariableService.setVariable(savedInstance.getId(), key, value));
+ variableOperations.setVariables(savedInstance.getId(), variables);
}
return converter.toDto(savedInstance);
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/impl/WorkflowLogServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/impl/WorkflowLogServiceImpl.java
index 9184da73..5896ed21 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/impl/WorkflowLogServiceImpl.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/service/impl/WorkflowLogServiceImpl.java
@@ -1,10 +1,12 @@
package com.qqchen.deploy.backend.workflow.service.impl;
-import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
+import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.workflow.dto.WorkflowLogDTO;
+import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowLogRepository;
+import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
import com.qqchen.deploy.backend.workflow.service.IWorkflowLogService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@@ -23,17 +25,33 @@ public class WorkflowLogServiceImpl extends BaseServiceImpl {
- WorkflowVariable newVar = new WorkflowVariable();
- newVar.setWorkflowInstanceId(workflowInstanceId);
- newVar.setName(name);
- newVar.setScope(VariableScopeEnum.GLOBAL.name());
- return newVar;
- });
-
- variable.setValue(objectMapper.writeValueAsString(value));
- variable.setType(value.getClass().getName());
- variableRepository.save(variable);
- } catch (JsonProcessingException e) {
- throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_TYPE_INVALID);
+ public void setVariables(Long workflowInstanceId, Map variables) {
+ if (variables == null || variables.isEmpty()) {
+ return;
}
+
+ // 先删除已有的变量
+ clearVariables(workflowInstanceId);
+
+ // 批量保存新变量
+ variables.forEach((key, value) -> {
+ try {
+ WorkflowVariable variable = new WorkflowVariable();
+ WorkflowInstance instance = new WorkflowInstance();
+ instance.setId(workflowInstanceId);
+ variable.setWorkflowInstance(instance);
+ variable.setName(key);
+ variable.setValue(objectMapper.writeValueAsString(value));
+ variableRepository.save(variable);
+ } catch (JsonProcessingException e) {
+ throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_SERIALIZE_ERROR);
+ }
+ });
}
@Override
- @Transactional(readOnly = true)
public Map getVariables(Long workflowInstanceId) {
List variables = variableRepository.findByWorkflowInstanceId(workflowInstanceId);
- return convertToMap(variables);
+ return deserializeVariables(variables);
}
@Override
- @Transactional(readOnly = true)
public Map getVariablesByScope(Long workflowInstanceId, VariableScopeEnum scope) {
- List variables = variableRepository.findByWorkflowInstanceIdAndScope(workflowInstanceId, scope.name());
- return convertToMap(variables);
+ List variables = variableRepository.findByWorkflowInstanceIdAndScope(workflowInstanceId, scope);
+ return deserializeVariables(variables);
}
@Override
@Transactional
- public void deleteVariables(Long workflowInstanceId) {
+ public void clearVariables(Long workflowInstanceId) {
variableRepository.deleteByWorkflowInstanceId(workflowInstanceId);
}
- private Map convertToMap(List variables) {
+ private Map deserializeVariables(List variables) {
Map result = new HashMap<>();
- for (WorkflowVariable variable : variables) {
+ variables.forEach(variable -> {
try {
- Class> type = Class.forName(variable.getType());
- Object value = objectMapper.readValue(variable.getValue(), type);
+ Object value = objectMapper.readValue(variable.getValue(), Object.class);
result.put(variable.getName(), value);
- } catch (Exception e) {
- throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_TYPE_INVALID);
+ } catch (JsonProcessingException e) {
+ throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_DESERIALIZE_ERROR);
}
- }
+ });
return result;
}
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml
index 4ac9e7a6..e3e359f3 100644
--- a/backend/src/main/resources/application.yml
+++ b/backend/src/main/resources/application.yml
@@ -4,7 +4,7 @@ spring:
datasource:
url: jdbc:mysql://localhost:3306/deploy-ease-platform?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: root
- password: root
+ password: ServBay.dev
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
diff --git a/backend/src/main/resources/dashboards/workflow-context-monitor.json b/backend/src/main/resources/dashboards/workflow-context-monitor.json
new file mode 100644
index 00000000..8b81c811
--- /dev/null
+++ b/backend/src/main/resources/dashboards/workflow-context-monitor.json
@@ -0,0 +1,202 @@
+{
+ "annotations": {
+ "list": []
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": null,
+ "links": [],
+ "panels": [
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "fieldConfig": {
+ "defaults": {},
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 0
+ },
+ "hiddenSeries": false,
+ "id": 2,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "7.5.5",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "rate(workflow_variable_operations_total{type=\"set\"}[5m])",
+ "legendFormat": "Set Operations",
+ "refId": "A"
+ },
+ {
+ "expr": "rate(workflow_variable_operations_total{type=\"get\"}[5m])",
+ "legendFormat": "Get Operations",
+ "refId": "B"
+ }
+ ],
+ "thresholds": [],
+ "timeRegions": [],
+ "title": "Variable Operations Rate",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "ops",
+ "label": "Operations/sec",
+ "logBase": 1,
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "fieldConfig": {
+ "defaults": {},
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 0
+ },
+ "hiddenSeries": false,
+ "id": 3,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "7.5.5",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "histogram_quantile(0.95, rate(workflow_variable_operation_duration_seconds_bucket[5m]))",
+ "legendFormat": "95th percentile",
+ "refId": "A"
+ },
+ {
+ "expr": "histogram_quantile(0.50, rate(workflow_variable_operation_duration_seconds_bucket[5m]))",
+ "legendFormat": "Median",
+ "refId": "B"
+ }
+ ],
+ "thresholds": [],
+ "timeRegions": [],
+ "title": "Operation Duration",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "s",
+ "label": "Duration",
+ "logBase": 1,
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false
+ }
+ }
+ ],
+ "refresh": "5s",
+ "schemaVersion": 27,
+ "style": "dark",
+ "tags": ["workflow", "variables"],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-1h",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "",
+ "title": "Workflow Context Monitor",
+ "uid": "workflow-context",
+ "version": 1
+}
diff --git a/backend/src/main/resources/db/migration/V1.0.0__init_schema.sql b/backend/src/main/resources/db/migration/V1.0.0__init_schema.sql
index 2c582c94..4cc4ab01 100644
--- a/backend/src/main/resources/db/migration/V1.0.0__init_schema.sql
+++ b/backend/src/main/resources/db/migration/V1.0.0__init_schema.sql
@@ -443,8 +443,7 @@ CREATE TABLE wf_workflow_instance (
end_time DATETIME(6) NULL COMMENT '结束时间',
error TEXT NULL COMMENT '错误信息',
- CONSTRAINT FK_workflow_instance_definition FOREIGN KEY (workflow_definition_id) REFERENCES wf_workflow_definition (id),
- CONSTRAINT UK_workflow_instance_business_key UNIQUE (business_key)
+ CONSTRAINT FK_workflow_instance_definition FOREIGN KEY (workflow_definition_id) REFERENCES wf_workflow_definition (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流实例表';
-- 节点实例表
@@ -495,17 +494,18 @@ CREATE TABLE wf_workflow_variable (
-- 工作流日志表
CREATE TABLE wf_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
- workflow_instance_id BIGINT NOT NULL,
- node_id VARCHAR(50),
- message VARCHAR(1000) NOT NULL,
- level VARCHAR(20) NOT NULL,
+ workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID',
+ node_id VARCHAR(50) NULL COMMENT '节点ID',
+ level VARCHAR(10) NOT NULL COMMENT '日志级别',
+ message TEXT NOT NULL COMMENT '日志内容',
detail TEXT,
create_time DATETIME NOT NULL,
create_by VARCHAR(50),
update_time DATETIME,
update_by VARCHAR(50),
version INT DEFAULT 0,
- deleted BOOLEAN DEFAULT FALSE
+ deleted BOOLEAN DEFAULT FALSE,
+ CONSTRAINT FK_workflow_log_instance FOREIGN KEY (workflow_instance_id) REFERENCES wf_workflow_instance (id)
);
-- 创建索引
diff --git a/backend/src/main/resources/messages.properties b/backend/src/main/resources/messages.properties
index a0f7a532..93d429e5 100644
--- a/backend/src/main/resources/messages.properties
+++ b/backend/src/main/resources/messages.properties
@@ -1,238 +1,242 @@
-# 通用响应
-response.success=操作成功
-response.error=系统错误
-response.invalid.param=无效的参数
-response.unauthorized=未授权
-response.forbidden=禁止访问
-response.not.found=资源未找到
-response.conflict=资源冲突
-response.unauthorized.full=访问此资源需要完全身份验证
+# \u901A\u7528\u54CD\u5E94
+response.success=\u64CD\u4F5C\u6210\u529F
+response.error=\u7CFB\u7EDF\u9519\u8BEF
+response.invalid.param=\u65E0\u6548\u7684\u53C2\u6570
+response.unauthorized=\u672A\u6388\u6743
+response.forbidden=\u7981\u6B62\u8BBF\u95EE
+response.not.found=\u8D44\u6E90\u672A\u627E\u5230
+response.conflict=\u8D44\u6E90\u51B2\u7A81
+response.unauthorized.full=\u8BBF\u95EE\u6B64\u8D44\u6E90\u9700\u8981\u5B8C\u5168\u8EAB\u4EFD\u9A8C\u8BC1
-# 业务错误
-tenant.not.found=租户不存在
-data.not.found=找不到ID为{0}的{1}
+# \u4E1A\u52A1\u9519\u8BEF
+tenant.not.found=\u79DF\u6237\u4E0D\u5B58\u5728
+data.not.found=\u627E\u4E0D\u5230ID\u4E3A{0}\u7684{1}
-# 用户相关
-user.not.found=用户不存在
-user.username.exists=用户名"{0}"已存在
-user.email.exists=邮箱"{0}"已存在
-user.login.error=用户名或密码错误
+# \u7528\u6237\u76F8\u5173
+user.not.found=\u7528\u6237\u4E0D\u5B58\u5728
+user.username.exists=\u7528\u6237\u540D"{0}"\u5DF2\u5B58\u5728
+user.email.exists=\u90AE\u7BB1"{0}"\u5DF2\u5B58\u5728
+user.login.error=\u7528\u6237\u540D\u6216\u5BC6\u7801\u9519\u8BEF
-# 系统异常消息
-system.optimistic.lock.error=数据已被其他用户修改,请刷新后重试
-system.pessimistic.lock.error=数据正被其他用户操作,请稍后重试
-system.concurrent.update.error=并发更新冲突,请重试
-system.retry.exceeded.error=操作重试次数超限,请稍后再试
+# \u7CFB\u7EDF\u5F02\u5E38\u6D88\u606F
+system.optimistic.lock.error=\u6570\u636E\u5DF2\u88AB\u5176\u4ED6\u7528\u6237\u4FEE\u6539\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5
+system.pessimistic.lock.error=\u6570\u636E\u6B63\u88AB\u5176\u4ED6\u7528\u6237\u64CD\u4F5C\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5
+system.concurrent.update.error=\u5E76\u53D1\u66F4\u65B0\u51B2\u7A81\uFF0C\u8BF7\u91CD\u8BD5
+system.retry.exceeded.error=\u64CD\u4F5C\u91CD\u8BD5\u6B21\u6570\u8D85\u9650\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5
# Entity Not Found Messages
-entity.not.found.id=找不到ID为{0}的实体
+entity.not.found.id=\u627E\u4E0D\u5230ID\u4E3A{0}\u7684\u5B9E\u4F53
entity.not.found.message={0}
-entity.not.found.name.id=找不到ID为{1}的{0}
+entity.not.found.name.id=\u627E\u4E0D\u5230ID\u4E3A{1}\u7684{0}
-# 依赖注入相关
-dependency.injection.service.not.found=找不到实体 {0} 对应的服务 (尝试过的bean名称: {1})
-dependency.injection.repository.not.found=找不到实体 {0} 对应的Repository: {1}
-dependency.injection.converter.not.found=找不到实体 {0} 对应的Converter: {1}
-dependency.injection.entitypath.failed=初始化实体 {0} 的EntityPath失败: {1}
+# \u4F9D\u8D56\u6CE8\u5165\u76F8\u5173
+dependency.injection.service.not.found=\u627E\u4E0D\u5230\u5B9E\u4F53 {0} \u5BF9\u5E94\u7684\u670D\u52A1 (\u5C1D\u8BD5\u8FC7\u7684bean\u540D\u79F0: {1})
+dependency.injection.repository.not.found=\u627E\u4E0D\u5230\u5B9E\u4F53 {0} \u5BF9\u5E94\u7684Repository: {1}
+dependency.injection.converter.not.found=\u627E\u4E0D\u5230\u5B9E\u4F53 {0} \u5BF9\u5E94\u7684Converter: {1}
+dependency.injection.entitypath.failed=\u521D\u59CB\u5316\u5B9E\u4F53 {0} \u7684EntityPath\u5931\u8D25: {1}
-# JWT相关
-jwt.token.expired=登录已过期,请重新登录
-jwt.token.invalid=无效的登录凭证
-jwt.token.missing=未提供登录凭证
+# JWT\u76F8\u5173
+jwt.token.expired=\u767B\u5F55\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
+jwt.token.invalid=\u65E0\u6548\u7684\u767B\u5F55\u51ED\u8BC1
+jwt.token.missing=\u672A\u63D0\u4F9B\u767B\u5F55\u51ED\u8BC1
-# 角色相关错误消息
-role.not.found=角色不存在
-role.code.exists=角色编码"{0}"已存在
-role.name.exists=角色名称"{0}"已存在
-role.in.use=角色正在使用中,无法删除
-role.admin.cannot.delete=不能删除超级管理员角色
-role.admin.cannot.update=不能修改超级管理员角色
-role.tag.name.exists=标签名称已存在
-role.tag.not.found=标签不存在
-role.tag.in.use=标签正在使用中,无法删除
+# \u89D2\u8272\u76F8\u5173\u9519\u8BEF\u6D88\u606F
+role.not.found=\u89D2\u8272\u4E0D\u5B58\u5728
+role.code.exists=\u89D2\u8272\u7F16\u7801"{0}"\u5DF2\u5B58\u5728
+role.name.exists=\u89D2\u8272\u540D\u79F0"{0}"\u5DF2\u5B58\u5728
+role.in.use=\u89D2\u8272\u6B63\u5728\u4F7F\u7528\u4E2D\uFF0C\u65E0\u6CD5\u5220\u9664
+role.admin.cannot.delete=\u4E0D\u80FD\u5220\u9664\u8D85\u7EA7\u7BA1\u7406\u5458\u89D2\u8272
+role.admin.cannot.update=\u4E0D\u80FD\u4FEE\u6539\u8D85\u7EA7\u7BA1\u7406\u5458\u89D2\u8272
+role.tag.name.exists=\u6807\u7B7E\u540D\u79F0\u5DF2\u5B58\u5728
+role.tag.not.found=\u6807\u7B7E\u4E0D\u5B58\u5728
+role.tag.in.use=\u6807\u7B7E\u6B63\u5728\u4F7F\u7528\u4E2D\uFF0C\u65E0\u6CD5\u5220\u9664
-# 部门相关
-department.not.found=部门不存在
-department.code.exists=部门编码已存在
-department.name.exists=部门名称已存在
-department.parent.not.found=上级部门不存在
-department.has.children=该部门下有子部门,无法删除
+# \u90E8\u95E8\u76F8\u5173
+department.not.found=\u90E8\u95E8\u4E0D\u5B58\u5728
+department.code.exists=\u90E8\u95E8\u7F16\u7801\u5DF2\u5B58\u5728
+department.name.exists=\u90E8\u95E8\u540D\u79F0\u5DF2\u5B58\u5728
+department.parent.not.found=\u4E0A\u7EA7\u90E8\u95E8\u4E0D\u5B58\u5728
+department.has.children=\u8BE5\u90E8\u95E8\u4E0B\u6709\u5B50\u90E8\u95E8\uFF0C\u65E0\u6CD5\u5220\u9664
-# 权限相关
-permission.not.found=权限不存在
-permission.code.exists=权限编码{0}已存在
-permission.name.exists=权限名称{0}已存在
-permission.already.assigned=该权限已分配给角色
-permission.assign.failed=权限分配失败
+# \u6743\u9650\u76F8\u5173
+permission.not.found=\u6743\u9650\u4E0D\u5B58\u5728
+permission.code.exists=\u6743\u9650\u7F16\u7801{0}\u5DF2\u5B58\u5728
+permission.name.exists=\u6743\u9650\u540D\u79F0{0}\u5DF2\u5B58\u5728
+permission.already.assigned=\u8BE5\u6743\u9650\u5DF2\u5206\u914D\u7ED9\u89D2\u8272
+permission.assign.failed=\u6743\u9650\u5206\u914D\u5931\u8D25
-# 第三方系统相关
-external.system.name.exists=系统名称"{0}"已存在
-external.system.type.url.exists=系统类型和URL组合"{0}"已存在
-external.system.disabled=系统已禁用
-external.system.sync.failed=系统数据同步失败
-external.system.type.not.supported=不支持的系统类型
+# \u7B2C\u4E09\u65B9\u7CFB\u7EDF\u76F8\u5173
+external.system.name.exists=\u7CFB\u7EDF\u540D\u79F0"{0}"\u5DF2\u5B58\u5728
+external.system.type.url.exists=\u7CFB\u7EDF\u7C7B\u578B\u548CURL\u7EC4\u5408"{0}"\u5DF2\u5B58\u5728
+external.system.disabled=\u7CFB\u7EDF\u5DF2\u7981\u7528
+external.system.sync.failed=\u7CFB\u7EDF\u6570\u636E\u540C\u6B65\u5931\u8D25
+external.system.type.not.supported=\u4E0D\u652F\u6301\u7684\u7CFB\u7EDF\u7C7B\u578B
-# Git系统相关错误
-external.system.git.auth.type.error=Git系统只支持Token认证
-external.system.git.token.required=Git系统必须提供Token
+# Git\u7CFB\u7EDF\u76F8\u5173\u9519\u8BEF
+external.system.git.auth.type.error=Git\u7CFB\u7EDF\u53EA\u652F\u6301Token\u8BA4\u8BC1
+external.system.git.token.required=Git\u7CFB\u7EDF\u5FC5\u987B\u63D0\u4F9BToken
-# 仓库相关错误消息
-repository.group.not.found=仓库组不存在
-repository.group.name.exists=仓库组名称"{0}"已存在
-repository.group.path.exists=仓库组路径"{0}"已存在
-repository.project.not.found=仓库项目不存在
-repository.project.name.exists=仓库项目名称"{0}"已存在
-repository.project.path.exists=仓库项目路径"{0}"已存在
-repository.branch.not.found=分支不存在
-repository.branch.name.exists=分支名称"{0}"已存在
-repository.sync.in.progress=仓库同步正在进行中
-repository.sync.failed=仓库同步失败:{0}
-repository.sync.history.not.found=同步历史记录不存在
+# \u4ED3\u5E93\u76F8\u5173\u9519\u8BEF\u6D88\u606F
+repository.group.not.found=\u4ED3\u5E93\u7EC4\u4E0D\u5B58\u5728
+repository.group.name.exists=\u4ED3\u5E93\u7EC4\u540D\u79F0"{0}"\u5DF2\u5B58\u5728
+repository.group.path.exists=\u4ED3\u5E93\u7EC4\u8DEF\u5F84"{0}"\u5DF2\u5B58\u5728
+repository.project.not.found=\u4ED3\u5E93\u9879\u76EE\u4E0D\u5B58\u5728
+repository.project.name.exists=\u4ED3\u5E93\u9879\u76EE\u540D\u79F0"{0}"\u5DF2\u5B58\u5728
+repository.project.path.exists=\u4ED3\u5E93\u9879\u76EE\u8DEF\u5F84"{0}"\u5DF2\u5B58\u5728
+repository.branch.not.found=\u5206\u652F\u4E0D\u5B58\u5728
+repository.branch.name.exists=\u5206\u652F\u540D\u79F0"{0}"\u5DF2\u5B58\u5728
+repository.sync.in.progress=\u4ED3\u5E93\u540C\u6B65\u6B63\u5728\u8FDB\u884C\u4E2D
+repository.sync.failed=\u4ED3\u5E93\u540C\u6B65\u5931\u8D25\uFF1A{0}
+repository.sync.history.not.found=\u540C\u6B65\u5386\u53F2\u8BB0\u5F55\u4E0D\u5B58\u5728
-# 工作流相关错误消息
-workflow.definition.not.found=工作流定义不存在
-workflow.definition.code.exists=工作流定义编码"{0}"已存在
-workflow.definition.name.exists=工作流定义名称"{0}"已存在
-workflow.definition.invalid.content=工作流定义内容无效:{0}
-workflow.definition.not.published=工作流定义未发布
-workflow.definition.already.published=工作流定义已发布
-workflow.definition.cannot.delete=工作流定义已被使用,无法删除
+# \u5DE5\u4F5C\u6D41\u76F8\u5173\u9519\u8BEF\u6D88\u606F
+workflow.definition.not.found=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u4E0D\u5B58\u5728
+workflow.definition.code.exists=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u7F16\u7801"{0}"\u5DF2\u5B58\u5728
+workflow.definition.name.exists=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u540D\u79F0"{0}"\u5DF2\u5B58\u5728
+workflow.definition.invalid.content=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u5185\u5BB9\u65E0\u6548\uFF1A{0}
+workflow.definition.not.published=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u672A\u53D1\u5E03
+workflow.definition.already.published=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u5DF2\u53D1\u5E03
+workflow.definition.cannot.delete=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u5DF2\u88AB\u4F7F\u7528\uFF0C\u65E0\u6CD5\u5220\u9664
-workflow.instance.not.found=工作流实例不存在
-workflow.instance.cannot.start=工作流实例无法启动
-workflow.instance.cannot.cancel=工作流实例无法取消
-workflow.instance.cannot.pause=工作流实例无法暂停
-workflow.instance.cannot.resume=工作流实例无法恢复
-workflow.instance.cannot.retry=工作流实例无法重试
+workflow.instance.not.found=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u4E0D\u5B58\u5728
+workflow.instance.cannot.start=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u65E0\u6CD5\u542F\u52A8
+workflow.instance.cannot.cancel=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u65E0\u6CD5\u53D6\u6D88
+workflow.instance.cannot.pause=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u65E0\u6CD5\u6682\u505C
+workflow.instance.cannot.resume=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u65E0\u6CD5\u6062\u590D
+workflow.instance.cannot.retry=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u65E0\u6CD5\u91CD\u8BD5
-# 节点相关错误消息
-node.instance.not.found=节点实例不存在
-node.instance.cannot.retry=节点实例无法重试
-node.instance.cannot.skip=节点实例无法跳过
-node.executor.not.found=节点执行器不存在
+# \u8282\u70B9\u76F8\u5173\u9519\u8BEF\u6D88\u606F
+node.instance.not.found=\u8282\u70B9\u5B9E\u4F8B\u4E0D\u5B58\u5728
+node.instance.cannot.retry=\u8282\u70B9\u5B9E\u4F8B\u65E0\u6CD5\u91CD\u8BD5
+node.instance.cannot.skip=\u8282\u70B9\u5B9E\u4F8B\u65E0\u6CD5\u8DF3\u8FC7
+node.executor.not.found=\u8282\u70B9\u6267\u884C\u5668\u4E0D\u5B58\u5728
-# 工作流相关消息
-workflow.not.found=工作流定义不存在
-workflow.code.exists=工作流编码已存在
-workflow.name.exists=工作流名称已存在
-workflow.disabled=工作流已禁用
-workflow.not.published=工作流未发布
-workflow.already.published=工作流已发布
-workflow.already.disabled=工作流已禁用
-workflow.instance.not.found=工作流实例不存在
-workflow.instance.already.completed=工作流实例已完成
-workflow.instance.already.canceled=工作流实例已取消
-workflow.instance.not.running=工作流实例未运行
-workflow.node.not.found=工作流节点不存在
-workflow.node.type.not.supported=不支持的节点类型
-workflow.node.config.invalid=节点配置无效
-workflow.node.execution.failed=节点执行失败
-workflow.node.timeout=节点执行超时
-workflow.variable.not.found=工作流变量不存在
-workflow.variable.type.invalid=工作流变量类型无效
-workflow.permission.denied=工作流权限不足
-workflow.approval.required=需要审批
-workflow.approval.rejected=审批被拒绝
-workflow.dependency.not.satisfied=依赖条件不满足
-workflow.circular.dependency=存在循环依赖
-workflow.schedule.invalid=调度配置无效
-workflow.concurrent.limit.exceeded=超出并发限制
+# \u5DE5\u4F5C\u6D41\u76F8\u5173\u6D88\u606F
+workflow.not.found=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u4E0D\u5B58\u5728
+workflow.code.exists=\u5DE5\u4F5C\u6D41\u7F16\u7801\u5DF2\u5B58\u5728
+workflow.name.exists=\u5DE5\u4F5C\u6D41\u540D\u79F0\u5DF2\u5B58\u5728
+workflow.disabled=\u5DE5\u4F5C\u6D41\u5DF2\u7981\u7528
+workflow.not.published=\u5DE5\u4F5C\u6D41\u672A\u53D1\u5E03
+workflow.already.published=\u5DE5\u4F5C\u6D41\u5DF2\u53D1\u5E03
+workflow.already.disabled=\u5DE5\u4F5C\u6D41\u5DF2\u7981\u7528
+workflow.instance.not.found=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u4E0D\u5B58\u5728
+workflow.instance.already.completed=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u5DF2\u5B8C\u6210
+workflow.instance.already.canceled=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u5DF2\u53D6\u6D88
+workflow.instance.not.running=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u672A\u8FD0\u884C
+workflow.node.not.found=\u5DE5\u4F5C\u6D41\u8282\u70B9\u4E0D\u5B58\u5728
+workflow.node.type.not.supported=\u4E0D\u652F\u6301\u7684\u8282\u70B9\u7C7B\u578B
+workflow.node.config.invalid=\u8282\u70B9\u914D\u7F6E\u65E0\u6548
+workflow.node.execution.failed=\u8282\u70B9\u6267\u884C\u5931\u8D25
+workflow.node.timeout=\u8282\u70B9\u6267\u884C\u8D85\u65F6
+workflow.variable.not.found=\u5DE5\u4F5C\u6D41\u53D8\u91CF\u4E0D\u5B58\u5728
+workflow.variable.type.invalid=\u5DE5\u4F5C\u6D41\u53D8\u91CF\u7C7B\u578B\u65E0\u6548
+workflow.permission.denied=\u5DE5\u4F5C\u6D41\u6743\u9650\u4E0D\u8DB3
+workflow.approval.required=\u9700\u8981\u5BA1\u6279
+workflow.approval.rejected=\u5BA1\u6279\u88AB\u62D2\u7EDD
+workflow.dependency.not.satisfied=\u4F9D\u8D56\u6761\u4EF6\u4E0D\u6EE1\u8DB3
+workflow.circular.dependency=\u5B58\u5728\u5FAA\u73AF\u4F9D\u8D56
+workflow.schedule.invalid=\u8C03\u5EA6\u914D\u7F6E\u65E0\u6548
+workflow.concurrent.limit.exceeded=\u8D85\u51FA\u5E76\u53D1\u9650\u5236
# Workflow error messages
-workflow.not.found=工作流定义不存在
-workflow.code.exists=工作流编码已存在
-workflow.name.exists=工作流名称已存在
-workflow.invalid.status=工作流状态无效
-workflow.node.not.found=工作流节点不存在
-workflow.node.config.error=工作流节点配置错误
-workflow.execution.error=工作流执行错误
-workflow.not.draft=只有草稿状态的工作流定义可以发布
-workflow.not.published=只有已发布状态的工作流定义可以禁用
-workflow.not.disabled=只有已禁用状态的工作流定义可以启用
+workflow.not.found=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u4E0D\u5B58\u5728
+workflow.code.exists=\u5DE5\u4F5C\u6D41\u7F16\u7801\u5DF2\u5B58\u5728
+workflow.name.exists=\u5DE5\u4F5C\u6D41\u540D\u79F0\u5DF2\u5B58\u5728
+workflow.invalid.status=\u5DE5\u4F5C\u6D41\u72B6\u6001\u65E0\u6548
+workflow.node.not.found=\u5DE5\u4F5C\u6D41\u8282\u70B9\u4E0D\u5B58\u5728
+workflow.node.config.error=\u5DE5\u4F5C\u6D41\u8282\u70B9\u914D\u7F6E\u9519\u8BEF
+workflow.execution.error=\u5DE5\u4F5C\u6D41\u6267\u884C\u9519\u8BEF
+workflow.not.draft=\u53EA\u6709\u8349\u7A3F\u72B6\u6001\u7684\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u53EF\u4EE5\u53D1\u5E03
+workflow.not.published=\u53EA\u6709\u5DF2\u53D1\u5E03\u72B6\u6001\u7684\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u53EF\u4EE5\u7981\u7528
+workflow.not.disabled=\u53EA\u6709\u5DF2\u7981\u7528\u72B6\u6001\u7684\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u53EF\u4EE5\u542F\u7528
# System level messages (1xxx)
-success=操作成功
-system.error=系统错误
-param.error=参数错误
-unauthorized=未授权
-forbidden=禁止访问
-not.found=资源不存在
-method.not.allowed=方法不允许
-conflict=资源冲突
-too.many.requests=请求过于频繁
-internal.server.error=内部服务器错误
+success=\u64CD\u4F5C\u6210\u529F
+system.error=\u7CFB\u7EDF\u9519\u8BEF
+param.error=\u53C2\u6570\u9519\u8BEF
+unauthorized=\u672A\u6388\u6743
+forbidden=\u7981\u6B62\u8BBF\u95EE
+not.found=\u8D44\u6E90\u4E0D\u5B58\u5728
+method.not.allowed=\u65B9\u6CD5\u4E0D\u5141\u8BB8
+conflict=\u8D44\u6E90\u51B2\u7A81
+too.many.requests=\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41
+internal.server.error=\u5185\u90E8\u670D\u52A1\u5668\u9519\u8BEF
# Business level messages (2xxx)
# Common business messages (2000-2099)
-business.error=业务错误
-data.not.found=数据不存在
-data.already.exists=数据已存在
-data.validation.failed=数据验证失败
-operation.not.allowed=操作不允许
+business.error=\u4E1A\u52A1\u9519\u8BEF
+data.not.found=\u6570\u636E\u4E0D\u5B58\u5728
+data.already.exists=\u6570\u636E\u5DF2\u5B58\u5728
+data.validation.failed=\u6570\u636E\u9A8C\u8BC1\u5931\u8D25
+operation.not.allowed=\u64CD\u4F5C\u4E0D\u5141\u8BB8
# Workflow related messages (2100-2199)
-workflow.not.found=工作流不存在
-workflow.already.exists=工作流已存在
-workflow.not.published=工作流未发布
-workflow.config.invalid=工作流配置无效
-workflow.node.not.found=工作流节点不存在
-workflow.node.execution.failed=工作流节点执行失败
-workflow.instance.not.found=工作流实例不存在
-workflow.instance.not.running=工作流实例未运行
-workflow.variable.not.found=工作流变量不存在
-workflow.log.not.found=工作流日志不存在
-workflow.transition.invalid=工作流流转配置无效
-workflow.node.type.not.supported=不支持的节点类型
-workflow.condition.invalid=工作流条件配置无效
+workflow.not.found=\u5DE5\u4F5C\u6D41\u4E0D\u5B58\u5728
+workflow.already.exists=\u5DE5\u4F5C\u6D41\u5DF2\u5B58\u5728
+workflow.not.published=\u5DE5\u4F5C\u6D41\u672A\u53D1\u5E03
+workflow.config.invalid=\u5DE5\u4F5C\u6D41\u914D\u7F6E\u65E0\u6548
+workflow.node.not.found=\u5DE5\u4F5C\u6D41\u8282\u70B9\u4E0D\u5B58\u5728
+workflow.node.execution.failed=\u5DE5\u4F5C\u6D41\u8282\u70B9\u6267\u884C\u5931\u8D25
+workflow.instance.not.found=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u4E0D\u5B58\u5728
+workflow.instance.not.running=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u672A\u8FD0\u884C
+workflow.variable.not.found=\u5DE5\u4F5C\u6D41\u53D8\u91CF\u4E0D\u5B58\u5728
+workflow.log.not.found=\u5DE5\u4F5C\u6D41\u65E5\u5FD7\u4E0D\u5B58\u5728
+workflow.transition.invalid=\u5DE5\u4F5C\u6D41\u6D41\u8F6C\u914D\u7F6E\u65E0\u6548
+workflow.node.type.not.supported=\u4E0D\u652F\u6301\u7684\u8282\u70B9\u7C7B\u578B
+workflow.condition.invalid=\u5DE5\u4F5C\u6D41\u6761\u4EF6\u914D\u7F6E\u65E0\u6548
-# 工作流相关错误消息
-workflow.not.found=工作流定义不存在
-workflow.code.exists=工作流编码已存在
-workflow.name.exists=工作流名称已存在
-workflow.disabled=工作流已禁用
-workflow.not.published=工作流未发布
-workflow.already.published=工作流已发布
-workflow.already.disabled=工作流已禁用
-workflow.not.draft=工作流不是草稿状态
-workflow.not.disabled=工作流不是禁用状态
-workflow.invalid.status=工作流状态无效
-workflow.config.invalid=工作流配置无效
-workflow.transition.invalid=工作流流转规则无效
-workflow.condition.invalid=工作流条件配置无效
+# \u5DE5\u4F5C\u6D41\u76F8\u5173\u9519\u8BEF\u6D88\u606F
+workflow.not.found=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u4E0D\u5B58\u5728
+workflow.code.exists=\u5DE5\u4F5C\u6D41\u7F16\u7801\u5DF2\u5B58\u5728
+workflow.name.exists=\u5DE5\u4F5C\u6D41\u540D\u79F0\u5DF2\u5B58\u5728
+workflow.disabled=\u5DE5\u4F5C\u6D41\u5DF2\u7981\u7528
+workflow.not.published=\u5DE5\u4F5C\u6D41\u672A\u53D1\u5E03
+workflow.already.published=\u5DE5\u4F5C\u6D41\u5DF2\u53D1\u5E03
+workflow.already.disabled=\u5DE5\u4F5C\u6D41\u5DF2\u7981\u7528
+workflow.not.draft=\u5DE5\u4F5C\u6D41\u4E0D\u662F\u8349\u7A3F\u72B6\u6001
+workflow.not.disabled=\u5DE5\u4F5C\u6D41\u4E0D\u662F\u7981\u7528\u72B6\u6001
+workflow.invalid.status=\u5DE5\u4F5C\u6D41\u72B6\u6001\u65E0\u6548
+workflow.config.invalid=\u5DE5\u4F5C\u6D41\u914D\u7F6E\u65E0\u6548
+workflow.transition.invalid=\u5DE5\u4F5C\u6D41\u6D41\u8F6C\u89C4\u5219\u65E0\u6548
+workflow.condition.invalid=\u5DE5\u4F5C\u6D41\u6761\u4EF6\u914D\u7F6E\u65E0\u6548
-workflow.instance.not.found=工作流实例不存在
-workflow.instance.already.completed=工作流实例已完成
-workflow.instance.already.canceled=工作流实例已取消
-workflow.instance.not.running=工作流实例未运行
-workflow.instance.not.paused=工作流实例不是暂停状态
+workflow.instance.not.found=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u4E0D\u5B58\u5728
+workflow.instance.already.completed=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u5DF2\u5B8C\u6210
+workflow.instance.already.canceled=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u5DF2\u53D6\u6D88
+workflow.instance.not.running=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u672A\u8FD0\u884C
+workflow.instance.not.paused=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u4E0D\u662F\u6682\u505C\u72B6\u6001
-workflow.node.not.found=工作流节点不存在
-workflow.node.type.not.supported=不支持的节点类型
-workflow.node.config.invalid=节点配置无效
-workflow.node.execution.failed=节点执行失败
-workflow.node.timeout=节点执行超时
-workflow.node.config.error=节点配置错误
+workflow.node.not.found=\u5DE5\u4F5C\u6D41\u8282\u70B9\u4E0D\u5B58\u5728
+workflow.node.type.not.supported=\u4E0D\u652F\u6301\u7684\u8282\u70B9\u7C7B\u578B
+workflow.node.config.invalid=\u8282\u70B9\u914D\u7F6E\u65E0\u6548
+workflow.node.execution.failed=\u8282\u70B9\u6267\u884C\u5931\u8D25
+workflow.node.timeout=\u8282\u70B9\u6267\u884C\u8D85\u65F6
+workflow.node.config.error=\u8282\u70B9\u914D\u7F6E\u9519\u8BEF
-workflow.execution.error=工作流执行错误
-workflow.variable.not.found=工作流变量不存在
-workflow.variable.type.invalid=工作流变量类型无效
-workflow.permission.denied=无权限操作工作流
-workflow.approval.required=需要审批
-workflow.approval.rejected=审批被拒绝
-workflow.dependency.not.satisfied=工作流依赖条件未满足
-workflow.circular.dependency=工作流存在循环依赖
-workflow.schedule.invalid=工作流调度配置无效
-workflow.concurrent.limit.exceeded=工作流并发限制超出
+workflow.execution.error=\u5DE5\u4F5C\u6D41\u6267\u884C\u9519\u8BEF
+workflow.variable.not.found=\u5DE5\u4F5C\u6D41\u53D8\u91CF\u4E0D\u5B58\u5728
+workflow.variable.type.invalid=\u5DE5\u4F5C\u6D41\u53D8\u91CF\u7C7B\u578B\u65E0\u6548
+workflow.permission.denied=\u65E0\u6743\u9650\u64CD\u4F5C\u5DE5\u4F5C\u6D41
+workflow.approval.required=\u9700\u8981\u5BA1\u6279
+workflow.approval.rejected=\u5BA1\u6279\u88AB\u62D2\u7EDD
+workflow.dependency.not.satisfied=\u5DE5\u4F5C\u6D41\u4F9D\u8D56\u6761\u4EF6\u672A\u6EE1\u8DB3
+workflow.circular.dependency=\u5DE5\u4F5C\u6D41\u5B58\u5728\u5FAA\u73AF\u4F9D\u8D56
+workflow.schedule.invalid=\u5DE5\u4F5C\u6D41\u8C03\u5EA6\u914D\u7F6E\u65E0\u6548
+workflow.concurrent.limit.exceeded=\u5DE5\u4F5C\u6D41\u5E76\u53D1\u9650\u5236\u8D85\u51FA
-# 工作流配置相关错误消息
-workflow.node.config.empty=节点配置不能为空
-workflow.transition.config.empty=流转配置不能为空
-workflow.form.config.empty=表单配置不能为空
-workflow.graph.config.empty=图形配置不能为空
+# \u5DE5\u4F5C\u6D41\u914D\u7F6E\u76F8\u5173\u9519\u8BEF\u6D88\u606F
+workflow.node.config.empty=\u8282\u70B9\u914D\u7F6E\u4E0D\u80FD\u4E3A\u7A7A
+workflow.transition.config.empty=\u6D41\u8F6C\u914D\u7F6E\u4E0D\u80FD\u4E3A\u7A7A
+workflow.form.config.empty=\u8868\u5355\u914D\u7F6E\u4E0D\u80FD\u4E3A\u7A7A
+workflow.graph.config.empty=\u56FE\u5F62\u914D\u7F6E\u4E0D\u80FD\u4E3A\u7A7A
-# 工作流节点类型错误 (2200-2299)
-workflow.node.type.not.found=节点类型不存在或已删除
-workflow.node.type.disabled=节点类型已禁用,无法使用
-workflow.node.type.code.exists=节点类型编码已存在
-workflow.node.type.invalid.category=无效的节点类型分类
-workflow.node.type.invalid.executor=无效的执行器配置
\ No newline at end of file
+# \u5DE5\u4F5C\u6D41\u8282\u70B9\u7C7B\u578B\u9519\u8BEF (2200-2299)
+workflow.node.type.not.found=\u8282\u70B9\u7C7B\u578B\u4E0D\u5B58\u5728\u6216\u5DF2\u5220\u9664
+workflow.node.type.disabled=\u8282\u70B9\u7C7B\u578B\u5DF2\u7981\u7528\uFF0C\u65E0\u6CD5\u4F7F\u7528
+workflow.node.type.code.exists=\u8282\u70B9\u7C7B\u578B\u7F16\u7801\u5DF2\u5B58\u5728
+workflow.node.type.invalid.category=\u65E0\u6548\u7684\u8282\u70B9\u7C7B\u578B\u5206\u7C7B
+workflow.node.type.invalid.executor=\u65E0\u6548\u7684\u6267\u884C\u5668\u914D\u7F6E
+workflow.node.executor.not.found=\u672A\u627E\u5230\u5DE5\u4F5C\u6D41\u8282\u70B9\u6267\u884C\u5668: {0}
+
+workflow.variable.serialize.error=\u5DE5\u4F5C\u6D41\u53D8\u91CF\u5E8F\u5217\u5316\u5931\u8D25: {0}
+workflow.variable.deserialize.error=\u5DE5\u4F5C\u6D41\u53D8\u91CF\u53CD\u5E8F\u5217\u5316\u5931\u8D25: {0}
diff --git a/backend/src/test/java/com/qqchen/deploy/backend/workflow/service/ConcurrentWorkflowVariableOperationsTest.java b/backend/src/test/java/com/qqchen/deploy/backend/workflow/service/ConcurrentWorkflowVariableOperationsTest.java
new file mode 100644
index 00000000..e98e4e1b
--- /dev/null
+++ b/backend/src/test/java/com/qqchen/deploy/backend/workflow/service/ConcurrentWorkflowVariableOperationsTest.java
@@ -0,0 +1,117 @@
+package com.qqchen.deploy.backend.workflow.service;
+
+import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
+import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
+import com.qqchen.deploy.backend.workflow.service.impl.ConcurrentWorkflowVariableOperations;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.transaction.support.TransactionTemplate;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class ConcurrentWorkflowVariableOperationsTest {
+
+ @Mock
+ private IWorkflowVariableService variableService;
+
+ @Mock
+ private TransactionTemplate transactionTemplate;
+
+ @Mock
+ private WorkflowContext.Factory contextFactory;
+
+ @InjectMocks
+ private ConcurrentWorkflowVariableOperations operations;
+
+ private WorkflowInstance instance;
+ private WorkflowContext context;
+
+ @BeforeEach
+ void setUp() {
+ instance = new WorkflowInstance();
+ instance.setId(1L);
+
+ context = mock(WorkflowContext.class);
+
+ when(contextFactory.create(any(), anyMap())).thenReturn(context);
+ when(transactionTemplate.execute(any())).thenAnswer(invocation -> {
+ return invocation.getArgument(0, TransactionCallback.class).doInTransaction(null);
+ });
+ }
+
+ @Test
+ void setVariables_ShouldHandleConcurrentAccess() throws InterruptedException {
+ // 准备测试数据
+ int threadCount = 10;
+ ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
+ CountDownLatch latch = new CountDownLatch(threadCount);
+
+ // 模拟多线程并发访问
+ for (int i = 0; i < threadCount; i++) {
+ final int index = i;
+ executorService.submit(() -> {
+ try {
+ Map variables = new HashMap<>();
+ variables.put("key" + index, "value" + index);
+ operations.setVariables(instance, variables);
+ } finally {
+ latch.countDown();
+ }
+ });
+ }
+
+ // 等待所有线程完成
+ latch.await();
+ executorService.shutdown();
+
+ // 验证结果
+ verify(variableService, times(threadCount)).setVariable(eq(instance), anyString(), any());
+ }
+
+ @Test
+ void getVariables_ShouldReturnCachedValues() {
+ // 准备测试数据
+ Map expectedVariables = new HashMap<>();
+ expectedVariables.put("key1", "value1");
+ when(context.getVariables()).thenReturn(expectedVariables);
+
+ // 第一次调用
+ Map result1 = operations.getVariables(instance);
+ assertEquals(expectedVariables, result1);
+
+ // 第二次调用应该返回缓存的结果
+ Map result2 = operations.getVariables(instance);
+ assertEquals(expectedVariables, result2);
+
+ // 验证 variableService 只被调用一次
+ verify(variableService, times(1)).getVariables(instance);
+ }
+
+ @Test
+ void clearContext_ShouldRemoveFromCache() {
+ // 首先获取变量以确保上下文被缓存
+ operations.getVariables(instance);
+
+ // 清除上下文
+ operations.clearContext(instance);
+
+ // 再次获取变量,应该重新从服务加载
+ operations.getVariables(instance);
+
+ // 验证 variableService 被调用了两次
+ verify(variableService, times(2)).getVariables(instance);
+ }
+}