增加工作流实例实现
This commit is contained in:
parent
c8a2c5ac2c
commit
ff7544a127
@ -39,7 +39,7 @@ public class NodeDefinition extends Entity<Long> {
|
||||
/**
|
||||
* 节点类型
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(nullable = false)
|
||||
private NodeTypeEnum type;
|
||||
|
||||
|
||||
@ -39,6 +39,7 @@ public class NodeInstance extends Entity<Long> {
|
||||
/**
|
||||
* 节点类型
|
||||
*/
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(nullable = false)
|
||||
private NodeTypeEnum nodeType;
|
||||
|
||||
@ -51,6 +52,7 @@ public class NodeInstance extends Entity<Long> {
|
||||
/**
|
||||
* 节点状态
|
||||
*/
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(nullable = false)
|
||||
private NodeStatusEnum status;
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ public class WorkflowDefinition extends Entity<Long> {
|
||||
/**
|
||||
* 工作流状态
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(nullable = false)
|
||||
private WorkflowStatusEnum status = WorkflowStatusEnum.DRAFT;
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ public class WorkflowInstance extends Entity<Long> {
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(nullable = false)
|
||||
private WorkflowStatusEnum status = WorkflowStatusEnum.RUNNING;
|
||||
|
||||
|
||||
@ -10,13 +10,14 @@ import lombok.Getter;
|
||||
@AllArgsConstructor
|
||||
public enum NodeTypeEnum {
|
||||
|
||||
START("START", "开始节点"),
|
||||
END("END", "结束节点"),
|
||||
TASK("TASK", "任务节点"),
|
||||
GATEWAY("GATEWAY", "网关节点"),
|
||||
SUB_PROCESS("SUB_PROCESS", "子流程节点"),
|
||||
SHELL("SHELL", "Shell脚本节点");
|
||||
START(0, "START", "开始节点"),
|
||||
END(1, "END", "结束节点"),
|
||||
TASK(2, "TASK", "任务节点"),
|
||||
GATEWAY(3, "GATEWAY", "网关节点"),
|
||||
SUB_PROCESS(4, "SUB_PROCESS", "子流程节点"),
|
||||
SHELL(5, "SHELL", "Shell脚本节点");
|
||||
|
||||
private final int value;
|
||||
private final String code;
|
||||
private final String description;
|
||||
}
|
||||
@ -11,21 +11,22 @@ import lombok.Getter;
|
||||
public enum WorkflowStatusEnum {
|
||||
|
||||
// 工作流定义状态
|
||||
DRAFT("DRAFT", "草稿"),
|
||||
PUBLISHED("PUBLISHED", "已发布"),
|
||||
DISABLED("DISABLED", "已禁用"),
|
||||
DRAFT(0, "DRAFT", "草稿"),
|
||||
PUBLISHED(1, "PUBLISHED", "已发布"),
|
||||
DISABLED(2, "DISABLED", "已禁用"),
|
||||
|
||||
// 工作流实例状态
|
||||
CREATED("CREATED", "已创建"),
|
||||
PENDING("PENDING", "等待执行"),
|
||||
RUNNING("RUNNING", "执行中"),
|
||||
PAUSED("PAUSED", "已暂停"),
|
||||
COMPLETED("COMPLETED", "已完成"),
|
||||
FAILED("FAILED", "执行失败"),
|
||||
CANCELLED("CANCELLED", "已取消"),
|
||||
SUSPENDED("SUSPENDED", "已暂停"),
|
||||
TERMINATED("TERMINATED", "已终止");
|
||||
CREATED(3, "CREATED", "已创建"),
|
||||
PENDING(4, "PENDING", "等待执行"),
|
||||
RUNNING(5, "RUNNING", "执行中"),
|
||||
PAUSED(6, "PAUSED", "已暂停"),
|
||||
COMPLETED(7, "COMPLETED", "已完成"),
|
||||
FAILED(8, "FAILED", "执行失败"),
|
||||
CANCELLED(9, "CANCELLED", "已取消"),
|
||||
SUSPENDED(10, "SUSPENDED", "已暂停"),
|
||||
TERMINATED(11, "TERMINATED", "已终止");
|
||||
|
||||
private final int value;
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
|
||||
@ -4,8 +4,53 @@ import com.qqchen.deploy.backend.framework.service.IBaseService;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工作流实例服务接口
|
||||
*/
|
||||
public interface IWorkflowInstanceService extends IBaseService<WorkflowInstance, WorkflowInstanceDTO, Long> {
|
||||
|
||||
/**
|
||||
* 创建工作流实例
|
||||
*
|
||||
* @param definitionId 工作流定义ID
|
||||
* @param businessKey 业务标识
|
||||
* @param variables 初始变量
|
||||
* @return 工作流实例
|
||||
*/
|
||||
WorkflowInstanceDTO createInstance(Long definitionId, String businessKey, Map<String, Object> variables);
|
||||
|
||||
/**
|
||||
* 启动工作流实例
|
||||
*
|
||||
* @param id 实例ID
|
||||
* @return 工作流实例
|
||||
*/
|
||||
WorkflowInstanceDTO startInstance(Long id);
|
||||
|
||||
/**
|
||||
* 暂停工作流实例
|
||||
*
|
||||
* @param id 实例ID
|
||||
* @return 工作流实例
|
||||
*/
|
||||
WorkflowInstanceDTO pauseInstance(Long id);
|
||||
|
||||
/**
|
||||
* 恢复工作流实例
|
||||
*
|
||||
* @param id 实例ID
|
||||
* @return 工作流实例
|
||||
*/
|
||||
WorkflowInstanceDTO resumeInstance(Long id);
|
||||
|
||||
/**
|
||||
* 终止工作流实例
|
||||
*
|
||||
* @param id 实例ID
|
||||
* @param reason 终止原因
|
||||
* @return 工作流实例
|
||||
*/
|
||||
WorkflowInstanceDTO terminateInstance(Long id, String reason);
|
||||
}
|
||||
@ -1,11 +1,23 @@
|
||||
package com.qqchen.deploy.backend.workflow.service.impl;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||
import com.qqchen.deploy.backend.framework.exception.BusinessException;
|
||||
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
|
||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
|
||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
|
||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
|
||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowVariableService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工作流实例服务实现类
|
||||
@ -13,4 +25,122 @@ import org.springframework.stereotype.Service;
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstance, WorkflowInstanceDTO, Long> implements IWorkflowInstanceService {
|
||||
|
||||
@Resource
|
||||
private IWorkflowDefinitionRepository workflowDefinitionRepository;
|
||||
|
||||
@Resource
|
||||
private IWorkflowInstanceRepository workflowInstanceRepository;
|
||||
|
||||
@Resource
|
||||
private IWorkflowVariableService workflowVariableService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public WorkflowInstanceDTO createInstance(Long definitionId, String businessKey, Map<String, Object> variables) {
|
||||
// 1. 查询工作流定义
|
||||
WorkflowDefinition definition = workflowDefinitionRepository.findById(definitionId)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND));
|
||||
|
||||
// 2. 检查工作流定义状态
|
||||
if (definition.getStatus() != WorkflowStatusEnum.PUBLISHED) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_NOT_PUBLISHED);
|
||||
}
|
||||
if (!definition.getEnabled()) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_DISABLED);
|
||||
}
|
||||
|
||||
// 3. 创建工作流实例
|
||||
WorkflowInstance instance = new WorkflowInstance();
|
||||
instance.setDefinition(definition);
|
||||
instance.setBusinessKey(businessKey);
|
||||
instance.setStatus(WorkflowStatusEnum.CREATED);
|
||||
final WorkflowInstance savedInstance = workflowInstanceRepository.save(instance);
|
||||
|
||||
// 4. 设置初始变量
|
||||
if (variables != null && !variables.isEmpty()) {
|
||||
variables.forEach((key, value) -> workflowVariableService.setVariable(savedInstance.getId(), key, value));
|
||||
}
|
||||
|
||||
return converter.toDto(savedInstance);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public WorkflowInstanceDTO startInstance(Long id) {
|
||||
// 1. 查询工作流实例
|
||||
WorkflowInstance instance = workflowInstanceRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
|
||||
|
||||
// 2. 检查状态
|
||||
if (instance.getStatus() != WorkflowStatusEnum.CREATED) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
|
||||
}
|
||||
|
||||
// 3. 更新状态
|
||||
instance.setStatus(WorkflowStatusEnum.RUNNING);
|
||||
instance.setStartTime(LocalDateTime.now());
|
||||
instance = workflowInstanceRepository.save(instance);
|
||||
|
||||
return converter.toDto(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public WorkflowInstanceDTO pauseInstance(Long id) {
|
||||
// 1. 查询工作流实例
|
||||
WorkflowInstance instance = workflowInstanceRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
|
||||
|
||||
// 2. 检查状态
|
||||
if (!instance.getStatus().canPause()) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
|
||||
}
|
||||
|
||||
// 3. 更新状态
|
||||
instance.setStatus(WorkflowStatusEnum.PAUSED);
|
||||
instance = workflowInstanceRepository.save(instance);
|
||||
|
||||
return converter.toDto(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public WorkflowInstanceDTO resumeInstance(Long id) {
|
||||
// 1. 查询工作流实例
|
||||
WorkflowInstance instance = workflowInstanceRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
|
||||
|
||||
// 2. 检查状态
|
||||
if (!instance.getStatus().canResume()) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
|
||||
}
|
||||
|
||||
// 3. 更新状态
|
||||
instance.setStatus(WorkflowStatusEnum.RUNNING);
|
||||
instance = workflowInstanceRepository.save(instance);
|
||||
|
||||
return converter.toDto(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public WorkflowInstanceDTO terminateInstance(Long id, String reason) {
|
||||
// 1. 查询工作流实例
|
||||
WorkflowInstance instance = workflowInstanceRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
|
||||
|
||||
// 2. 检查状态
|
||||
if (instance.getStatus().isFinalStatus()) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
|
||||
}
|
||||
|
||||
// 3. <EFBFBD><EFBFBD><EFBFBD>新状态
|
||||
instance.setStatus(WorkflowStatusEnum.TERMINATED);
|
||||
instance.setEndTime(LocalDateTime.now());
|
||||
instance.setError(reason);
|
||||
instance = workflowInstanceRepository.save(instance);
|
||||
|
||||
return converter.toDto(instance);
|
||||
}
|
||||
}
|
||||
@ -45,7 +45,7 @@ CREATE TABLE sys_department (
|
||||
CONSTRAINT UK_department_code UNIQUE (code)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='部门表';
|
||||
|
||||
-- 用户表
|
||||
-- 用<EFBFBD><EFBFBD><EFBFBD>表
|
||||
CREATE TABLE IF NOT EXISTS sys_user (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
|
||||
create_by VARCHAR(255) NULL COMMENT '创建人',
|
||||
@ -394,7 +394,7 @@ CREATE TABLE wf_workflow_definition (
|
||||
code VARCHAR(100) NOT NULL COMMENT '工作流编码',
|
||||
name VARCHAR(100) NOT NULL COMMENT '工作流名称',
|
||||
description VARCHAR(255) NULL COMMENT '工作流描述',
|
||||
status VARCHAR(20) NOT NULL COMMENT '工作流状态(DRAFT/PUBLISHED/DISABLED)',
|
||||
status TINYINT NOT NULL COMMENT '工作流状态(0:草稿,1:已发布,2:已禁用)',
|
||||
version_no INT NOT NULL DEFAULT 1 COMMENT '版本号',
|
||||
node_config TEXT NULL COMMENT '节点配置(JSON)',
|
||||
transition_config TEXT NULL COMMENT '流转配置(JSON)',
|
||||
@ -418,7 +418,7 @@ CREATE TABLE wf_node_definition (
|
||||
workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID',
|
||||
node_id VARCHAR(100) NOT NULL COMMENT '节点ID',
|
||||
name VARCHAR(100) NOT NULL COMMENT '节点名称',
|
||||
type VARCHAR(20) NOT NULL COMMENT '节点类型(START/END/TASK/GATEWAY)',
|
||||
type TINYINT NOT NULL COMMENT '节点类型(0:开始节点,1:结束节点,2:任务节点,3:网关节点,4:子流程节点,5:Shell脚本节点)',
|
||||
config TEXT NULL COMMENT '节点配置(JSON)',
|
||||
order_num INT NOT NULL DEFAULT 0 COMMENT '排序号',
|
||||
|
||||
@ -438,7 +438,7 @@ CREATE TABLE wf_workflow_instance (
|
||||
|
||||
workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID',
|
||||
business_key VARCHAR(100) NOT NULL COMMENT '业务标识',
|
||||
status VARCHAR(20) NOT NULL COMMENT '状态(PENDING/RUNNING/COMPLETED/FAILED/CANCELLED)',
|
||||
status TINYINT NOT NULL COMMENT '状态(3:已创建,4:等待执行,5:执行中,6:已暂停,7:已完成,8:执行失败,9:已取消,10:已暂停,11:已终止)',
|
||||
start_time DATETIME(6) NULL COMMENT '开始时间',
|
||||
end_time DATETIME(6) NULL COMMENT '结束时间',
|
||||
error TEXT NULL COMMENT '错误信息',
|
||||
@ -459,9 +459,9 @@ CREATE TABLE wf_node_instance (
|
||||
|
||||
workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID',
|
||||
node_id VARCHAR(100) NOT NULL COMMENT '节点ID',
|
||||
node_type VARCHAR(20) NOT NULL COMMENT '节点类型',
|
||||
node_type TINYINT NOT NULL COMMENT '节点类型(0:开始节点,1:结束节点,2:任务节点,3:网关节点,4:子流程节点,5:Shell脚本节点)',
|
||||
name VARCHAR(100) NOT NULL COMMENT '节点名称',
|
||||
status VARCHAR(20) NOT NULL COMMENT '状态(PENDING/RUNNING/COMPLETED/FAILED/CANCELLED/SKIPPED)',
|
||||
status TINYINT NOT NULL COMMENT '状态(3:已创建,4:等待执行,5:执行中,6:已暂停,7:已完成,8:执行失败,9:已取消,10:已暂停,11:已终止)',
|
||||
start_time DATETIME(6) NULL COMMENT '开始时间',
|
||||
end_time DATETIME(6) NULL COMMENT '结束时间',
|
||||
config TEXT NULL COMMENT '节点配置(JSON)',
|
||||
|
||||
@ -163,7 +163,7 @@ INSERT INTO wf_workflow_definition (
|
||||
) VALUES (
|
||||
1, 'admin', NOW(), 0, 'admin', NOW(), 0,
|
||||
'DEPLOY_WORKFLOW', '标准部署流程', '标准的应用部署流程,包含代码拉取、编译构建、部署等步骤',
|
||||
'PUBLISHED', 1,
|
||||
1, 1,
|
||||
'{"startNode":{"type":"START","name":"开始"},"endNode":{"type":"END","name":"结束"},"taskNodes":[{"id":"build","type":"TASK","name":"构建"},{"id":"deploy","type":"TASK","name":"部署"}]}',
|
||||
'{"transitions":[{"from":"startNode","to":"build"},{"from":"build","to":"deploy"},{"from":"deploy","to":"endNode"}]}',
|
||||
'{"fields":[{"name":"appName","label":"应用名称","type":"input","required":true},{"name":"branch","label":"分支","type":"select","required":true}]}',
|
||||
@ -178,22 +178,22 @@ INSERT INTO wf_node_definition (
|
||||
) VALUES
|
||||
-- 开始节点
|
||||
(1, 'admin', NOW(), 0, 'admin', NOW(), 0,
|
||||
1, 'startNode', '开始', 'START',
|
||||
1, 'startNode', '开始', 0,
|
||||
'{"type":"START","name":"开始"}',
|
||||
1),
|
||||
-- 构建节点
|
||||
(2, 'admin', NOW(), 0, 'admin', NOW(), 0,
|
||||
1, 'build', '构建', 'TASK',
|
||||
1, 'build', '构建', 2,
|
||||
'{"type":"TASK","name":"构建","executor":"JENKINS","jenkinsJob":"app-build"}',
|
||||
2),
|
||||
-- 部署节点
|
||||
(3, 'admin', NOW(), 0, 'admin', NOW(), 0,
|
||||
1, 'deploy', '部署', 'TASK',
|
||||
1, 'deploy', '部署', 2,
|
||||
'{"type":"TASK","name":"部署","executor":"SHELL","script":"./deploy.sh"}',
|
||||
3),
|
||||
-- 结束节点
|
||||
(4, 'admin', NOW(), 0, 'admin', NOW(), 0,
|
||||
1, 'endNode', '结束', 'END',
|
||||
1, 'endNode', '结束', 1,
|
||||
'{"type":"END","name":"结束"}',
|
||||
4);
|
||||
|
||||
@ -203,7 +203,7 @@ INSERT INTO wf_workflow_instance (
|
||||
workflow_definition_id, business_key, status, start_time, end_time, error
|
||||
) VALUES (
|
||||
1, 'admin', NOW(), 0, 'admin', NOW(), 0,
|
||||
1, 'TEST-APP-001', 'RUNNING',
|
||||
1, 'TEST-APP-001', 5,
|
||||
NOW(), NULL, NULL
|
||||
);
|
||||
|
||||
@ -214,25 +214,25 @@ INSERT INTO wf_node_instance (
|
||||
) VALUES
|
||||
-- 开始节点实例
|
||||
(1, 'admin', NOW(), 0, 'admin', NOW(), 0,
|
||||
1, 'startNode', 'START', '开始', 'COMPLETED',
|
||||
1, 'startNode', 0, '开始', 7,
|
||||
DATE_SUB(NOW(), INTERVAL 5 MINUTE), DATE_SUB(NOW(), INTERVAL 4 MINUTE),
|
||||
'{"type":"START","name":"开始"}',
|
||||
'{"appName":"test-app","branch":"master"}', '{}', NULL, NULL),
|
||||
-- 构建节点实例
|
||||
(2, 'admin', NOW(), 0, 'admin', NOW(), 0,
|
||||
1, 'build', 'TASK', '构建', 'RUNNING',
|
||||
1, 'build', 2, '构建', 5,
|
||||
DATE_SUB(NOW(), INTERVAL 3 MINUTE), NULL,
|
||||
'{"type":"TASK","name":"构建","executor":"JENKINS","jenkinsJob":"app-build"}',
|
||||
'{"appName":"test-app","branch":"master"}', NULL, NULL, 'startNode'),
|
||||
-- 部署节点实例
|
||||
(3, 'admin', NOW(), 0, 'admin', NOW(), 0,
|
||||
1, 'deploy', 'TASK', '部署', 'RUNNING',
|
||||
1, 'deploy', 2, '部署', 5,
|
||||
DATE_SUB(NOW(), INTERVAL 2 MINUTE), NULL,
|
||||
'{"type":"TASK","name":"部署","executor":"SHELL","script":"./deploy.sh"}',
|
||||
'{"appName":"test-app","branch":"master"}', NULL, NULL, 'build'),
|
||||
-- 结束节点实例
|
||||
(4, 'admin', NOW(), 0, 'admin', NOW(), 0,
|
||||
1, 'endNode', 'END', '结束', 'COMPLETED',
|
||||
1, 'endNode', 1, '结束', 7,
|
||||
DATE_SUB(NOW(), INTERVAL 1 MINUTE), DATE_SUB(NOW(), INTERVAL 0 MINUTE),
|
||||
'{"type":"END","name":"结束"}',
|
||||
'{}', '{}', NULL, 'deploy');
|
||||
|
||||
@ -0,0 +1,301 @@
|
||||
package com.qqchen.deploy.backend.workflow.engine;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||
import com.qqchen.deploy.backend.workflow.engine.context.DefaultWorkflowContext;
|
||||
import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
|
||||
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
|
||||
import com.qqchen.deploy.backend.workflow.engine.executor.NodeExecutor;
|
||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
|
||||
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
|
||||
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
|
||||
import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository;
|
||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
|
||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class DefaultWorkflowEngineTest {
|
||||
|
||||
@Mock
|
||||
private IWorkflowDefinitionRepository workflowDefinitionRepository;
|
||||
|
||||
@Mock
|
||||
private IWorkflowInstanceRepository workflowInstanceRepository;
|
||||
|
||||
@Mock
|
||||
private INodeInstanceRepository nodeInstanceRepository;
|
||||
|
||||
@Mock
|
||||
private Map<NodeTypeEnum, NodeExecutor> nodeExecutors;
|
||||
|
||||
@Mock
|
||||
private DefaultWorkflowContext.Factory workflowContextFactory;
|
||||
|
||||
@Mock
|
||||
private NodeExecutor startNodeExecutor;
|
||||
|
||||
@Mock
|
||||
private NodeExecutor taskNodeExecutor;
|
||||
|
||||
@Mock
|
||||
private DefaultWorkflowContext workflowContext;
|
||||
|
||||
@InjectMocks
|
||||
private DefaultWorkflowEngine workflowEngine;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
|
||||
// 配置基本的Mock行为
|
||||
when(nodeExecutors.get(NodeTypeEnum.START)).thenReturn(startNodeExecutor);
|
||||
when(nodeExecutors.get(NodeTypeEnum.TASK)).thenReturn(taskNodeExecutor);
|
||||
when(workflowContextFactory.create(any())).thenReturn(workflowContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
void startWorkflow_Success() {
|
||||
// 准备测试数据
|
||||
String workflowCode = "test-workflow";
|
||||
String businessKey = "test-key";
|
||||
Map<String, Object> variables = new HashMap<>();
|
||||
variables.put("key1", "value1");
|
||||
|
||||
WorkflowDefinition definition = new WorkflowDefinition();
|
||||
definition.setCode(workflowCode);
|
||||
definition.setStatus(WorkflowStatusEnum.PUBLISHED);
|
||||
|
||||
NodeInstance startNode = new NodeInstance();
|
||||
startNode.setId(1L);
|
||||
startNode.setNodeType(NodeTypeEnum.START);
|
||||
startNode.setStatus(NodeStatusEnum.PENDING);
|
||||
|
||||
// 配置Mock行为
|
||||
when(workflowDefinitionRepository.findByCodeAndDeletedFalse(workflowCode)).thenReturn(definition);
|
||||
when(workflowInstanceRepository.save(any())).thenAnswer(i -> i.getArgument(0));
|
||||
when(nodeInstanceRepository.save(any())).thenReturn(startNode);
|
||||
doNothing().when(startNodeExecutor).execute(any(), any());
|
||||
|
||||
// 执行测试
|
||||
WorkflowInstance result = workflowEngine.startWorkflow(workflowCode, businessKey, variables);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertEquals(WorkflowStatusEnum.RUNNING, result.getStatus());
|
||||
assertNotNull(result.getStartTime());
|
||||
|
||||
verify(workflowDefinitionRepository).findByCodeAndDeletedFalse(workflowCode);
|
||||
verify(workflowInstanceRepository).save(any());
|
||||
verify(nodeInstanceRepository).save(any());
|
||||
verify(workflowContextFactory).create(any());
|
||||
verify(workflowContext, times(variables.size())).setVariable(anyString(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void startWorkflow_WorkflowNotFound() {
|
||||
// 准备测试数据
|
||||
String workflowCode = "non-existent";
|
||||
|
||||
// 配置Mock行为
|
||||
when(workflowDefinitionRepository.findByCodeAndDeletedFalse(workflowCode)).thenReturn(null);
|
||||
|
||||
// 执行测试并验证异常
|
||||
WorkflowEngineException exception = assertThrows(WorkflowEngineException.class,
|
||||
() -> workflowEngine.startWorkflow(workflowCode, "test", null));
|
||||
assertEquals(ResponseCode.WORKFLOW_NOT_FOUND.name(), exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void startWorkflow_WorkflowNotPublished() {
|
||||
// 准备测试数据
|
||||
String workflowCode = "draft-workflow";
|
||||
WorkflowDefinition definition = new WorkflowDefinition();
|
||||
definition.setCode(workflowCode);
|
||||
definition.setStatus(WorkflowStatusEnum.DRAFT);
|
||||
|
||||
// 配置Mock行为
|
||||
when(workflowDefinitionRepository.findByCodeAndDeletedFalse(workflowCode)).thenReturn(definition);
|
||||
|
||||
// 执行测试并验证异常
|
||||
WorkflowEngineException exception = assertThrows(WorkflowEngineException.class,
|
||||
() -> workflowEngine.startWorkflow(workflowCode, "test", null));
|
||||
assertEquals(ResponseCode.WORKFLOW_NOT_PUBLISHED.name(), exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeNode_Success() {
|
||||
// 准备测试数据
|
||||
Long nodeInstanceId = 1L;
|
||||
NodeInstance nodeInstance = new NodeInstance();
|
||||
nodeInstance.setId(nodeInstanceId);
|
||||
nodeInstance.setNodeType(NodeTypeEnum.TASK);
|
||||
nodeInstance.setStatus(NodeStatusEnum.PENDING);
|
||||
|
||||
WorkflowInstance instance = new WorkflowInstance();
|
||||
instance.setStatus(WorkflowStatusEnum.RUNNING);
|
||||
nodeInstance.setWorkflowInstance(instance);
|
||||
|
||||
// 配置Mock行为
|
||||
when(nodeInstanceRepository.findById(nodeInstanceId)).thenReturn(Optional.of(nodeInstance));
|
||||
doNothing().when(taskNodeExecutor).execute(any(), any());
|
||||
|
||||
// 执行测试
|
||||
workflowEngine.executeNode(nodeInstanceId);
|
||||
|
||||
// 验证结果
|
||||
assertEquals(NodeStatusEnum.COMPLETED, nodeInstance.getStatus());
|
||||
assertNotNull(nodeInstance.getEndTime());
|
||||
|
||||
verify(nodeInstanceRepository).findById(nodeInstanceId);
|
||||
verify(taskNodeExecutor).execute(any(), any());
|
||||
verify(nodeInstanceRepository).save(nodeInstance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeNode_NodeNotFound() {
|
||||
// 准备测试数据
|
||||
Long nodeInstanceId = 999L;
|
||||
|
||||
// 配置Mock行为
|
||||
when(nodeInstanceRepository.findById(nodeInstanceId)).thenReturn(Optional.empty());
|
||||
|
||||
// 执行测试并验证异常
|
||||
WorkflowEngineException exception = assertThrows(WorkflowEngineException.class,
|
||||
() -> workflowEngine.executeNode(nodeInstanceId));
|
||||
assertEquals(ResponseCode.WORKFLOW_NODE_NOT_FOUND.name(), exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeNode_WorkflowNotRunning() {
|
||||
// 准备测试数据
|
||||
Long nodeInstanceId = 1L;
|
||||
NodeInstance nodeInstance = new NodeInstance();
|
||||
nodeInstance.setId(nodeInstanceId);
|
||||
nodeInstance.setNodeType(NodeTypeEnum.TASK);
|
||||
|
||||
WorkflowInstance instance = new WorkflowInstance();
|
||||
instance.setStatus(WorkflowStatusEnum.COMPLETED);
|
||||
nodeInstance.setWorkflowInstance(instance);
|
||||
|
||||
// 配置Mock行为
|
||||
when(nodeInstanceRepository.findById(nodeInstanceId)).thenReturn(Optional.of(nodeInstance));
|
||||
|
||||
// 执行测试并验证异常
|
||||
WorkflowEngineException exception = assertThrows(WorkflowEngineException.class,
|
||||
() -> workflowEngine.executeNode(nodeInstanceId));
|
||||
assertEquals(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING.name(), exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void terminateWorkflow_Success() {
|
||||
// 准备测试数据
|
||||
Long instanceId = 1L;
|
||||
String reason = "Test termination";
|
||||
|
||||
WorkflowInstance instance = new WorkflowInstance();
|
||||
instance.setId(instanceId);
|
||||
instance.setStatus(WorkflowStatusEnum.RUNNING);
|
||||
|
||||
NodeInstance runningNode = new NodeInstance();
|
||||
runningNode.setNodeType(NodeTypeEnum.TASK);
|
||||
runningNode.setStatus(NodeStatusEnum.RUNNING);
|
||||
|
||||
// 配置Mock行为
|
||||
when(workflowInstanceRepository.findById(instanceId)).thenReturn(Optional.of(instance));
|
||||
when(nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(instanceId, NodeStatusEnum.RUNNING))
|
||||
.thenReturn(Arrays.asList(runningNode));
|
||||
doNothing().when(taskNodeExecutor).terminate(any(), any());
|
||||
|
||||
// 执行测试
|
||||
workflowEngine.terminateWorkflow(instanceId, reason);
|
||||
|
||||
// 验证结果
|
||||
assertEquals(WorkflowStatusEnum.TERMINATED, instance.getStatus());
|
||||
assertEquals(reason, instance.getError());
|
||||
assertNotNull(instance.getEndTime());
|
||||
assertEquals(NodeStatusEnum.TERMINATED, runningNode.getStatus());
|
||||
assertNotNull(runningNode.getEndTime());
|
||||
|
||||
verify(workflowInstanceRepository).findById(instanceId);
|
||||
verify(nodeInstanceRepository).findByWorkflowInstanceIdAndStatus(instanceId, NodeStatusEnum.RUNNING);
|
||||
verify(taskNodeExecutor).terminate(any(), any());
|
||||
verify(nodeInstanceRepository).save(runningNode);
|
||||
verify(workflowInstanceRepository).save(instance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void pauseWorkflow_Success() {
|
||||
// 准备测试数据
|
||||
Long instanceId = 1L;
|
||||
WorkflowInstance instance = new WorkflowInstance();
|
||||
instance.setId(instanceId);
|
||||
instance.setStatus(WorkflowStatusEnum.RUNNING);
|
||||
|
||||
NodeInstance runningNode = new NodeInstance();
|
||||
runningNode.setStatus(NodeStatusEnum.RUNNING);
|
||||
|
||||
// 配置Mock行为
|
||||
when(workflowInstanceRepository.findById(instanceId)).thenReturn(Optional.of(instance));
|
||||
when(nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(instanceId, NodeStatusEnum.RUNNING))
|
||||
.thenReturn(Arrays.asList(runningNode));
|
||||
|
||||
// 执行测试
|
||||
workflowEngine.pauseWorkflow(instanceId);
|
||||
|
||||
// 验证结果
|
||||
assertEquals(WorkflowStatusEnum.PAUSED, instance.getStatus());
|
||||
assertEquals(NodeStatusEnum.PAUSED, runningNode.getStatus());
|
||||
|
||||
verify(workflowInstanceRepository).findById(instanceId);
|
||||
verify(nodeInstanceRepository).findByWorkflowInstanceIdAndStatus(instanceId, NodeStatusEnum.RUNNING);
|
||||
verify(nodeInstanceRepository).save(runningNode);
|
||||
verify(workflowInstanceRepository).save(instance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resumeWorkflow_Success() {
|
||||
// 准备测试数据
|
||||
Long instanceId = 1L;
|
||||
WorkflowInstance instance = new WorkflowInstance();
|
||||
instance.setId(instanceId);
|
||||
instance.setStatus(WorkflowStatusEnum.PAUSED);
|
||||
|
||||
NodeInstance pausedNode = new NodeInstance();
|
||||
pausedNode.setId(2L);
|
||||
pausedNode.setStatus(NodeStatusEnum.PAUSED);
|
||||
|
||||
// 配置Mock行为
|
||||
when(workflowInstanceRepository.findById(instanceId)).thenReturn(Optional.of(instance));
|
||||
when(nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(instanceId, NodeStatusEnum.PAUSED))
|
||||
.thenReturn(Arrays.asList(pausedNode));
|
||||
|
||||
// 执行测试
|
||||
workflowEngine.resumeWorkflow(instanceId);
|
||||
|
||||
// 验证结果
|
||||
assertEquals(WorkflowStatusEnum.RUNNING, instance.getStatus());
|
||||
assertEquals(NodeStatusEnum.RUNNING, pausedNode.getStatus());
|
||||
|
||||
verify(workflowInstanceRepository).findById(instanceId);
|
||||
verify(nodeInstanceRepository).findByWorkflowInstanceIdAndStatus(instanceId, NodeStatusEnum.PAUSED);
|
||||
verify(nodeInstanceRepository).save(pausedNode);
|
||||
verify(workflowInstanceRepository).save(instance);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package com.qqchen.deploy.backend.workflow.engine.executor;
|
||||
|
||||
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
|
||||
import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
|
||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
|
||||
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
class StartNodeExecutorTest {
|
||||
|
||||
@Mock
|
||||
private WorkflowContext workflowContext;
|
||||
|
||||
@InjectMocks
|
||||
private StartNodeExecutor startNodeExecutor;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNodeType_ShouldReturnStartType() {
|
||||
assertEquals(NodeTypeEnum.START, startNodeExecutor.getNodeType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void execute_ShouldCompleteNodeAndLog() {
|
||||
// 准备测试数据
|
||||
NodeInstance nodeInstance = new NodeInstance();
|
||||
nodeInstance.setNodeType(NodeTypeEnum.START);
|
||||
nodeInstance.setStatus(NodeStatusEnum.PENDING);
|
||||
|
||||
// 执行测试
|
||||
startNodeExecutor.execute(nodeInstance, workflowContext);
|
||||
|
||||
// 验证结果
|
||||
assertEquals(NodeStatusEnum.COMPLETED, nodeInstance.getStatus());
|
||||
verify(workflowContext).log("开始节点执行完成", LogLevelEnum.INFO);
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate_ShouldDoNothing() {
|
||||
// 开始节点无需配置,验证方法不会抛出异常
|
||||
startNodeExecutor.validate(null);
|
||||
startNodeExecutor.validate("");
|
||||
startNodeExecutor.validate("{}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void terminate_ShouldDoNothing() {
|
||||
// 开始节点无需终止操作,验证方法不会抛出异常
|
||||
NodeInstance nodeInstance = new NodeInstance();
|
||||
startNodeExecutor.terminate(nodeInstance, workflowContext);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,175 @@
|
||||
//package com.qqchen.deploy.backend.workflow.engine.executor;
|
||||
//
|
||||
//import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
|
||||
//import com.qqchen.deploy.backend.workflow.engine.executor.task.TaskExecutor;
|
||||
//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.NodeTypeEnum;
|
||||
//import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository;
|
||||
//import org.junit.jupiter.api.BeforeEach;
|
||||
//import org.junit.jupiter.api.Test;
|
||||
//import org.mockito.InjectMocks;
|
||||
//import org.mockito.Mock;
|
||||
//import org.mockito.MockitoAnnotations;
|
||||
//
|
||||
//import java.util.HashMap;
|
||||
//import java.util.Map;
|
||||
//
|
||||
//import static org.junit.jupiter.api.Assertions.*;
|
||||
//import static org.mockito.ArgumentMatchers.any;
|
||||
//import static org.mockito.Mockito.*;
|
||||
//
|
||||
//class TaskNodeExecutorTest {
|
||||
//
|
||||
// @Mock
|
||||
// private INodeInstanceRepository nodeInstanceRepository;
|
||||
//
|
||||
// @Mock
|
||||
// private Map<String, TaskExecutor> taskExecutors;
|
||||
//
|
||||
// @Mock
|
||||
// private TaskExecutor shellTaskExecutor;
|
||||
//
|
||||
// @Mock
|
||||
// private WorkflowContext context;
|
||||
//
|
||||
// @InjectMocks
|
||||
// private TaskNodeExecutor taskNodeExecutor;
|
||||
//
|
||||
// @BeforeEach
|
||||
// void setUp() {
|
||||
// MockitoAnnotations.openMocks(this);
|
||||
//
|
||||
// // 设置任务执行器映射
|
||||
// when(taskExecutors.get("SHELL")).thenReturn(shellTaskExecutor);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testExecute() {
|
||||
// // 准备测试数据
|
||||
// WorkflowInstance instance = new WorkflowInstance();
|
||||
// instance.setId(1L);
|
||||
//
|
||||
// NodeInstance taskNode = new NodeInstance();
|
||||
// taskNode.setId(1L);
|
||||
// taskNode.setNodeType(NodeTypeEnum.TASK);
|
||||
// taskNode.setStatus(NodeStatusEnum.PENDING);
|
||||
// taskNode.setConfig("{\"type\":\"SHELL\",\"script\":\"echo hello\"}");
|
||||
//
|
||||
// Map<String, Object> input = new HashMap<>();
|
||||
// input.put("param1", "value1");
|
||||
//
|
||||
// // 设置Mock行为
|
||||
// when(context.getWorkflowInstance()).thenReturn(instance);
|
||||
// when(context.getVariables()).thenReturn(input);
|
||||
// when(shellTaskExecutor.execute(any(), any())).thenReturn(true);
|
||||
//
|
||||
// // 执行测试
|
||||
// boolean result = taskNodeExecutor.execute(context, taskNode);
|
||||
//
|
||||
// // 验证结果
|
||||
// assertTrue(result);
|
||||
// assertEquals(NodeStatusEnum.RUNNING, taskNode.getStatus());
|
||||
// assertNotNull(taskNode.getStartTime());
|
||||
// verify(nodeInstanceRepository, times(1)).save(taskNode);
|
||||
// verify(shellTaskExecutor, times(1)).execute(any(), any());
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testExecuteWithError() {
|
||||
// // 准备测试数据
|
||||
// WorkflowInstance instance = new WorkflowInstance();
|
||||
// instance.setId(1L);
|
||||
//
|
||||
// NodeInstance taskNode = new NodeInstance();
|
||||
// taskNode.setId(1L);
|
||||
// taskNode.setNodeType(NodeTypeEnum.TASK);
|
||||
// taskNode.setStatus(NodeStatusEnum.PENDING);
|
||||
// taskNode.setConfig("{\"type\":\"SHELL\",\"script\":\"echo hello\"}");
|
||||
//
|
||||
// // 设置Mock行为
|
||||
// when(context.getWorkflowInstance()).thenReturn(instance);
|
||||
// when(shellTaskExecutor.execute(any(), any())).thenReturn(false);
|
||||
//
|
||||
// // 执行测试
|
||||
// boolean result = taskNodeExecutor.execute(context, taskNode);
|
||||
//
|
||||
// // 验证结果
|
||||
// assertFalse(result);
|
||||
// assertEquals(NodeStatusEnum.FAILED, taskNode.getStatus());
|
||||
// assertNotNull(taskNode.getStartTime());
|
||||
// assertNotNull(taskNode.getEndTime());
|
||||
// verify(nodeInstanceRepository, times(1)).save(taskNode);
|
||||
// verify(shellTaskExecutor, times(1)).execute(any(), any());
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testCanExecute() {
|
||||
// // 准备测试数据
|
||||
// NodeInstance taskNode = new NodeInstance();
|
||||
// taskNode.setNodeType(NodeTypeEnum.TASK);
|
||||
// taskNode.setStatus(NodeStatusEnum.PENDING);
|
||||
//
|
||||
// // 执行测试
|
||||
// boolean result = taskNodeExecutor.canExecute(taskNode);
|
||||
//
|
||||
// // 验证结果
|
||||
// assertTrue(result);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testCannotExecuteWrongType() {
|
||||
// // 准备测试数据
|
||||
// NodeInstance startNode = new NodeInstance();
|
||||
// startNode.setNodeType(NodeTypeEnum.START);
|
||||
// startNode.setStatus(NodeStatusEnum.PENDING);
|
||||
//
|
||||
// // 执行测试
|
||||
// boolean result = taskNodeExecutor.canExecute(startNode);
|
||||
//
|
||||
// // 验证结果
|
||||
// assertFalse(result);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testCannotExecuteWrongStatus() {
|
||||
// // 准备测试数据
|
||||
// NodeInstance taskNode = new NodeInstance();
|
||||
// taskNode.setNodeType(NodeTypeEnum.TASK);
|
||||
// taskNode.setStatus(NodeStatusEnum.RUNNING);
|
||||
//
|
||||
// // 执行测试
|
||||
// boolean result = taskNodeExecutor.canExecute(taskNode);
|
||||
//
|
||||
// // 验证结果
|
||||
// assertFalse(result);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testExecuteWithInvalidConfig() {
|
||||
// // 准备测试数据
|
||||
// WorkflowInstance instance = new WorkflowInstance();
|
||||
// instance.setId(1L);
|
||||
//
|
||||
// NodeInstance taskNode = new NodeInstance();
|
||||
// taskNode.setId(1L);
|
||||
// taskNode.setNodeType(NodeTypeEnum.TASK);
|
||||
// taskNode.setStatus(NodeStatusEnum.PENDING);
|
||||
// taskNode.setConfig("invalid json");
|
||||
//
|
||||
// // 设置Mock行为
|
||||
// when(context.getWorkflowInstance()).thenReturn(instance);
|
||||
//
|
||||
// // 执行测试
|
||||
// boolean result = taskNodeExecutor.execute(context, taskNode);
|
||||
//
|
||||
// // 验证结果
|
||||
// assertFalse(result);
|
||||
// assertEquals(NodeStatusEnum.FAILED, taskNode.getStatus());
|
||||
// assertNotNull(taskNode.getStartTime());
|
||||
// assertNotNull(taskNode.getEndTime());
|
||||
// assertNotNull(taskNode.getError());
|
||||
// verify(nodeInstanceRepository, times(1)).save(taskNode);
|
||||
// }
|
||||
//}
|
||||
@ -0,0 +1,162 @@
|
||||
package com.qqchen.deploy.backend.workflow.service;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||
import com.qqchen.deploy.backend.framework.exception.BusinessException;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
|
||||
import com.qqchen.deploy.backend.workflow.repository.INodeDefinitionRepository;
|
||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
|
||||
import com.qqchen.deploy.backend.workflow.service.impl.WorkflowDefinitionServiceImpl;
|
||||
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 java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class WorkflowDefinitionServiceTest {
|
||||
|
||||
@Mock
|
||||
private IWorkflowDefinitionRepository workflowDefinitionRepository;
|
||||
|
||||
@Mock
|
||||
private INodeDefinitionRepository nodeDefinitionRepository;
|
||||
|
||||
@InjectMocks
|
||||
private WorkflowDefinitionServiceImpl workflowDefinitionService;
|
||||
|
||||
private WorkflowDefinitionDTO dto;
|
||||
private WorkflowDefinition entity;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
dto = new WorkflowDefinitionDTO();
|
||||
dto.setCode("TEST-WF");
|
||||
dto.setName("Test Workflow");
|
||||
dto.setDescription("Test Description");
|
||||
dto.setNodeConfig("{}");
|
||||
dto.setTransitionConfig("{}");
|
||||
dto.setFormDefinition("{}");
|
||||
dto.setGraphDefinition("{}");
|
||||
|
||||
entity = new WorkflowDefinition();
|
||||
entity.setId(1L);
|
||||
entity.setCode("TEST-WF");
|
||||
entity.setName("Test Workflow");
|
||||
entity.setStatus(WorkflowStatusEnum.DRAFT);
|
||||
entity.setEnabled(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void create_Success() {
|
||||
when(workflowDefinitionRepository.existsByCodeAndDeletedFalse(anyString())).thenReturn(false);
|
||||
when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity);
|
||||
|
||||
WorkflowDefinitionDTO result = workflowDefinitionService.create(dto);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(WorkflowStatusEnum.DRAFT, result.getStatus());
|
||||
assertTrue(result.getEnabled());
|
||||
verify(workflowDefinitionRepository).existsByCodeAndDeletedFalse(dto.getCode());
|
||||
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void create_CodeExists_ThrowsException() {
|
||||
when(workflowDefinitionRepository.existsByCodeAndDeletedFalse(anyString())).thenReturn(true);
|
||||
|
||||
BusinessException exception = assertThrows(BusinessException.class,
|
||||
() -> workflowDefinitionService.create(dto));
|
||||
assertEquals(ResponseCode.WORKFLOW_CODE_EXISTS, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void publish_Success() {
|
||||
entity.setStatus(WorkflowStatusEnum.DRAFT);
|
||||
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
|
||||
when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity);
|
||||
|
||||
WorkflowDefinitionDTO result = workflowDefinitionService.publish(1L);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(WorkflowStatusEnum.PUBLISHED, result.getStatus());
|
||||
verify(workflowDefinitionRepository).findById(1L);
|
||||
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void publish_NotDraft_ThrowsException() {
|
||||
entity.setStatus(WorkflowStatusEnum.PUBLISHED);
|
||||
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
|
||||
|
||||
BusinessException exception = assertThrows(BusinessException.class,
|
||||
() -> workflowDefinitionService.publish(1L));
|
||||
assertEquals(ResponseCode.WORKFLOW_NOT_DRAFT, exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void disable_Success() {
|
||||
entity.setStatus(WorkflowStatusEnum.PUBLISHED);
|
||||
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
|
||||
when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity);
|
||||
|
||||
WorkflowDefinitionDTO result = workflowDefinitionService.disable(1L);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(WorkflowStatusEnum.DISABLED, result.getStatus());
|
||||
verify(workflowDefinitionRepository).findById(1L);
|
||||
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void enable_Success() {
|
||||
entity.setStatus(WorkflowStatusEnum.DISABLED);
|
||||
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
|
||||
when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity);
|
||||
|
||||
WorkflowDefinitionDTO result = workflowDefinitionService.enable(1L);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(WorkflowStatusEnum.PUBLISHED, result.getStatus());
|
||||
verify(workflowDefinitionRepository).findById(1L);
|
||||
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createNewVersion_Success() {
|
||||
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
|
||||
when(workflowDefinitionRepository.findLatestVersionByCode(anyString())).thenReturn(1);
|
||||
when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity);
|
||||
|
||||
WorkflowDefinitionDTO result = workflowDefinitionService.createNewVersion(1L);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(WorkflowStatusEnum.DRAFT, result.getStatus());
|
||||
assertTrue(result.getEnabled());
|
||||
verify(workflowDefinitionRepository).findById(1L);
|
||||
verify(workflowDefinitionRepository).findLatestVersionByCode(entity.getCode());
|
||||
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void findAllVersions_Success() {
|
||||
when(workflowDefinitionRepository.findAllVersionsByCode(anyString()))
|
||||
.thenReturn(Arrays.asList(entity));
|
||||
|
||||
var results = workflowDefinitionService.findAllVersions("TEST-WF");
|
||||
|
||||
assertNotNull(results);
|
||||
assertFalse(results.isEmpty());
|
||||
assertEquals(1, results.size());
|
||||
verify(workflowDefinitionRepository).findAllVersionsByCode("TEST-WF");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,187 @@
|
||||
//package com.qqchen.deploy.backend.workflow.service;
|
||||
//
|
||||
//import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||
//import com.qqchen.deploy.backend.framework.exception.BusinessException;
|
||||
//import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
|
||||
//import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||
//import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||
//import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
|
||||
//import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
|
||||
//import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
|
||||
//import com.qqchen.deploy.backend.workflow.service.impl.WorkflowInstanceServiceImpl;
|
||||
//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 java.util.HashMap;
|
||||
//import java.util.Map;
|
||||
//import java.util.Optional;
|
||||
//
|
||||
//import static org.junit.jupiter.api.Assertions.*;
|
||||
//import static org.mockito.ArgumentMatchers.any;
|
||||
//import static org.mockito.ArgumentMatchers.anyLong;
|
||||
//import static org.mockito.Mockito.*;
|
||||
//
|
||||
//@ExtendWith(MockitoExtension.class)
|
||||
//class WorkflowInstanceServiceTest {
|
||||
//
|
||||
// @Mock
|
||||
// private IWorkflowDefinitionRepository workflowDefinitionRepository;
|
||||
//
|
||||
// @Mock
|
||||
// private IWorkflowInstanceRepository workflowInstanceRepository;
|
||||
//
|
||||
// @Mock
|
||||
// private IWorkflowVariableService workflowVariableService;
|
||||
//
|
||||
// @InjectMocks
|
||||
// private WorkflowInstanceServiceImpl workflowInstanceService;
|
||||
//
|
||||
// private WorkflowDefinition definition;
|
||||
// private WorkflowInstance instance;
|
||||
// private Map<String, Object> variables;
|
||||
//
|
||||
// @BeforeEach
|
||||
// void setUp() {
|
||||
// definition = new WorkflowDefinition();
|
||||
// definition.setId(1L);
|
||||
// definition.setCode("TEST-WF");
|
||||
// definition.setName("Test Workflow");
|
||||
// definition.setStatus(WorkflowStatusEnum.PUBLISHED);
|
||||
// definition.setEnabled(true);
|
||||
//
|
||||
// instance = new WorkflowInstance();
|
||||
// instance.setId(1L);
|
||||
// instance.setDefinition(definition);
|
||||
// instance.setBusinessKey("TEST-KEY");
|
||||
// instance.setStatus(WorkflowStatusEnum.CREATED);
|
||||
//
|
||||
// variables = new HashMap<>();
|
||||
// variables.put("key1", "value1");
|
||||
// variables.put("key2", "value2");
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void createInstance_Success() {
|
||||
// when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(definition));
|
||||
// when(workflowInstanceRepository.save(any(WorkflowInstance.class))).thenReturn(instance);
|
||||
//
|
||||
// WorkflowInstanceDTO result = workflowInstanceService.createInstance(1L, "TEST-KEY", variables);
|
||||
//
|
||||
// assertNotNull(result);
|
||||
// assertEquals(WorkflowStatusEnum.CREATED, result.getStatus());
|
||||
// assertEquals("TEST-KEY", result.getBusinessKey());
|
||||
// verify(workflowDefinitionRepository).findById(1L);
|
||||
// verify(workflowInstanceRepository).save(any(WorkflowInstance.class));
|
||||
// verify(workflowVariableService, times(2)).setVariable(anyLong(), anyString(), any());
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void createInstance_WorkflowNotFound_ThrowsException() {
|
||||
// when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.empty());
|
||||
//
|
||||
// BusinessException exception = assertThrows(BusinessException.class,
|
||||
// () -> workflowInstanceService.createInstance(1L, "TEST-KEY", variables));
|
||||
// assertEquals(ResponseCode.WORKFLOW_NOT_FOUND, exception.getCode());
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void createInstance_WorkflowNotPublished_ThrowsException() {
|
||||
// definition.setStatus(WorkflowStatusEnum.DRAFT);
|
||||
// when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(definition));
|
||||
//
|
||||
// BusinessException exception = assertThrows(BusinessException.class,
|
||||
// () -> workflowInstanceService.createInstance(1L, "TEST-KEY", variables));
|
||||
// assertEquals(ResponseCode.WORKFLOW_NOT_PUBLISHED, exception.getCode());
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void startInstance_Success() {
|
||||
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.of(instance));
|
||||
// when(workflowInstanceRepository.save(any(WorkflowInstance.class))).thenReturn(instance);
|
||||
//
|
||||
// WorkflowInstanceDTO result = workflowInstanceService.startInstance(1L);
|
||||
//
|
||||
// assertNotNull(result);
|
||||
// assertEquals(WorkflowStatusEnum.RUNNING, result.getStatus());
|
||||
// assertNotNull(result.getStartTime());
|
||||
// verify(workflowInstanceRepository).findById(1L);
|
||||
// verify(workflowInstanceRepository).save(any(WorkflowInstance.class));
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void startInstance_NotFound_ThrowsException() {
|
||||
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.empty());
|
||||
//
|
||||
// BusinessException exception = assertThrows(BusinessException.class,
|
||||
// () -> workflowInstanceService.startInstance(1L));
|
||||
// assertEquals(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND, exception.getCode());
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void startInstance_NotCreated_ThrowsException() {
|
||||
// instance.setStatus(WorkflowStatusEnum.RUNNING);
|
||||
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.of(instance));
|
||||
//
|
||||
// BusinessException exception = assertThrows(BusinessException.class,
|
||||
// () -> workflowInstanceService.startInstance(1L));
|
||||
// assertEquals(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING, exception.getCode());
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void pauseInstance_Success() {
|
||||
// instance.setStatus(WorkflowStatusEnum.RUNNING);
|
||||
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.of(instance));
|
||||
// when(workflowInstanceRepository.save(any(WorkflowInstance.class))).thenReturn(instance);
|
||||
//
|
||||
// WorkflowInstanceDTO result = workflowInstanceService.pauseInstance(1L);
|
||||
//
|
||||
// assertNotNull(result);
|
||||
// assertEquals(WorkflowStatusEnum.PAUSED, result.getStatus());
|
||||
// verify(workflowInstanceRepository).findById(1L);
|
||||
// verify(workflowInstanceRepository).save(any(WorkflowInstance.class));
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void resumeInstance_Success() {
|
||||
// instance.setStatus(WorkflowStatusEnum.PAUSED);
|
||||
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.of(instance));
|
||||
// when(workflowInstanceRepository.save(any(WorkflowInstance.class))).thenReturn(instance);
|
||||
//
|
||||
// WorkflowInstanceDTO result = workflowInstanceService.resumeInstance(1L);
|
||||
//
|
||||
// assertNotNull(result);
|
||||
// assertEquals(WorkflowStatusEnum.RUNNING, result.getStatus());
|
||||
// verify(workflowInstanceRepository).findById(1L);
|
||||
// verify(workflowInstanceRepository).save(any(WorkflowInstance.class));
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void terminateInstance_Success() {
|
||||
// instance.setStatus(WorkflowStatusEnum.RUNNING);
|
||||
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.of(instance));
|
||||
// when(workflowInstanceRepository.save(any(WorkflowInstance.class))).thenReturn(instance);
|
||||
//
|
||||
// WorkflowInstanceDTO result = workflowInstanceService.terminateInstance(1L, "Test reason");
|
||||
//
|
||||
// assertNotNull(result);
|
||||
// assertEquals(WorkflowStatusEnum.TERMINATED, result.getStatus());
|
||||
// assertEquals("Test reason", result.getError());
|
||||
// assertNotNull(result.getEndTime());
|
||||
// verify(workflowInstanceRepository).findById(1L);
|
||||
// verify(workflowInstanceRepository).save(any(WorkflowInstance.class));
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void terminateInstance_AlreadyTerminated_ThrowsException() {
|
||||
// instance.setStatus(WorkflowStatusEnum.TERMINATED);
|
||||
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.of(instance));
|
||||
//
|
||||
// BusinessException exception = assertThrows(BusinessException.class,
|
||||
// () -> workflowInstanceService.terminateInstance(1L, "Test reason"));
|
||||
// assertEquals(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING, exception.getCode());
|
||||
// }
|
||||
//}
|
||||
Loading…
Reference in New Issue
Block a user