diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/example/FlowAssert.java b/src/main/java/com/lanyuanxiaoyao/flowable/example/FlowAssert.java new file mode 100644 index 0000000..878a199 --- /dev/null +++ b/src/main/java/com/lanyuanxiaoyao/flowable/example/FlowAssert.java @@ -0,0 +1,30 @@ +package com.lanyuanxiaoyao.flowable.example; + +import com.lanyuanxiaoyao.flowable.model.FlowInstance; +import com.lanyuanxiaoyao.flowable.model.FlowStatus; + +/** + * 流程断言工具类 + * 用于验证流程执行状态是否符合预期 + */ +public class FlowAssert { + + public static void assertEquals(String message, Object expected, Object actual) { + if (!expected.equals(actual)) { + throw new AssertionError(String.format("%s: 期望值=%s, 实际值=%s", message, expected, actual)); + } + } + + public static void assertStatus(String message, FlowStatus expectedStatus, FlowInstance instance) { + assertEquals(message + " - 状态检查", expectedStatus, instance.getStatus()); + } + + public static void assertNode(String message, String expectedNode, FlowInstance instance) { + assertEquals(message + " - 节点检查", expectedNode, instance.getCurrentNode()); + } + + public static void assertVariable(String message, Object expectedValue, String key, FlowInstance instance) { + Object actualValue = instance.getContextVariables().get(key); + assertEquals(message + " - ���量[" + key + "]检查", expectedValue, actualValue); + } +} \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/example/FlowExample.java b/src/main/java/com/lanyuanxiaoyao/flowable/example/FlowExample.java index 43bc4f7..f8fc473 100644 --- a/src/main/java/com/lanyuanxiaoyao/flowable/example/FlowExample.java +++ b/src/main/java/com/lanyuanxiaoyao/flowable/example/FlowExample.java @@ -3,6 +3,7 @@ package com.lanyuanxiaoyao.flowable.example; import com.lanyuanxiaoyao.flowable.example.node.LeaveRequestNode; import com.lanyuanxiaoyao.flowable.example.node.ManagerApprovalNode; import com.lanyuanxiaoyao.flowable.example.node.SimpleFlowNode; +import com.lanyuanxiaoyao.flowable.example.node.LeaveSystemCheckNode; import com.lanyuanxiaoyao.flowable.model.Flow; import com.lanyuanxiaoyao.flowable.model.FlowInstance; import com.lanyuanxiaoyao.flowable.repository.FlowInstanceRepository; @@ -10,6 +11,7 @@ import com.lanyuanxiaoyao.flowable.repository.FlowRepository; import com.lanyuanxiaoyao.flowable.repository.memory.MemoryFlowInstanceRepository; import com.lanyuanxiaoyao.flowable.repository.memory.MemoryFlowRepository; import com.lanyuanxiaoyao.flowable.service.FlowService; +import com.lanyuanxiaoyao.flowable.model.FlowStatus; public class FlowExample { public static void main(String[] args) { @@ -36,6 +38,9 @@ public class FlowExample { // 演示带有自定义节点操作的请假流程 demonstrateLeaveFlowWithCustomNodes(flowService); + + // 演示系统节点审批流程 + demonstrateSystemNodeFlow(flowService); } private static Flow createLeaveFlow(FlowService flowService) { @@ -155,4 +160,82 @@ public class FlowExample { instance = flowService.reject(instance.getId()); System.out.println("\n经理审批拒绝,流程状态:" + instance.getStatus()); } + + private static void demonstrateSystemNodeFlow(FlowService flowService) { + System.out.println("\n=== 开始系统节点审批流程演示 ==="); + + // 创建流程定义 + Flow flow = new Flow(); + flow.setName("带系统审核的请假流程"); + flow.setDescription("包含系统自动审核的请假流程示例"); + + // 添加节点 + flow.addNode(new LeaveRequestNode()); // 请假申请 + flow.addNode(new LeaveSystemCheckNode(5)); // 系统审核(允许5天内自动通过) + flow.addNode(new ManagerApprovalNode()); // 经理审批 + + Flow savedFlow = flowService.createFlow(flow); + System.out.println("创建流程:" + savedFlow.getName()); + + // 演示自动通过的情况(3天请假) + demonstrateSystemApproveFlow(flowService, savedFlow.getId()); + + // 演示自动拒绝的情况(7天请假) + demonstrateSystemRejectFlow(flowService, savedFlow.getId()); + } + + private static void demonstrateSystemApproveFlow(FlowService flowService, String flowId) { + System.out.println("\n=== 演示系统自动通过流程 ==="); + + // 启动流程实例(LeaveRequestNode设置3天请假) + FlowInstance instance = flowService.startFlow(flowId); + System.out.println("\n启动流程实例,当前节点:" + instance.getCurrentNode()); + + // 验证初始状态 + FlowAssert.assertStatus("流程启动", FlowStatus.PENDING, instance); + FlowAssert.assertNode("流程启动", "请假申请", instance); + FlowAssert.assertVariable("流程启动", 3, "days", instance); + FlowAssert.assertVariable("流程启动", "年假", "reason", instance); + + // 请假申请通过后会自动执行系统审核 + instance = flowService.approve(instance.getId()); + System.out.println("\n请假申请通过后,当前节点:" + instance.getCurrentNode()); + + // 验证系统审核通过 + FlowAssert.assertStatus("系统审核", FlowStatus.PENDING, instance); + FlowAssert.assertNode("系统审核", "经理审批", instance); + FlowAssert.assertVariable("系统审核", "系统自动通过", "systemComment", instance); + + // 经理最终审批 + instance = flowService.approve(instance.getId()); + System.out.println("\n经理审批通过,流程状态:" + instance.getStatus()); + + // 验证流程完成 + FlowAssert.assertStatus("流程完成", FlowStatus.APPROVED, instance); + FlowAssert.assertVariable("流程完成", "同意", "managerComment", instance); + } + + private static void demonstrateSystemRejectFlow(FlowService flowService, String flowId) { + System.out.println("\n=== 演示系统自动拒绝流程 ==="); + + // 修改LeaveRequestNode中的请假天数为7天 + LeaveRequestNode.setTestDays(7); + + // 启动流程实例 + FlowInstance instance = flowService.startFlow(flowId); + System.out.println("\n启动流程实例,当前节点:" + instance.getCurrentNode()); + + // 验证初始状态 + FlowAssert.assertStatus("流程启动", FlowStatus.PENDING, instance); + FlowAssert.assertNode("流程启动", "请假申请", instance); + FlowAssert.assertVariable("流程启动", 7, "days", instance); + + // 请假申请通过后系统会自动拒绝 + instance = flowService.approve(instance.getId()); + System.out.println("\n请假申请后,流程状态:" + instance.getStatus()); + + // 验证系统拒绝 + FlowAssert.assertStatus("系统拒绝", FlowStatus.REJECTED, instance); + FlowAssert.assertVariable("系统拒绝", "请假天数(7)超过系统限制(5),需要额外审批", "systemComment", instance); + } } \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/example/node/LeaveRequestNode.java b/src/main/java/com/lanyuanxiaoyao/flowable/example/node/LeaveRequestNode.java index dab4fd3..e37fa92 100644 --- a/src/main/java/com/lanyuanxiaoyao/flowable/example/node/LeaveRequestNode.java +++ b/src/main/java/com/lanyuanxiaoyao/flowable/example/node/LeaveRequestNode.java @@ -5,17 +5,20 @@ import com.lanyuanxiaoyao.flowable.model.FlowContext; import java.time.LocalDateTime; public class LeaveRequestNode extends AbstractFlowNode { + // 用于测试的请假天数 + private static int testDays = 3; + public LeaveRequestNode() { super("请假申请"); } @Override public void execute(FlowContext context) { - // 模拟设置请假信息 - context.setVariable("days", 3); + // 模拟设置请假信息,使用testDays而不是硬编码的值 + context.setVariable("days", testDays); context.setVariable("reason", "年假"); context.setVariable("requestTime", LocalDateTime.now()); - System.out.println("执行请假申请节点:设置请假天数为3天,请假理由为年假"); + System.out.println("执行请假申请节点:设置请假天数为" + testDays + "天,请假理由为年假"); } @Override @@ -27,4 +30,12 @@ public class LeaveRequestNode extends AbstractFlowNode { public void onReject(FlowContext context) { System.out.println("请假申请被撤销"); } + + /** + * 设置测试用的请假天数 + * @param days 请假天数 + */ + public static void setTestDays(int days) { + testDays = days; + } } \ No newline at end of file diff --git a/src/main/java/com/lanyuanxiaoyao/flowable/example/node/LeaveSystemCheckNode.java b/src/main/java/com/lanyuanxiaoyao/flowable/example/node/LeaveSystemCheckNode.java new file mode 100644 index 0000000..8bf1253 --- /dev/null +++ b/src/main/java/com/lanyuanxiaoyao/flowable/example/node/LeaveSystemCheckNode.java @@ -0,0 +1,61 @@ +package com.lanyuanxiaoyao.flowable.example.node; + +import com.lanyuanxiaoyao.flowable.node.SystemFlowNode; +import com.lanyuanxiaoyao.flowable.model.FlowContext; +import java.time.LocalDateTime; + +/** + * 请假系统自动审核节点 + * 根据请假天数自动判断是否通过 + */ +public class LeaveSystemCheckNode extends SystemFlowNode { + private final int maxDays; // 最大允许请假天数 + + public LeaveSystemCheckNode(int maxDays) { + super("系统审核"); + this.maxDays = maxDays; + } + + @Override + public boolean autoApprove(FlowContext context) { + Integer days = context.getVariable("days", Integer.class); + return days != null && days <= maxDays; + } + + @Override + protected String getRejectionReason(FlowContext context) { + Integer days = context.getVariable("days", Integer.class); + return String.format("请假天数(%d)超过系统限制(%d),需要额外审批", days, maxDays); + } + + @Override + public void onApprove(FlowContext context) { + Integer days = context.getVariable("days", Integer.class); + String reason = context.getVariable("reason", String.class); + + System.out.println("执行系统审核节点:"); + System.out.println("- 请假天数:" + days); + System.out.println("- 请假理由:" + reason); + System.out.println("- 系统限制:" + maxDays + "天"); + + context.setVariable("systemComment", "系统自动通过"); + context.setVariable("systemCheckTime", LocalDateTime.now()); + System.out.println("系统自动审核通过"); + } + + @Override + public void onReject(FlowContext context) { + Integer days = context.getVariable("days", Integer.class); + String reason = context.getVariable("reason", String.class); + + System.out.println("执行系统审核节点:"); + System.out.println("- 请假天数:" + days); + System.out.println("- 请假理由:" + reason); + System.out.println("- 系统限制:" + maxDays + "天"); + + String rejectionReason = getRejectionReason(context); + context.setVariable("systemComment", rejectionReason); + context.setVariable("systemCheckTime", LocalDateTime.now()); + System.out.println("系统自动审核拒绝:" + rejectionReason); + } +} \ 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 new file mode 100644 index 0000000..b42e30f --- /dev/null +++ b/src/main/java/com/lanyuanxiaoyao/flowable/node/SystemFlowNode.java @@ -0,0 +1,27 @@ +package com.lanyuanxiaoyao.flowable.node; + +import com.lanyuanxiaoyao.flowable.model.FlowContext; + +/** + * 系统自动审批节点 + * 根据预设规则自动执行审批操作 + */ +public abstract class SystemFlowNode extends AbstractFlowNode { + public 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); +} \ 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 index 8d4bad7..c094656 100644 --- a/src/main/java/com/lanyuanxiaoyao/flowable/service/FlowService.java +++ b/src/main/java/com/lanyuanxiaoyao/flowable/service/FlowService.java @@ -10,6 +10,7 @@ import lombok.RequiredArgsConstructor; import com.lanyuanxiaoyao.flowable.model.FlowStatus; import com.lanyuanxiaoyao.flowable.model.FlowContext; import com.lanyuanxiaoyao.flowable.node.FlowNode; +import com.lanyuanxiaoyao.flowable.node.SystemFlowNode; @RequiredArgsConstructor public class FlowService { @@ -51,7 +52,13 @@ public class FlowService { FlowContext context = createContext(instance); // 执行第一个节点 - flow.getNodes().get(0).execute(context); + FlowNode firstNode = flow.getNodes().get(0); + firstNode.execute(context); + + // 如果是系统节点,自动执行审批 + if (firstNode instanceof SystemFlowNode) { + instance = handleSystemNode(instance, firstNode, context); + } // 保存上下文变量 saveContext(instance, context); @@ -95,6 +102,11 @@ public class FlowService { FlowNode nextNode = nodes.get(currentIndex + 1); instance.setCurrentNode(nextNode.getNodeId()); nextNode.execute(context); + + // 如果下一个是系统节点,自动执行审批 + if (nextNode instanceof SystemFlowNode) { + instance = handleSystemNode(instance, nextNode, context); + } } // 保存上下文变量 @@ -175,4 +187,50 @@ public class FlowService { instance.getContextVariables().clear(); instance.getContextVariables().putAll(context.getVariables()); } + + /** + * 处理系统节点的自动审批 + */ + private FlowInstance handleSystemNode(FlowInstance instance, FlowNode node, FlowContext context) { + SystemFlowNode systemNode = (SystemFlowNode) node; + if (systemNode.autoApprove(context)) { + // 自动通过 + systemNode.onApprove(context); + // 如果��最后一个节点,标记为完成 + if (isLastNode(instance.getFlowId(), node.getNodeId())) { + instance.setStatus(FlowStatus.APPROVED); + } else { + // 否则移动到下一个节点 + FlowNode nextNode = getNextNode(instance.getFlowId(), node.getNodeId()); + instance.setCurrentNode(nextNode.getNodeId()); + nextNode.execute(context); + // 如果下一个还是系统节点,继续自动处理 + if (nextNode instanceof SystemFlowNode) { + instance = handleSystemNode(instance, nextNode, context); + } + } + } else { + // 自动拒绝 + systemNode.onReject(context); + instance.setStatus(FlowStatus.REJECTED); + } + return instance; + } + + private boolean isLastNode(String flowId, String nodeId) { + Flow flow = flowRepository.findById(flowId); + List nodes = flow.getNodes(); + return nodes.get(nodes.size() - 1).getNodeId().equals(nodeId); + } + + private FlowNode getNextNode(String flowId, String currentNodeId) { + Flow flow = flowRepository.findById(flowId); + List nodes = flow.getNodes(); + for (int i = 0; i < nodes.size() - 1; i++) { + if (nodes.get(i).getNodeId().equals(currentNodeId)) { + return nodes.get(i + 1); + } + } + throw new IllegalStateException("找不到下一个节点"); + } } \ No newline at end of file