From 13b936634667607be6d0a78b1435e1abe23e4501 Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Tue, 31 Dec 2024 08:51:54 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=8E=E5=A4=B4=E5=86=8D=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lanyuanxiaoyao/flowable/model/Flow.java | 149 --------- .../flowable/model/FlowContext.java | 44 --- .../flowable/model/FlowStatus.java | 35 -- .../flowable/node/AbstractFlowNode.java | 21 -- .../flowable/node/FlowNode.java | 24 -- .../flowable/node/SimpleFlowNode.java | 13 - .../flowable/node/SystemFlowNode.java | 36 --- .../flowable/repository/FlowRepository.java | 11 - .../repository/MemoryFlowRepository.java | 33 -- .../flowable/service/FlowService.java | 164 ---------- .../flowable/FlowServiceTest.java | 299 ------------------ .../flowable/node/LeaveRequestNode.java | 31 -- .../flowable/node/LeaveSystemCheckNode.java | 34 -- .../flowable/node/ManagerApprovalNode.java | 25 -- 14 files changed, 919 deletions(-) delete mode 100644 src/main/java/com/lanyuanxiaoyao/flowable/model/Flow.java delete mode 100644 src/main/java/com/lanyuanxiaoyao/flowable/model/FlowContext.java delete mode 100644 src/main/java/com/lanyuanxiaoyao/flowable/model/FlowStatus.java delete mode 100644 src/main/java/com/lanyuanxiaoyao/flowable/node/AbstractFlowNode.java delete mode 100644 src/main/java/com/lanyuanxiaoyao/flowable/node/FlowNode.java delete mode 100644 src/main/java/com/lanyuanxiaoyao/flowable/node/SimpleFlowNode.java delete mode 100644 src/main/java/com/lanyuanxiaoyao/flowable/node/SystemFlowNode.java delete mode 100644 src/main/java/com/lanyuanxiaoyao/flowable/repository/FlowRepository.java delete mode 100644 src/main/java/com/lanyuanxiaoyao/flowable/repository/MemoryFlowRepository.java delete mode 100644 src/main/java/com/lanyuanxiaoyao/flowable/service/FlowService.java delete mode 100644 src/test/java/com/lanyuanxiaoyao/flowable/FlowServiceTest.java delete mode 100644 src/test/java/com/lanyuanxiaoyao/flowable/node/LeaveRequestNode.java delete mode 100644 src/test/java/com/lanyuanxiaoyao/flowable/node/LeaveSystemCheckNode.java delete mode 100644 src/test/java/com/lanyuanxiaoyao/flowable/node/ManagerApprovalNode.java diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/model/Flow.java b/src/main/java/com/lanyuanxiaoyao/flowable/model/Flow.java deleted file mode 100644 index 0bd67ec..0000000 --- a/src/main/java/com/lanyuanxiaoyao/flowable/model/Flow.java +++ /dev/null @@ -1,149 +0,0 @@ -package com.lanyuanxiaoyao.flowable.model; - -import com.lanyuanxiaoyao.flowable.node.FlowNode; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import lombok.Data; - -/** - * 流程实例类 - * 包含流程定义和执行状态 - */ -@Data -public class Flow { - /** - * 流程实例ID - */ - private String id; - - /** - * 流程名称 - */ - private String name; - - /** - * 流程描述 - */ - private String description; - - /** - * 流程节点列表 - * 按照列表顺序依次执行,每个节点都是一个可执行的审批操作 - */ - private List nodes; - - /** - * 当前执行到的节点ID - */ - private String currentNode; - - /** - * 当前流程状态 - */ - private FlowStatus status; - - /** - * 流程上下文变量 - * 用于存储流程执行过程中的数据,实现节点间的数据传递 - * 这些数据会随着流程实例一起持久化 - */ - private Map contextVariables; - - /** - * 创建时间 - */ - private LocalDateTime createTime; - - /** - * 最后更新时间 - */ - private LocalDateTime updateTime; - - public Flow() { - this.nodes = new ArrayList<>(); - this.contextVariables = new HashMap<>(); - this.status = FlowStatus.PENDING; - this.currentNode = null; - } - - /** - * 添加一个新的流程节点 - * - * @param node 要添加的流程节点 - */ - public void addNode(FlowNode node) { - nodes.add(node); - } - - /** - * 获取当前节点 - * - * @return 当前节点对象 - */ - public FlowNode getCurrentNodeObject() { - return nodes.stream() - .filter(node -> node.getNodeId().equals(currentNode)) - .findFirst() - .orElseThrow(() -> new IllegalStateException("找不到当前节点")); - } - - /** - * 获取下一个节点 - * - * @return 下一个节点对象,如果已经是最后一个节点则返回null - */ - public FlowNode getNextNode() { - for (int i = 0; i < nodes.size() - 1; i++) { - if (nodes.get(i).getNodeId().equals(currentNode)) { - return nodes.get(i + 1); - } - } - return null; - } - - /** - * 判断当前是否是最后一个节点 - * - * @return true表示是最后一个节点 - */ - public boolean isLastNode() { - return nodes.get(nodes.size() - 1).getNodeId().equals(currentNode); - } - - /** - * 移动到下一个节点 - * - * @throws IllegalStateException 如果已经是最后一个节点 - */ - public void moveToNextNode() { - FlowNode nextNode = getNextNode(); - if (nextNode == null) { - throw new IllegalStateException("已经是最后一个节点"); - } - currentNode = nextNode.getNodeId(); - } - - /** - * 创建流程上下文 - * 从持久化的上下���变量中恢复数据 - */ - public FlowContext createContext() { - FlowContext context = new FlowContext(); - context.setFlowId(id); - // 使用新的 Map 避免直接修改存储的数据 - context.setVariables(new HashMap<>(contextVariables)); - return context; - } - - /** - * 保存上下文变量 - * 将变量持久化到流程实例中 - */ - public void saveContext(FlowContext context) { - // 使用新的 Map 保存数据的副本 - this.contextVariables = new HashMap<>(context.getVariables()); - } -} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/model/FlowContext.java b/src/main/java/com/lanyuanxiaoyao/flowable/model/FlowContext.java deleted file mode 100644 index 13e0253..0000000 --- a/src/main/java/com/lanyuanxiaoyao/flowable/model/FlowContext.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.lanyuanxiaoyao.flowable.model; - -import java.util.HashMap; -import java.util.Map; -import lombok.Data; - -/** - * 流程上下文 - * 用于在节点间传递数据 - */ -@Data -public class FlowContext { - /** - * 流程ID - */ - private String flowId; - - /** - * 上下文变量 - */ - private Map variables; - - public FlowContext() { - this.variables = new HashMap<>(); - } - - /** - * 获取指定类型的变量值 - */ - public T getVariable(String key, Class type) { - Object value = variables.get(key); - if (value == null) { - return null; - } - return type.cast(value); - } - - /** - * 设置变量值 - */ - public void setVariable(String key, Object value) { - variables.put(key, value); - } -} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/model/FlowStatus.java b/src/main/java/com/lanyuanxiaoyao/flowable/model/FlowStatus.java deleted file mode 100644 index 2b7e90c..0000000 --- a/src/main/java/com/lanyuanxiaoyao/flowable/model/FlowStatus.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.lanyuanxiaoyao.flowable.model; - -/** - * 流程状态枚举 - * 用于表示流程实例的当前状态 - */ -public enum FlowStatus { - /** - * 进行中:流程正在执行中 - */ - PENDING("进行中"), - - /** - * 已通过:流程已经完成并通过 - */ - APPROVED("已通过"), - - /** - * 已拒绝:流程被拒绝 - */ - REJECTED("已拒绝"); - - /** - * 状态的中文描述 - */ - private final String description; - - FlowStatus(String description) { - this.description = description; - } - - public String getDescription() { - return description; - } -} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/node/AbstractFlowNode.java b/src/main/java/com/lanyuanxiaoyao/flowable/node/AbstractFlowNode.java deleted file mode 100644 index fe54ced..0000000 --- a/src/main/java/com/lanyuanxiaoyao/flowable/node/AbstractFlowNode.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.lanyuanxiaoyao.flowable.node; - -import com.lanyuanxiaoyao.flowable.model.FlowContext; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public abstract class AbstractFlowNode implements FlowNode { - private final String nodeId; - - @Override - public void onApprove(FlowContext context) { - // 默认实现为空 - } - - @Override - public void onReject(FlowContext context) { - // 默认实现为空 - } -} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/node/FlowNode.java b/src/main/java/com/lanyuanxiaoyao/flowable/node/FlowNode.java deleted file mode 100644 index 0a2d0b7..0000000 --- a/src/main/java/com/lanyuanxiaoyao/flowable/node/FlowNode.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.lanyuanxiaoyao.flowable.node; - -import com.lanyuanxiaoyao.flowable.model.FlowContext; - -/** - * 流程节点接口 - * 定义节点的基本操作 - */ -public interface FlowNode { - /** - * 获取节点ID - */ - String getNodeId(); - - /** - * 处理通过操作 - */ - void onApprove(FlowContext context); - - /** - * 处理拒绝操作 - */ - void onReject(FlowContext context); -} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/node/SimpleFlowNode.java b/src/main/java/com/lanyuanxiaoyao/flowable/node/SimpleFlowNode.java deleted file mode 100644 index 9d49d86..0000000 --- a/src/main/java/com/lanyuanxiaoyao/flowable/node/SimpleFlowNode.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.lanyuanxiaoyao.flowable.node; - -import com.lanyuanxiaoyao.flowable.model.FlowContext; - -/** - * 简单流程节点 - * 仅用于演示基本的审批流程 - */ -public class SimpleFlowNode extends AbstractFlowNode { - public SimpleFlowNode(String nodeId) { - super(nodeId); - } -} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/node/SystemFlowNode.java b/src/main/java/com/lanyuanxiaoyao/flowable/node/SystemFlowNode.java deleted file mode 100644 index cffeda7..0000000 --- a/src/main/java/com/lanyuanxiaoyao/flowable/node/SystemFlowNode.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.lanyuanxiaoyao.flowable.node; - -import com.lanyuanxiaoyao.flowable.model.FlowContext; - -/** - * 系统节点基类 - * 提供自动审批功能 - */ -public abstract class SystemFlowNode extends AbstractFlowNode { - protected SystemFlowNode(String nodeId) { - super(nodeId); - } - - /** - * 判断是否自动通过 - * - * @param context 流程上下文 - * @return true表示自动通过,false表示自动拒绝 - */ - public abstract boolean autoApprove(FlowContext context); - - /** - * 获取拒绝原因 - * - * @param context 流程上下文 - * @return 拒绝原因 - */ - protected abstract String getRejectionReason(FlowContext context); - - @Override - public void onReject(FlowContext context) { - // 设置拒绝原因到上下文 - String rejectionReason = getRejectionReason(context); - context.setVariable("systemComment", rejectionReason); - } -} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/repository/FlowRepository.java b/src/main/java/com/lanyuanxiaoyao/flowable/repository/FlowRepository.java deleted file mode 100644 index 88243b4..0000000 --- a/src/main/java/com/lanyuanxiaoyao/flowable/repository/FlowRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.lanyuanxiaoyao.flowable.repository; - -import com.lanyuanxiaoyao.flowable.model.Flow; - -public interface FlowRepository { - Flow save(Flow flow); - - Flow update(Flow flow); - - Flow findById(String id); -} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/repository/MemoryFlowRepository.java b/src/main/java/com/lanyuanxiaoyao/flowable/repository/MemoryFlowRepository.java deleted file mode 100644 index 9757364..0000000 --- a/src/main/java/com/lanyuanxiaoyao/flowable/repository/MemoryFlowRepository.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.lanyuanxiaoyao.flowable.repository; - -import com.lanyuanxiaoyao.flowable.model.Flow; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -public class MemoryFlowRepository implements FlowRepository { - private final Map flows = new ConcurrentHashMap<>(); - - @Override - public Flow save(Flow flow) { - if (flow.getId() == null) { - flow.setId(UUID.randomUUID().toString()); - } - flows.put(flow.getId(), flow); - return flow; - } - - @Override - public Flow update(Flow flow) { - if (flow.getId() == null || !flows.containsKey(flow.getId())) { - throw new IllegalArgumentException("找不到对应的流程实例"); - } - flows.put(flow.getId(), flow); - return flow; - } - - @Override - public Flow findById(String id) { - return flows.get(id); - } -} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/service/FlowService.java b/src/main/java/com/lanyuanxiaoyao/flowable/service/FlowService.java deleted file mode 100644 index bbe0b14..0000000 --- a/src/main/java/com/lanyuanxiaoyao/flowable/service/FlowService.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.lanyuanxiaoyao.flowable.service; - -import com.lanyuanxiaoyao.flowable.model.Flow; -import com.lanyuanxiaoyao.flowable.model.FlowContext; -import com.lanyuanxiaoyao.flowable.model.FlowStatus; -import com.lanyuanxiaoyao.flowable.node.FlowNode; -import com.lanyuanxiaoyao.flowable.node.SystemFlowNode; -import com.lanyuanxiaoyao.flowable.repository.FlowRepository; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.HashMap; -import lombok.RequiredArgsConstructor; - -/** - * 流程服务类 - * 提供流程的创建、启动、审批等核心功能 - */ -@RequiredArgsConstructor -public class FlowService { - private final FlowRepository flowRepository; - - /** - * 启动一个新的流程实例 - * - * @throws IllegalStateException 如果流程已经启动或已经结束 - */ - public Flow startFlow(String flowId) { - Flow flow = findFlowById(flowId); - - // 检查流程状态 - if (!FlowStatus.PENDING.equals(flow.getStatus()) || - flow.getCurrentNode() != null) { // 使用当前节点是否为空来判断是否已启动 - throw new IllegalStateException("流程已经启动或已经结束"); - } - - // 设置初始节点 - if (!flow.getNodes().isEmpty()) { - flow.setCurrentNode(flow.getNodes().get(0).getNodeId()); - } - flow.setUpdateTime(LocalDateTime.now()); - - // 保存流程实例 - flow = flowRepository.update(flow); - - // 如果第一个节点是系统节点,自动执行审批 - if (flow.getCurrentNodeObject() instanceof SystemFlowNode) { - return executeNode(flow); - } - - return flow; - } - - /** - * 审批通过当前节点 - */ - public Flow approve(String flowId) { - Flow flow = findFlowById(flowId); - validateFlowNotCompleted(flow); - - FlowContext context = flow.createContext(); - FlowNode currentNode = flow.getCurrentNodeObject(); - currentNode.onApprove(context); - - if (flow.isLastNode()) { - flow.setStatus(FlowStatus.APPROVED); - return updateFlow(flow, context); - } else { - flow = updateFlow(flow, context); // 先保存当前节点的���态 - flow.moveToNextNode(); - return executeNode(flow); // 执行下一个节点(可能是系统节点) - } - } - - /** - * 拒绝当前节点,结束流程 - */ - public Flow reject(String flowId) { - Flow flow = findFlowById(flowId); - validateFlowNotCompleted(flow); - - FlowContext context = flow.createContext(); - flow.getCurrentNodeObject().onReject(context); - flow.saveContext(context); // 保存拒绝操作的上下文变量 - flow.setStatus(FlowStatus.REJECTED); - - return updateFlow(flow, context); - } - - /** - * 获取流程实例 - */ - public Flow getFlow(String flowId) { - return findFlowById(flowId); - } - - // 私有辅助方法 - - private void validateFlowNodes(Flow flow) { - if (flow.getNodes() == null || flow.getNodes().isEmpty()) { - throw new IllegalArgumentException("流程节点不能为空"); - } - } - - private void initializeFlow(Flow flow) { - flow.setCreateTime(LocalDateTime.now()); - flow.setUpdateTime(LocalDateTime.now()); - } - - private Flow findFlowById(String flowId) { - Flow flow = flowRepository.findById(flowId); - if (flow == null) { - throw new IllegalArgumentException("找不到对应的流程"); - } - return flow; - } - - private void validateFlowNotCompleted(Flow flow) { - if (FlowStatus.APPROVED.equals(flow.getStatus()) || - FlowStatus.REJECTED.equals(flow.getStatus())) { - throw new IllegalStateException("当前流程已经结束"); - } - } - - private Flow executeNode(Flow flow) { - FlowContext context = flow.createContext(); - FlowNode currentNode = flow.getCurrentNodeObject(); - - // 如果是系统节点,自动执行审批 - if (currentNode instanceof SystemFlowNode) { - return handleSystemNode(flow, currentNode, context); - } - - return updateFlow(flow, context); - } - - private Flow handleSystemNode(Flow flow, FlowNode node, FlowContext context) { - SystemFlowNode systemNode = (SystemFlowNode) node; - - if (systemNode.autoApprove(context)) { - // 自动通过 - systemNode.onApprove(context); - flow = updateFlow(flow, context); // 先保存当前状态 - - if (!flow.isLastNode()) { - flow.moveToNextNode(); - return executeNode(flow); // 执行下一个节点 - } else { - flow.setStatus(FlowStatus.APPROVED); - return updateFlow(flow, context); - } - } else { - // 自动拒绝 - systemNode.onReject(context); - flow.setStatus(FlowStatus.REJECTED); - return updateFlow(flow, context); - } - } - - private Flow updateFlow(Flow flow, FlowContext context) { - flow.saveContext(context); - flow.setUpdateTime(LocalDateTime.now()); - return flowRepository.update(flow); - } -} \ No newline at end of file diff --git a/src/test/java/com/lanyuanxiaoyao/flowable/FlowServiceTest.java b/src/test/java/com/lanyuanxiaoyao/flowable/FlowServiceTest.java deleted file mode 100644 index c3b4cdd..0000000 --- a/src/test/java/com/lanyuanxiaoyao/flowable/FlowServiceTest.java +++ /dev/null @@ -1,299 +0,0 @@ -package com.lanyuanxiaoyao.flowable; - -import com.lanyuanxiaoyao.flowable.model.Flow; -import com.lanyuanxiaoyao.flowable.model.FlowStatus; -import com.lanyuanxiaoyao.flowable.node.LeaveRequestNode; -import com.lanyuanxiaoyao.flowable.node.LeaveSystemCheckNode; -import com.lanyuanxiaoyao.flowable.node.ManagerApprovalNode; -import com.lanyuanxiaoyao.flowable.repository.FlowRepository; -import com.lanyuanxiaoyao.flowable.repository.MemoryFlowRepository; -import com.lanyuanxiaoyao.flowable.service.FlowService; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@DisplayName("流程服务测试") -class FlowServiceTest { - private FlowService flowService; - private Flow leaveFlow; - private Flow longLeaveFlow; // 新增长期请假流程 - - @BeforeEach - void setUp() { - // 初始化仓储���务 - FlowRepository flowRepository = new MemoryFlowRepository(); - flowService = new FlowService(flowRepository); - - // 创建标准请假流程(3天) - leaveFlow = new Flow(); - leaveFlow.setName("标准请假流程"); - leaveFlow.setDescription("3天以内的请假流程"); - leaveFlow.addNode(new LeaveRequestNode(3, "年假")); - leaveFlow.addNode(new LeaveSystemCheckNode(5)); - leaveFlow.addNode(new ManagerApprovalNode()); - leaveFlow = flowRepository.save(leaveFlow); - - // 创建长期请假流程(7天) - longLeaveFlow = new Flow(); - longLeaveFlow.setName("长期请假流程"); - longLeaveFlow.setDescription("7天的请假流程"); - longLeaveFlow.addNode(new LeaveRequestNode(7, "年假")); - longLeaveFlow.addNode(new LeaveSystemCheckNode(5)); - longLeaveFlow.addNode(new ManagerApprovalNode()); - longLeaveFlow = flowRepository.save(longLeaveFlow); - } - - @Nested - @DisplayName("流程启动测试") - class FlowStartTest { - @Test - @DisplayName("找不到流程时启动应抛出异常") - void shouldThrowExceptionWhenFlowNotFound() { - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> flowService.startFlow("non-existent-id") - ); - - assertEquals("找不到对应的流程", exception.getMessage()); - } - - @Test - @DisplayName("启动流程时应正确初始化状态") - void shouldInitializeStateWhenStartFlow() { - // 获取流程实例验证初始状态 - Flow flow = flowService.getFlow(leaveFlow.getId()); - - // 验证初始状态 - assertNotNull(flow.getId(), "流程ID不应为空"); - assertEquals(FlowStatus.PENDING, flow.getStatus(), "初始状态应为PENDING"); - assertTrue(flow.getContextVariables().isEmpty(), "上下文变量应为空"); - - // 启动流程 - flow = flowService.startFlow(leaveFlow.getId()); - - // 验证启动后状态 - assertEquals(FlowStatus.PENDING, flow.getStatus(), "启动后状态应为PENDING"); - assertEquals("请假申请", flow.getCurrentNode(), "应从第一个节点开始"); - assertNotNull(flow.getUpdateTime(), "更新时间不应为空"); - // 验证第一个节点执行后的变量 - assertEquals(3, flow.getContextVariables().get("days"), "应包含请假天数"); - assertEquals("年假", flow.getContextVariables().get("reason"), "应包含请假理由"); - } - - @Test - @DisplayName("不能重复启动已经结束的流程") - void shouldNotStartCompletedFlow() { - // 先启动并完成一个流程 - Flow completedFlow = flowService.startFlow(leaveFlow.getId()); - completedFlow = flowService.approve(completedFlow.getId()); - final Flow finalFlow = flowService.approve(completedFlow.getId()); - - // 尝试重新启动 - IllegalStateException exception = assertThrows( - IllegalStateException.class, - () -> flowService.startFlow(finalFlow.getId()) - ); - - assertEquals("流程已经启动或已经结束", exception.getMessage()); - } - - @Test - @DisplayName("不能重复启动已经拒绝的流程") - void shouldNotStartRejectedFlow() { - // 先启动并拒绝一个流程 - Flow rejectedFlow = flowService.startFlow(leaveFlow.getId()); - final Flow finalFlow = flowService.reject(rejectedFlow.getId()); - - // 尝试重新启动 - IllegalStateException exception = assertThrows( - IllegalStateException.class, - () -> flowService.startFlow(finalFlow.getId()) - ); - - assertEquals("流程已经启动或已经结束", exception.getMessage()); - } - - @Test - @DisplayName("不能重复启动正在进行的流程") - void shouldNotStartRunningFlow() { - // 先启动流程 - final Flow runningFlow = flowService.startFlow(leaveFlow.getId()); - - // 尝试重新启动 - IllegalStateException exception = assertThrows( - IllegalStateException.class, - () -> flowService.startFlow(runningFlow.getId()) - ); - - assertEquals("流程已经启动或已经结束", exception.getMessage()); - } - } - - @Nested - @DisplayName("系统节点审批流程测试") - class SystemNodeFlowTest { - @Test - @DisplayName("请假天数在限制内时应自动通过系统审核") - void shouldAutoApproveWhenLeaveDaysWithinLimit() { - Flow flow = flowService.startFlow(leaveFlow.getId()); - - // 验证初始状态 - assertEquals(FlowStatus.PENDING, flow.getStatus()); - assertEquals("请假申请", flow.getCurrentNode()); - assertEquals(3, flow.getContextVariables().get("days")); - assertEquals("年假", flow.getContextVariables().get("reason")); - - // 提交请假申请 - flow = flowService.approve(flow.getId()); - - // 验证系统审核通过 - assertEquals(FlowStatus.PENDING, flow.getStatus()); - assertEquals("经理审批", flow.getCurrentNode()); - assertEquals("系统自动通过", flow.getContextVariables().get("systemComment")); - - // 经理审批 - flow = flowService.approve(flow.getId()); - - // 验证流程完成 - assertEquals(FlowStatus.APPROVED, flow.getStatus()); - assertEquals("同意", flow.getContextVariables().get("managerComment")); - } - - @Test - @DisplayName("请假天数超出限制时应被系统自动拒绝") - void shouldAutoRejectWhenLeaveDaysExceedLimit() { - Flow flow = flowService.startFlow(longLeaveFlow.getId()); - - // 验证初始状态 - assertEquals(FlowStatus.PENDING, flow.getStatus()); - assertEquals("请假申请", flow.getCurrentNode()); - assertEquals(7, flow.getContextVariables().get("days")); - - // 提交请假申请 - flow = flowService.approve(flow.getId()); - - // 验证系统拒绝 - assertEquals(FlowStatus.REJECTED, flow.getStatus()); - assertEquals("请假天数(7)超过系统限制(5),需要额外审批", - flow.getContextVariables().get("systemComment")); - } - } - - @Nested - @DisplayName("人工审批流程测试") - class ManualApprovalTest { - @Test - @DisplayName("经理应能直接拒绝请假申请") - void shouldAllowManagerToReject() { - Flow flow = flowService.startFlow(leaveFlow.getId()); - - // 通过请假申请和系统审核 - flow = flowService.approve(flow.getId()); - - // 经理拒绝 - flow = flowService.reject(flow.getId()); - - assertEquals(FlowStatus.REJECTED, flow.getStatus()); - assertEquals("经理审批", flow.getCurrentNode()); - } - - @Test - @DisplayName("已完成的流程不能再次审批") - void shouldNotAllowApproveCompletedFlow() { - Flow completedFlow = flowService.startFlow(leaveFlow.getId()); - completedFlow = flowService.approve(completedFlow.getId()); - final Flow finalFlow = flowService.approve(completedFlow.getId()); - - IllegalStateException exception = assertThrows( - IllegalStateException.class, - () -> flowService.approve(finalFlow.getId()) - ); - - assertEquals("当前流程已经结束", exception.getMessage()); - } - - @Test - @DisplayName("已拒绝的流程不能再次审批") - void shouldNotAllowApproveRejectedFlow() { - Flow rejectedFlow = flowService.startFlow(leaveFlow.getId()); - final Flow finalFlow = flowService.reject(rejectedFlow.getId()); - - IllegalStateException exception = assertThrows( - IllegalStateException.class, - () -> flowService.approve(finalFlow.getId()) - ); - - assertEquals("当前流程已经结束", exception.getMessage()); - } - } - - @Nested - @DisplayName("流程变量测试") - class FlowVariableTest { - @Test - @DisplayName("流程变量应在节点间正确传递") - void shouldPassVariablesBetweenNodes() { - Flow flow = flowService.startFlow(leaveFlow.getId()); - - // 验证请假申请节点设置的变量 - assertEquals(3, flow.getContextVariables().get("days")); - assertEquals("年假", flow.getContextVariables().get("reason")); - - // 提交请假申请,验证系统审核节点的变量 - flow = flowService.approve(flow.getId()); - assertEquals("系统自动通过", flow.getContextVariables().get("systemComment")); - - // 经理审批,验证所有变量都被保留 - flow = flowService.approve(flow.getId()); - assertEquals(3, flow.getContextVariables().get("days")); - assertEquals("年假", flow.getContextVariables().get("reason")); - assertEquals("系统自动通过", flow.getContextVariables().get("systemComment")); - assertEquals("同意", flow.getContextVariables().get("managerComment")); - } - - @Test - @DisplayName("流程变量应该在重新获取流程时保持不变") - void shouldPersistVariablesWhenReloadFlow() { - Flow flow = flowService.startFlow(leaveFlow.getId()); - - // 提交请假申请 - flow = flowService.approve(flow.getId()); - String flowId = flow.getId(); - - // 重新获取流程实例 - Flow reloadedFlow = flowService.getFlow(flowId); - - // 验证上下文变量被正确保存 - assertEquals(3, reloadedFlow.getContextVariables().get("days"), "请假天数应被保存"); - assertEquals("年假", reloadedFlow.getContextVariables().get("reason"), "请假理由应被保存"); - assertEquals("系统自动通过", reloadedFlow.getContextVariables().get("systemComment"), "系统审核结果应被保存"); - } - - @Test - @DisplayName("流程变量应该在流程结束后仍然保持") - void shouldKeepVariablesAfterFlowCompleted() { - Flow flow = flowService.startFlow(leaveFlow.getId()); - - // 完成整个流程 - flow = flowService.approve(flow.getId()); - flow = flowService.approve(flow.getId()); - String flowId = flow.getId(); - - // 重新获取已完成的流程 - Flow completedFlow = flowService.getFlow(flowId); - - // 验证所有变量都被保存 - assertEquals(FlowStatus.APPROVED, completedFlow.getStatus(), "流程状态应为已通过"); - assertEquals(3, completedFlow.getContextVariables().get("days"), "请假天数应被保存"); - assertEquals("年假", completedFlow.getContextVariables().get("reason"), "请假理由应被保存"); - assertEquals("系统自动通过", completedFlow.getContextVariables().get("systemComment"), "系统审核结果应被保存"); - assertEquals("同意", completedFlow.getContextVariables().get("managerComment"), "经理审批结果应被保存"); - } - } -} \ No newline at end of file diff --git a/src/test/java/com/lanyuanxiaoyao/flowable/node/LeaveRequestNode.java b/src/test/java/com/lanyuanxiaoyao/flowable/node/LeaveRequestNode.java deleted file mode 100644 index 6ffadf1..0000000 --- a/src/test/java/com/lanyuanxiaoyao/flowable/node/LeaveRequestNode.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.lanyuanxiaoyao.flowable.node; - -import com.lanyuanxiaoyao.flowable.model.FlowContext; - -/** - * 请假申请节点 - * 测试用例中的示例节点,用于演示流程节点的实现 - */ -public class LeaveRequestNode extends AbstractFlowNode { - private final int days; - private final String reason; - - public LeaveRequestNode(int days, String reason) { - super("请假申请"); - this.days = days; - this.reason = reason; - } - - @Override - public void onApprove(FlowContext context) { - // 设置请假信息到上下文 - context.setVariable("days", days); - context.setVariable("reason", reason); - context.setVariable("submitTime", System.currentTimeMillis()); - } - - @Override - public void onReject(FlowContext context) { - context.setVariable("rejectReason", "申请已撤销"); - } -} \ No newline at end of file diff --git a/src/test/java/com/lanyuanxiaoyao/flowable/node/LeaveSystemCheckNode.java b/src/test/java/com/lanyuanxiaoyao/flowable/node/LeaveSystemCheckNode.java deleted file mode 100644 index 1a9515e..0000000 --- a/src/test/java/com/lanyuanxiaoyao/flowable/node/LeaveSystemCheckNode.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.lanyuanxiaoyao.flowable.node; - -import com.lanyuanxiaoyao.flowable.model.FlowContext; - -/** - * 系统自动审核节点 - * 测试用例中的示例节点,用于演示系统节点的实现 - */ -public class LeaveSystemCheckNode extends SystemFlowNode { - private final int maxDays; - - public LeaveSystemCheckNode(int maxDays) { - super("系统审核"); - this.maxDays = maxDays; - } - - @Override - public boolean autoApprove(FlowContext context) { - int days = (int) context.getVariables().get("days"); - return days <= maxDays; - } - - @Override - public void onApprove(FlowContext context) { - context.setVariable("systemComment", "系统自动通过"); - context.setVariable("systemCheckTime", System.currentTimeMillis()); - } - - @Override - protected String getRejectionReason(FlowContext context) { - int days = (int) context.getVariables().get("days"); - return String.format("请假天数(%d)超过系统限制(%d),需要额外审批", days, maxDays); - } -} \ No newline at end of file diff --git a/src/test/java/com/lanyuanxiaoyao/flowable/node/ManagerApprovalNode.java b/src/test/java/com/lanyuanxiaoyao/flowable/node/ManagerApprovalNode.java deleted file mode 100644 index 0a741b9..0000000 --- a/src/test/java/com/lanyuanxiaoyao/flowable/node/ManagerApprovalNode.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.lanyuanxiaoyao.flowable.node; - -import com.lanyuanxiaoyao.flowable.model.FlowContext; - -/** - * 经理审批节点 - * 测试用例中的示例节点,用于演示人工审批节点的实现 - */ -public class ManagerApprovalNode extends AbstractFlowNode { - public ManagerApprovalNode() { - super("经理审批"); - } - - @Override - public void onApprove(FlowContext context) { - context.setVariable("managerComment", "同意"); - context.setVariable("approveTime", System.currentTimeMillis()); - } - - @Override - public void onReject(FlowContext context) { - context.setVariable("managerComment", "不同意"); - context.setVariable("rejectTime", System.currentTimeMillis()); - } -} \ No newline at end of file