可正常启动,工作流可以正常运行,但是只有一个开始节点。
This commit is contained in:
parent
7bbdeefa9b
commit
591abe983d
@ -196,6 +196,11 @@
|
|||||||
<artifactId>commons-exec</artifactId>
|
<artifactId>commons-exec</artifactId>
|
||||||
<version>1.3</version>
|
<version>1.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
<version>3.1.8</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@ -137,7 +137,18 @@ public enum ResponseCode {
|
|||||||
WORKFLOW_NODE_TYPE_DISABLED(2201, "workflow.node.type.disabled"),
|
WORKFLOW_NODE_TYPE_DISABLED(2201, "workflow.node.type.disabled"),
|
||||||
WORKFLOW_NODE_TYPE_CODE_EXISTS(2202, "workflow.node.type.code.exists"),
|
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_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 int code;
|
||||||
private final String messageKey; // 国际化消息key
|
private final String messageKey; // 国际化消息key
|
||||||
|
|||||||
@ -45,14 +45,14 @@ public class WorkflowLogApiController extends BaseController<WorkflowLog, Workfl
|
|||||||
|
|
||||||
@Operation(summary = "记录日志")
|
@Operation(summary = "记录日志")
|
||||||
@PostMapping("/record")
|
@PostMapping("/record")
|
||||||
public Response<Void> log(
|
public Response<Void> recordLog(
|
||||||
@Parameter(description = "工作流实例ID", required = true) @RequestParam Long workflowInstanceId,
|
@Parameter(description = "工作流实例ID", required = true) @RequestParam Long workflowInstanceId,
|
||||||
@Parameter(description = "节点ID") @RequestParam(required = false) String nodeId,
|
@Parameter(description = "节点ID") @RequestParam(required = false) String nodeId,
|
||||||
@Parameter(description = "日志内容", required = true) @RequestParam String message,
|
@Parameter(description = "日志内容", required = true) @RequestParam String message,
|
||||||
@Parameter(description = "日志级别", required = true) @RequestParam LogLevelEnum level,
|
@Parameter(description = "日志级别", required = true) @RequestParam LogLevelEnum level,
|
||||||
@Parameter(description = "详细信息") @RequestParam(required = false) String detail
|
@Parameter(description = "详细信息") @RequestParam(required = false) String detail
|
||||||
) {
|
) {
|
||||||
workflowLogService.log(workflowInstanceId, nodeId, message, level, detail);
|
workflowLogService.recordLog(workflowInstanceId, nodeId, message, level, detail);
|
||||||
return Response.success();
|
return Response.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,6 @@ import org.mapstruct.Mapping;
|
|||||||
public interface NodeInstanceConverter extends BaseConverter<NodeInstance, NodeInstanceDTO> {
|
public interface NodeInstanceConverter extends BaseConverter<NodeInstance, NodeInstanceDTO> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Mapping(target = "workflowInstanceId", source = "workflowInstanceId")
|
|
||||||
@Mapping(target = "nodeId", source = "nodeId")
|
@Mapping(target = "nodeId", source = "nodeId")
|
||||||
@Mapping(target = "nodeType", source = "nodeType")
|
@Mapping(target = "nodeType", source = "nodeType")
|
||||||
@Mapping(target = "name", source = "name")
|
@Mapping(target = "name", source = "name")
|
||||||
@ -28,7 +27,7 @@ public interface NodeInstanceConverter extends BaseConverter<NodeInstance, NodeI
|
|||||||
NodeInstanceDTO toDto(NodeInstance entity);
|
NodeInstanceDTO toDto(NodeInstance entity);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Mapping(target = "workflowInstanceId", source = "workflowInstanceId")
|
@Mapping(target = "workflowInstance", ignore = true)
|
||||||
@Mapping(target = "nodeId", source = "nodeId")
|
@Mapping(target = "nodeId", source = "nodeId")
|
||||||
@Mapping(target = "nodeType", source = "nodeType")
|
@Mapping(target = "nodeType", source = "nodeType")
|
||||||
@Mapping(target = "name", source = "name")
|
@Mapping(target = "name", source = "name")
|
||||||
@ -40,6 +39,5 @@ public interface NodeInstanceConverter extends BaseConverter<NodeInstance, NodeI
|
|||||||
@Mapping(target = "output", source = "output")
|
@Mapping(target = "output", source = "output")
|
||||||
@Mapping(target = "error", source = "error")
|
@Mapping(target = "error", source = "error")
|
||||||
@Mapping(target = "preNodeId", source = "preNodeId")
|
@Mapping(target = "preNodeId", source = "preNodeId")
|
||||||
@Mapping(target = "workflowInstance", ignore = true)
|
|
||||||
NodeInstance toEntity(NodeInstanceDTO dto);
|
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.dto.WorkflowDefinitionDTO;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||||
import org.mapstruct.Mapper;
|
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> {
|
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.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工作流定义DTO
|
* 工作流定义DTO
|
||||||
*/
|
*/
|
||||||
@ -36,37 +34,30 @@ public class WorkflowDefinitionDTO extends BaseDTO {
|
|||||||
/**
|
/**
|
||||||
* 工作流状态
|
* 工作流状态
|
||||||
*/
|
*/
|
||||||
@NotNull(message = "工作流状态不能为空")
|
|
||||||
private WorkflowDefinitionStatusEnum status;
|
private WorkflowDefinitionStatusEnum status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 版本号
|
* 版本号
|
||||||
*/
|
*/
|
||||||
@NotNull(message = "版本号不能为空")
|
|
||||||
private Integer version;
|
private Integer version;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 节点定义列表
|
* 节点配置(JSON)
|
||||||
*/
|
|
||||||
private List<NodeDefinitionDTO> nodes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 节点配置
|
|
||||||
*/
|
*/
|
||||||
private String nodeConfig;
|
private String nodeConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流转配置
|
* 流转配置(JSON)
|
||||||
*/
|
*/
|
||||||
private String transitionConfig;
|
private String transitionConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表单定义
|
* 表单定义(JSON)
|
||||||
*/
|
*/
|
||||||
private String formDefinition;
|
private String formDefinition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图形信息
|
* 图形信息(JSON)
|
||||||
*/
|
*/
|
||||||
private String graphDefinition;
|
private String graphDefinition;
|
||||||
|
|
||||||
@ -80,4 +71,4 @@ public class WorkflowDefinitionDTO extends BaseDTO {
|
|||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
private String remark;
|
private String remark;
|
||||||
}
|
}
|
||||||
@ -1,41 +1,46 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.dto;
|
package com.qqchen.deploy.backend.workflow.dto;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
|
||||||
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
|
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作流日志DTO
|
||||||
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class WorkflowLogDTO extends BaseDTO {
|
public class WorkflowLogDTO extends BaseDTO {
|
||||||
|
|
||||||
/**
|
|
||||||
* 工作流实例ID
|
|
||||||
*/
|
|
||||||
@NotNull(message = "工作流实例ID不能为空")
|
|
||||||
private Long workflowInstanceId;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 节点ID
|
* 节点ID
|
||||||
*/
|
*/
|
||||||
private String nodeId;
|
private String nodeId;
|
||||||
|
|
||||||
/**
|
|
||||||
* 日志内容
|
|
||||||
*/
|
|
||||||
@NotBlank(message = "日志内容不能为空")
|
|
||||||
private String message;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日志级别
|
* 日志级别
|
||||||
*/
|
*/
|
||||||
@NotNull(message = "日志级别不能为空")
|
|
||||||
private LogLevelEnum level;
|
private LogLevelEnum level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志内容
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 详细信息
|
* 详细信息
|
||||||
*/
|
*/
|
||||||
private String detail;
|
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.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.context.DefaultWorkflowContext;
|
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.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.executor.NodeExecutor;
|
import com.qqchen.deploy.backend.workflow.engine.executor.NodeExecutor;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
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.INodeInstanceRepository;
|
||||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
|
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
|
||||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
|
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
|
||||||
|
import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -41,7 +42,7 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
|
|||||||
private Map<NodeTypeEnum, NodeExecutor> nodeExecutors;
|
private Map<NodeTypeEnum, NodeExecutor> nodeExecutors;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private DefaultWorkflowContext.Factory workflowContextFactory;
|
private WorkflowVariableOperations variableOperations;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -59,19 +60,19 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
|
|||||||
|
|
||||||
// 2. 创建工作流实例
|
// 2. 创建工作流实例
|
||||||
WorkflowInstance instance = new WorkflowInstance();
|
WorkflowInstance instance = new WorkflowInstance();
|
||||||
instance.setDefinition(definition);
|
instance.setWorkflowDefinition(definition);
|
||||||
|
instance.setBusinessKey(businessKey);
|
||||||
// 设置工作流实例初始状态为 PENDING
|
// 设置工作流实例初始状态为 PENDING
|
||||||
instance.setStatus(WorkflowInstanceStatusEnum.PENDING);
|
instance.setStatus(WorkflowInstanceStatusEnum.PENDING);
|
||||||
workflowInstanceRepository.save(instance);
|
workflowInstanceRepository.save(instance);
|
||||||
|
|
||||||
// 3. 创建工作流上下文
|
// 3. 设置工作流变量
|
||||||
WorkflowContext context = workflowContextFactory.create(instance);
|
if (variables != null && !variables.isEmpty()) {
|
||||||
if (variables != null) {
|
variableOperations.setVariables(instance.getId(), variables);
|
||||||
variables.forEach((key, value) -> context.setVariable(key, value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 创建开始节点实例并启动工作流
|
// 4. 创建开始节点实例并启动工作流
|
||||||
NodeInstance startNode = createStartNode(definition, instance.getId());
|
NodeInstance startNode = createStartNode(definition, instance);
|
||||||
instance.start();
|
instance.start();
|
||||||
workflowInstanceRepository.save(instance);
|
workflowInstanceRepository.save(instance);
|
||||||
executeNode(startNode.getId());
|
executeNode(startNode.getId());
|
||||||
@ -90,43 +91,33 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
|
|||||||
throw new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
|
throw new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// 获取节点执行器
|
||||||
// 1. 获取节点执行器
|
NodeExecutor executor = nodeExecutors.get(nodeInstance.getNodeType());
|
||||||
NodeExecutor executor = nodeExecutors.get(nodeInstance.getNodeType());
|
if (executor == null) {
|
||||||
if (executor == null) {
|
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTOR_NOT_FOUND);
|
||||||
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_TYPE_NOT_SUPPORTED);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 执行节点
|
// 创建工作流上下文
|
||||||
WorkflowContext context = workflowContextFactory.create(instance);
|
WorkflowContextOperations context = DefaultWorkflowContext.builder()
|
||||||
executor.execute(nodeInstance, context);
|
.workflowInstance(instance)
|
||||||
|
.variableOperations(variableOperations)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 执行节点
|
||||||
|
executor.execute(nodeInstance, context);
|
||||||
|
|
||||||
// 3. 更新节点状态
|
// 3. 更新节点状态
|
||||||
nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
|
nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
|
||||||
nodeInstance.setEndTime(LocalDateTime.now());
|
nodeInstance.setEndTime(LocalDateTime.now());
|
||||||
nodeInstanceRepository.save(nodeInstance);
|
nodeInstanceRepository.save(nodeInstance);
|
||||||
|
|
||||||
// 4. 检查是否所有节点都已完成
|
// 4. 检查是否所有节点都已完成
|
||||||
List<NodeInstance> uncompletedNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatusNot(
|
List<NodeInstance> uncompletedNodes = nodeInstanceRepository.findByWorkflowInstanceAndStatusNot(
|
||||||
instance.getId(), NodeStatusEnum.COMPLETED);
|
instance, NodeStatusEnum.COMPLETED);
|
||||||
|
|
||||||
if (uncompletedNodes.isEmpty()) {
|
if (uncompletedNodes.isEmpty()) {
|
||||||
instance.complete();
|
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);
|
workflowInstanceRepository.save(instance);
|
||||||
|
|
||||||
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED, e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,15 +127,11 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
|
|||||||
NodeInstance nodeInstance = nodeInstanceRepository.findById(nodeInstanceId)
|
NodeInstance nodeInstance = nodeInstanceRepository.findById(nodeInstanceId)
|
||||||
.orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_NOT_FOUND));
|
.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();
|
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);
|
nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
|
||||||
@ -157,23 +144,12 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
|
|||||||
public void terminateWorkflow(Long instanceId, String reason) {
|
public void terminateWorkflow(Long instanceId, String reason) {
|
||||||
WorkflowInstance instance = workflowInstanceRepository.findById(instanceId)
|
WorkflowInstance instance = workflowInstanceRepository.findById(instanceId)
|
||||||
.orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
|
.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);
|
instance.terminate(reason);
|
||||||
workflowInstanceRepository.save(instance);
|
workflowInstanceRepository.save(instance);
|
||||||
|
|
||||||
|
// 清理上下文缓存
|
||||||
|
variableOperations.clearVariables(instance.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -219,8 +195,8 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
|
|||||||
workflowInstanceRepository.save(instance);
|
workflowInstanceRepository.save(instance);
|
||||||
|
|
||||||
// 获取失败的节点
|
// 获取失败的节点
|
||||||
List<NodeInstance> failedNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
|
List<NodeInstance> failedNodes = nodeInstanceRepository.findByWorkflowInstanceAndStatus(
|
||||||
instanceId, NodeStatusEnum.FAILED);
|
instance, NodeStatusEnum.FAILED);
|
||||||
|
|
||||||
// 重试失败的节点
|
// 重试失败的节点
|
||||||
for (NodeInstance node : failedNodes) {
|
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();
|
NodeInstance startNode = new NodeInstance();
|
||||||
startNode.setWorkflowInstanceId(instanceId);
|
startNode.setWorkflowInstance(instance);
|
||||||
startNode.setNodeId("start");
|
startNode.setNodeId("start");
|
||||||
startNode.setNodeType(NodeTypeEnum.START);
|
startNode.setNodeType(NodeTypeEnum.START);
|
||||||
startNode.setName("开始节点");
|
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.NodeInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||||
|
import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -10,14 +12,15 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 工作流上下文
|
* 工作流上下文
|
||||||
|
* 负责管理工作流执行过程中的运行时状态
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class WorkflowContext {
|
public class WorkflowContext {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工作流实例
|
* 工作流实例ID
|
||||||
*/
|
*/
|
||||||
private WorkflowInstance workflowInstance;
|
private final Long instanceId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当前节点实例
|
* 当前节点实例
|
||||||
@ -30,32 +33,57 @@ public class WorkflowContext {
|
|||||||
private List<NodeInstance> allNodes;
|
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() {
|
public WorkflowContext(Long instanceId, WorkflowVariableOperations variableOperations) {
|
||||||
this.variables = new ConcurrentHashMap<>();
|
this.instanceId = instanceId;
|
||||||
this.tempVariables = new ConcurrentHashMap<>();
|
this.tempVariables = new ConcurrentHashMap<>();
|
||||||
|
this.variableOperations = variableOperations;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取变量值
|
* 获取变量值(委托给 WorkflowVariableOperations)
|
||||||
|
* @deprecated 建议直接使用 WorkflowVariableOperations
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public Object getVariable(String key) {
|
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) {
|
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,10 +100,17 @@ public class WorkflowContext {
|
|||||||
tempVariables.put(key, value);
|
tempVariables.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有临时变量
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getTempVariables() {
|
||||||
|
return new ConcurrentHashMap<>(tempVariables);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清除临时变量
|
* 清除临时变量
|
||||||
*/
|
*/
|
||||||
public void clearTempVariables() {
|
public void clearTempVariables() {
|
||||||
tempVariables.clear();
|
tempVariables.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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.system.enums.LogLevelEnum;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowLogService;
|
import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
|
||||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowVariableService;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.springframework.stereotype.Component;
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class DefaultWorkflowContext implements WorkflowContext {
|
/**
|
||||||
|
* 默认工作流上下文实现
|
||||||
|
*/
|
||||||
|
public class DefaultWorkflowContext implements WorkflowContextOperations {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(DefaultWorkflowContext.class);
|
||||||
|
|
||||||
@Getter
|
@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;
|
||||||
private final IWorkflowLogService logService;
|
this.variableOperations = variableOperations;
|
||||||
|
|
||||||
private DefaultWorkflowContext(WorkflowInstance instance, IWorkflowVariableService variableService, IWorkflowLogService logService) {
|
|
||||||
this.instance = instance;
|
|
||||||
this.variables = new HashMap<>();
|
|
||||||
this.variableService = variableService;
|
|
||||||
this.logService = logService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorkflowInstance getInstance() {
|
||||||
|
return workflowInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getVariable(String key) {
|
||||||
|
return variableOperations.getVariable(workflowInstance.getId(), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVariable(String key, Object value) {
|
||||||
|
variableOperations.setVariable(workflowInstance.getId(), key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVariables(Map<String, Object> variables) {
|
||||||
|
variableOperations.setVariables(workflowInstance.getId(), variables);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getVariables() {
|
public Map<String, Object> getVariables() {
|
||||||
return new HashMap<>(variables);
|
return variableOperations.getVariables(workflowInstance.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getVariable(String name) {
|
|
||||||
return variables.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setVariable(String name, Object value) {
|
|
||||||
variables.put(name, value);
|
|
||||||
variableService.setVariable(instance.getId(), name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void log(String message, LogLevelEnum level) {
|
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
|
@Override
|
||||||
public void log(String message, String detail, LogLevelEnum level) {
|
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 Factory(IWorkflowVariableService variableService, IWorkflowLogService logService) {
|
|
||||||
this.variableService = variableService;
|
|
||||||
this.logService = logService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DefaultWorkflowContext create(WorkflowInstance instance) {
|
|
||||||
return new DefaultWorkflowContext(instance, variableService, logService);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public static class Builder {
|
||||||
|
private WorkflowInstance workflowInstance;
|
||||||
|
|
||||||
|
private WorkflowVariableOperations variableOperations;
|
||||||
|
|
||||||
|
public Builder workflowInstance(WorkflowInstance workflowInstance) {
|
||||||
|
this.workflowInstance = workflowInstance;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder variableOperations(WorkflowVariableOperations variableOperations) {
|
||||||
|
this.variableOperations = variableOperations;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultWorkflowContext build() {
|
||||||
|
return new DefaultWorkflowContext(workflowInstance, variableOperations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,28 +6,37 @@ import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工作流上下文
|
* 工作流上下文操作接口
|
||||||
|
* 定义工作流执行过程中的上下文操作能力
|
||||||
*/
|
*/
|
||||||
public interface WorkflowContext {
|
public interface WorkflowContextOperations {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取工作流实例
|
* 获取工作流实例
|
||||||
*/
|
*/
|
||||||
WorkflowInstance getInstance();
|
WorkflowInstance getInstance();
|
||||||
|
|
||||||
|
void setVariables(Map<String, Object> variables);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有变量
|
* 获取所有变量
|
||||||
|
* @deprecated 使用 WorkflowVariableOperations 替代
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
Map<String, Object> getVariables();
|
Map<String, Object> getVariables();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取变量
|
* 获取变量
|
||||||
|
* @deprecated 使用 WorkflowVariableOperations 替代
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
Object getVariable(String name);
|
Object getVariable(String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置变量
|
* 设置变量
|
||||||
|
* @deprecated 使用 WorkflowVariableOperations 替代
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
void setVariable(String name, Object value);
|
void setVariable(String name, Object value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,4 +48,4 @@ public interface WorkflowContext {
|
|||||||
* 记录日志(带详情)
|
* 记录日志(带详情)
|
||||||
*/
|
*/
|
||||||
void log(String message, String detail, LogLevelEnum level);
|
void log(String message, String detail, LogLevelEnum level);
|
||||||
}
|
}
|
||||||
@ -3,7 +3,7 @@ package com.qqchen.deploy.backend.workflow.engine.executor;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
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.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||||
@ -34,7 +34,7 @@ public class EndNodeExecutor implements NodeExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(NodeInstance nodeInstance, WorkflowContext context) {
|
public void execute(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
// 1. 完成结束节点
|
// 1. 完成结束节点
|
||||||
nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
|
nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
|
||||||
nodeInstance.setEndTime(LocalDateTime.now());
|
nodeInstance.setEndTime(LocalDateTime.now());
|
||||||
@ -59,7 +59,7 @@ public class EndNodeExecutor implements NodeExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
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.engine.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
|
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
|
||||||
@ -31,7 +31,7 @@ public class GatewayNodeExecutor implements NodeExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(NodeInstance nodeInstance, WorkflowContext context) {
|
public void execute(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
try {
|
try {
|
||||||
GatewayConfig config = objectMapper.readValue(nodeInstance.getConfig(), GatewayConfig.class);
|
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 {
|
try {
|
||||||
for (GatewayCondition condition : config.getConditions()) {
|
for (GatewayCondition condition : config.getConditions()) {
|
||||||
if (evaluateCondition(condition.getExpression(), context)) {
|
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 {
|
try {
|
||||||
List<String> nextNodeIds = new ArrayList<>();
|
List<String> nextNodeIds = new ArrayList<>();
|
||||||
for (GatewayCondition condition : config.getConditions()) {
|
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 {
|
try {
|
||||||
List<String> nextNodeIds = new ArrayList<>();
|
List<String> nextNodeIds = new ArrayList<>();
|
||||||
for (GatewayCondition condition : config.getConditions()) {
|
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 {
|
try {
|
||||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
|
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
|
||||||
evaluationContext.setVariables(context.getVariables());
|
evaluationContext.setVariables(context.getVariables());
|
||||||
@ -140,7 +140,7 @@ public class GatewayNodeExecutor implements NodeExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
|
public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
// Gateway nodes are instant operations, no need to terminate
|
// Gateway nodes are instant operations, no need to terminate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.engine.executor;
|
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.entity.NodeInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
|
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.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
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.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
|
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
|
||||||
@ -26,7 +26,7 @@ public class StartNodeExecutor implements NodeExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(NodeInstance nodeInstance, WorkflowContext context) {
|
public void execute(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
// 开始节点直接完成
|
// 开始节点直接完成
|
||||||
nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
|
nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
|
||||||
context.log("开始节点执行完成", LogLevelEnum.INFO);
|
context.log("开始节点执行完成", LogLevelEnum.INFO);
|
||||||
@ -43,7 +43,7 @@ public class StartNodeExecutor implements NodeExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.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.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
|
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
|
||||||
@ -25,7 +25,7 @@ public class TaskNodeExecutor implements NodeExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(NodeInstance nodeInstance, WorkflowContext context) {
|
public void execute(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
try {
|
try {
|
||||||
// 1. 解析任务配置
|
// 1. 解析任务配置
|
||||||
TaskConfig config = parseConfig(nodeInstance.getConfig());
|
TaskConfig config = parseConfig(nodeInstance.getConfig());
|
||||||
@ -57,7 +57,7 @@ public class TaskNodeExecutor implements NodeExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
|
public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
// 终止任务执行
|
// 终止任务执行
|
||||||
TaskConfig config = parseConfig(nodeInstance.getConfig());
|
TaskConfig config = parseConfig(nodeInstance.getConfig());
|
||||||
terminateTask(config, nodeInstance, context);
|
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()) {
|
switch (config.getType()) {
|
||||||
case HTTP:
|
case HTTP:
|
||||||
executeHttpTask(config, nodeInstance, context);
|
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()) {
|
switch (config.getType()) {
|
||||||
case HTTP:
|
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请求执行
|
// TODO: 实现HTTP请求执行
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeJavaTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContext context) {
|
private void executeJavaTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
// TODO: 实现Java方法调用
|
// TODO: 实现Java方法调用
|
||||||
}
|
}
|
||||||
|
|
||||||
private void terminateHttpTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContext context) {
|
private void terminateHttpTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
// TODO: 实现HTTP请求终止
|
// TODO: 实现HTTP请求终止
|
||||||
}
|
}
|
||||||
|
|
||||||
private void terminateJavaTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContext context) {
|
private void terminateJavaTask(TaskConfig config, NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
// TODO: 实现Java方法终止
|
// TODO: 实现Java方法终止
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,7 +3,7 @@ package com.qqchen.deploy.backend.workflow.engine.executor.task;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.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.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -21,7 +21,7 @@ public class HttpTaskExecutor implements TaskExecutor {
|
|||||||
private final RestTemplate restTemplate = new RestTemplate();
|
private final RestTemplate restTemplate = new RestTemplate();
|
||||||
|
|
||||||
@Override
|
@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");
|
String url = (String) parameters.get("url");
|
||||||
HttpMethod method = HttpMethod.valueOf((String) parameters.getOrDefault("method", "GET"));
|
HttpMethod method = HttpMethod.valueOf((String) parameters.getOrDefault("method", "GET"));
|
||||||
Object body = parameters.get("body");
|
Object body = parameters.get("body");
|
||||||
@ -55,7 +55,7 @@ public class HttpTaskExecutor implements TaskExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
|
public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
// HTTP请求无需终止操作
|
// HTTP请求无需终止操作
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,7 +3,7 @@ package com.qqchen.deploy.backend.workflow.engine.executor.task;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.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.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -24,14 +24,14 @@ public class JavaTaskExecutor implements TaskExecutor {
|
|||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
@Override
|
@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 className = parameters.get("className").toString();
|
||||||
String methodName = parameters.get("methodName").toString();
|
String methodName = parameters.get("methodName").toString();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Class<?> clazz = Class.forName(className);
|
Class<?> clazz = Class.forName(className);
|
||||||
Object instance = applicationContext.getBean(clazz);
|
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);
|
method.invoke(instance, nodeInstance, context, parameters);
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR, "Class not found: " + className);
|
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR, "Class not found: " + className);
|
||||||
@ -43,7 +43,7 @@ public class JavaTaskExecutor implements TaskExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
|
public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
// Java任务无法中断,记录日志
|
// Java任务无法中断,记录日志
|
||||||
context.log("Java task cannot be terminated: " + nodeInstance.getNodeId(), LogLevelEnum.WARN);
|
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.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.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.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -21,7 +21,7 @@ public class ShellTaskExecutor implements TaskExecutor {
|
|||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
@Override
|
@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");
|
String command = (String) parameters.get("command");
|
||||||
Integer timeout = (Integer) parameters.getOrDefault("timeout", 300);
|
Integer timeout = (Integer) parameters.getOrDefault("timeout", 300);
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ public class ShellTaskExecutor implements TaskExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
|
public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
// TODO: 实现Shell命令终止逻辑
|
// TODO: 实现Shell命令终止逻辑
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.engine.executor.task;
|
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 com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||||
|
|
||||||
import java.util.Map;
|
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;
|
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.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.engine.executor.NodeExecutor;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
|
||||||
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
|
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.IWorkflowLogService;
|
||||||
|
import com.qqchen.deploy.backend.workflow.service.WorkflowVariableOperations;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 抽象节点执行器
|
* 抽象节点执行器
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class AbstractNodeExecutor implements NodeExecutor {
|
public abstract class AbstractNodeExecutor implements NodeExecutor {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
protected WorkflowVariableOperations variableOperations;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
protected INodeInstanceRepository nodeInstanceRepository;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
protected IWorkflowLogService workflowLogService;
|
protected IWorkflowLogService workflowLogService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(NodeInstance nodeInstance, WorkflowContext context) {
|
public void execute(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
try {
|
try {
|
||||||
// 1. 记录系统日志
|
// 1. 前置处理
|
||||||
logSystem(context.getInstance(), LogLevelEnum.INFO,
|
beforeExecute(nodeInstance, context);
|
||||||
String.format("开始执行节点: %s[%s]", nodeInstance.getName(), nodeInstance.getNodeId()),
|
|
||||||
null);
|
|
||||||
|
|
||||||
// 2. 记录节点开始日志
|
// 2. 执行节点逻辑
|
||||||
logNodeStart(nodeInstance);
|
|
||||||
|
|
||||||
// 3. 执行节点
|
|
||||||
doExecute(nodeInstance, context);
|
doExecute(nodeInstance, context);
|
||||||
|
|
||||||
// 4. 记录节点完成日志
|
// 3. 后置处理
|
||||||
logNodeComplete(nodeInstance);
|
afterExecute(nodeInstance, context);
|
||||||
|
|
||||||
|
// 4. 更新节点状态
|
||||||
|
updateNodeStatus(nodeInstance, true);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// 5. 记录错误日志
|
// 5. 异常处理
|
||||||
logSystem(context.getInstance(), LogLevelEnum.ERROR,
|
handleExecutionError(nodeInstance, context, e);
|
||||||
String.format("节点执行失败: %s[%s]", nodeInstance.getName(), nodeInstance.getNodeId()),
|
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED, e);
|
||||||
e.getMessage());
|
|
||||||
logNodeError(nodeInstance, e.getMessage());
|
|
||||||
throw 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) {
|
protected void logSystem(NodeInstance nodeInstance, LogLevelEnum level, String message, String detail) {
|
||||||
workflowLogService.log(instance.getId(), null, message, level, detail);
|
workflowLogService.log(nodeInstance.getWorkflowInstance(), nodeInstance.getNodeId(), level, message, detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 记录节点开始日志
|
* 记录节点开始日志
|
||||||
*/
|
*/
|
||||||
protected void logNodeStart(NodeInstance nodeInstance) {
|
protected void logNodeStart(NodeInstance nodeInstance) {
|
||||||
workflowLogService.log(nodeInstance.getWorkflowInstanceId(), nodeInstance.getNodeId(),
|
workflowLogService.log(nodeInstance.getWorkflowInstance(), nodeInstance.getNodeId(),
|
||||||
String.format("节点开始执行: %s", nodeInstance.getName()), LogLevelEnum.INFO, null);
|
LogLevelEnum.INFO, String.format("节点开始执行: %s", nodeInstance.getName()), null);
|
||||||
nodeInstance.setStatus(NodeStatusEnum.RUNNING);
|
nodeInstance.setStatus(NodeStatusEnum.RUNNING);
|
||||||
|
nodeInstance.setStartTime(LocalDateTime.now());
|
||||||
|
nodeInstanceRepository.save(nodeInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 记录节点完成日志
|
* 记录节点完成日志
|
||||||
*/
|
*/
|
||||||
protected void logNodeComplete(NodeInstance nodeInstance) {
|
protected void logNodeComplete(NodeInstance nodeInstance) {
|
||||||
workflowLogService.log(nodeInstance.getWorkflowInstanceId(), nodeInstance.getNodeId(),
|
workflowLogService.log(nodeInstance.getWorkflowInstance(), nodeInstance.getNodeId(),
|
||||||
String.format("节点执行完成: %s", nodeInstance.getName()), LogLevelEnum.INFO, null);
|
LogLevelEnum.INFO, String.format("节点执行完成: %s", nodeInstance.getName()), null);
|
||||||
nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 记录节点错误日志
|
* 记录节点错误日志
|
||||||
*/
|
*/
|
||||||
protected void logNodeError(NodeInstance nodeInstance, String error) {
|
protected void logNodeError(NodeInstance nodeInstance, String error) {
|
||||||
workflowLogService.log(nodeInstance.getWorkflowInstanceId(), nodeInstance.getNodeId(),
|
workflowLogService.log(nodeInstance.getWorkflowInstance(), nodeInstance.getNodeId(),
|
||||||
String.format("节点执行失败: %s", nodeInstance.getName()), LogLevelEnum.ERROR, error);
|
LogLevelEnum.ERROR, String.format("节点执行失败: %s", nodeInstance.getName()), error);
|
||||||
nodeInstance.setStatus(NodeStatusEnum.FAILED);
|
|
||||||
nodeInstance.setError(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;
|
package com.qqchen.deploy.backend.workflow.engine.node.executor;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
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.system.enums.LogLevelEnum;
|
||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
|
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
|
||||||
|
import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContextOperations;
|
||||||
|
import com.qqchen.deploy.backend.workflow.engine.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.Data;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -16,6 +17,7 @@ import jakarta.annotation.Resource;
|
|||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
@ -27,6 +29,9 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
|
|||||||
@Resource
|
@Resource
|
||||||
private ObjectMapper objectMapper;
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private WorkflowVariableOperations variableOperations;
|
||||||
|
|
||||||
private final ExecutorService executorService = Executors.newCachedThreadPool();
|
private final ExecutorService executorService = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -62,7 +67,7 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doExecute(NodeInstance nodeInstance, WorkflowContext context) {
|
protected void doExecute(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
try {
|
try {
|
||||||
String configJson = nodeInstance.getConfig();
|
String configJson = nodeInstance.getConfig();
|
||||||
ShellConfig config = objectMapper.readValue(configJson, ShellConfig.class);
|
ShellConfig config = objectMapper.readValue(configJson, ShellConfig.class);
|
||||||
@ -85,9 +90,7 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
lastException = e;
|
lastException = e;
|
||||||
if (attempt < maxAttempts) {
|
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);
|
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 processBuilder = new ProcessBuilder();
|
||||||
processBuilder.command("sh", "-c", config.getScript());
|
processBuilder.command("sh", "-c", config.getScript());
|
||||||
|
|
||||||
@ -149,12 +152,12 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
|
|||||||
exitCode, String.join("\n", error)));
|
exitCode, String.join("\n", error)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置输出结果
|
// 设置输出变量
|
||||||
nodeInstance.setOutput(String.join("\n", output));
|
Map<String, Object> outputVariables = new HashMap<>();
|
||||||
if (!error.isEmpty()) {
|
outputVariables.put("shellOutput", String.join("\n", output));
|
||||||
nodeInstance.setError(String.join("\n", error));
|
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);
|
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
|
@Override
|
||||||
public void terminate(NodeInstance nodeInstance, WorkflowContext context) {
|
public void terminate(NodeInstance nodeInstance, WorkflowContextOperations context) {
|
||||||
// TODO: 实现终止Shell进程的逻辑
|
// TODO: 实现终止Shell进程的逻辑
|
||||||
context.log("Shell node termination is not implemented yet", LogLevelEnum.WARN);
|
context.log("Shell node termination is not implemented yet", LogLevelEnum.WARN);
|
||||||
}
|
}
|
||||||
@ -187,4 +190,4 @@ public class ShellNodeExecutor extends AbstractNodeExecutor {
|
|||||||
private Map<String, String> environment; // 环境变量
|
private Map<String, String> environment; // 环境变量
|
||||||
private Integer successExitCode; // 成功退出码
|
private Integer successExitCode; // 成功退出码
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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.system.enums.LogLevelEnum;
|
||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||||
import com.qqchen.deploy.backend.workflow.engine.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.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeDefinition;
|
import com.qqchen.deploy.backend.workflow.entity.NodeDefinition;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
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列表
|
// 1. 获取下一个节点ID列表
|
||||||
List<String> nextNodeIds = transitionRuleEngine.getNextNodeIds(currentNode, definition, context);
|
List<String> nextNodeIds = transitionRuleEngine.getNextNodeIds(currentNode, definition, context);
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ public class TransitionExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NodeInstance nextNode = new NodeInstance();
|
NodeInstance nextNode = new NodeInstance();
|
||||||
nextNode.setWorkflowInstanceId(currentNode.getWorkflowInstanceId());
|
nextNode.setWorkflowInstance(currentNode.getWorkflowInstance());
|
||||||
nextNode.setNodeId(nodeId);
|
nextNode.setNodeId(nodeId);
|
||||||
nextNode.setNodeType(nextNodeDef.getType());
|
nextNode.setNodeType(nextNodeDef.getType());
|
||||||
nextNode.setName(nextNodeDef.getName());
|
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.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
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.engine.exception.WorkflowEngineException;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||||
@ -28,7 +28,7 @@ public class TransitionRuleEngine {
|
|||||||
/**
|
/**
|
||||||
* 获取下一个节点ID列表
|
* 获取下一个节点ID列表
|
||||||
*/
|
*/
|
||||||
public List<String> getNextNodeIds(NodeInstance currentNode, WorkflowDefinition definition, WorkflowContext context) {
|
public List<String> getNextNodeIds(NodeInstance currentNode, WorkflowDefinition definition, WorkflowContextOperations context) {
|
||||||
try {
|
try {
|
||||||
// 解析流转规则
|
// 解析流转规则
|
||||||
List<TransitionRule> rules = parseTransitionRules(definition.getTransitionConfig());
|
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()) {
|
if (condition == null || condition.trim().isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,17 +17,11 @@ import java.time.LocalDateTime;
|
|||||||
@LogicDelete
|
@LogicDelete
|
||||||
public class NodeInstance extends Entity<Long> {
|
public class NodeInstance extends Entity<Long> {
|
||||||
|
|
||||||
/**
|
|
||||||
* 工作流实例ID,用于优化查询
|
|
||||||
*/
|
|
||||||
@Column(name = "workflow_instance_id", nullable = false)
|
|
||||||
private Long workflowInstanceId;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工作流实例
|
* 工作流实例
|
||||||
*/
|
*/
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "workflow_instance_id", insertable = false, updatable = false)
|
@JoinColumn(name = "workflow_instance_id", nullable = false)
|
||||||
private WorkflowInstance workflowInstance;
|
private WorkflowInstance workflowInstance;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -99,10 +93,6 @@ public class NodeInstance extends Entity<Long> {
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getWorkflowInstanceId() {
|
|
||||||
return workflowInstanceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPreNodeId() {
|
public String getPreNodeId() {
|
||||||
return preNodeId;
|
return preNodeId;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,12 +75,6 @@ public class WorkflowDefinition extends Entity<Long> {
|
|||||||
@Column(name = "graph_definition", columnDefinition = "TEXT")
|
@Column(name = "graph_definition", columnDefinition = "TEXT")
|
||||||
private String graphDefinition;
|
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;
|
private String businessKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.entity;
|
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.annotation.LogicDelete;
|
||||||
import com.qqchen.deploy.backend.framework.domain.Entity;
|
import com.qqchen.deploy.backend.framework.domain.Entity;
|
||||||
|
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@ -13,38 +16,27 @@ import lombok.EqualsAndHashCode;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@jakarta.persistence.Entity
|
|
||||||
@Table(name = "wf_log")
|
@Table(name = "wf_log")
|
||||||
@LogicDelete
|
@LogicDelete
|
||||||
|
@jakarta.persistence.Entity
|
||||||
public class WorkflowLog extends Entity<Long> {
|
public class WorkflowLog extends Entity<Long> {
|
||||||
|
|
||||||
/**
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
* 工作流实例ID
|
@JoinColumn(name = "workflow_instance_id", nullable = false)
|
||||||
*/
|
private WorkflowInstance workflowInstance;
|
||||||
@Column(name = "workflow_instance_id", nullable = false)
|
|
||||||
private Long workflowInstanceId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 节点ID
|
|
||||||
*/
|
|
||||||
@Column(name = "node_id")
|
@Column(name = "node_id")
|
||||||
private String nodeId;
|
private String nodeId;
|
||||||
|
|
||||||
/**
|
|
||||||
* 日志内容
|
|
||||||
*/
|
|
||||||
@Column(nullable = false)
|
|
||||||
private String content;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日志级别
|
* 日志级别
|
||||||
*/
|
*/
|
||||||
@Column(nullable = false)
|
@Column(name = "level")
|
||||||
private LogLevelEnum level;
|
private LogLevelEnum level;
|
||||||
|
|
||||||
/**
|
@Column(name = "message", nullable = false, columnDefinition = "TEXT")
|
||||||
* 详细信息
|
private String message;
|
||||||
*/
|
|
||||||
@Column(columnDefinition = "TEXT")
|
@Column(name = "detail", columnDefinition = "TEXT")
|
||||||
private String detail;
|
private String detail;
|
||||||
}
|
}
|
||||||
@ -18,10 +18,11 @@ import lombok.EqualsAndHashCode;
|
|||||||
public class WorkflowVariable extends Entity<Long> {
|
public class WorkflowVariable extends Entity<Long> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工作流实例ID
|
* 工作流实例
|
||||||
*/
|
*/
|
||||||
@Column(name = "workflow_instance_id", nullable = false)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
private Long workflowInstanceId;
|
@JoinColumn(name = "workflow_instance_id", nullable = false)
|
||||||
|
private WorkflowInstance workflowInstance;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 变量名称
|
* 变量名称
|
||||||
@ -52,4 +53,4 @@ public class WorkflowVariable extends Entity<Long> {
|
|||||||
*/
|
*/
|
||||||
@Column(name = "node_id")
|
@Column(name = "node_id")
|
||||||
private String nodeId;
|
private String nodeId;
|
||||||
}
|
}
|
||||||
@ -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.framework.repository.IBaseRepository;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||||
|
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
|
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
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")
|
@Query("SELECT n FROM NodeInstance n WHERE n.workflowInstance = :instance AND n.deleted = false ORDER BY n.createTime")
|
||||||
List<NodeInstance> findByWorkflowInstanceIdOrderByCreateTime(@Param("instanceId") Long instanceId);
|
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;
|
package com.qqchen.deploy.backend.workflow.repository;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
|
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 com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
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);
|
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);
|
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;
|
package com.qqchen.deploy.backend.workflow.repository;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
|
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.entity.WorkflowVariable;
|
||||||
|
import com.qqchen.deploy.backend.workflow.enums.VariableScopeEnum;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
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")
|
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance = :instance AND v.deleted = false")
|
||||||
List<WorkflowVariable> findByWorkflowInstanceId(@Param("instanceId") Long instanceId);
|
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")
|
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance = :instance AND v.scope = :scope AND v.deleted = false")
|
||||||
List<WorkflowVariable> findByWorkflowInstanceIdAndScope(@Param("instanceId") Long instanceId, @Param("scope") String scope);
|
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")
|
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance = :instance AND v.scope = :scope AND v.nodeId = :nodeId AND v.deleted = false")
|
||||||
List<WorkflowVariable> findByWorkflowInstanceIdAndScopeAndNodeId(
|
List<WorkflowVariable> findByWorkflowInstanceAndScopeAndNodeId(
|
||||||
@Param("instanceId") Long instanceId,
|
@Param("instance") WorkflowInstance instance,
|
||||||
@Param("scope") String scope,
|
@Param("scope") String scope,
|
||||||
@Param("nodeId") String nodeId);
|
@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")
|
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance = :instance AND v.name = :name AND v.scope = :scope AND v.deleted = false")
|
||||||
Optional<WorkflowVariable> findByWorkflowInstanceIdAndNameAndScope(
|
Optional<WorkflowVariable> findByWorkflowInstanceAndNameAndScope(
|
||||||
@Param("instanceId") Long instanceId,
|
@Param("instance") WorkflowInstance instance,
|
||||||
@Param("name") String name,
|
@Param("name") String name,
|
||||||
@Param("scope") String scope);
|
@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;
|
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.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.dto.WorkflowLogDTO;
|
||||||
|
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
|
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -13,22 +14,49 @@ import java.util.List;
|
|||||||
public interface IWorkflowLogService extends IBaseService<WorkflowLog, WorkflowLogDTO, Long> {
|
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);
|
List<WorkflowLogDTO> getLogs(Long workflowInstanceId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询节点实例的所有日志
|
* 查询节点实例的所有日志
|
||||||
|
*
|
||||||
|
* @param workflowInstanceId 工作流实例ID
|
||||||
|
* @param nodeId 节点ID
|
||||||
|
* @return 日志列表
|
||||||
*/
|
*/
|
||||||
List<WorkflowLogDTO> getNodeLogs(Long workflowInstanceId, String nodeId);
|
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.framework.service.IBaseService;
|
||||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowVariableDTO;
|
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.entity.WorkflowVariable;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.VariableScopeEnum;
|
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);
|
Map<String, Object> getVariables(Long workflowInstanceId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定作用域的变量
|
* 获取指定作用域的变量
|
||||||
|
*
|
||||||
|
* @param scope 变量作用域
|
||||||
|
* @return 变量映射
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getVariablesByScope(Long workflowInstanceId, VariableScopeEnum scope);
|
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.IWorkflowDefinitionRepository;
|
||||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
|
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
|
||||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
|
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 jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -34,7 +34,7 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
|
|||||||
private IWorkflowInstanceRepository workflowInstanceRepository;
|
private IWorkflowInstanceRepository workflowInstanceRepository;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private IWorkflowVariableService workflowVariableService;
|
private WorkflowVariableOperations variableOperations;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -60,7 +60,7 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
|
|||||||
|
|
||||||
// 4. 设置初始变量
|
// 4. 设置初始变量
|
||||||
if (variables != null && !variables.isEmpty()) {
|
if (variables != null && !variables.isEmpty()) {
|
||||||
variables.forEach((key, value) -> workflowVariableService.setVariable(savedInstance.getId(), key, value));
|
variableOperations.setVariables(savedInstance.getId(), variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
return converter.toDto(savedInstance);
|
return converter.toDto(savedInstance);
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
package com.qqchen.deploy.backend.workflow.service.impl;
|
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.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.dto.WorkflowLogDTO;
|
||||||
|
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
|
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
|
||||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowLogRepository;
|
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 com.qqchen.deploy.backend.workflow.service.IWorkflowLogService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -23,17 +25,33 @@ public class WorkflowLogServiceImpl extends BaseServiceImpl<WorkflowLog, Workflo
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private IWorkflowLogRepository logRepository;
|
private IWorkflowLogRepository logRepository;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IWorkflowInstanceService workflowInstanceService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void log(Long workflowInstanceId, String nodeId, String message, LogLevelEnum level, String detail) {
|
public WorkflowLog log(WorkflowInstance instance, String nodeId, LogLevelEnum level, String message, String detail) {
|
||||||
WorkflowLog log = new WorkflowLog();
|
WorkflowLog workflowLog = new WorkflowLog();
|
||||||
log.setWorkflowInstanceId(workflowInstanceId);
|
workflowLog.setWorkflowInstance(instance);
|
||||||
log.setNodeId(nodeId);
|
workflowLog.setNodeId(nodeId);
|
||||||
log.setContent(message);
|
workflowLog.setLevel(level);
|
||||||
log.setLevel(level);
|
workflowLog.setMessage(message);
|
||||||
log.setDetail(detail);
|
workflowLog.setDetail(detail);
|
||||||
repository.save(log);
|
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
|
@Override
|
||||||
@ -52,8 +70,8 @@ public class WorkflowLogServiceImpl extends BaseServiceImpl<WorkflowLog, Workflo
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteLogs(Long workflowInstanceId) {
|
public void deleteLogs(WorkflowInstance workflowInstance) {
|
||||||
logRepository.deleteByWorkflowInstanceId(workflowInstanceId);
|
logRepository.deleteByWorkflowInstance(workflowInstance);
|
||||||
log.debug("删除工作流日志成功: instanceId={}", workflowInstanceId);
|
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.exception.BusinessException;
|
||||||
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
|
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
|
||||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowVariableDTO;
|
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.entity.WorkflowVariable;
|
||||||
import com.qqchen.deploy.backend.workflow.enums.VariableScopeEnum;
|
import com.qqchen.deploy.backend.workflow.enums.VariableScopeEnum;
|
||||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowVariableRepository;
|
import com.qqchen.deploy.backend.workflow.repository.IWorkflowVariableRepository;
|
||||||
@ -29,57 +30,58 @@ public class WorkflowVariableServiceImpl extends BaseServiceImpl<WorkflowVariabl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void setVariable(Long workflowInstanceId, String name, Object value) {
|
public void setVariables(Long workflowInstanceId, Map<String, Object> variables) {
|
||||||
try {
|
if (variables == null || variables.isEmpty()) {
|
||||||
WorkflowVariable variable = variableRepository.findByWorkflowInstanceIdAndNameAndScope(
|
return;
|
||||||
workflowInstanceId, name, VariableScopeEnum.GLOBAL.name())
|
|
||||||
.orElseGet(() -> {
|
|
||||||
WorkflowVariable newVar = new WorkflowVariable();
|
|
||||||
newVar.setWorkflowInstanceId(workflowInstanceId);
|
|
||||||
newVar.setName(name);
|
|
||||||
newVar.setScope(VariableScopeEnum.GLOBAL.name());
|
|
||||||
return newVar;
|
|
||||||
});
|
|
||||||
|
|
||||||
variable.setValue(objectMapper.writeValueAsString(value));
|
|
||||||
variable.setType(value.getClass().getName());
|
|
||||||
variableRepository.save(variable);
|
|
||||||
} catch (JsonProcessingException e) {
|
|
||||||
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_TYPE_INVALID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 先删除已有的变量
|
||||||
|
clearVariables(workflowInstanceId);
|
||||||
|
|
||||||
|
// 批量保存新变量
|
||||||
|
variables.forEach((key, value) -> {
|
||||||
|
try {
|
||||||
|
WorkflowVariable variable = new WorkflowVariable();
|
||||||
|
WorkflowInstance instance = new WorkflowInstance();
|
||||||
|
instance.setId(workflowInstanceId);
|
||||||
|
variable.setWorkflowInstance(instance);
|
||||||
|
variable.setName(key);
|
||||||
|
variable.setValue(objectMapper.writeValueAsString(value));
|
||||||
|
variableRepository.save(variable);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_SERIALIZE_ERROR);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public Map<String, Object> getVariables(Long workflowInstanceId) {
|
public Map<String, Object> getVariables(Long workflowInstanceId) {
|
||||||
List<WorkflowVariable> variables = variableRepository.findByWorkflowInstanceId(workflowInstanceId);
|
List<WorkflowVariable> variables = variableRepository.findByWorkflowInstanceId(workflowInstanceId);
|
||||||
return convertToMap(variables);
|
return deserializeVariables(variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public Map<String, Object> getVariablesByScope(Long workflowInstanceId, VariableScopeEnum scope) {
|
public Map<String, Object> getVariablesByScope(Long workflowInstanceId, VariableScopeEnum scope) {
|
||||||
List<WorkflowVariable> variables = variableRepository.findByWorkflowInstanceIdAndScope(workflowInstanceId, scope.name());
|
List<WorkflowVariable> variables = variableRepository.findByWorkflowInstanceIdAndScope(workflowInstanceId, scope);
|
||||||
return convertToMap(variables);
|
return deserializeVariables(variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteVariables(Long workflowInstanceId) {
|
public void clearVariables(Long workflowInstanceId) {
|
||||||
variableRepository.deleteByWorkflowInstanceId(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<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
for (WorkflowVariable variable : variables) {
|
variables.forEach(variable -> {
|
||||||
try {
|
try {
|
||||||
Class<?> type = Class.forName(variable.getType());
|
Object value = objectMapper.readValue(variable.getValue(), Object.class);
|
||||||
Object value = objectMapper.readValue(variable.getValue(), type);
|
|
||||||
result.put(variable.getName(), value);
|
result.put(variable.getName(), value);
|
||||||
} catch (Exception e) {
|
} catch (JsonProcessingException e) {
|
||||||
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_TYPE_INVALID);
|
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_DESERIALIZE_ERROR);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4,7 +4,7 @@ spring:
|
|||||||
datasource:
|
datasource:
|
||||||
url: jdbc:mysql://localhost:3306/deploy-ease-platform?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
|
url: jdbc:mysql://localhost:3306/deploy-ease-platform?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
|
||||||
username: root
|
username: root
|
||||||
password: root
|
password: ServBay.dev
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
jpa:
|
jpa:
|
||||||
hibernate:
|
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 '结束时间',
|
end_time DATETIME(6) NULL COMMENT '结束时间',
|
||||||
error TEXT NULL COMMENT '错误信息',
|
error TEXT NULL COMMENT '错误信息',
|
||||||
|
|
||||||
CONSTRAINT FK_workflow_instance_definition FOREIGN KEY (workflow_definition_id) REFERENCES wf_workflow_definition (id),
|
CONSTRAINT FK_workflow_instance_definition FOREIGN KEY (workflow_definition_id) REFERENCES wf_workflow_definition (id)
|
||||||
CONSTRAINT UK_workflow_instance_business_key UNIQUE (business_key)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流实例表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流实例表';
|
||||||
|
|
||||||
-- 节点实例表
|
-- 节点实例表
|
||||||
@ -495,17 +494,18 @@ CREATE TABLE wf_workflow_variable (
|
|||||||
-- 工作流日志表
|
-- 工作流日志表
|
||||||
CREATE TABLE wf_log (
|
CREATE TABLE wf_log (
|
||||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
workflow_instance_id BIGINT NOT NULL,
|
workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID',
|
||||||
node_id VARCHAR(50),
|
node_id VARCHAR(50) NULL COMMENT '节点ID',
|
||||||
message VARCHAR(1000) NOT NULL,
|
level VARCHAR(10) NOT NULL COMMENT '日志级别',
|
||||||
level VARCHAR(20) NOT NULL,
|
message TEXT NOT NULL COMMENT '日志内容',
|
||||||
detail TEXT,
|
detail TEXT,
|
||||||
create_time DATETIME NOT NULL,
|
create_time DATETIME NOT NULL,
|
||||||
create_by VARCHAR(50),
|
create_by VARCHAR(50),
|
||||||
update_time DATETIME,
|
update_time DATETIME,
|
||||||
update_by VARCHAR(50),
|
update_by VARCHAR(50),
|
||||||
version INT DEFAULT 0,
|
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 @@
|
|||||||
# 通用响应
|
# \u901A\u7528\u54CD\u5E94
|
||||||
response.success=操作成功
|
response.success=\u64CD\u4F5C\u6210\u529F
|
||||||
response.error=系统错误
|
response.error=\u7CFB\u7EDF\u9519\u8BEF
|
||||||
response.invalid.param=无效的参数
|
response.invalid.param=\u65E0\u6548\u7684\u53C2\u6570
|
||||||
response.unauthorized=未授权
|
response.unauthorized=\u672A\u6388\u6743
|
||||||
response.forbidden=禁止访问
|
response.forbidden=\u7981\u6B62\u8BBF\u95EE
|
||||||
response.not.found=资源未找到
|
response.not.found=\u8D44\u6E90\u672A\u627E\u5230
|
||||||
response.conflict=资源冲突
|
response.conflict=\u8D44\u6E90\u51B2\u7A81
|
||||||
response.unauthorized.full=访问此资源需要完全身份验证
|
response.unauthorized.full=\u8BBF\u95EE\u6B64\u8D44\u6E90\u9700\u8981\u5B8C\u5168\u8EAB\u4EFD\u9A8C\u8BC1
|
||||||
|
|
||||||
# 业务错误
|
# \u4E1A\u52A1\u9519\u8BEF
|
||||||
tenant.not.found=租户不存在
|
tenant.not.found=\u79DF\u6237\u4E0D\u5B58\u5728
|
||||||
data.not.found=找不到ID为{0}的{1}
|
data.not.found=\u627E\u4E0D\u5230ID\u4E3A{0}\u7684{1}
|
||||||
|
|
||||||
# 用户相关
|
# \u7528\u6237\u76F8\u5173
|
||||||
user.not.found=用户不存在
|
user.not.found=\u7528\u6237\u4E0D\u5B58\u5728
|
||||||
user.username.exists=用户名"{0}"已存在
|
user.username.exists=\u7528\u6237\u540D"{0}"\u5DF2\u5B58\u5728
|
||||||
user.email.exists=邮箱"{0}"已存在
|
user.email.exists=\u90AE\u7BB1"{0}"\u5DF2\u5B58\u5728
|
||||||
user.login.error=用户名或密码错误
|
user.login.error=\u7528\u6237\u540D\u6216\u5BC6\u7801\u9519\u8BEF
|
||||||
|
|
||||||
# 系统异常消息
|
# \u7CFB\u7EDF\u5F02\u5E38\u6D88\u606F
|
||||||
system.optimistic.lock.error=数据已被其他用户修改,请刷新后重试
|
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=数据正被其他用户操作,请稍后重试
|
system.pessimistic.lock.error=\u6570\u636E\u6B63\u88AB\u5176\u4ED6\u7528\u6237\u64CD\u4F5C\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5
|
||||||
system.concurrent.update.error=并发更新冲突,请重试
|
system.concurrent.update.error=\u5E76\u53D1\u66F4\u65B0\u51B2\u7A81\uFF0C\u8BF7\u91CD\u8BD5
|
||||||
system.retry.exceeded.error=操作重试次数超限,请稍后再试
|
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 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.message={0}
|
||||||
entity.not.found.name.id=找不到ID为{1}的{0}
|
entity.not.found.name.id=\u627E\u4E0D\u5230ID\u4E3A{1}\u7684{0}
|
||||||
|
|
||||||
# 依赖注入相关
|
# \u4F9D\u8D56\u6CE8\u5165\u76F8\u5173
|
||||||
dependency.injection.service.not.found=找不到实体 {0} 对应的服务 (尝试过的bean名称: {1})
|
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=找不到实体 {0} 对应的Repository: {1}
|
dependency.injection.repository.not.found=\u627E\u4E0D\u5230\u5B9E\u4F53 {0} \u5BF9\u5E94\u7684Repository: {1}
|
||||||
dependency.injection.converter.not.found=找不到实体 {0} 对应的Converter: {1}
|
dependency.injection.converter.not.found=\u627E\u4E0D\u5230\u5B9E\u4F53 {0} \u5BF9\u5E94\u7684Converter: {1}
|
||||||
dependency.injection.entitypath.failed=初始化实体 {0} 的EntityPath失败: {1}
|
dependency.injection.entitypath.failed=\u521D\u59CB\u5316\u5B9E\u4F53 {0} \u7684EntityPath\u5931\u8D25: {1}
|
||||||
|
|
||||||
# JWT相关
|
# JWT\u76F8\u5173
|
||||||
jwt.token.expired=登录已过期,请重新登录
|
jwt.token.expired=\u767B\u5F55\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
|
||||||
jwt.token.invalid=无效的登录凭证
|
jwt.token.invalid=\u65E0\u6548\u7684\u767B\u5F55\u51ED\u8BC1
|
||||||
jwt.token.missing=未提供登录凭证
|
jwt.token.missing=\u672A\u63D0\u4F9B\u767B\u5F55\u51ED\u8BC1
|
||||||
|
|
||||||
# 角色相关错误消息
|
# \u89D2\u8272\u76F8\u5173\u9519\u8BEF\u6D88\u606F
|
||||||
role.not.found=角色不存在
|
role.not.found=\u89D2\u8272\u4E0D\u5B58\u5728
|
||||||
role.code.exists=角色编码"{0}"已存在
|
role.code.exists=\u89D2\u8272\u7F16\u7801"{0}"\u5DF2\u5B58\u5728
|
||||||
role.name.exists=角色名称"{0}"已存在
|
role.name.exists=\u89D2\u8272\u540D\u79F0"{0}"\u5DF2\u5B58\u5728
|
||||||
role.in.use=角色正在使用中,无法删除
|
role.in.use=\u89D2\u8272\u6B63\u5728\u4F7F\u7528\u4E2D\uFF0C\u65E0\u6CD5\u5220\u9664
|
||||||
role.admin.cannot.delete=不能删除超级管理员角色
|
role.admin.cannot.delete=\u4E0D\u80FD\u5220\u9664\u8D85\u7EA7\u7BA1\u7406\u5458\u89D2\u8272
|
||||||
role.admin.cannot.update=不能修改超级管理员角色
|
role.admin.cannot.update=\u4E0D\u80FD\u4FEE\u6539\u8D85\u7EA7\u7BA1\u7406\u5458\u89D2\u8272
|
||||||
role.tag.name.exists=标签名称已存在
|
role.tag.name.exists=\u6807\u7B7E\u540D\u79F0\u5DF2\u5B58\u5728
|
||||||
role.tag.not.found=标签不存在
|
role.tag.not.found=\u6807\u7B7E\u4E0D\u5B58\u5728
|
||||||
role.tag.in.use=标签正在使用中,无法删除
|
role.tag.in.use=\u6807\u7B7E\u6B63\u5728\u4F7F\u7528\u4E2D\uFF0C\u65E0\u6CD5\u5220\u9664
|
||||||
|
|
||||||
# 部门相关
|
# \u90E8\u95E8\u76F8\u5173
|
||||||
department.not.found=部门不存在
|
department.not.found=\u90E8\u95E8\u4E0D\u5B58\u5728
|
||||||
department.code.exists=部门编码已存在
|
department.code.exists=\u90E8\u95E8\u7F16\u7801\u5DF2\u5B58\u5728
|
||||||
department.name.exists=部门名称已存在
|
department.name.exists=\u90E8\u95E8\u540D\u79F0\u5DF2\u5B58\u5728
|
||||||
department.parent.not.found=上级部门不存在
|
department.parent.not.found=\u4E0A\u7EA7\u90E8\u95E8\u4E0D\u5B58\u5728
|
||||||
department.has.children=该部门下有子部门,无法删除
|
department.has.children=\u8BE5\u90E8\u95E8\u4E0B\u6709\u5B50\u90E8\u95E8\uFF0C\u65E0\u6CD5\u5220\u9664
|
||||||
|
|
||||||
# 权限相关
|
# \u6743\u9650\u76F8\u5173
|
||||||
permission.not.found=权限不存在
|
permission.not.found=\u6743\u9650\u4E0D\u5B58\u5728
|
||||||
permission.code.exists=权限编码{0}已存在
|
permission.code.exists=\u6743\u9650\u7F16\u7801{0}\u5DF2\u5B58\u5728
|
||||||
permission.name.exists=权限名称{0}已存在
|
permission.name.exists=\u6743\u9650\u540D\u79F0{0}\u5DF2\u5B58\u5728
|
||||||
permission.already.assigned=该权限已分配给角色
|
permission.already.assigned=\u8BE5\u6743\u9650\u5DF2\u5206\u914D\u7ED9\u89D2\u8272
|
||||||
permission.assign.failed=权限分配失败
|
permission.assign.failed=\u6743\u9650\u5206\u914D\u5931\u8D25
|
||||||
|
|
||||||
# 第三方系统相关
|
# \u7B2C\u4E09\u65B9\u7CFB\u7EDF\u76F8\u5173
|
||||||
external.system.name.exists=系统名称"{0}"已存在
|
external.system.name.exists=\u7CFB\u7EDF\u540D\u79F0"{0}"\u5DF2\u5B58\u5728
|
||||||
external.system.type.url.exists=系统类型和URL组合"{0}"已存在
|
external.system.type.url.exists=\u7CFB\u7EDF\u7C7B\u578B\u548CURL\u7EC4\u5408"{0}"\u5DF2\u5B58\u5728
|
||||||
external.system.disabled=系统已禁用
|
external.system.disabled=\u7CFB\u7EDF\u5DF2\u7981\u7528
|
||||||
external.system.sync.failed=系统数据同步失败
|
external.system.sync.failed=\u7CFB\u7EDF\u6570\u636E\u540C\u6B65\u5931\u8D25
|
||||||
external.system.type.not.supported=不支持的系统类型
|
external.system.type.not.supported=\u4E0D\u652F\u6301\u7684\u7CFB\u7EDF\u7C7B\u578B
|
||||||
|
|
||||||
# Git系统相关错误
|
# Git\u7CFB\u7EDF\u76F8\u5173\u9519\u8BEF
|
||||||
external.system.git.auth.type.error=Git系统只支持Token认证
|
external.system.git.auth.type.error=Git\u7CFB\u7EDF\u53EA\u652F\u6301Token\u8BA4\u8BC1
|
||||||
external.system.git.token.required=Git系统必须提供Token
|
external.system.git.token.required=Git\u7CFB\u7EDF\u5FC5\u987B\u63D0\u4F9BToken
|
||||||
|
|
||||||
# 仓库相关错误消息
|
# \u4ED3\u5E93\u76F8\u5173\u9519\u8BEF\u6D88\u606F
|
||||||
repository.group.not.found=仓库组不存在
|
repository.group.not.found=\u4ED3\u5E93\u7EC4\u4E0D\u5B58\u5728
|
||||||
repository.group.name.exists=仓库组名称"{0}"已存在
|
repository.group.name.exists=\u4ED3\u5E93\u7EC4\u540D\u79F0"{0}"\u5DF2\u5B58\u5728
|
||||||
repository.group.path.exists=仓库组路径"{0}"已存在
|
repository.group.path.exists=\u4ED3\u5E93\u7EC4\u8DEF\u5F84"{0}"\u5DF2\u5B58\u5728
|
||||||
repository.project.not.found=仓库项目不存在
|
repository.project.not.found=\u4ED3\u5E93\u9879\u76EE\u4E0D\u5B58\u5728
|
||||||
repository.project.name.exists=仓库项目名称"{0}"已存在
|
repository.project.name.exists=\u4ED3\u5E93\u9879\u76EE\u540D\u79F0"{0}"\u5DF2\u5B58\u5728
|
||||||
repository.project.path.exists=仓库项目路径"{0}"已存在
|
repository.project.path.exists=\u4ED3\u5E93\u9879\u76EE\u8DEF\u5F84"{0}"\u5DF2\u5B58\u5728
|
||||||
repository.branch.not.found=分支不存在
|
repository.branch.not.found=\u5206\u652F\u4E0D\u5B58\u5728
|
||||||
repository.branch.name.exists=分支名称"{0}"已存在
|
repository.branch.name.exists=\u5206\u652F\u540D\u79F0"{0}"\u5DF2\u5B58\u5728
|
||||||
repository.sync.in.progress=仓库同步正在进行中
|
repository.sync.in.progress=\u4ED3\u5E93\u540C\u6B65\u6B63\u5728\u8FDB\u884C\u4E2D
|
||||||
repository.sync.failed=仓库同步失败:{0}
|
repository.sync.failed=\u4ED3\u5E93\u540C\u6B65\u5931\u8D25\uFF1A{0}
|
||||||
repository.sync.history.not.found=同步历史记录不存在
|
repository.sync.history.not.found=\u540C\u6B65\u5386\u53F2\u8BB0\u5F55\u4E0D\u5B58\u5728
|
||||||
|
|
||||||
# 工作流相关错误消息
|
# \u5DE5\u4F5C\u6D41\u76F8\u5173\u9519\u8BEF\u6D88\u606F
|
||||||
workflow.definition.not.found=工作流定义不存在
|
workflow.definition.not.found=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u4E0D\u5B58\u5728
|
||||||
workflow.definition.code.exists=工作流定义编码"{0}"已存在
|
workflow.definition.code.exists=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u7F16\u7801"{0}"\u5DF2\u5B58\u5728
|
||||||
workflow.definition.name.exists=工作流定义名称"{0}"已存在
|
workflow.definition.name.exists=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u540D\u79F0"{0}"\u5DF2\u5B58\u5728
|
||||||
workflow.definition.invalid.content=工作流定义内容无效:{0}
|
workflow.definition.invalid.content=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u5185\u5BB9\u65E0\u6548\uFF1A{0}
|
||||||
workflow.definition.not.published=工作流定义未发布
|
workflow.definition.not.published=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u672A\u53D1\u5E03
|
||||||
workflow.definition.already.published=工作流定义已发布
|
workflow.definition.already.published=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u5DF2\u53D1\u5E03
|
||||||
workflow.definition.cannot.delete=工作流定义已被使用,无法删除
|
workflow.definition.cannot.delete=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u5DF2\u88AB\u4F7F\u7528\uFF0C\u65E0\u6CD5\u5220\u9664
|
||||||
|
|
||||||
workflow.instance.not.found=工作流实例不存在
|
workflow.instance.not.found=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u4E0D\u5B58\u5728
|
||||||
workflow.instance.cannot.start=工作流实例无法启动
|
workflow.instance.cannot.start=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u65E0\u6CD5\u542F\u52A8
|
||||||
workflow.instance.cannot.cancel=工作流实例无法取消
|
workflow.instance.cannot.cancel=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u65E0\u6CD5\u53D6\u6D88
|
||||||
workflow.instance.cannot.pause=工作流实例无法暂停
|
workflow.instance.cannot.pause=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u65E0\u6CD5\u6682\u505C
|
||||||
workflow.instance.cannot.resume=工作流实例无法恢复
|
workflow.instance.cannot.resume=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u65E0\u6CD5\u6062\u590D
|
||||||
workflow.instance.cannot.retry=工作流实例无法重试
|
workflow.instance.cannot.retry=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u65E0\u6CD5\u91CD\u8BD5
|
||||||
|
|
||||||
# 节点相关错误消息
|
# \u8282\u70B9\u76F8\u5173\u9519\u8BEF\u6D88\u606F
|
||||||
node.instance.not.found=节点实例不存在
|
node.instance.not.found=\u8282\u70B9\u5B9E\u4F8B\u4E0D\u5B58\u5728
|
||||||
node.instance.cannot.retry=节点实例无法重试
|
node.instance.cannot.retry=\u8282\u70B9\u5B9E\u4F8B\u65E0\u6CD5\u91CD\u8BD5
|
||||||
node.instance.cannot.skip=节点实例无法跳过
|
node.instance.cannot.skip=\u8282\u70B9\u5B9E\u4F8B\u65E0\u6CD5\u8DF3\u8FC7
|
||||||
node.executor.not.found=节点执行器不存在
|
node.executor.not.found=\u8282\u70B9\u6267\u884C\u5668\u4E0D\u5B58\u5728
|
||||||
|
|
||||||
# 工作流相关消息
|
# \u5DE5\u4F5C\u6D41\u76F8\u5173\u6D88\u606F
|
||||||
workflow.not.found=工作流定义不存在
|
workflow.not.found=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u4E0D\u5B58\u5728
|
||||||
workflow.code.exists=工作流编码已存在
|
workflow.code.exists=\u5DE5\u4F5C\u6D41\u7F16\u7801\u5DF2\u5B58\u5728
|
||||||
workflow.name.exists=工作流名称已存在
|
workflow.name.exists=\u5DE5\u4F5C\u6D41\u540D\u79F0\u5DF2\u5B58\u5728
|
||||||
workflow.disabled=工作流已禁用
|
workflow.disabled=\u5DE5\u4F5C\u6D41\u5DF2\u7981\u7528
|
||||||
workflow.not.published=工作流未发布
|
workflow.not.published=\u5DE5\u4F5C\u6D41\u672A\u53D1\u5E03
|
||||||
workflow.already.published=工作流已发布
|
workflow.already.published=\u5DE5\u4F5C\u6D41\u5DF2\u53D1\u5E03
|
||||||
workflow.already.disabled=工作流已禁用
|
workflow.already.disabled=\u5DE5\u4F5C\u6D41\u5DF2\u7981\u7528
|
||||||
workflow.instance.not.found=工作流实例不存在
|
workflow.instance.not.found=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u4E0D\u5B58\u5728
|
||||||
workflow.instance.already.completed=工作流实例已完成
|
workflow.instance.already.completed=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u5DF2\u5B8C\u6210
|
||||||
workflow.instance.already.canceled=工作流实例已取消
|
workflow.instance.already.canceled=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u5DF2\u53D6\u6D88
|
||||||
workflow.instance.not.running=工作流实例未运行
|
workflow.instance.not.running=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u672A\u8FD0\u884C
|
||||||
workflow.node.not.found=工作流节点不存在
|
workflow.node.not.found=\u5DE5\u4F5C\u6D41\u8282\u70B9\u4E0D\u5B58\u5728
|
||||||
workflow.node.type.not.supported=不支持的节点类型
|
workflow.node.type.not.supported=\u4E0D\u652F\u6301\u7684\u8282\u70B9\u7C7B\u578B
|
||||||
workflow.node.config.invalid=节点配置无效
|
workflow.node.config.invalid=\u8282\u70B9\u914D\u7F6E\u65E0\u6548
|
||||||
workflow.node.execution.failed=节点执行失败
|
workflow.node.execution.failed=\u8282\u70B9\u6267\u884C\u5931\u8D25
|
||||||
workflow.node.timeout=节点执行超时
|
workflow.node.timeout=\u8282\u70B9\u6267\u884C\u8D85\u65F6
|
||||||
workflow.variable.not.found=工作流变量不存在
|
workflow.variable.not.found=\u5DE5\u4F5C\u6D41\u53D8\u91CF\u4E0D\u5B58\u5728
|
||||||
workflow.variable.type.invalid=工作流变量类型无效
|
workflow.variable.type.invalid=\u5DE5\u4F5C\u6D41\u53D8\u91CF\u7C7B\u578B\u65E0\u6548
|
||||||
workflow.permission.denied=工作流权限不足
|
workflow.permission.denied=\u5DE5\u4F5C\u6D41\u6743\u9650\u4E0D\u8DB3
|
||||||
workflow.approval.required=需要审批
|
workflow.approval.required=\u9700\u8981\u5BA1\u6279
|
||||||
workflow.approval.rejected=审批被拒绝
|
workflow.approval.rejected=\u5BA1\u6279\u88AB\u62D2\u7EDD
|
||||||
workflow.dependency.not.satisfied=依赖条件不满足
|
workflow.dependency.not.satisfied=\u4F9D\u8D56\u6761\u4EF6\u4E0D\u6EE1\u8DB3
|
||||||
workflow.circular.dependency=存在循环依赖
|
workflow.circular.dependency=\u5B58\u5728\u5FAA\u73AF\u4F9D\u8D56
|
||||||
workflow.schedule.invalid=调度配置无效
|
workflow.schedule.invalid=\u8C03\u5EA6\u914D\u7F6E\u65E0\u6548
|
||||||
workflow.concurrent.limit.exceeded=超出并发限制
|
workflow.concurrent.limit.exceeded=\u8D85\u51FA\u5E76\u53D1\u9650\u5236
|
||||||
|
|
||||||
# Workflow error messages
|
# Workflow error messages
|
||||||
workflow.not.found=工作流定义不存在
|
workflow.not.found=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u4E0D\u5B58\u5728
|
||||||
workflow.code.exists=工作流编码已存在
|
workflow.code.exists=\u5DE5\u4F5C\u6D41\u7F16\u7801\u5DF2\u5B58\u5728
|
||||||
workflow.name.exists=工作流名称已存在
|
workflow.name.exists=\u5DE5\u4F5C\u6D41\u540D\u79F0\u5DF2\u5B58\u5728
|
||||||
workflow.invalid.status=工作流状态无效
|
workflow.invalid.status=\u5DE5\u4F5C\u6D41\u72B6\u6001\u65E0\u6548
|
||||||
workflow.node.not.found=工作流节点不存在
|
workflow.node.not.found=\u5DE5\u4F5C\u6D41\u8282\u70B9\u4E0D\u5B58\u5728
|
||||||
workflow.node.config.error=工作流节点配置错误
|
workflow.node.config.error=\u5DE5\u4F5C\u6D41\u8282\u70B9\u914D\u7F6E\u9519\u8BEF
|
||||||
workflow.execution.error=工作流执行错误
|
workflow.execution.error=\u5DE5\u4F5C\u6D41\u6267\u884C\u9519\u8BEF
|
||||||
workflow.not.draft=只有草稿状态的工作流定义可以发布
|
workflow.not.draft=\u53EA\u6709\u8349\u7A3F\u72B6\u6001\u7684\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u53EF\u4EE5\u53D1\u5E03
|
||||||
workflow.not.published=只有已发布状态的工作流定义可以禁用
|
workflow.not.published=\u53EA\u6709\u5DF2\u53D1\u5E03\u72B6\u6001\u7684\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u53EF\u4EE5\u7981\u7528
|
||||||
workflow.not.disabled=只有已禁用状态的工作流定义可以启用
|
workflow.not.disabled=\u53EA\u6709\u5DF2\u7981\u7528\u72B6\u6001\u7684\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u53EF\u4EE5\u542F\u7528
|
||||||
|
|
||||||
# System level messages (1xxx)
|
# System level messages (1xxx)
|
||||||
success=操作成功
|
success=\u64CD\u4F5C\u6210\u529F
|
||||||
system.error=系统错误
|
system.error=\u7CFB\u7EDF\u9519\u8BEF
|
||||||
param.error=参数错误
|
param.error=\u53C2\u6570\u9519\u8BEF
|
||||||
unauthorized=未授权
|
unauthorized=\u672A\u6388\u6743
|
||||||
forbidden=禁止访问
|
forbidden=\u7981\u6B62\u8BBF\u95EE
|
||||||
not.found=资源不存在
|
not.found=\u8D44\u6E90\u4E0D\u5B58\u5728
|
||||||
method.not.allowed=方法不允许
|
method.not.allowed=\u65B9\u6CD5\u4E0D\u5141\u8BB8
|
||||||
conflict=资源冲突
|
conflict=\u8D44\u6E90\u51B2\u7A81
|
||||||
too.many.requests=请求过于频繁
|
too.many.requests=\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41
|
||||||
internal.server.error=内部服务器错误
|
internal.server.error=\u5185\u90E8\u670D\u52A1\u5668\u9519\u8BEF
|
||||||
|
|
||||||
# Business level messages (2xxx)
|
# Business level messages (2xxx)
|
||||||
# Common business messages (2000-2099)
|
# Common business messages (2000-2099)
|
||||||
business.error=业务错误
|
business.error=\u4E1A\u52A1\u9519\u8BEF
|
||||||
data.not.found=数据不存在
|
data.not.found=\u6570\u636E\u4E0D\u5B58\u5728
|
||||||
data.already.exists=数据已存在
|
data.already.exists=\u6570\u636E\u5DF2\u5B58\u5728
|
||||||
data.validation.failed=数据验证失败
|
data.validation.failed=\u6570\u636E\u9A8C\u8BC1\u5931\u8D25
|
||||||
operation.not.allowed=操作不允许
|
operation.not.allowed=\u64CD\u4F5C\u4E0D\u5141\u8BB8
|
||||||
|
|
||||||
# Workflow related messages (2100-2199)
|
# Workflow related messages (2100-2199)
|
||||||
workflow.not.found=工作流不存在
|
workflow.not.found=\u5DE5\u4F5C\u6D41\u4E0D\u5B58\u5728
|
||||||
workflow.already.exists=工作流已存在
|
workflow.already.exists=\u5DE5\u4F5C\u6D41\u5DF2\u5B58\u5728
|
||||||
workflow.not.published=工作流未发布
|
workflow.not.published=\u5DE5\u4F5C\u6D41\u672A\u53D1\u5E03
|
||||||
workflow.config.invalid=工作流配置无效
|
workflow.config.invalid=\u5DE5\u4F5C\u6D41\u914D\u7F6E\u65E0\u6548
|
||||||
workflow.node.not.found=工作流节点不存在
|
workflow.node.not.found=\u5DE5\u4F5C\u6D41\u8282\u70B9\u4E0D\u5B58\u5728
|
||||||
workflow.node.execution.failed=工作流节点执行失败
|
workflow.node.execution.failed=\u5DE5\u4F5C\u6D41\u8282\u70B9\u6267\u884C\u5931\u8D25
|
||||||
workflow.instance.not.found=工作流实例不存在
|
workflow.instance.not.found=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u4E0D\u5B58\u5728
|
||||||
workflow.instance.not.running=工作流实例未运行
|
workflow.instance.not.running=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u672A\u8FD0\u884C
|
||||||
workflow.variable.not.found=工作流变量不存在
|
workflow.variable.not.found=\u5DE5\u4F5C\u6D41\u53D8\u91CF\u4E0D\u5B58\u5728
|
||||||
workflow.log.not.found=工作流日志不存在
|
workflow.log.not.found=\u5DE5\u4F5C\u6D41\u65E5\u5FD7\u4E0D\u5B58\u5728
|
||||||
workflow.transition.invalid=工作流流转配置无效
|
workflow.transition.invalid=\u5DE5\u4F5C\u6D41\u6D41\u8F6C\u914D\u7F6E\u65E0\u6548
|
||||||
workflow.node.type.not.supported=不支持的节点类型
|
workflow.node.type.not.supported=\u4E0D\u652F\u6301\u7684\u8282\u70B9\u7C7B\u578B
|
||||||
workflow.condition.invalid=工作流条件配置无效
|
workflow.condition.invalid=\u5DE5\u4F5C\u6D41\u6761\u4EF6\u914D\u7F6E\u65E0\u6548
|
||||||
|
|
||||||
# 工作流相关错误消息
|
# \u5DE5\u4F5C\u6D41\u76F8\u5173\u9519\u8BEF\u6D88\u606F
|
||||||
workflow.not.found=工作流定义不存在
|
workflow.not.found=\u5DE5\u4F5C\u6D41\u5B9A\u4E49\u4E0D\u5B58\u5728
|
||||||
workflow.code.exists=工作流编码已存在
|
workflow.code.exists=\u5DE5\u4F5C\u6D41\u7F16\u7801\u5DF2\u5B58\u5728
|
||||||
workflow.name.exists=工作流名称已存在
|
workflow.name.exists=\u5DE5\u4F5C\u6D41\u540D\u79F0\u5DF2\u5B58\u5728
|
||||||
workflow.disabled=工作流已禁用
|
workflow.disabled=\u5DE5\u4F5C\u6D41\u5DF2\u7981\u7528
|
||||||
workflow.not.published=工作流未发布
|
workflow.not.published=\u5DE5\u4F5C\u6D41\u672A\u53D1\u5E03
|
||||||
workflow.already.published=工作流已发布
|
workflow.already.published=\u5DE5\u4F5C\u6D41\u5DF2\u53D1\u5E03
|
||||||
workflow.already.disabled=工作流已禁用
|
workflow.already.disabled=\u5DE5\u4F5C\u6D41\u5DF2\u7981\u7528
|
||||||
workflow.not.draft=工作流不是草稿状态
|
workflow.not.draft=\u5DE5\u4F5C\u6D41\u4E0D\u662F\u8349\u7A3F\u72B6\u6001
|
||||||
workflow.not.disabled=工作流不是禁用状态
|
workflow.not.disabled=\u5DE5\u4F5C\u6D41\u4E0D\u662F\u7981\u7528\u72B6\u6001
|
||||||
workflow.invalid.status=工作流状态无效
|
workflow.invalid.status=\u5DE5\u4F5C\u6D41\u72B6\u6001\u65E0\u6548
|
||||||
workflow.config.invalid=工作流配置无效
|
workflow.config.invalid=\u5DE5\u4F5C\u6D41\u914D\u7F6E\u65E0\u6548
|
||||||
workflow.transition.invalid=工作流流转规则无效
|
workflow.transition.invalid=\u5DE5\u4F5C\u6D41\u6D41\u8F6C\u89C4\u5219\u65E0\u6548
|
||||||
workflow.condition.invalid=工作流条件配置无效
|
workflow.condition.invalid=\u5DE5\u4F5C\u6D41\u6761\u4EF6\u914D\u7F6E\u65E0\u6548
|
||||||
|
|
||||||
workflow.instance.not.found=工作流实例不存在
|
workflow.instance.not.found=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u4E0D\u5B58\u5728
|
||||||
workflow.instance.already.completed=工作流实例已完成
|
workflow.instance.already.completed=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u5DF2\u5B8C\u6210
|
||||||
workflow.instance.already.canceled=工作流实例已取消
|
workflow.instance.already.canceled=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u5DF2\u53D6\u6D88
|
||||||
workflow.instance.not.running=工作流实例未运行
|
workflow.instance.not.running=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u672A\u8FD0\u884C
|
||||||
workflow.instance.not.paused=工作流实例不是暂停状态
|
workflow.instance.not.paused=\u5DE5\u4F5C\u6D41\u5B9E\u4F8B\u4E0D\u662F\u6682\u505C\u72B6\u6001
|
||||||
|
|
||||||
workflow.node.not.found=工作流节点不存在
|
workflow.node.not.found=\u5DE5\u4F5C\u6D41\u8282\u70B9\u4E0D\u5B58\u5728
|
||||||
workflow.node.type.not.supported=不支持的节点类型
|
workflow.node.type.not.supported=\u4E0D\u652F\u6301\u7684\u8282\u70B9\u7C7B\u578B
|
||||||
workflow.node.config.invalid=节点配置无效
|
workflow.node.config.invalid=\u8282\u70B9\u914D\u7F6E\u65E0\u6548
|
||||||
workflow.node.execution.failed=节点执行失败
|
workflow.node.execution.failed=\u8282\u70B9\u6267\u884C\u5931\u8D25
|
||||||
workflow.node.timeout=节点执行超时
|
workflow.node.timeout=\u8282\u70B9\u6267\u884C\u8D85\u65F6
|
||||||
workflow.node.config.error=节点配置错误
|
workflow.node.config.error=\u8282\u70B9\u914D\u7F6E\u9519\u8BEF
|
||||||
|
|
||||||
workflow.execution.error=工作流执行错误
|
workflow.execution.error=\u5DE5\u4F5C\u6D41\u6267\u884C\u9519\u8BEF
|
||||||
workflow.variable.not.found=工作流变量不存在
|
workflow.variable.not.found=\u5DE5\u4F5C\u6D41\u53D8\u91CF\u4E0D\u5B58\u5728
|
||||||
workflow.variable.type.invalid=工作流变量类型无效
|
workflow.variable.type.invalid=\u5DE5\u4F5C\u6D41\u53D8\u91CF\u7C7B\u578B\u65E0\u6548
|
||||||
workflow.permission.denied=无权限操作工作流
|
workflow.permission.denied=\u65E0\u6743\u9650\u64CD\u4F5C\u5DE5\u4F5C\u6D41
|
||||||
workflow.approval.required=需要审批
|
workflow.approval.required=\u9700\u8981\u5BA1\u6279
|
||||||
workflow.approval.rejected=审批被拒绝
|
workflow.approval.rejected=\u5BA1\u6279\u88AB\u62D2\u7EDD
|
||||||
workflow.dependency.not.satisfied=工作流依赖条件未满足
|
workflow.dependency.not.satisfied=\u5DE5\u4F5C\u6D41\u4F9D\u8D56\u6761\u4EF6\u672A\u6EE1\u8DB3
|
||||||
workflow.circular.dependency=工作流存在循环依赖
|
workflow.circular.dependency=\u5DE5\u4F5C\u6D41\u5B58\u5728\u5FAA\u73AF\u4F9D\u8D56
|
||||||
workflow.schedule.invalid=工作流调度配置无效
|
workflow.schedule.invalid=\u5DE5\u4F5C\u6D41\u8C03\u5EA6\u914D\u7F6E\u65E0\u6548
|
||||||
workflow.concurrent.limit.exceeded=工作流并发限制超出
|
workflow.concurrent.limit.exceeded=\u5DE5\u4F5C\u6D41\u5E76\u53D1\u9650\u5236\u8D85\u51FA
|
||||||
|
|
||||||
# 工作流配置相关错误消息
|
# \u5DE5\u4F5C\u6D41\u914D\u7F6E\u76F8\u5173\u9519\u8BEF\u6D88\u606F
|
||||||
workflow.node.config.empty=节点配置不能为空
|
workflow.node.config.empty=\u8282\u70B9\u914D\u7F6E\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
workflow.transition.config.empty=流转配置不能为空
|
workflow.transition.config.empty=\u6D41\u8F6C\u914D\u7F6E\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
workflow.form.config.empty=表单配置不能为空
|
workflow.form.config.empty=\u8868\u5355\u914D\u7F6E\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
workflow.graph.config.empty=图形配置不能为空
|
workflow.graph.config.empty=\u56FE\u5F62\u914D\u7F6E\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
|
|
||||||
# 工作流节点类型错误 (2200-2299)
|
# \u5DE5\u4F5C\u6D41\u8282\u70B9\u7C7B\u578B\u9519\u8BEF (2200-2299)
|
||||||
workflow.node.type.not.found=节点类型不存在或已删除
|
workflow.node.type.not.found=\u8282\u70B9\u7C7B\u578B\u4E0D\u5B58\u5728\u6216\u5DF2\u5220\u9664
|
||||||
workflow.node.type.disabled=节点类型已禁用,无法使用
|
workflow.node.type.disabled=\u8282\u70B9\u7C7B\u578B\u5DF2\u7981\u7528\uFF0C\u65E0\u6CD5\u4F7F\u7528
|
||||||
workflow.node.type.code.exists=节点类型编码已存在
|
workflow.node.type.code.exists=\u8282\u70B9\u7C7B\u578B\u7F16\u7801\u5DF2\u5B58\u5728
|
||||||
workflow.node.type.invalid.category=无效的节点类型分类
|
workflow.node.type.invalid.category=\u65E0\u6548\u7684\u8282\u70B9\u7C7B\u578B\u5206\u7C7B
|
||||||
workflow.node.type.invalid.executor=无效的执行器配置
|
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