This commit is contained in:
戚辰先生 2024-12-07 21:01:19 +08:00
parent 3dc944d255
commit 7bbdeefa9b
12 changed files with 401 additions and 71 deletions

View File

@ -105,6 +105,7 @@ public enum ResponseCode {
WORKFLOW_INSTANCE_ALREADY_COMPLETED(2711, "workflow.instance.already.completed"), WORKFLOW_INSTANCE_ALREADY_COMPLETED(2711, "workflow.instance.already.completed"),
WORKFLOW_INSTANCE_ALREADY_CANCELED(2712, "workflow.instance.already.canceled"), WORKFLOW_INSTANCE_ALREADY_CANCELED(2712, "workflow.instance.already.canceled"),
WORKFLOW_INSTANCE_NOT_RUNNING(2713, "workflow.instance.not.running"), WORKFLOW_INSTANCE_NOT_RUNNING(2713, "workflow.instance.not.running"),
WORKFLOW_INSTANCE_NOT_PAUSED(2714, "workflow.instance.not.paused"),
WORKFLOW_NODE_NOT_FOUND(2720, "workflow.node.not.found"), WORKFLOW_NODE_NOT_FOUND(2720, "workflow.node.not.found"),
WORKFLOW_NODE_TYPE_NOT_SUPPORTED(2721, "workflow.node.type.not.supported"), WORKFLOW_NODE_TYPE_NOT_SUPPORTED(2721, "workflow.node.type.not.supported"),
WORKFLOW_NODE_CONFIG_INVALID(2722, "workflow.node.config.invalid"), WORKFLOW_NODE_CONFIG_INVALID(2722, "workflow.node.config.invalid"),

View File

@ -60,9 +60,8 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
// 2. 创建工作流实例 // 2. 创建工作流实例
WorkflowInstance instance = new WorkflowInstance(); WorkflowInstance instance = new WorkflowInstance();
instance.setDefinition(definition); instance.setDefinition(definition);
// 设置工作流实例状态 // 设置工作流实例初始状态为 PENDING
instance.setStatus(WorkflowInstanceStatusEnum.RUNNING); instance.setStatus(WorkflowInstanceStatusEnum.PENDING);
instance.setStartTime(LocalDateTime.now());
workflowInstanceRepository.save(instance); workflowInstanceRepository.save(instance);
// 3. 创建工作流上下文 // 3. 创建工作流上下文
@ -71,8 +70,10 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
variables.forEach((key, value) -> context.setVariable(key, value)); variables.forEach((key, value) -> context.setVariable(key, value));
} }
// 4. 创建开始节点实例 // 4. 创建开始节点实例并启动工作流
NodeInstance startNode = createStartNode(definition, instance.getId()); NodeInstance startNode = createStartNode(definition, instance.getId());
instance.start();
workflowInstanceRepository.save(instance);
executeNode(startNode.getId()); executeNode(startNode.getId());
return instance; return instance;
@ -85,8 +86,7 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
.orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_NOT_FOUND)); .orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_NOT_FOUND));
WorkflowInstance instance = nodeInstance.getWorkflowInstance(); WorkflowInstance instance = nodeInstance.getWorkflowInstance();
// 检查工作流实例状态 if (!instance.canExecuteNode()) {
if (instance.getStatus() != WorkflowInstanceStatusEnum.RUNNING) {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING); throw new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
} }
@ -100,15 +100,32 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
// 2. 执行节点 // 2. 执行节点
WorkflowContext context = workflowContextFactory.create(instance); WorkflowContext context = workflowContextFactory.create(instance);
executor.execute(nodeInstance, context); executor.execute(nodeInstance, context);
// 3. 更新节点状态
nodeInstance.setStatus(NodeStatusEnum.COMPLETED); nodeInstance.setStatus(NodeStatusEnum.COMPLETED);
nodeInstance.setEndTime(LocalDateTime.now()); nodeInstance.setEndTime(LocalDateTime.now());
nodeInstanceRepository.save(nodeInstance); nodeInstanceRepository.save(nodeInstance);
// 4. 检查是否所有节点都已完成
List<NodeInstance> uncompletedNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatusNot(
instance.getId(), NodeStatusEnum.COMPLETED);
if (uncompletedNodes.isEmpty()) {
instance.complete();
workflowInstanceRepository.save(instance);
}
} catch (Exception e) { } catch (Exception e) {
// 更新节点状态为失败
nodeInstance.setStatus(NodeStatusEnum.FAILED); nodeInstance.setStatus(NodeStatusEnum.FAILED);
nodeInstance.setEndTime(LocalDateTime.now()); nodeInstance.setEndTime(LocalDateTime.now());
nodeInstance.setError(e.getMessage()); nodeInstance.setError(e.getMessage());
nodeInstanceRepository.save(nodeInstance); nodeInstanceRepository.save(nodeInstance);
// 更新工作流实例状态
instance.updateError(e.getMessage());
workflowInstanceRepository.save(instance);
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED, e); throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED, e);
} }
} }
@ -142,29 +159,20 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
.orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND)); .orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
// 检查工作流实例状态 // 检查工作流实例状态
if (instance.getStatus() != WorkflowInstanceStatusEnum.RUNNING) { if (!instance.canTerminate()) {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING); throw new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
} }
// 终止所有运行中的节点 // 终止所有运行中的节点
List<NodeInstance> runningNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus( List<NodeInstance> runningNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
instanceId, NodeStatusEnum.RUNNING); instanceId, NodeStatusEnum.RUNNING);
WorkflowContext context = workflowContextFactory.create(instance);
for (NodeInstance node : runningNodes) { for (NodeInstance node : runningNodes) {
NodeExecutor executor = nodeExecutors.get(node.getNodeType());
if (executor != null) {
executor.terminate(node, context);
}
node.setStatus(NodeStatusEnum.TERMINATED); node.setStatus(NodeStatusEnum.TERMINATED);
node.setEndTime(LocalDateTime.now()); node.setEndTime(LocalDateTime.now());
nodeInstanceRepository.save(node); nodeInstanceRepository.save(node);
} }
// 更新工作流实例状态 instance.terminate(reason);
instance.setStatus(WorkflowInstanceStatusEnum.TERMINATED);
instance.setEndTime(LocalDateTime.now());
instance.setError(reason);
workflowInstanceRepository.save(instance); workflowInstanceRepository.save(instance);
} }
@ -174,22 +182,11 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
WorkflowInstance instance = workflowInstanceRepository.findById(instanceId) WorkflowInstance instance = workflowInstanceRepository.findById(instanceId)
.orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND)); .orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
// 检查工作流实例状态 if (!instance.canPause()) {
if (instance.getStatus() != WorkflowInstanceStatusEnum.RUNNING) {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING); throw new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
} }
// 暂停所有运行中的节点 instance.pause();
List<NodeInstance> runningNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
instanceId, NodeStatusEnum.RUNNING);
for (NodeInstance node : runningNodes) {
node.setStatus(NodeStatusEnum.PAUSED);
nodeInstanceRepository.save(node);
}
// 更新工作流实例状态
instance.setStatus(WorkflowInstanceStatusEnum.PAUSED);
workflowInstanceRepository.save(instance); workflowInstanceRepository.save(instance);
} }
@ -199,24 +196,51 @@ public class DefaultWorkflowEngine implements WorkflowEngine {
WorkflowInstance instance = workflowInstanceRepository.findById(instanceId) WorkflowInstance instance = workflowInstanceRepository.findById(instanceId)
.orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND)); .orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
// 检查工作流实例状态 if (!instance.canResume()) {
if (instance.getStatus() != WorkflowInstanceStatusEnum.PAUSED) { throw new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_PAUSED);
}
instance.resume();
workflowInstanceRepository.save(instance);
}
@Override
@Transactional
public void retryWorkflow(Long instanceId) {
WorkflowInstance instance = workflowInstanceRepository.findById(instanceId)
.orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
if (!instance.canRetry()) {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING); throw new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
} }
// 恢复所有暂停的节点 // 重试工作流实例
List<NodeInstance> pausedNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus( instance.retry();
instanceId, NodeStatusEnum.PAUSED); workflowInstanceRepository.save(instance);
for (NodeInstance node : pausedNodes) { // 获取失败的节点
node.setStatus(NodeStatusEnum.RUNNING); List<NodeInstance> failedNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
instanceId, NodeStatusEnum.FAILED);
// 重试失败的节点
for (NodeInstance node : failedNodes) {
node.setStatus(NodeStatusEnum.PENDING);
node.setError(null);
nodeInstanceRepository.save(node); nodeInstanceRepository.save(node);
executeNode(node.getId()); executeNode(node.getId());
} }
}
// 更新工作流实例状态 @Override
instance.setStatus(WorkflowInstanceStatusEnum.RUNNING); @Transactional
workflowInstanceRepository.save(instance); public void checkTimeout(Long instanceId, long timeoutMillis) {
WorkflowInstance instance = workflowInstanceRepository.findById(instanceId)
.orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
if (instance.isTimeout(timeoutMillis)) {
String error = "Workflow execution timeout after " + timeoutMillis + " milliseconds";
terminateWorkflow(instanceId, error);
}
} }
private NodeInstance createStartNode(WorkflowDefinition definition, Long instanceId) { private NodeInstance createStartNode(WorkflowDefinition definition, Long instanceId) {

View File

@ -38,4 +38,16 @@ public interface WorkflowEngine {
* 恢复工作流实例 * 恢复工作流实例
*/ */
void resumeWorkflow(Long instanceId); void resumeWorkflow(Long instanceId);
/**
* 重试工作流实例
*/
void retryWorkflow(Long instanceId);
/**
* 检查工作流实例是否超时
* @param instanceId 工作流实例ID
* @param timeoutMillis 超时时间毫秒
*/
void checkTimeout(Long instanceId, long timeoutMillis);
} }

View File

@ -81,10 +81,87 @@ public class WorkflowDefinition extends Entity<Long> {
@OneToMany(mappedBy = "workflowDefinition", cascade = CascadeType.ALL, orphanRemoval = true) @OneToMany(mappedBy = "workflowDefinition", cascade = CascadeType.ALL, orphanRemoval = true)
private List<NodeDefinition> nodes = new ArrayList<>(); private List<NodeDefinition> nodes = new ArrayList<>();
/**
* 版本号
*/
@Column(nullable = false)
private Integer version = 1;
/** /**
* 获取流转配置 * 获取流转配置
*/ */
public String getTransitionConfig() { public String getTransitionConfig() {
return transitionConfig; return transitionConfig;
} }
/**
* 检查是否可以发布
*/
public boolean canPublish() {
return status == WorkflowDefinitionStatusEnum.DRAFT;
}
/**
* 检查是否可以禁用
*/
public boolean canDisable() {
return status == WorkflowDefinitionStatusEnum.PUBLISHED;
}
/**
* 检查是否可以启用
*/
public boolean canEnable() {
return status == WorkflowDefinitionStatusEnum.DISABLED;
}
/**
* 检查是否可以更新
*/
public boolean canUpdate() {
return status == WorkflowDefinitionStatusEnum.DRAFT;
}
/**
* 发布工作流定义
*/
public void publish() {
if (!canPublish()) {
throw new IllegalStateException("Cannot publish workflow definition in " + status + " status");
}
this.status = WorkflowDefinitionStatusEnum.PUBLISHED;
}
/**
* 禁用工作流定义
*/
public void disable() {
if (!canDisable()) {
throw new IllegalStateException("Cannot disable workflow definition in " + status + " status");
}
this.status = WorkflowDefinitionStatusEnum.DISABLED;
}
/**
* 启用工作流定义
*/
public void enable() {
if (!canEnable()) {
throw new IllegalStateException("Cannot enable workflow definition in " + status + " status");
}
this.status = WorkflowDefinitionStatusEnum.PUBLISHED;
}
/**
* 创建新版本
*/
public WorkflowDefinition createNewVersion() {
WorkflowDefinition newVersion = new WorkflowDefinition();
newVersion.setCode(this.code);
newVersion.setName(this.name);
newVersion.setDescription(this.description);
newVersion.setVersion(this.version + 1);
newVersion.setStatus(WorkflowDefinitionStatusEnum.DRAFT);
return newVersion;
}
} }

