增加工作流实例实现

This commit is contained in:
dengqichen 2024-12-05 18:00:21 +08:00
parent c8a2c5ac2c
commit ff7544a127
15 changed files with 1107 additions and 39 deletions

View File

@ -39,7 +39,7 @@ public class NodeDefinition extends Entity<Long> {
/** /**
* 节点类型 * 节点类型
*/ */
@Enumerated(EnumType.STRING) @Enumerated(EnumType.ORDINAL)
@Column(nullable = false) @Column(nullable = false)
private NodeTypeEnum type; private NodeTypeEnum type;

View File

@ -39,6 +39,7 @@ public class NodeInstance extends Entity<Long> {
/** /**
* 节点类型 * 节点类型
*/ */
@Enumerated(EnumType.ORDINAL)
@Column(nullable = false) @Column(nullable = false)
private NodeTypeEnum nodeType; private NodeTypeEnum nodeType;
@ -51,6 +52,7 @@ public class NodeInstance extends Entity<Long> {
/** /**
* 节点状态 * 节点状态
*/ */
@Enumerated(EnumType.ORDINAL)
@Column(nullable = false) @Column(nullable = false)
private NodeStatusEnum status; private NodeStatusEnum status;

View File

@ -41,7 +41,7 @@ public class WorkflowDefinition extends Entity<Long> {
/** /**
* 工作流状态 * 工作流状态
*/ */
@Enumerated(EnumType.STRING) @Enumerated(EnumType.ORDINAL)
@Column(nullable = false) @Column(nullable = false)
private WorkflowStatusEnum status = WorkflowStatusEnum.DRAFT; private WorkflowStatusEnum status = WorkflowStatusEnum.DRAFT;

View File

@ -40,7 +40,7 @@ public class WorkflowInstance extends Entity<Long> {
/** /**
* 状态 * 状态
*/ */
@Enumerated(EnumType.STRING) @Enumerated(EnumType.ORDINAL)
@Column(nullable = false) @Column(nullable = false)
private WorkflowStatusEnum status = WorkflowStatusEnum.RUNNING; private WorkflowStatusEnum status = WorkflowStatusEnum.RUNNING;

View File

@ -10,13 +10,14 @@ import lombok.Getter;
@AllArgsConstructor @AllArgsConstructor
public enum NodeTypeEnum { public enum NodeTypeEnum {
START("START", "开始节点"), START(0, "START", "开始节点"),
END("END", "结束节点"), END(1, "END", "结束节点"),
TASK("TASK", "任务节点"), TASK(2, "TASK", "任务节点"),
GATEWAY("GATEWAY", "网关节点"), GATEWAY(3, "GATEWAY", "网关节点"),
SUB_PROCESS("SUB_PROCESS", "子流程节点"), SUB_PROCESS(4, "SUB_PROCESS", "子流程节点"),
SHELL("SHELL", "Shell脚本节点"); SHELL(5, "SHELL", "Shell脚本节点");
private final int value;
private final String code; private final String code;
private final String description; private final String description;
} }

View File

@ -11,21 +11,22 @@ import lombok.Getter;
public enum WorkflowStatusEnum { public enum WorkflowStatusEnum {
// 工作流定义状态 // 工作流定义状态
DRAFT("DRAFT", "草稿"), DRAFT(0, "DRAFT", "草稿"),
PUBLISHED("PUBLISHED", "已发布"), PUBLISHED(1, "PUBLISHED", "已发布"),
DISABLED("DISABLED", "已禁用"), DISABLED(2, "DISABLED", "已禁用"),
// 工作流实例状态 // 工作流实例状态
CREATED("CREATED", "已创建"), CREATED(3, "CREATED", "已创建"),
PENDING("PENDING", "等待执行"), PENDING(4, "PENDING", "等待执行"),
RUNNING("RUNNING", "执行中"), RUNNING(5, "RUNNING", "执行中"),
PAUSED("PAUSED", "已暂停"), PAUSED(6, "PAUSED", "已暂停"),
COMPLETED("COMPLETED", "已完成"), COMPLETED(7, "COMPLETED", "已完成"),
FAILED("FAILED", "执行失败"), FAILED(8, "FAILED", "执行失败"),
CANCELLED("CANCELLED", "已取消"), CANCELLED(9, "CANCELLED", "已取消"),
SUSPENDED("SUSPENDED", "已暂停"), SUSPENDED(10, "SUSPENDED", "已暂停"),
TERMINATED("TERMINATED", "已终止"); TERMINATED(11, "TERMINATED", "已终止");
private final int value;
private final String code; private final String code;
private final String description; private final String description;

View File

@ -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.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance; import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import java.util.Map;
/** /**
* 工作流实例服务接口 * 工作流实例服务接口
*/ */
public interface IWorkflowInstanceService extends IBaseService<WorkflowInstance, WorkflowInstanceDTO, Long> { 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);
} }

View File

@ -1,11 +1,23 @@
package com.qqchen.deploy.backend.workflow.service.impl; 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.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO; 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.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.IWorkflowInstanceService;
import com.qqchen.deploy.backend.workflow.service.IWorkflowVariableService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.Map;
/** /**
* 工作流实例服务实现类 * 工作流实例服务实现类
@ -13,4 +25,122 @@ import org.springframework.stereotype.Service;
@Slf4j @Slf4j
@Service @Service
public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstance, WorkflowInstanceDTO, Long> implements IWorkflowInstanceService { 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);
}
} }

View File

@ -45,7 +45,7 @@ CREATE TABLE sys_department (
CONSTRAINT UK_department_code UNIQUE (code) CONSTRAINT UK_department_code UNIQUE (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='部门表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='部门表';
-- 用 -- 用<EFBFBD><EFBFBD><EFBFBD>
CREATE TABLE IF NOT EXISTS sys_user ( CREATE TABLE IF NOT EXISTS sys_user (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人', create_by VARCHAR(255) NULL COMMENT '创建人',
@ -394,7 +394,7 @@ CREATE TABLE wf_workflow_definition (
code VARCHAR(100) NOT NULL COMMENT '工作流编码', code VARCHAR(100) NOT NULL COMMENT '工作流编码',
name VARCHAR(100) NOT NULL COMMENT '工作流名称', name VARCHAR(100) NOT NULL COMMENT '工作流名称',
description VARCHAR(255) 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 '版本号', version_no INT NOT NULL DEFAULT 1 COMMENT '版本号',
node_config TEXT NULL COMMENT '节点配置(JSON)', node_config TEXT NULL COMMENT '节点配置(JSON)',
transition_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', workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID',
node_id VARCHAR(100) NOT NULL COMMENT '节点ID', node_id VARCHAR(100) NOT NULL COMMENT '节点ID',
name VARCHAR(100) NOT NULL COMMENT '节点名称', 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子流程节点5Shell脚本节点',
config TEXT NULL COMMENT '节点配置(JSON)', config TEXT NULL COMMENT '节点配置(JSON)',
order_num INT NOT NULL DEFAULT 0 COMMENT '排序号', 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', workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID',
business_key VARCHAR(100) NOT NULL COMMENT '业务标识', 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 '开始时间', start_time DATETIME(6) NULL COMMENT '开始时间',
end_time DATETIME(6) NULL COMMENT '结束时间', end_time DATETIME(6) NULL COMMENT '结束时间',
error TEXT NULL COMMENT '错误信息', error TEXT NULL COMMENT '错误信息',
@ -459,9 +459,9 @@ CREATE TABLE wf_node_instance (
workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID', workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID',
node_id VARCHAR(100) 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子流程节点5Shell脚本节点',
name VARCHAR(100) NOT NULL COMMENT '节点名称', 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 '开始时间', start_time DATETIME(6) NULL COMMENT '开始时间',
end_time DATETIME(6) NULL COMMENT '结束时间', end_time DATETIME(6) NULL COMMENT '结束时间',
config TEXT NULL COMMENT '节点配置(JSON)', config TEXT NULL COMMENT '节点配置(JSON)',

View File

@ -163,7 +163,7 @@ INSERT INTO wf_workflow_definition (
) VALUES ( ) VALUES (
1, 'admin', NOW(), 0, 'admin', NOW(), 0, 1, 'admin', NOW(), 0, 'admin', NOW(), 0,
'DEPLOY_WORKFLOW', '标准部署流程', '标准的应用部署流程,包含代码拉取、编译构建、部署等步骤', '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":"部署"}]}', '{"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"}]}', '{"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}]}', '{"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 ) VALUES
-- 开始节点 -- 开始节点
(1, 'admin', NOW(), 0, 'admin', NOW(), 0, (1, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'startNode', '开始', 'START', 1, 'startNode', '开始', 0,
'{"type":"START","name":"开始"}', '{"type":"START","name":"开始"}',
1), 1),
-- 构建节点 -- 构建节点
(2, 'admin', NOW(), 0, 'admin', NOW(), 0, (2, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'build', '构建', 'TASK', 1, 'build', '构建', 2,
'{"type":"TASK","name":"构建","executor":"JENKINS","jenkinsJob":"app-build"}', '{"type":"TASK","name":"构建","executor":"JENKINS","jenkinsJob":"app-build"}',
2), 2),
-- 部署节点 -- 部署节点
(3, 'admin', NOW(), 0, 'admin', NOW(), 0, (3, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'deploy', '部署', 'TASK', 1, 'deploy', '部署', 2,
'{"type":"TASK","name":"部署","executor":"SHELL","script":"./deploy.sh"}', '{"type":"TASK","name":"部署","executor":"SHELL","script":"./deploy.sh"}',
3), 3),
-- 结束节点 -- 结束节点
(4, 'admin', NOW(), 0, 'admin', NOW(), 0, (4, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'endNode', '结束', 'END', 1, 'endNode', '结束', 1,
'{"type":"END","name":"结束"}', '{"type":"END","name":"结束"}',
4); 4);
@ -203,7 +203,7 @@ INSERT INTO wf_workflow_instance (
workflow_definition_id, business_key, status, start_time, end_time, error workflow_definition_id, business_key, status, start_time, end_time, error
) VALUES ( ) VALUES (
1, 'admin', NOW(), 0, 'admin', NOW(), 0, 1, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'TEST-APP-001', 'RUNNING', 1, 'TEST-APP-001', 5,
NOW(), NULL, NULL NOW(), NULL, NULL
); );
@ -214,25 +214,25 @@ INSERT INTO wf_node_instance (
) VALUES ) VALUES
-- 开始节点实例 -- 开始节点实例
(1, 'admin', NOW(), 0, 'admin', NOW(), 0, (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), DATE_SUB(NOW(), INTERVAL 5 MINUTE), DATE_SUB(NOW(), INTERVAL 4 MINUTE),
'{"type":"START","name":"开始"}', '{"type":"START","name":"开始"}',
'{"appName":"test-app","branch":"master"}', '{}', NULL, NULL), '{"appName":"test-app","branch":"master"}', '{}', NULL, NULL),
-- 构建节点实例 -- 构建节点实例
(2, 'admin', NOW(), 0, 'admin', NOW(), 0, (2, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'build', 'TASK', '构建', 'RUNNING', 1, 'build', 2, '构建', 5,
DATE_SUB(NOW(), INTERVAL 3 MINUTE), NULL, DATE_SUB(NOW(), INTERVAL 3 MINUTE), NULL,
'{"type":"TASK","name":"构建","executor":"JENKINS","jenkinsJob":"app-build"}', '{"type":"TASK","name":"构建","executor":"JENKINS","jenkinsJob":"app-build"}',
'{"appName":"test-app","branch":"master"}', NULL, NULL, 'startNode'), '{"appName":"test-app","branch":"master"}', NULL, NULL, 'startNode'),
-- 部署节点实例 -- 部署节点实例
(3, 'admin', NOW(), 0, 'admin', NOW(), 0, (3, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'deploy', 'TASK', '部署', 'RUNNING', 1, 'deploy', 2, '部署', 5,
DATE_SUB(NOW(), INTERVAL 2 MINUTE), NULL, DATE_SUB(NOW(), INTERVAL 2 MINUTE), NULL,
'{"type":"TASK","name":"部署","executor":"SHELL","script":"./deploy.sh"}', '{"type":"TASK","name":"部署","executor":"SHELL","script":"./deploy.sh"}',
'{"appName":"test-app","branch":"master"}', NULL, NULL, 'build'), '{"appName":"test-app","branch":"master"}', NULL, NULL, 'build'),
-- 结束节点实例 -- 结束节点实例
(4, 'admin', NOW(), 0, 'admin', NOW(), 0, (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), DATE_SUB(NOW(), INTERVAL 1 MINUTE), DATE_SUB(NOW(), INTERVAL 0 MINUTE),
'{"type":"END","name":"结束"}', '{"type":"END","name":"结束"}',
'{}', '{}', NULL, 'deploy'); '{}', '{}', NULL, 'deploy');

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
// }
//}

View File

@ -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");
}
}

View File

@ -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());
// }
//}