闽公网安备 35020302035485号
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": "同意退款,已安排打款"
}
响应:财务审核已提交,结果:passpublic 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 类的日志就行。