View File

@ -25,6 +25,12 @@ public class WorkflowInstance extends Entity<Long> {
@JoinColumn(name = "workflow_definition_id", nullable = false) @JoinColumn(name = "workflow_definition_id", nullable = false)
private WorkflowDefinition workflowDefinition; private WorkflowDefinition workflowDefinition;
/**
* 工作流定义ID用于查询优化
*/
@Column(name = "workflow_definition_id", insertable = false, updatable = false)
private Long workflowDefinitionId;
/** /**
* 项目环境ID * 项目环境ID
*/ */
@ -68,4 +74,176 @@ public class WorkflowInstance extends Entity<Long> {
public void setDefinition(WorkflowDefinition definition) { public void setDefinition(WorkflowDefinition definition) {
this.workflowDefinition = definition; this.workflowDefinition = definition;
} }
/**
* 检查是否可以启动
*/
public boolean canStart() {
return status == WorkflowInstanceStatusEnum.CREATED || status == WorkflowInstanceStatusEnum.PENDING;
}
/**
* 检查是否可以暂停
*/
public boolean canPause() {
return status == WorkflowInstanceStatusEnum.RUNNING;
}
/**
* 检查是否可以恢复
*/
public boolean canResume() {
return status == WorkflowInstanceStatusEnum.PAUSED;
}
/**
* 检查是否可以终止
*/
public boolean canTerminate() {
return status == WorkflowInstanceStatusEnum.RUNNING ||
status == WorkflowInstanceStatusEnum.PAUSED;
}
/**
* 检查是否可以完成
*/
public boolean canComplete() {
return status == WorkflowInstanceStatusEnum.RUNNING;
}
/**
* 检查是否可以执行节点
*/
public boolean canExecuteNode() {
return status == WorkflowInstanceStatusEnum.RUNNING;
}
/**
* 启动工作流实例
*/
public void start() {
if (!canStart()) {
throw new IllegalStateException("Cannot start workflow instance in " + status + " status");
}
this.status = WorkflowInstanceStatusEnum.RUNNING;
this.startTime = LocalDateTime.now();
}
/**
* 暂停工作流实例
*/
public void pause() {
if (!canPause()) {
throw new IllegalStateException("Cannot pause workflow instance in " + status + " status");
}
this.status = WorkflowInstanceStatusEnum.PAUSED;
}
/**
* 恢复工作流实例
*/
public void resume() {
if (!canResume()) {
throw new IllegalStateException("Cannot resume workflow instance in " + status + " status");
}
this.status = WorkflowInstanceStatusEnum.RUNNING;
}
/**
* 终止工作流实例
*/
public void terminate(String reason) {
if (!canTerminate()) {
throw new IllegalStateException("Cannot terminate workflow instance in " + status + " status");
}
this.status = WorkflowInstanceStatusEnum.TERMINATED;
this.endTime = LocalDateTime.now();
this.error = reason;
}
/**
* 完成工作流实例
*/
public void complete() {
if (!canComplete()) {
throw new IllegalStateException("Cannot complete workflow instance in " + status + " status");
}
this.status = WorkflowInstanceStatusEnum.COMPLETED;
this.endTime = LocalDateTime.now();
}
/**
* 标记工作流实例为失败
*/
public void fail(String error) {
this.status = WorkflowInstanceStatusEnum.FAILED;
this.endTime = LocalDateTime.now();
this.error = error;
}
/**
* 检查工作流实例是否处于活动状态
*/
public boolean isActive() {
return status == WorkflowInstanceStatusEnum.RUNNING ||
status == WorkflowInstanceStatusEnum.PENDING ||
status == WorkflowInstanceStatusEnum.PAUSED;
}
/**
* 检查工作流实例是否已经结束
*/
public boolean isEnded() {
return status == WorkflowInstanceStatusEnum.COMPLETED ||
status == WorkflowInstanceStatusEnum.TERMINATED ||
status == WorkflowInstanceStatusEnum.FAILED;
}
/**
* 检查工作流实例是否可以重试
*/
public boolean canRetry() {
return status == WorkflowInstanceStatusEnum.FAILED;
}
/**
* 重试工作流实例
*/
public void retry() {
if (!canRetry()) {
throw new IllegalStateException("Cannot retry workflow instance in " + status + " status");
}
this.status = WorkflowInstanceStatusEnum.PENDING;
this.error = null;
}
/**
* 获取工作流实例的运行时长毫秒
*/
public long getDuration() {
if (startTime == null) {
return 0;
}
LocalDateTime end = endTime != null ? endTime : LocalDateTime.now();
return java.time.Duration.between(startTime, end).toMillis();
}
/**
* 检查工作流实例是否超时
* @param timeoutMillis 超时时间毫秒
*/
public boolean isTimeout(long timeoutMillis) {
return startTime != null && getDuration() > timeoutMillis;
}
/**
* 更新工作流实例的错误信息
*/
public void updateError(String error) {
this.error = error;
if (this.status == WorkflowInstanceStatusEnum.RUNNING) {
this.status = WorkflowInstanceStatusEnum.FAILED;
this.endTime = LocalDateTime.now();
}
}
} }

