public void handleOrder(Order order) { // 第一步:判断订单类型 if (order.getType() == OrderType.NORMAL) { // 普通订单判断支付方式 if (order.getPayType() == PayType.WECHAT) { // 微信支付判断是否满减 if (order.getAmount() >= 100) { // 满减后判断是否老用户 if (order.getUser().isOldUser()) { order.setDiscount(0.9); } else { order.setDiscount(1.0); } wechatPayService.pay(order); } else { if (order.getUser().isOldUser()) { order.setDiscount(0.95); } wechatPayService.pay(order); } } else if (order.getPayType() == PayType.ALIPAY) { // 支付宝支付又一套判断... if (order.getAmount() >= 200) { // 此处省略800字嵌套... } } } else if (order.getType() == OrderType.GROUP) { // 团购订单再来一套独立的if-else... } // 后面还有库存判断、日志记录、消息推送的嵌套... }改完这个逻辑,我眼睛都快成斗鸡眼了,测试的时候还漏了个 “新用户用支付宝支付满 150 减 20” 的场景,结果线上出了 bug,当晚加班到凌晨两点 —— 这 if-else,简直就是开发者的 “职场 PUA 神器”!后来我痛定思痛,把这套逻辑用流程编排重构了一遍,现在不管加什么新规则,都是 “插拔式” 操作,再也不用在嵌套里找不着北。今天就跟大家好好唠唠,怎么用流程编排跟 if-else 彻底说再见。
public void handleOrder(Order order) { getOrderInfo(order); if (order.getType() == OrderType.NORMAL) { checkNormalPay(order); calculateNormalDiscount(order); } else if (order.getType() == OrderType.GROUP) { checkGroupPay(order); calculateGroupDiscount(order); } deductStock(order); logOrder(order); }而流程编排是 “把步骤的执行顺序也交给框架管理”,你不用写 if-else 调用,只需要告诉框架 “第一步执行 A,第二步执行 B,第三步根据条件选 C 或 D”,剩下的事儿框架全帮你干了。就像你不用亲自指挥后厨的每个师傅,只需要告诉餐厅 “我要一份番茄炒蛋”,餐厅的流程体系会自动让采购买番茄鸡蛋、切配师傅切菜、掌勺师傅炒菜 —— 这才是流程编排的核心:把 “指挥逻辑” 和 “执行逻辑” 彻底分开。
// 订单流程节点接口 public interface OrderProcessNode { // 处理订单流程,返回是否继续执行下一个节点 boolean process(Order order); }返回boolean是为了控制流程:返回true表示继续执行下一个节点,返回false表示终止流程(比如支付验证失败,就不用执行后面的库存扣减了)。
// 获取订单基础信息 public class OrderInfoNode implements OrderProcessNode { @Override public boolean process(Order order) { System.out.println("第一步:获取订单基础信息"); // 模拟从数据库获取订单详情 order.setProductName("iPhone 15"); order.setAmount(5999.0); return true; // 继续执行下一个节点 } }② 订单类型判断节点(策略模式在这里用)因为不同订单类型的处理逻辑不同,这里用策略模式,先定义订单类型处理器接口:
// 订单类型处理器接口(策略接口) public interface OrderTypeHandler { void handle(Order order); } // 普通订单处理器(具体策略) public class NormalOrderHandler implements OrderTypeHandler { @Override public void handle(Order order) { System.out.println("处理普通订单逻辑"); order.setTypeDesc("普通订单,支持微信/支付宝支付"); } // 堆代码 duidaima.com // 团购订单处理器(具体策略) public class GroupOrderHandler implements OrderTypeHandler { @Override public void handle(Order order) { System.out.println("处理团购订单逻辑"); order.setTypeDesc("团购订单,需满3人成团"); } }然后实现订单类型判断节点,根据订单类型选择对应的处理器:
// 订单类型判断节点 public class OrderTypeNode implements OrderProcessNode { // 策略工厂,根据订单类型获取对应的处理器 private Map<OrderType, OrderTypeHandler> typeHandlerMap; // 构造方法初始化策略工厂 public OrderTypeNode() { typeHandlerMap = new HashMap<>(); typeHandlerMap.put(OrderType.NORMAL, new NormalOrderHandler()); typeHandlerMap.put(OrderType.GROUP, new GroupOrderHandler()); } @Override public boolean process(Order order) { System.out.println("第二步:判断订单类型"); // 根据订单类型获取处理器,执行对应逻辑 OrderTypeHandler handler = typeHandlerMap.get(order.getType()); if (handler == null) { System.out.println("未知订单类型,终止流程"); return false; // 终止流程 } handler.handle(order); return true; // 继续执行下一个节点 } }③ 支付验证节点
// 支付验证节点 public class PayCheckNode implements OrderProcessNode { @Override public boolean process(Order order) { System.out.println("第三步:验证支付方式"); if (order.getPayType() == PayType.WECHAT || order.getPayType() == PayType.ALIPAY) { System.out.println("支付方式合法:" + order.getPayType()); return true; } else { System.out.println("不支持的支付方式:" + order.getPayType() + ",终止流程"); return false; } } }④ 折扣计算节点
// 折扣计算节点 public class DiscountCalculateNode implements OrderProcessNode { @Override public boolean process(Order order) { System.out.println("第四步:计算订单折扣"); double discount = 1.0; // 老用户折扣 if (order.getUser().isOldUser()) { discount -= 0.1; // 老用户9折 System.out.println("老用户享受9折优惠"); } // 满减折扣 if (order.getAmount() >= 5000) { discount -= 0.05; // 满5000再减5% System.out.println("满5000元再减5%"); } // 防止折扣低于0.5 order.setDiscount(Math.max(discount, 0.5)); System.out.println("最终折扣:" + order.getDiscount()); return true; } }⑤ 库存扣减节点
// 库存扣减节点 publicclass StockDeductNode implements OrderProcessNode { private StockService stockService = new StockService(); // 模拟库存服务 @Override public boolean process(Order order) { System.out.println("第五步:扣减订单库存"); boolean deductSuccess = stockService.deductStock(order.getProductId(), order.getQuantity()); if (deductSuccess) { System.out.println("库存扣减成功"); returntrue; } else { System.out.println("库存不足,终止流程"); returnfalse; } } }⑥ 日志记录节点
// 日志记录节点 public class OrderLogNode implements OrderProcessNode { private LogService logService = new LogService(); // 模拟日志服务 @Override public boolean process(Order order) { System.out.println("第六步:记录订单日志"); logService.recordLog("订单" + order.getOrderId() + "处理完成,最终金额:" + order.getAmount() * order.getDiscount()); return true; } }3. 第三步:构建责任链,串联所有节点
// 订单流程管理器(责任链) publicclassOrderProcessChain { // 用链表存储所有节点,保证执行顺序 private LinkedList<OrderProcessNode> nodeList = new LinkedList<>(); // 添加节点到链尾 public void addNode(OrderProcessNode node) { nodeList.add(node); } // 执行流程:依次调用每个节点的process方法 public void execute(Order order) { for (OrderProcessNode node : nodeList) { boolean continueNext = node.process(order); if (!continueNext) { System.out.println("流程在节点[" + node.getClass().getSimpleName() + "]终止"); return; } } System.out.println("所有流程节点执行完成!"); } }4. 第四步:测试流程执行效果
public classOrderProcessTest { public static void main(String[] args) { // 1. 创建订单对象 Order order = new Order(); order.setOrderId("ORDER_20250826_001"); order.setType(OrderType.NORMAL); // 普通订单 order.setPayType(PayType.WECHAT); // 微信支付 order.setProductId("PROD_001"); order.setQuantity(1); User user = new User(); user.setOldUser(true); // 老用户 order.setUser(user); // 2. 构建流程链,按顺序添加节点 OrderProcessChain chain = new OrderProcessChain(); chain.addNode(new OrderInfoNode()); chain.addNode(new OrderTypeNode()); chain.addNode(new PayCheckNode()); chain.addNode(new DiscountCalculateNode()); chain.addNode(new StockDeductNode()); chain.addNode(new OrderLogNode()); // 3. 执行流程 System.out.println("开始处理订单:" + order.getOrderId()); chain.execute(order); } }运行结果如下:
// 新用户支付宝首单折扣节点 publicclass NewUserAlipayDiscountNode implements OrderProcessNode { @Override public boolean process(Order order) { System.out.println("新增步骤:新用户支付宝首单折扣"); if (!order.getUser().isOldUser() && order.getPayType() == PayType.ALIPAY && order.isFirstOrder()) { order.setAmount(order.getAmount() - 200); System.out.println("新用户首单支付宝支付,立减200元,优惠后金额:" + order.getAmount()); } returntrue; } } // 构建流程链时添加这个节点 chain.addNode(new NewUserAlipayDiscountNode()); // 加在折扣计算节点前面完全不用动原来的任何代码,这就是 “插拔式” 开发的爽快感!
public void changeOrderStatus(Order order, String event) { if (order.getStatus() == OrderStatus.WAIT_PAY) { if ("PAY".equals(event)) { order.setStatus(OrderStatus.PAID); } elseif ("CANCEL".equals(event)) { order.setStatus(OrderStatus.CANCELED); } } elseif (order.getStatus() == OrderStatus.PAID) { if ("CONFIRM".equals(event)) { order.setStatus(OrderStatus.WAIT_SHIP); } elseif ("REFUND".equals(event)) { order.setStatus(OrderStatus.CANCELED); } } elseif (order.getStatus() == OrderStatus.WAIT_SHIP) { if ("SHIP".equals(event)) { order.setStatus(OrderStatus.SHIPPED); } } // 还有更多状态判断... }用 Spring StateMachine 重构后,代码会清爽很多。
<dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-core</artifactId> <version>3.2.0</version> </dependency>2. 第二步:定义订单状态和事件
// 订单状态枚举 publicenum OrderStatus { WAIT_PAY("待支付"), PAID("已支付"), WAIT_SHIP("待发货"), SHIPPED("已发货"), COMPLETED("已完成"), CANCELED("已取消"); private final String desc; OrderStatus(String desc) { this.desc = desc; } } // 订单事件枚举(触发状态变化的动作) publicenum OrderEvent { PAY("用户付款"), CANCEL("用户取消"), CONFIRM("商家确认收款"), REFUND("退款"), SHIP("商家发货"), CONFIRM_RECEIVE("用户确认收货"); private final String desc; OrderEvent(String desc) { this.desc = desc; } }3. 第三步:配置状态机
@Configuration @EnableStateMachineFactory// 启用状态机工厂,方便创建多个状态机实例 publicclass OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderEvent> { // 配置状态机的初始状态和所有状态 @Override public void configure(StateMachineStateConfigurer<OrderStatus, OrderEvent> states) throws Exception { states .withStates() .initial(OrderStatus.WAIT_PAY) // 初始状态:待支付 .states(EnumSet.allOf(OrderStatus.class)); // 所有状态 } // 配置状态流转规则(核心) @Override public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvent> transitions) throws Exception { transitions // 1. 待支付 → 已支付:触发事件PAY .withExternal() .source(OrderStatus.WAIT_PAY) .target(OrderStatus.PAID) .event(OrderEvent.PAY) .and() // 2. 待支付 → 已取消:触发事件CANCEL .withExternal() .source(OrderStatus.WAIT_PAY) .target(OrderStatus.CANCELED) .event(OrderEvent.CANCEL) .and() // 3. 已支付 → 待发货:触发事件CONFIRM .withExternal() .source(OrderStatus.PAID) .target(OrderStatus.WAIT_SHIP) .event(OrderEvent.CONFIRM) .and() // 4. 已支付 → 已取消:触发事件REFUND .withExternal() .source(OrderStatus.PAID) .target(OrderStatus.CANCELED) .event(OrderEvent.REFUND) .and() // 5. 待发货 → 已发货:触发事件SHIP .withExternal() .source(OrderStatus.WAIT_SHIP) .target(OrderStatus.SHIPPED) .event(OrderEvent.SHIP) .and() // 6. 已发货 → 已完成:触发事件CONFIRM_RECEIVE .withExternal() .source(OrderStatus.SHIPPED) .target(OrderStatus.COMPLETED) .event(OrderEvent.CONFIRM_RECEIVE); } // 配置状态机监听器,监听状态变化事件 @Bean public StateMachineListener<OrderStatus, OrderEvent> orderStateListener() { returnnew StateMachineListenerAdapter<OrderStatus, OrderEvent>() { @Override public void stateChanged(State<OrderStatus, OrderEvent> from, State<OrderStatus, OrderEvent> to) { System.out.println("订单状态变化:" + (from == null ? "初始状态" : from.getId().getDesc()) + " → " + to.getId().getDesc()); } @Override public void eventNotAccepted(Message<OrderEvent> event) { System.out.println("不支持的事件:" + event.getPayload().getDesc() + ",当前订单状态可能不允许此操作"); } }; } // 注册监听器 @Override public void configure(StateMachineConfigurationConfigurer<OrderStatus, OrderEvent> config) throws Exception { config .withConfiguration() .listener(orderStateListener()); } }4. 第四步:创建订单状态服务
@Service publicclassOrderStateService { @Autowired private StateMachineFactory<OrderStatus, OrderEvent> stateMachineFactory; // 线程安全:每个订单用一个独立的状态机实例 private Map<String, StateMachine<OrderStatus, OrderEvent>> orderStateMachineMap = new ConcurrentHashMap<>(); // 初始化订单状态机(订单创建时调用) public void initOrderStateMachine(String orderId) { StateMachine<OrderStatus, OrderEvent> stateMachine = stateMachineFactory.getStateMachine(); // 设置订单ID作为状态机的ID,方便后续关联 stateMachine.getExtendedState().getVariables().put("orderId", orderId); orderStateMachineMap.put(orderId, stateMachine); System.out.println("订单[" + orderId + "]状态机初始化完成,初始状态:" + stateMachine.getState().getId().getDesc()); } // 触发订单状态变化(核心方法) public boolean sendEvent(String orderId, OrderEvent event) { StateMachine<OrderStatus, OrderEvent> stateMachine = orderStateMachineMap.get(orderId); if (stateMachine == null) { System.out.println("订单[" + orderId + "]状态机未初始化"); returnfalse; } // 发送事件,触发状态变化 return stateMachine.sendEvent(event); } // 获取订单当前状态 public OrderStatus getCurrentState(String orderId) { StateMachine<OrderStatus, OrderEvent> stateMachine = orderStateMachineMap.get(orderId); if (stateMachine == null) { returnnull; } return stateMachine.getState().getId(); } }5. 第五步:测试状态流转
@SpringBootTest publicclassOrderStateMachineTest { @Autowired private OrderStateService orderStateService; @Test public void testOrderStateFlow() { String orderId = "ORDER_20250826_002"; // 1. 初始化订单状态机 orderStateService.initOrderStateMachine(orderId); // 2. 触发"用户付款"事件:待支付→已支付 boolean paySuccess = orderStateService.sendEvent(orderId, OrderEvent.PAY); System.out.println("触发用户付款事件:" + (paySuccess ? "成功" : "失败")); System.out.println("当前订单状态:" + orderStateService.getCurrentState(orderId).getDesc() + "\n"); // 3. 触发"商家确认收款"事件:已支付→待发货 boolean confirmSuccess = orderStateService.sendEvent(orderId, OrderEvent.CONFIRM); System.out.println("触发商家确认收款事件:" + (confirmSuccess ? "成功" : "失败")); System.out.println("当前订单状态:" + orderStateService.getCurrentState(orderId).getDesc() + "\n"); // 4. 触发"商家发货"事件:待发货→已发货 boolean shipSuccess = orderStateService.sendEvent(orderId, OrderEvent.SHIP); System.out.println("触发商家发货事件:" + (shipSuccess ? "成功" : "失败")); System.out.println("当前订单状态:" + orderStateService.getCurrentState(orderId).getDesc() + "\n"); // 5. 触发"用户确认收货"事件:已发货→已完成 boolean confirmReceiveSuccess = orderStateService.sendEvent(orderId, OrderEvent.CONFIRM_RECEIVE); System.out.println("触发用户确认收货事件:" + (confirmReceiveSuccess ? "成功" : "失败")); System.out.println("当前订单状态:" + orderStateService.getCurrentState(orderId).getDesc() + "\n"); // 6. 尝试触发"退款"事件(已完成状态不支持退款,会失败) boolean refundSuccess = orderStateService.sendEvent(orderId, OrderEvent.REFUND); System.out.println("触发退款事件:" + (refundSuccess ? "成功" : "失败")); System.out.println("当前订单状态:" + orderStateService.getCurrentState(orderId).getDesc()); } }运行结果如下:
当前订单状态:已完成
.withExternal() .source(OrderStatus.SHIPPED) .target(OrderStatus.CANCELED) .event(OrderEvent.REJECT_REFUND); // 新增“拒收退款”事件是不是比改 if-else 舒服多了?
<dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter</artifactId> <version>7.0.0.M2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>这里用 H2 内存数据库,方便测试。
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100501/BPMN20.xsd" xmlns:flowable="http://flowable.org/bpmn" id="Definitions_1" targetNamespace="http://flowable.org/bpmn"> <process id="afterSalesRefundProcess" name="电商售后退款流程" isExecutable="true"> <!-- 开始事件:用户提交退款申请 --> <startEvent id="startEvent1" name="用户提交退款申请"> <extensionElements> <!-- 定义流程变量:订单ID、退款金额、退款原因、用户ID --> <flowable:formProperty id="orderId" name="订单ID" type="string" required="true"/> <flowable:formProperty id="refundAmount" name="退款金额" type="double" required="true"/> <flowable:formProperty id="refundReason" name="退款原因" type="string" required="true"/> <flowable:formProperty id="userId" name="用户ID" type="string" required="true"/> </extensionElements> </startEvent> <!-- 服务任务:系统验证退款金额 --> <serviceTask id="validateRefundAmountTask" name="验证退款金额" flowable:delegateExpression="${validateRefundAmountDelegate}"/> <!-- 排他网关:验证结果判断 --> <exclusiveGateway id="validateGateway" name="验证结果"/> <sequenceFlow id="flow1" sourceRef="startEvent1" targetRef="validateRefundAmountTask"/> <sequenceFlow id="flow2" sourceRef="validateRefundAmountTask" targetRef="validateGateway"/> <!-- 服务任务:驳回用户申请 --> <serviceTask id="rejectTask" name="驳回用户申请" flowable:delegateExpression="${rejectRefundDelegate}"/> <!-- 结束事件:流程结束 --> <endEvent id="endEvent1" name="流程结束"/> <sequenceFlow id="flow3" sourceRef="rejectTask" targetRef="endEvent1"/> <!-- 用户任务:商家审核 --> <userTask id="merchantAuditTask" name="商家审核" flowable:assignee="merchant"> <extensionElements> <!-- 商家审核结果:通过/不通过 --> <flowable:formProperty id="merchantAuditResult" name="审核结果" type="enum" required="true"> <flowable:value id="pass" name="通过"/> <flowable:value id="reject" name="不通过"/> </flowable:formProperty> <flowable:formProperty id="merchantAuditComment" name="审核意见" type="string"/> </extensionElements> </userTask> <!-- 排他网关:商家审核结果判断 --> <exclusiveGateway id="merchantAuditGateway" name="商家审核结果"/> <sequenceFlow id="flow4" sourceRef="validateGateway" targetRef="merchantAuditTask"> <!-- 条件:验证通过 --> <conditionExpression xsi:type="tFormalExpression">#{validateResult == 'pass'}</conditionExpression> </sequenceFlow> <sequenceFlow id="flow5" sourceRef="validateGateway" targetRef="rejectTask"> <!-- 条件:验证失败 --> <conditionExpression xsi:type="tFormalExpression">#{validateResult == 'reject'}</conditionExpression> </sequenceFlow> <!-- 用户任务:财务审核 --> <userTask id="financeAuditTask" name="财务审核" flowable:assignee="finance"> <extensionElements> <!-- 财务审核结果:通过/不通过 --> <flowable:formProperty id="financeAuditResult" name="审核结果" type="enum" required="true"> <flowable:value id="pass" name="通过"/> <flowable:value id="reject" name="不通过"/> </flowable:formProperty> <flowable:formProperty id="financeAuditComment" name="审核意见" type="string"/> </extensionElements> </userTask> <sequenceFlow id="flow6" sourceRef="merchantAuditTask" targetRef="merchantAuditGateway"/> <sequenceFlow id="flow7" sourceRef="merchantAuditGateway" targetRef="financeAuditTask"> <!-- 条件:商家审核通过 --> <conditionExpression xsi:type="tFormalExpression">#{merchantAuditResult == 'pass'}</conditionExpression> </sequenceFlow> <sequenceFlow id="flow8" sourceRef="merchantAuditGateway" targetRef="rejectTask"> <!-- 条件:商家审核不通过 --> <conditionExpression xsi:type="tFormalExpression">#{merchantAuditResult == 'reject'}</conditionExpression> </sequenceFlow> <!-- 排他网关:财务审核结果判断 --> <exclusiveGateway id="financeAuditGateway" name="财务审核结果"/> <!-- 服务任务:执行退款 --> <serviceTask id="executeRefundTask" name="执行退款" flowable:delegateExpression="${executeRefundDelegate}"/> <!-- 服务任务:发送退款通知 --> <serviceTask id="sendNotifyTask" name="发送退款通知" flowable:delegateExpression="${sendRefundNotifyDelegate}"/> <sequenceFlow id="flow9" sourceRef="financeAuditTask" targetRef="financeAuditGateway"/> <sequenceFlow id="flow10" sourceRef="financeAuditGateway" targetRef="executeRefundTask"> <!-- 条件:财务审核通过 --> <conditionExpression xsi:type="tFormalExpression">#{financeAuditResult == 'pass'}</conditionExpression> </sequenceFlow> <sequenceFlow id="flow11" sourceRef="financeAuditGateway" targetRef="rejectTask"> <!-- 条件:财务审核不通过 --> <conditionExpression xsi:type="tFormalExpression">#{financeAuditResult == 'reject'}</conditionExpression> </sequenceFlow> <sequenceFlow id="flow12" sourceRef="executeRefundTask" targetRef="sendNotifyTask"/> <sequenceFlow id="flow13" sourceRef="sendNotifyTask" targetRef="endEvent1"/> </process> </definitions>3. 第三步:实现流程任务的业务逻辑
@Component("validateRefundAmountDelegate") publicclass ValidateRefundAmountDelegate implements JavaDelegate { // 模拟订单服务,获取订单金额 @Autowired private OrderService orderService; @Override public void execute(DelegateExecution execution) { System.out.println("开始验证退款金额"); // 获取流程变量 String orderId = (String) execution.getVariable("orderId"); Double refundAmount = (Double) execution.getVariable("refundAmount"); // 从订单服务获取订单金额 Double orderAmount = orderService.getOrderAmount(orderId); System.out.println("订单[" + orderId + "]金额:" + orderAmount + ",申请退款金额:" + refundAmount); // 验证逻辑:退款金额≤订单金额 if (refundAmount <= orderAmount && refundAmount > 0) { execution.setVariable("validateResult", "pass"); System.out.println("退款金额验证通过"); } else { execution.setVariable("validateResult", "reject"); execution.setVariable("rejectReason", "退款金额不合法(需大于0且≤订单金额)"); System.out.println("退款金额验证失败:" + execution.getVariable("rejectReason")); } } }② 驳回退款 Delegate
@Component("rejectRefundDelegate") publicclass RejectRefundDelegate implements JavaDelegate { @Autowired private RefundService refundService; @Override publicvoid execute(DelegateExecution execution) { System.out.println("开始处理驳回退款申请"); String orderId = (String) execution.getVariable("orderId"); String rejectReason = (String) execution.getVariable("rejectReason"); // 更新退款单状态为“已驳回” refundService.updateRefundStatus(orderId, RefundStatus.REJECTED, rejectReason); System.out.println("订单[" + orderId + "]退款申请已驳回,原因:" + rejectReason); } }③ 执行退款 Delegate
@Component("executeRefundDelegate") publicclass ExecuteRefundDelegate implements JavaDelegate { @Autowired private RefundService refundService; @Override public void execute(DelegateExecution execution) { System.out.println("开始执行退款操作"); String orderId = (String) execution.getVariable("orderId"); Double refundAmount = (Double) execution.getVariable("refundAmount"); // 调用支付网关执行退款 boolean refundSuccess = refundService.executeRefund(orderId, refundAmount); if (refundSuccess) { // 更新退款单状态为“已退款” refundService.updateRefundStatus(orderId, RefundStatus.REFUNDED, "退款成功"); execution.setVariable("refundResult", "success"); System.out.println("订单[" + orderId + "]退款执行成功,金额:" + refundAmount); } else { // 更新退款单状态为“退款失败” refundService.updateRefundStatus(orderId, RefundStatus.REFUND_FAILED, "支付网关退款失败"); execution.setVariable("refundResult", "fail"); System.out.println("订单[" + orderId + "]退款执行失败"); } } }④ 发送退款通知 Delegate
@Component("sendRefundNotifyDelegate") publicclass SendRefundNotifyDelegate implements JavaDelegate { @Autowired private SmsService smsService; @Override publicvoid execute(DelegateExecution execution) { System.out.println("开始发送退款通知"); String userId = (String) execution.getVariable("userId"); String orderId = (String) execution.getVariable("orderId"); Double refundAmount = (Double) execution.getVariable("refundAmount"); // 获取用户手机号(模拟) String phone = smsService.getUserPhone(userId); // 发送短信通知 String content = "【电商平台】您的订单" + orderId + "已成功退款" + refundAmount + "元,请注意查收。"; smsService.sendSms(phone, content); System.out.println("已向用户[" + userId + "]的手机号[" + phone + "]发送退款通知:" + content); } }4. 第四步:写接口测试流程
@RestController @RequestMapping("/refund") publicclass RefundController { @Autowired private RuntimeService runtimeService; // Flowable的运行时服务,用于启动流程 @Autowired private TaskService taskService; // Flowable的任务服务,用于处理用户任务(审核) // 1. 用户提交退款申请(启动流程) @PostMapping("/apply") publicString applyRefund(@RequestBody RefundApplyDTO applyDTO) { // 设置流程变量 Map<String, Object> variables = new HashMap<>(); variables.put("orderId", applyDTO.getOrderId()); variables.put("refundAmount", applyDTO.getRefundAmount()); variables.put("refundReason", applyDTO.getRefundReason()); variables.put("userId", applyDTO.getUserId()); // 启动流程实例 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("afterSalesRefundProcess", variables); System.out.println("退款流程已启动,流程实例ID:" + processInstance.getId()); return"退款申请提交成功,流程实例ID:" + processInstance.getId(); } // 2. 商家审核退款申请 @PostMapping("/merchant/audit") publicString merchantAudit(@RequestBody MerchantAuditDTO auditDTO) { // 根据流程实例ID和任务负责人(商家)查询任务 Task task = taskService.createTaskQuery() .processInstanceId(auditDTO.getProcessInstanceId()) .taskAssignee("merchant") // 商家的用户ID .singleResult(); if (task == null) { return"未找到待审核的任务"; } // 设置审核结果变量 Map<String, Object> variables = new HashMap<>(); variables.put("merchantAuditResult", auditDTO.getAuditResult()); // pass/reject variables.put("merchantAuditComment", auditDTO.getAuditComment()); // 如果审核不通过,设置驳回原因 if ("reject".equals(auditDTO.getAuditResult())) { variables.put("rejectReason", "商家审核不通过:" + auditDTO.getAuditComment()); } // 完成任务(提交审核结果) taskService.complete(task.getId(), variables); return"商家审核已提交,结果:" + auditDTO.getAuditResult(); } // 3. 财务审核退款申请 @PostMapping("/finance/audit") publicString financeAudit(@RequestBody FinanceAuditDTO auditDTO) { // 根据流程实例ID和任务负责人(财务)查询任务 Task task = taskService.createTaskQuery() .processInstanceId(auditDTO.getProcessInstanceId()) .taskAssignee("finance") // 财务的用户ID .singleResult(); if (task == null) { return"未找到待审核的任务"; } // 设置审核结果变量 Map<String, Object> variables = new HashMap<>(); variables.put("financeAuditResult", auditDTO.getAuditResult()); // pass/reject variables.put("financeAuditComment", auditDTO.getAuditComment()); // 如果审核不通过,设置驳回原因 if ("reject".equals(auditDTO.getAuditResult())) { variables.put("rejectReason", "财务审核不通过:" + auditDTO.getAuditComment()); } // 完成任务(提交审核结果) taskService.complete(task.getId(), variables); return"财务审核已提交,结果:" + auditDTO.getAuditResult(); } // 4. 查询流程状态 @GetMapping("/status/{processInstanceId}") publicString getProcessStatus(@PathVariableString processInstanceId) { ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processInstanceId(processInstanceId) .singleResult(); if (processInstance == null) { // 流程已结束,查询历史状态 HistoricProcessInstance historicProcessInstance = runtimeService.createHistoricProcessInstanceQuery() .processInstanceId(processInstanceId) .singleResult(); return"流程已结束,结束时间:" + historicProcessInstance.getEndTime() + ",状态:" + historicProcessInstance.getState(); } else { // 流程未结束,查询当前任务 Task currentTask = taskService.createTaskQuery() .processInstanceId(processInstanceId) .singleResult(); String currentTaskName = currentTask != null ? currentTask.getName() : "无当前任务"; return"流程运行中,当前状态:" + processInstance.getState() + ",当前任务:" + currentTaskName; } } }5. 第五步:测试流程完整运行
{ "orderId": "ORDER_20250826_003", "refundAmount": 100.0, "refundReason": "商品质量问题", "userId": "USER_001" }响应:退款申请提交成功,流程实例ID:12501
{ "processInstanceId": "12501", "auditResult": "pass", "auditComment": "同意退款" }响应:商家审核已提交,结果:pass
{ "processInstanceId": "12501", "auditResult": "pass", "auditComment": "同意退款,已安排打款" }响应:财务审核已提交,结果:pass
public double getDiscount(User user) { if (user.getLevel() == Level.VIP1) return 0.95; else if (user.getLevel() == Level.VIP2) return 0.9; else return 1.0; }结果非要用责任链模式,拆成Vip1DiscountNode、Vip2DiscountNode、NormalUserDiscountNode,还建个流程链 —— 这就是 “为了用技术而用技术”,反而增加了代码量和维护成本。记住:业务简单用 if-else,业务复杂用流程编排。判断标准很简单:如果你的 if-else 嵌套超过 3 层,或者业务规则经常变动,再考虑流程编排。
// 改进后的库存扣减节点 publicclass StockDeductNode implements OrderProcessNode { private StockService stockService = new StockService(); private RetryTemplate retryTemplate; // 重试模板 public StockDeductNode() { // 初始化重试模板:重试3次,每次间隔1秒 retryTemplate = new RetryTemplate(); retryTemplate.setRetryPolicy(new SimpleRetryPolicy(3)); retryTemplate.setBackOffPolicy(new FixedBackOffPolicy() {{ setBackOffPeriod(1000); }}); } @Override public boolean process(Order order) { try { // 重试3次库存扣减 return retryTemplate.execute(context -> { boolean deductSuccess = stockService.deductStock(order.getProductId(), order.getQuantity()); if (!deductSuccess) { thrownew RuntimeException("库存扣减失败,重试中..."); } System.out.println("库存扣减成功"); returntrue; }); } catch (Exception e) { // 重试失败后降级:记录日志,触发人工介入 System.out.println("库存扣减失败(已重试3次),触发人工处理:" + e.getMessage()); sendAlertToStaff(order); // 发送告警给运营人员 returnfalse; } } }不管用哪种流程编排方案,都要考虑 “节点失败了怎么办”,是重试、降级还是终止流程,必须提前定义好。
<sequenceFlow id="flow4" sourceRef="validateGateway" targetRef="merchantAuditTask"> <conditionExpression xsi:type="tFormalExpression"> #{validateResult == 'pass' && refundAmount > 100 && userLevel == 'VIP' && orderCreateTime > '2025-08-01'} </conditionExpression> </sequenceFlow>这种表达式写多了,调试起来特别麻烦 —— 流程走到这一步没按预期走,你得去看 BPMN 图里的表达式有没有写错,还得查流程变量的值,比看 Java 代码还费劲。正确的做法是:复杂逻辑写在 Delegate 类里,BPMN 图只负责流程走向。表达式只用来做简单的条件判断(比如#{validateResult == 'pass'}),这样调试的时候,直接看 Delegate 类的日志就行。