可正常启动,工作流可以正常运行,但是只有一个开始节点。
This commit is contained in:
parent
7bbdeefa9b
commit
591abe983d
@ -196,6 +196,11 @@
|
||||
<artifactId>commons-exec</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>3.1.8</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -45,14 +45,14 @@ public class WorkflowLogApiController extends BaseController<WorkflowLog, Workfl
|
||||
|
||||
@Operation(summary = "记录日志")
|
||||
@PostMapping("/record")
|
||||
public Response<Void> log(
|
||||
public Response<Void> 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();
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,6 @@ import org.mapstruct.Mapping;
|
||||
public interface NodeInstanceConverter extends BaseConverter<NodeInstance, NodeInstanceDTO> {
|
||||
|
||||
@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<NodeInstance, NodeI
|
||||
NodeInstanceDTO toDto(NodeInstance entity);
|
||||
|
||||
@Override
|
||||
@Mapping(target = "workflowInstanceId", source = "workflowInstanceId")
|
||||
@Mapping(target = "workflowInstance", ignore = true)
|
||||
@Mapping(target = "nodeId", source = "nodeId")
|
||||
@Mapping(target = "nodeType", source = "nodeType")
|
||||
@Mapping(target = "name", source = "name")
|
||||
@ -40,6 +39,5 @@ public interface NodeInstanceConverter extends BaseConverter<NodeInstance, NodeI
|
||||
@Mapping(target = "output", source = "output")
|
||||
@Mapping(target = "error", source = "error")
|
||||
@Mapping(target = "preNodeId", source = "preNodeId")
|
||||
@Mapping(target = "workflowInstance", ignore = true)
|
||||
NodeInstance toEntity(NodeInstanceDTO dto);
|
||||
}
|
||||
@ -4,19 +4,10 @@ import com.qqchen.deploy.backend.framework.converter.BaseConverter;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
/**
|
||||
* 工作流定义转换器
|
||||
*/
|
||||
@Mapper(config = BaseConverter.class, uses = {NodeDefinitionConverter.class})
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface WorkflowDefinitionConverter extends BaseConverter<WorkflowDefinition, WorkflowDefinitionDTO> {
|
||||
|
||||
@Override
|
||||
@Mapping(target = "nodes", source = "nodes")
|
||||
WorkflowDefinitionDTO toDto(WorkflowDefinition entity);
|
||||
|
||||
@Override
|
||||
@Mapping(target = "nodes", source = "nodes")
|
||||
WorkflowDefinition toEntity(WorkflowDefinitionDTO dto);
|
||||
}
|
||||
@ -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<NodeDefinitionDTO> nodes;
|
||||
|
||||
/**
|
||||
* 节点配置
|
||||
* 节点配置(JSON)
|
||||
*/
|
||||
private String nodeConfig;
|
||||
|
||||
/**
|
||||
* 流转配置
|
||||
* 流转配置(JSON)
|
||||
*/
|
||||
private String transitionConfig;
|
||||
|
||||
/**
|
||||
* 表单定义
|
||||
* 表单定义(JSON)
|
||||
*/
|
||||
private String formDefinition;
|
||||
|
||||
/**
|
||||
* 图形信息
|
||||
* 图形信息(JSON)
|
||||
*/
|
||||
private String graphDefinition;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String createBy;
|
||||
}
|
||||
@ -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<NodeTypeEnum, NodeExecutor> 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,15 +91,19 @@ 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);
|
||||
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTOR_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 2. 执行节点
|
||||
WorkflowContext context = workflowContextFactory.create(instance);
|
||||
// 创建工作流上下文
|
||||
WorkflowContextOperations context = DefaultWorkflowContext.builder()
|
||||
.workflowInstance(instance)
|
||||
.variableOperations(variableOperations)
|
||||
.build();
|
||||
|
||||
// 执行节点
|
||||
executor.execute(nodeInstance, context);
|
||||
|
||||
// 3. 更新节点状态
|
||||
@ -107,27 +112,13 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
|
||||
nodeInstanceRepository.save(nodeInstance);
|
||||
|
||||
// 4. 检查是否所有节点都已完成
|
||||
List<NodeInstance> uncompletedNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatusNot(
|
||||
instance.getId(), NodeStatusEnum.COMPLETED);
|
||||
List<NodeInstance> uncompletedNodes = nodeInstanceRepository.findByWorkflowInstanceAndStatusNot(
|
||||
instance, 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());
|
||||
workflowInstanceRepository.save(instance);
|
||||
|
||||
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -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);
|
||||
@ -158,22 +145,11 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
|
||||
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<NodeInstance> 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<NodeInstance> failedNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
|
||||
instanceId, NodeStatusEnum.FAILED);
|
||||
List<NodeInstance> 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("开始节点");
|
||||
|
||||
@ -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<NodeInstance> allNodes;
|
||||
|
||||
/**
|
||||
* 工作流变量
|
||||
* 临时变量(节点间传递,不持久化)
|
||||
*/
|
||||
private Map<String, Object> variables;
|
||||
private final Map<String, Object> tempVariables;
|
||||
|
||||
/**
|
||||
* 临时变量(节点间传递)
|
||||
* 变量操作服务
|
||||
*/
|
||||
private Map<String, Object> 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<String, Object> vars = new ConcurrentHashMap<>();
|
||||
vars.put(key, value);
|
||||
variableOperations.setVariables(instanceId, vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有变量(委托给 WorkflowVariableOperations)
|
||||
* @deprecated 建议直接使用 WorkflowVariableOperations
|
||||
*/
|
||||
@Deprecated
|
||||
public Map<String, Object> getVariables() {
|
||||
return variableOperations.getVariables(instanceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置多个变量(委托给 WorkflowVariableOperations)
|
||||
* @deprecated 建议直接使用 WorkflowVariableOperations
|
||||
*/
|
||||
@Deprecated
|
||||
public void setVariables(Map<String, Object> variables) {
|
||||
variableOperations.setVariables(instanceId, variables);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,6 +100,13 @@ public class WorkflowContext {
|
||||
tempVariables.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有临时变量
|
||||
*/
|
||||
public Map<String, Object> getTempVariables() {
|
||||
return new ConcurrentHashMap<>(tempVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除临时变量
|
||||
*/
|
||||
|
||||
@ -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 WorkflowInstance workflowInstance;
|
||||
|
||||
private final Map<String, Object> variables;
|
||||
private final WorkflowVariableOperations variableOperations;
|
||||
|
||||
private final IWorkflowVariableService variableService;
|
||||
public DefaultWorkflowContext(WorkflowInstance workflowInstance, WorkflowVariableOperations variableOperations) {
|
||||
this.workflowInstance = workflowInstance;
|
||||
this.variableOperations = variableOperations;
|
||||
}
|
||||
|
||||
private final IWorkflowLogService logService;
|
||||
@Override
|
||||
public WorkflowInstance getInstance() {
|
||||
return workflowInstance;
|
||||
}
|
||||
|
||||
private DefaultWorkflowContext(WorkflowInstance instance, IWorkflowVariableService variableService, IWorkflowLogService logService) {
|
||||
this.instance = instance;
|
||||
this.variables = new HashMap<>();
|
||||
this.variableService = variableService;
|
||||
this.logService = logService;
|
||||
@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<String, Object> variables) {
|
||||
variableOperations.setVariables(workflowInstance.getId(), variables);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getVariables() {
|
||||
return new HashMap<>(variables);
|
||||
}
|
||||
|
||||
@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);
|
||||
return variableOperations.getVariables(workflowInstance.getId());
|
||||
}
|
||||
|
||||
@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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 工作流上下文工厂类
|
||||
*/
|
||||
@Component
|
||||
public static class Factory {
|
||||
private final IWorkflowVariableService variableService;
|
||||
private final IWorkflowLogService logService;
|
||||
public static class Builder {
|
||||
private WorkflowInstance workflowInstance;
|
||||
|
||||
public Factory(IWorkflowVariableService variableService, IWorkflowLogService logService) {
|
||||
this.variableService = variableService;
|
||||
this.logService = logService;
|
||||
private WorkflowVariableOperations variableOperations;
|
||||
|
||||
public Builder workflowInstance(WorkflowInstance workflowInstance) {
|
||||
this.workflowInstance = workflowInstance;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultWorkflowContext create(WorkflowInstance instance) {
|
||||
return new DefaultWorkflowContext(instance, variableService, logService);
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -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<String, Object> variables);
|
||||
|
||||
/**
|
||||
* 获取所有变量
|
||||
* @deprecated 使用 WorkflowVariableOperations 替代
|
||||
*/
|
||||
@Deprecated
|
||||
Map<String, Object> getVariables();
|
||||
|
||||
/**
|
||||
* 获取变量
|
||||
* @deprecated 使用 WorkflowVariableOperations 替代
|
||||
*/
|
||||
@Deprecated
|
||||
Object getVariable(String name);
|
||||
|
||||
/**
|
||||
* 设置变量
|
||||
* @deprecated 使用 WorkflowVariableOperations 替代
|
||||
*/
|
||||
@Deprecated
|
||||
void setVariable(String name, Object value);
|
||||
|
||||
/**
|
||||
@ -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) {
|
||||
// 结束节点无需终止操作
|
||||
}
|
||||
|
||||
|
||||
@ -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<String> 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<String> 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
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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) {
|
||||
// 开始节点无需终止操作
|
||||
}
|
||||
|
||||
|
||||
@ -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方法终止
|
||||
}
|
||||
}
|
||||
@ -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<String, Object> parameters) {
|
||||
public void execute(NodeInstance nodeInstance, WorkflowContextOperations context, Map<String, Object> 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请求无需终止操作
|
||||
}
|
||||
}
|
||||
@ -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<String, Object> parameters) {
|
||||
public void execute(NodeInstance nodeInstance, WorkflowContextOperations context, Map<String, Object> 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);
|
||||
}
|
||||
|
||||
@ -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<String, Object> parameters) {
|
||||
public void execute(NodeInstance nodeInstance, WorkflowContextOperations context, Map<String, Object> 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命令终止逻辑
|
||||
}
|
||||
}
|
||||
@ -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<String, Object> parameters);
|
||||
void execute(NodeInstance nodeInstance, WorkflowContextOperations context, Map<String, Object> parameters);
|
||||
|
||||
/**
|
||||
* 终止任务
|
||||
*/
|
||||
void terminate(NodeInstance nodeInstance, WorkflowContext context);
|
||||
void terminate(NodeInstance nodeInstance, WorkflowContextOperations context);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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,11 +152,11 @@ 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<String, Object> 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);
|
||||
}
|
||||
|
||||
@ -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<String> 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());
|
||||
|
||||
@ -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<String> getNextNodeIds(NodeInstance currentNode, WorkflowDefinition definition, WorkflowContext context) {
|
||||
public List<String> getNextNodeIds(NodeInstance currentNode, WorkflowDefinition definition, WorkflowContextOperations context) {
|
||||
try {
|
||||
// 解析流转规则
|
||||
List<TransitionRule> 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;
|
||||
}
|
||||
|
||||
@ -17,17 +17,11 @@ import java.time.LocalDateTime;
|
||||
@LogicDelete
|
||||
public class NodeInstance extends Entity<Long> {
|
||||
|
||||
/**
|
||||
* 工作流实例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<Long> {
|
||||
return config;
|
||||
}
|
||||
|
||||
public Long getWorkflowInstanceId() {
|
||||
return workflowInstanceId;
|
||||
}
|
||||
|
||||
public String getPreNodeId() {
|
||||
return preNodeId;
|
||||
}
|
||||
|
||||
@ -75,12 +75,6 @@ public class WorkflowDefinition extends Entity<Long> {
|
||||
@Column(name = "graph_definition", columnDefinition = "TEXT")
|
||||
private String graphDefinition;
|
||||
|
||||
/**
|
||||
* 节点定义列表
|
||||
*/
|
||||
@OneToMany(mappedBy = "workflowDefinition", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private List<NodeDefinition> nodes = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
|
||||
@ -40,7 +40,7 @@ public class WorkflowInstance extends Entity<Long> {
|
||||
/**
|
||||
* 业务标识
|
||||
*/
|
||||
@Column(name = "business_key")
|
||||
@Column(name = "business_key", nullable = false)
|
||||
private String businessKey;
|
||||
|
||||
/**
|
||||
|
||||
@ -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<Long> {
|
||||
|
||||
/**
|
||||
* 工作流实例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;
|
||||
}
|
||||
@ -18,10 +18,11 @@ import lombok.EqualsAndHashCode;
|
||||
public class WorkflowVariable extends Entity<Long> {
|
||||
|
||||
/**
|
||||
* 工作流实例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;
|
||||
|
||||
/**
|
||||
* 变量名称
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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<NodeInstance, L
|
||||
/**
|
||||
* 查询工作流实例的所有节点
|
||||
*/
|
||||
@Query("SELECT n FROM NodeInstance n WHERE n.workflowInstanceId = :instanceId AND n.deleted = false ORDER BY n.createTime")
|
||||
List<NodeInstance> findByWorkflowInstanceIdOrderByCreateTime(@Param("instanceId") Long instanceId);
|
||||
@Query("SELECT n FROM NodeInstance n WHERE n.workflowInstance = :instance AND n.deleted = false ORDER BY n.createTime")
|
||||
List<NodeInstance> 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<NodeInstance> 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<NodeInstance> findByWorkflowInstanceIdAndStatus(
|
||||
@Param("workflowInstanceId") Long workflowInstanceId,
|
||||
@Param("status") NodeStatusEnum status);
|
||||
|
||||
/**
|
||||
* 查询指定状态的节点实例
|
||||
*/
|
||||
List<NodeInstance> findByWorkflowInstanceIdAndStatusAndDeletedFalse(Long workflowInstanceId, NodeStatusEnum status);
|
||||
List<NodeInstance> findByWorkflowInstanceAndStatusAndDeletedFalse(WorkflowInstance instance, NodeStatusEnum status);
|
||||
|
||||
List<NodeInstance> findByWorkflowInstanceIdAndStatus(Long workflowInstanceId, NodeStatusEnum status);
|
||||
List<NodeInstance> findByWorkflowInstanceAndStatus(WorkflowInstance instance, NodeStatusEnum status);
|
||||
|
||||
List<NodeInstance> findByWorkflowInstanceId(Long workflowInstanceId);
|
||||
List<NodeInstance> findByWorkflowInstance(WorkflowInstance instance);
|
||||
|
||||
void deleteByWorkflowInstanceId(Long workflowInstanceId);
|
||||
void deleteByWorkflowInstance(WorkflowInstance instance);
|
||||
|
||||
/**
|
||||
* 查询不是指定状态的节点实例
|
||||
*/
|
||||
List<NodeInstance> findByWorkflowInstanceIdAndStatusNot(Long workflowInstanceId, NodeStatusEnum status);
|
||||
List<NodeInstance> findByWorkflowInstanceAndStatusNot(WorkflowInstance instance, NodeStatusEnum status);
|
||||
}
|
||||
@ -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<WorkflowLog, Lon
|
||||
/**
|
||||
* 查询工作流实例的所有日志
|
||||
*/
|
||||
@Query("SELECT l FROM WorkflowLog l WHERE l.workflowInstanceId = :instanceId AND l.deleted = false ORDER BY l.createTime")
|
||||
@Query("SELECT l FROM WorkflowLog l WHERE l.workflowInstance.id = :instanceId AND l.deleted = false ORDER BY l.createTime")
|
||||
List<WorkflowLog> 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<WorkflowLog> findByWorkflowInstanceIdAndNodeId(@Param("instanceId") Long instanceId, @Param("nodeId") String nodeId);
|
||||
|
||||
/**
|
||||
* 删除工作流实例的所有日志
|
||||
*/
|
||||
void deleteByWorkflowInstanceId(Long instanceId);
|
||||
void deleteByWorkflowInstance(WorkflowInstance instance);
|
||||
}
|
||||
@ -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<WorkflowVar
|
||||
/**
|
||||
* 查询工作流实例的所有变量
|
||||
*/
|
||||
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstanceId = :instanceId AND v.deleted = false")
|
||||
List<WorkflowVariable> findByWorkflowInstanceId(@Param("instanceId") Long instanceId);
|
||||
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance = :instance AND v.deleted = false")
|
||||
List<WorkflowVariable> findByWorkflowInstance(@Param("instance") WorkflowInstance instance);
|
||||
|
||||
/**
|
||||
* 查询工作流实例的指定作用域的变量
|
||||
*/
|
||||
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstanceId = :instanceId AND v.scope = :scope AND v.deleted = false")
|
||||
List<WorkflowVariable> 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<WorkflowVariable> findByWorkflowInstanceAndScope(@Param("instance") WorkflowInstance instance, @Param("scope") String scope);
|
||||
|
||||
/**
|
||||
* 查询工作流实例的指定变量
|
||||
*/
|
||||
Optional<WorkflowVariable> findByWorkflowInstanceIdAndName(Long workflowInstanceId, String name);
|
||||
Optional<WorkflowVariable> 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<WorkflowVariable> 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<WorkflowVariable> 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<WorkflowVariable> 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<WorkflowVariable> findByWorkflowInstanceAndNameAndScope(
|
||||
@Param("instance") WorkflowInstance instance,
|
||||
@Param("name") String name,
|
||||
@Param("scope") String scope);
|
||||
|
||||
/**
|
||||
* 根据工作流实例ID查询所有变量
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
* @return 变量列表
|
||||
*/
|
||||
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance.id = :workflowInstanceId AND v.deleted = false")
|
||||
List<WorkflowVariable> 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<WorkflowVariable> 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);
|
||||
}
|
||||
@ -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<WorkflowLog, WorkflowLogDTO, Long> {
|
||||
|
||||
/**
|
||||
* 记录日志
|
||||
* 记录日志 (内部使用,由工作流引擎调用)
|
||||
*
|
||||
* @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<WorkflowLogDTO> getLogs(Long workflowInstanceId);
|
||||
|
||||
/**
|
||||
* 查询节点实例的所有日志
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
* @param nodeId 节点ID
|
||||
* @return 日志列表
|
||||
*/
|
||||
List<WorkflowLogDTO> getNodeLogs(Long workflowInstanceId, String nodeId);
|
||||
|
||||
/**
|
||||
* 删除工作流实例的所有日志
|
||||
*
|
||||
* @param workflowInstance 工作流实例
|
||||
*/
|
||||
void deleteLogs(Long workflowInstanceId);
|
||||
void deleteLogs(WorkflowInstance workflowInstance);
|
||||
}
|
||||
@ -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<WorkflowVariable,
|
||||
|
||||
/**
|
||||
* 设置变量
|
||||
*
|
||||
* @param variables 变量映射
|
||||
*/
|
||||
void setVariable(Long workflowInstanceId, String name, Object value);
|
||||
void setVariables(Long workflowInstanceId, Map<String, Object> variables);
|
||||
|
||||
/**
|
||||
* 获取所有变量
|
||||
*
|
||||
* @return 变量映射
|
||||
*/
|
||||
Map<String, Object> getVariables(Long workflowInstanceId);
|
||||
|
||||
/**
|
||||
* 获取指定作用域的变量
|
||||
*
|
||||
* @param scope 变量作用域
|
||||
* @return 变量映射
|
||||
*/
|
||||
Map<String, Object> getVariablesByScope(Long workflowInstanceId, VariableScopeEnum scope);
|
||||
|
||||
/**
|
||||
* 删除所有变量
|
||||
* 清除实例的所有变量
|
||||
*
|
||||
*/
|
||||
void deleteVariables(Long workflowInstanceId);
|
||||
void clearVariables(Long workflowInstanceId);
|
||||
}
|
||||
@ -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<String, Object> getVariables(Long workflowInstanceId);
|
||||
|
||||
/**
|
||||
* 设置工作流实例的变量
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
* @param variables 变量Map
|
||||
*/
|
||||
void setVariables(Long workflowInstanceId, Map<String, Object> 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);
|
||||
}
|
||||
@ -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<Long, Map<String, Object>> variableCache;
|
||||
|
||||
public ConcurrentWorkflowVariableOperations() {
|
||||
this.variableCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(30, TimeUnit.MINUTES)
|
||||
.maximumSize(10000)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getVariables(Long workflowInstanceId) {
|
||||
return variableCache.get(workflowInstanceId,
|
||||
id -> variableService.getVariables(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVariables(Long workflowInstanceId, Map<String, Object> variables) {
|
||||
if (variables == null || variables.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
variableService.setVariables(workflowInstanceId, variables);
|
||||
variableCache.put(workflowInstanceId, variables);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getVariable(Long workflowInstanceId, String key) {
|
||||
Map<String, Object> variables = getVariables(workflowInstanceId);
|
||||
return variables != null ? variables.get(key) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVariable(Long workflowInstanceId, String key, Object value) {
|
||||
Map<String, Object> 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);
|
||||
}
|
||||
}
|
||||
@ -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<WorkflowInstanc
|
||||
private IWorkflowInstanceRepository workflowInstanceRepository;
|
||||
|
||||
@Resource
|
||||
private IWorkflowVariableService workflowVariableService;
|
||||
private WorkflowVariableOperations variableOperations;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@ -60,7 +60,7 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
|
||||
|
||||
// 4. 设置初始变量
|
||||
if (variables != null && !variables.isEmpty()) {
|
||||
variables.forEach((key, value) -> workflowVariableService.setVariable(savedInstance.getId(), key, value));
|
||||
variableOperations.setVariables(savedInstance.getId(), variables);
|
||||
}
|
||||
|
||||
return converter.toDto(savedInstance);
|
||||
|
||||
@ -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;
|
||||
@ -24,16 +26,32 @@ public class WorkflowLogServiceImpl extends BaseServiceImpl<WorkflowLog, Workflo
|
||||
@Resource
|
||||
private IWorkflowLogRepository logRepository;
|
||||
|
||||
@Resource
|
||||
private IWorkflowInstanceService workflowInstanceService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void log(Long workflowInstanceId, String nodeId, String message, LogLevelEnum level, String detail) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(workflowInstanceId);
|
||||
log.setNodeId(nodeId);
|
||||
log.setContent(message);
|
||||
log.setLevel(level);
|
||||
log.setDetail(detail);
|
||||
repository.save(log);
|
||||
public WorkflowLog log(WorkflowInstance instance, String nodeId, LogLevelEnum level, String message, String detail) {
|
||||
WorkflowLog workflowLog = new WorkflowLog();
|
||||
workflowLog.setWorkflowInstance(instance);
|
||||
workflowLog.setNodeId(nodeId);
|
||||
workflowLog.setLevel(level);
|
||||
workflowLog.setMessage(message);
|
||||
workflowLog.setDetail(detail);
|
||||
return logRepository.save(workflowLog);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void recordLog(Long workflowInstanceId, String nodeId, String message, LogLevelEnum level, String detail) {
|
||||
// 先获取工作流实例
|
||||
WorkflowInstance instance = workflowInstanceService.findEntityById(workflowInstanceId);
|
||||
if (instance == null) {
|
||||
throw new IllegalArgumentException("工作流实例不存在: " + workflowInstanceId);
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
log(instance, nodeId, level, message, detail);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -52,8 +70,8 @@ public class WorkflowLogServiceImpl extends BaseServiceImpl<WorkflowLog, Workflo
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteLogs(Long workflowInstanceId) {
|
||||
logRepository.deleteByWorkflowInstanceId(workflowInstanceId);
|
||||
log.debug("删除工作流日志成功: instanceId={}", workflowInstanceId);
|
||||
public void deleteLogs(WorkflowInstance workflowInstance) {
|
||||
logRepository.deleteByWorkflowInstance(workflowInstance);
|
||||
log.debug("删除工作流日志成功: instanceId={}", workflowInstance.getId());
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||
import com.qqchen.deploy.backend.framework.exception.BusinessException;
|
||||
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
|
||||
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;
|
||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowVariableRepository;
|
||||
@ -29,57 +30,58 @@ public class WorkflowVariableServiceImpl extends BaseServiceImpl<WorkflowVariabl
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void setVariable(Long workflowInstanceId, String name, Object value) {
|
||||
try {
|
||||
WorkflowVariable variable = variableRepository.findByWorkflowInstanceIdAndNameAndScope(
|
||||
workflowInstanceId, name, VariableScopeEnum.GLOBAL.name())
|
||||
.orElseGet(() -> {
|
||||
WorkflowVariable newVar = new WorkflowVariable();
|
||||
newVar.setWorkflowInstanceId(workflowInstanceId);
|
||||
newVar.setName(name);
|
||||
newVar.setScope(VariableScopeEnum.GLOBAL.name());
|
||||
return newVar;
|
||||
});
|
||||
public void setVariables(Long workflowInstanceId, Map<String, Object> 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));
|
||||
variable.setType(value.getClass().getName());
|
||||
variableRepository.save(variable);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_TYPE_INVALID);
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_SERIALIZE_ERROR);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Map<String, Object> getVariables(Long workflowInstanceId) {
|
||||
List<WorkflowVariable> variables = variableRepository.findByWorkflowInstanceId(workflowInstanceId);
|
||||
return convertToMap(variables);
|
||||
return deserializeVariables(variables);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Map<String, Object> getVariablesByScope(Long workflowInstanceId, VariableScopeEnum scope) {
|
||||
List<WorkflowVariable> variables = variableRepository.findByWorkflowInstanceIdAndScope(workflowInstanceId, scope.name());
|
||||
return convertToMap(variables);
|
||||
List<WorkflowVariable> 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<String, Object> convertToMap(List<WorkflowVariable> variables) {
|
||||
private Map<String, Object> deserializeVariables(List<WorkflowVariable> variables) {
|
||||
Map<String, Object> 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;
|
||||
}
|
||||
}
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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)
|
||||
);
|
||||
|
||||
-- 创建索引
|
||||
|
||||
@ -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=无效的执行器配置
|
||||
# \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}
|
||||
|
||||
@ -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<String, Object> 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<String, Object> expectedVariables = new HashMap<>();
|
||||
expectedVariables.put("key1", "value1");
|
||||
when(context.getVariables()).thenReturn(expectedVariables);
|
||||
|
||||
// 第一次调用
|
||||
Map<String, Object> result1 = operations.getVariables(instance);
|
||||
assertEquals(expectedVariables, result1);
|
||||
|
||||
// 第二次调用应该返回缓存的结果
|
||||
Map<String, Object> 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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user