View File

@ -28,4 +28,9 @@ public interface INodeInstanceRepository extends IBaseRepository<NodeInstance, L
List<NodeInstance> findByWorkflowInstanceId(Long workflowInstanceId); List<NodeInstance> findByWorkflowInstanceId(Long workflowInstanceId);
void deleteByWorkflowInstanceId(Long workflowInstanceId); void deleteByWorkflowInstanceId(Long workflowInstanceId);
/**
* 查询不是指定状态的节点实例
*/
List<NodeInstance> findByWorkflowInstanceIdAndStatusNot(Long workflowInstanceId, NodeStatusEnum status);
} }

View File

@ -7,6 +7,8 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List; import java.util.List;
/** /**
@ -22,12 +24,38 @@ public interface IWorkflowInstanceRepository extends IBaseRepository<WorkflowIns
List<WorkflowInstance> findByStatusIn(@Param("statuses") List<WorkflowInstanceStatusEnum> statuses); List<WorkflowInstance> findByStatusIn(@Param("statuses") List<WorkflowInstanceStatusEnum> statuses);
/** /**
* 根据工作流定义ID和状态查询工作流实例数量 * 根据工作流定义ID和状态查询工作流实例
*/ */
long countByDefinitionIdAndStatus(Long definitionId, WorkflowInstanceStatusEnum status); List<WorkflowInstance> findByWorkflowDefinitionIdAndStatus(Long workflowDefinitionId, WorkflowInstanceStatusEnum status);
/**
* 根据工作流定义ID和状态统计工作流实例数量
*/
Long countByWorkflowDefinitionIdAndStatus(Long workflowDefinitionId, WorkflowInstanceStatusEnum status);
/** /**
* 查询项目环境的工作流实例 * 查询项目环境的工作流实例
*/ */
List<WorkflowInstance> findByProjectEnvIdAndDeletedFalseOrderByCreateTimeDesc(Long projectEnvId); List<WorkflowInstance> findByProjectEnvIdAndDeletedFalseOrderByCreateTimeDesc(Long projectEnvId);
/**
* 查找超过指定时间未完成的工作流实例
*/
@Query("SELECT wi FROM WorkflowInstance wi WHERE wi.status = :status AND wi.startTime < :beforeTime")
List<WorkflowInstance> findTimeoutInstances(@Param("status") WorkflowInstanceStatusEnum status,
@Param("beforeTime") LocalDateTime beforeTime);
/**
* 查找指定工作流定义下所有未结束的实例
*/
@Query("SELECT wi FROM WorkflowInstance wi WHERE wi.workflowDefinitionId = :definitionId AND wi.status IN :activeStatuses")
List<WorkflowInstance> findActiveInstancesByDefinitionId(@Param("definitionId") Long definitionId,
@Param("activeStatuses") Collection<WorkflowInstanceStatusEnum> activeStatuses);
/**
* 统计指定工作流定义下所有未结束的实例数量
*/
@Query("SELECT COUNT(wi) FROM WorkflowInstance wi WHERE wi.workflowDefinitionId = :definitionId AND wi.status IN :activeStatuses")
Long countActiveInstancesByDefinitionId(@Param("definitionId") Long definitionId,
@Param("activeStatuses") Collection<WorkflowInstanceStatusEnum> activeStatuses);
} }

View File

@ -204,6 +204,7 @@ workflow.instance.not.found=工作流实例不存在
workflow.instance.already.completed=工作流实例已完成 workflow.instance.already.completed=工作流实例已完成
workflow.instance.already.canceled=工作流实例已取消 workflow.instance.already.canceled=工作流实例已取消
workflow.instance.not.running=工作流实例未运行 workflow.instance.not.running=工作流实例未运行
workflow.instance.not.paused=工作流实例不是暂停状态
workflow.node.not.found=工作流节点不存在 workflow.node.not.found=工作流节点不存在
workflow.node.type.not.supported=不支持的节点类型 workflow.node.type.not.supported=不支持的节点类型

View File

@ -24,6 +24,7 @@ workflow.instance.not.found=Workflow instance not found
workflow.instance.already.completed=Workflow instance already completed workflow.instance.already.completed=Workflow instance already completed
workflow.instance.already.canceled=Workflow instance already canceled workflow.instance.already.canceled=Workflow instance already canceled
workflow.instance.not.running=Workflow instance is not running workflow.instance.not.running=Workflow instance is not running
workflow.instance.not.paused=Workflow instance is not paused
workflow.node.not.found=Workflow node not found workflow.node.not.found=Workflow node not found
workflow.node.type.not.supported=Unsupported node type workflow.node.type.not.supported=Unsupported node type

View File

@ -113,6 +113,7 @@ workflow.instance.not.found=工作流实例不存在
workflow.instance.already.completed=工作流实例已完成 workflow.instance.already.completed=工作流实例已完成
workflow.instance.already.canceled=工作流实例已取消 workflow.instance.already.canceled=工作流实例已取消
workflow.instance.not.running=工作流实例未运行 workflow.instance.not.running=工作流实例未运行
workflow.instance.not.paused=工作流实例未暂停
workflow.node.not.found=工作流节点不存在 workflow.node.not.found=工作流节点不存在
workflow.node.type.not.supported=不支持的节点类型 workflow.node.type.not.supported=不支持的节点类型

View File

@ -10,7 +10,8 @@ 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.NodeStatusEnum; import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum; import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum; import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnum;
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository; import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository; import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository; import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
@ -79,8 +80,9 @@ class DefaultWorkflowEngineTest {
variables.put("key1", "value1"); variables.put("key1", "value1");
WorkflowDefinition definition = new WorkflowDefinition(); WorkflowDefinition definition = new WorkflowDefinition();
definition.setId(1L);
definition.setCode(workflowCode); definition.setCode(workflowCode);
definition.setStatus(WorkflowStatusEnum.PUBLISHED); definition.setStatus(WorkflowDefinitionStatusEnum.PUBLISHED);
NodeInstance startNode = new NodeInstance(); NodeInstance startNode = new NodeInstance();
startNode.setId(1L); startNode.setId(1L);
@ -99,7 +101,7 @@ class DefaultWorkflowEngineTest {
// 验证结果 // 验证结果
assertNotNull(result); assertNotNull(result);
assertEquals(WorkflowStatusEnum.RUNNING, result.getStatus()); assertEquals(WorkflowInstanceStatusEnum.RUNNING, result.getStatus());
assertNotNull(result.getStartTime()); assertNotNull(result.getStartTime());
verify(workflowDefinitionRepository).findByCodeAndDeletedFalse(workflowCode); verify(workflowDefinitionRepository).findByCodeAndDeletedFalse(workflowCode);
@ -129,7 +131,7 @@ class DefaultWorkflowEngineTest {
String workflowCode = "draft-workflow"; String workflowCode = "draft-workflow";
WorkflowDefinition definition = new WorkflowDefinition(); WorkflowDefinition definition = new WorkflowDefinition();
definition.setCode(workflowCode); definition.setCode(workflowCode);
definition.setStatus(WorkflowStatusEnum.DRAFT); definition.setStatus(WorkflowDefinitionStatusEnum.DRAFT);
// 配置Mock行为 // 配置Mock行为
when(workflowDefinitionRepository.findByCodeAndDeletedFalse(workflowCode)).thenReturn(definition); when(workflowDefinitionRepository.findByCodeAndDeletedFalse(workflowCode)).thenReturn(definition);
@ -150,7 +152,7 @@ class DefaultWorkflowEngineTest {
nodeInstance.setStatus(NodeStatusEnum.PENDING); nodeInstance.setStatus(NodeStatusEnum.PENDING);
WorkflowInstance instance = new WorkflowInstance(); WorkflowInstance instance = new WorkflowInstance();
instance.setStatus(WorkflowStatusEnum.RUNNING); instance.setStatus(WorkflowInstanceStatusEnum.RUNNING);
nodeInstance.setWorkflowInstance(instance); nodeInstance.setWorkflowInstance(instance);
// 配置Mock行为 // 配置Mock行为
@ -192,7 +194,7 @@ class DefaultWorkflowEngineTest {
nodeInstance.setNodeType(NodeTypeEnum.TASK); nodeInstance.setNodeType(NodeTypeEnum.TASK);
WorkflowInstance instance = new WorkflowInstance(); WorkflowInstance instance = new WorkflowInstance();
instance.setStatus(WorkflowStatusEnum.COMPLETED); instance.setStatus(WorkflowInstanceStatusEnum.COMPLETED);
nodeInstance.setWorkflowInstance(instance); nodeInstance.setWorkflowInstance(instance);
// 配置Mock行为 // 配置Mock行为
@ -212,7 +214,7 @@ class DefaultWorkflowEngineTest {
WorkflowInstance instance = new WorkflowInstance(); WorkflowInstance instance = new WorkflowInstance();
instance.setId(instanceId); instance.setId(instanceId);
instance.setStatus(WorkflowStatusEnum.RUNNING); instance.setStatus(WorkflowInstanceStatusEnum.RUNNING);
NodeInstance runningNode = new NodeInstance(); NodeInstance runningNode = new NodeInstance();
runningNode.setNodeType(NodeTypeEnum.TASK); runningNode.setNodeType(NodeTypeEnum.TASK);
@ -228,7 +230,7 @@ class DefaultWorkflowEngineTest {
workflowEngine.terminateWorkflow(instanceId, reason); workflowEngine.terminateWorkflow(instanceId, reason);
// 验证结果 // 验证结果
assertEquals(WorkflowStatusEnum.TERMINATED, instance.getStatus()); assertEquals(WorkflowInstanceStatusEnum.TERMINATED, instance.getStatus());
assertEquals(reason, instance.getError()); assertEquals(reason, instance.getError());
assertNotNull(instance.getEndTime()); assertNotNull(instance.getEndTime());
assertEquals(NodeStatusEnum.TERMINATED, runningNode.getStatus()); assertEquals(NodeStatusEnum.TERMINATED, runningNode.getStatus());
@ -247,7 +249,7 @@ class DefaultWorkflowEngineTest {
Long instanceId = 1L; Long instanceId = 1L;
WorkflowInstance instance = new WorkflowInstance(); WorkflowInstance instance = new WorkflowInstance();
instance.setId(instanceId); instance.setId(instanceId);
instance.setStatus(WorkflowStatusEnum.RUNNING); instance.setStatus(WorkflowInstanceStatusEnum.RUNNING);
NodeInstance runningNode = new NodeInstance(); NodeInstance runningNode = new NodeInstance();
runningNode.setStatus(NodeStatusEnum.RUNNING); runningNode.setStatus(NodeStatusEnum.RUNNING);
@ -261,7 +263,7 @@ class DefaultWorkflowEngineTest {
workflowEngine.pauseWorkflow(instanceId); workflowEngine.pauseWorkflow(instanceId);
// 验证结果 // 验证结果
assertEquals(WorkflowStatusEnum.PAUSED, instance.getStatus()); assertEquals(WorkflowInstanceStatusEnum.PAUSED, instance.getStatus());
assertEquals(NodeStatusEnum.PAUSED, runningNode.getStatus()); assertEquals(NodeStatusEnum.PAUSED, runningNode.getStatus());
verify(workflowInstanceRepository).findById(instanceId); verify(workflowInstanceRepository).findById(instanceId);
@ -276,7 +278,7 @@ class DefaultWorkflowEngineTest {
Long instanceId = 1L; Long instanceId = 1L;
WorkflowInstance instance = new WorkflowInstance(); WorkflowInstance instance = new WorkflowInstance();
instance.setId(instanceId); instance.setId(instanceId);
instance.setStatus(WorkflowStatusEnum.PAUSED); instance.setStatus(WorkflowInstanceStatusEnum.PAUSED);
NodeInstance pausedNode = new NodeInstance(); NodeInstance pausedNode = new NodeInstance();
pausedNode.setId(2L); pausedNode.setId(2L);
@ -294,7 +296,7 @@ class DefaultWorkflowEngineTest {
workflowEngine.resumeWorkflow(instanceId); workflowEngine.resumeWorkflow(instanceId);
// 验证结果 // 验证结果
assertEquals(WorkflowStatusEnum.RUNNING, instance.getStatus()); assertEquals(WorkflowInstanceStatusEnum.RUNNING, instance.getStatus());
assertEquals(NodeStatusEnum.RUNNING, pausedNode.getStatus()); assertEquals(NodeStatusEnum.RUNNING, pausedNode.getStatus());
verify(workflowInstanceRepository).findById(instanceId); verify(workflowInstanceRepository).findById(instanceId);

View File

@ -4,7 +4,7 @@ import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException; import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO; import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition; import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum; import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.INodeDefinitionRepository; import com.qqchen.deploy.backend.workflow.repository.INodeDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository; import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.service.impl.WorkflowDefinitionServiceImpl; import com.qqchen.deploy.backend.workflow.service.impl.WorkflowDefinitionServiceImpl;
@ -52,7 +52,7 @@ class WorkflowDefinitionServiceTest {
entity.setId(1L); entity.setId(1L);
entity.setCode("TEST-WF"); entity.setCode("TEST-WF");
entity.setName("Test Workflow"); entity.setName("Test Workflow");
entity.setStatus(WorkflowStatusEnum.DRAFT); entity.setStatus(WorkflowDefinitionStatusEnum.DRAFT);
entity.setEnabled(true); entity.setEnabled(true);
} }
@ -64,7 +64,7 @@ class WorkflowDefinitionServiceTest {
WorkflowDefinitionDTO result = workflowDefinitionService.create(dto); WorkflowDefinitionDTO result = workflowDefinitionService.create(dto);
assertNotNull(result); assertNotNull(result);
assertEquals(WorkflowStatusEnum.DRAFT, result.getStatus()); assertEquals(WorkflowDefinitionStatusEnum.DRAFT, result.getStatus());
assertTrue(result.getEnabled()); assertTrue(result.getEnabled());
verify(workflowDefinitionRepository).existsByCodeAndDeletedFalse(dto.getCode()); verify(workflowDefinitionRepository).existsByCodeAndDeletedFalse(dto.getCode());
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class)); verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
@ -81,21 +81,21 @@ class WorkflowDefinitionServiceTest {
@Test @Test
void publish_Success() { void publish_Success() {
entity.setStatus(WorkflowStatusEnum.DRAFT); entity.setStatus(WorkflowDefinitionStatusEnum.DRAFT);
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity)); when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity); when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity);
WorkflowDefinitionDTO result = workflowDefinitionService.publish(1L); WorkflowDefinitionDTO result = workflowDefinitionService.publish(1L);
assertNotNull(result); assertNotNull(result);
assertEquals(WorkflowStatusEnum.PUBLISHED, result.getStatus()); assertEquals(WorkflowDefinitionStatusEnum.PUBLISHED, result.getStatus());
verify(workflowDefinitionRepository).findById(1L); verify(workflowDefinitionRepository).findById(1L);
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class)); verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
} }
@Test @Test
void publish_NotDraft_ThrowsException() { void publish_NotDraft_ThrowsException() {
entity.setStatus(WorkflowStatusEnum.PUBLISHED); entity.setStatus(WorkflowDefinitionStatusEnum.PUBLISHED);
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity)); when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
BusinessException exception = assertThrows(BusinessException.class, BusinessException exception = assertThrows(BusinessException.class,
@ -105,28 +105,28 @@ class WorkflowDefinitionServiceTest {
@Test @Test
void disable_Success() { void disable_Success() {
entity.setStatus(WorkflowStatusEnum.PUBLISHED); entity.setStatus(WorkflowDefinitionStatusEnum.PUBLISHED);
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity)); when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity); when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity);
WorkflowDefinitionDTO result = workflowDefinitionService.disable(1L); WorkflowDefinitionDTO result = workflowDefinitionService.disable(1L);
assertNotNull(result); assertNotNull(result);
assertEquals(WorkflowStatusEnum.DISABLED, result.getStatus()); assertEquals(WorkflowDefinitionStatusEnum.DISABLED, result.getStatus());
verify(workflowDefinitionRepository).findById(1L); verify(workflowDefinitionRepository).findById(1L);
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class)); verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
} }
@Test @Test
void enable_Success() { void enable_Success() {
entity.setStatus(WorkflowStatusEnum.DISABLED); entity.setStatus(WorkflowDefinitionStatusEnum.DISABLED);
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity)); when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity); when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity);
WorkflowDefinitionDTO result = workflowDefinitionService.enable(1L); WorkflowDefinitionDTO result = workflowDefinitionService.enable(1L);
assertNotNull(result); assertNotNull(result);
assertEquals(WorkflowStatusEnum.PUBLISHED, result.getStatus()); assertEquals(WorkflowDefinitionStatusEnum.PUBLISHED, result.getStatus());
verify(workflowDefinitionRepository).findById(1L); verify(workflowDefinitionRepository).findById(1L);
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class)); verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
} }
@ -140,7 +140,7 @@ class WorkflowDefinitionServiceTest {
WorkflowDefinitionDTO result = workflowDefinitionService.createNewVersion(1L); WorkflowDefinitionDTO result = workflowDefinitionService.createNewVersion(1L);
assertNotNull(result); assertNotNull(result);
assertEquals(WorkflowStatusEnum.DRAFT, result.getStatus()); assertEquals(WorkflowDefinitionStatusEnum.DRAFT, result.getStatus());
assertTrue(result.getEnabled()); assertTrue(result.getEnabled());
verify(workflowDefinitionRepository).findById(1L); verify(workflowDefinitionRepository).findById(1L);
verify(workflowDefinitionRepository).findLatestVersionByCode(entity.getCode()); verify(workflowDefinitionRepository).findLatestVersionByCode(entity.getCode());