基于 Spring 狀態(tài)機實現(xiàn)電商訂單狀態(tài)流轉(zhuǎn)管理
引言
在電商系統(tǒng)中,訂單狀態(tài)的流轉(zhuǎn)是核心業(yè)務(wù)流程之一。從用戶下單、支付、商家發(fā)貨到用戶收貨、售后等環(huán)節(jié),每個環(huán)節(jié)對應(yīng)不同的訂單狀態(tài),且狀態(tài)之間的轉(zhuǎn)移需要滿足嚴格的業(yè)務(wù)規(guī)則。
傳統(tǒng)的if-else分支判斷在狀態(tài)較多、轉(zhuǎn)移邏輯復雜時會變得難以維護,而Spring狀態(tài)機結(jié)合狀態(tài)模式可以很好地解決這個問題,讓狀態(tài)轉(zhuǎn)移邏輯更加清晰、可擴展。
實現(xiàn)
圖片
圖片
案例代碼
訂單狀態(tài)枚舉
定義訂單的所有狀態(tài),每個狀態(tài)對應(yīng)業(yè)務(wù)中的一個環(huán)節(jié):
@WithStateMachine
public enum OrderStatus {
WAIT_PAY("待支付"),
WAIT_DELIVER("待發(fā)貨"),
WAIT_RECEIVE("待收貨"),
COMPLETED("已完成"),
CLOSED("已關(guān)閉"),
AFTER_SALE("售后中");
@Getter
private final String desc;
OrderStatus(String desc) {
this.desc = desc;
}
}訂單事件枚舉
事件是觸發(fā)狀態(tài)轉(zhuǎn)移的動作,每個事件對應(yīng)一次狀態(tài)變更的觸發(fā)條件:
public enum OrderEvent {
PAY_SUCCESS("支付成功"),
DELIVER("倉庫發(fā)貨"),
RECEIVE("確認收貨"),
CANCEL("用戶取消/超時"),
APPLY_REFUND("審核退款"),
APPLY_AFTER_SALE("申請售后");
@Getter
private final String desc;
OrderEvent(String desc) {
this.desc = desc;
}
}狀態(tài)機配置
配置狀態(tài)機的狀態(tài)、事件、轉(zhuǎn)移邏輯及監(jiān)聽器:
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStatus, OrderEvent> {
/**
* 配置狀態(tài)機的“狀態(tài)集合”和“初始狀態(tài)”
*/
@Override
public void configure(StateMachineStateConfigurer<OrderStatus, OrderEvent> states) throws Exception {
states.withStates()
.initial(OrderStatus.WAIT_PAY) // 初始狀態(tài)為“待支付”
.states(EnumSet.allOf(OrderStatus.class)); // 注冊所有狀態(tài)
}
/**
* 配置狀態(tài)機的“轉(zhuǎn)移規(guī)則”(事件觸發(fā)狀態(tài)變更)
*/
@Override
public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvent> transitions) throws Exception {
transitions
// 待支付 → 待發(fā)貨:支付成功
.withExternal()
.source(OrderStatus.WAIT_PAY).target(OrderStatus.WAIT_DELIVER)
.event(OrderEvent.PAY_SUCCESS)
.and()
// 待支付 → 已關(guān)閉:用戶取消/超時
.withExternal()
.source(OrderStatus.WAIT_PAY).target(OrderStatus.CLOSED)
.event(OrderEvent.CANCEL)
.and()
// 待發(fā)貨 → 待收貨:倉庫發(fā)貨
.withExternal()
.source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE)
.event(OrderEvent.DELIVER)
.and()
// 待發(fā)貨 → 已關(guān)閉:審核退款
.withExternal()
.source(OrderStatus.WAIT_DELIVER).target(OrderStatus.CLOSED)
.event(OrderEvent.APPLY_REFUND)
.and()
// 待收貨 → 已完成:確認收貨
.withExternal()
.source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.COMPLETED)
.event(OrderEvent.RECEIVE)
.and()
// 已完成 → 售后中:申請售后
.withExternal()
.source(OrderStatus.COMPLETED).target(OrderStatus.AFTER_SALE)
.event(OrderEvent.APPLY_AFTER_SALE);
}
/**
* 配置狀態(tài)機的“全局配置”(如監(jiān)聽器)
*/
@Override
public void configure(StateMachineConfigurationConfigurer<OrderStatus, OrderEvent> config) throws Exception {
config.withConfiguration()
.listener(new OrderStateMachineListener()); // 注冊狀態(tài)變更監(jiān)聽器
}
}@Slf4j
@Component
public class OrderStateMachineListener extends StateMachineListenerAdapter<OrderStatus, OrderEvent> {
@Override
public void stateChanged(State<OrderStatus, OrderEvent> from, State<OrderStatus, OrderEvent> to) {
if (from != null) {
log.info("訂單狀態(tài)從: " + from.getId().getDesc() + " 變更為: " + to.getId().getDesc());
} else {
log.info("訂單初始狀態(tài): " + to.getId().getDesc());
}
}
}業(yè)務(wù)邏輯層實現(xiàn)
封裝狀態(tài)機觸發(fā)邏輯與數(shù)據(jù)庫操作,保證事務(wù)一致性:
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StateMachine<OrderStatus, OrderEvent> stateMachine;
@Autowired
private StateMachinePersister<OrderStatus, OrderEvent, Order> stateMachineMemPersister;
/**
* 創(chuàng)建訂單(初始狀態(tài)為待支付)
*/
@Transactional
public Order createOrder(Order order) {
order.setStatus(OrderStatus.WAIT_PAY);
orderMapper.insert(order);
return order;
}
/**
* 觸發(fā)狀態(tài)事件,更新訂單狀態(tài)
*/
@Transactional
public boolean triggerEvent(Long orderId, OrderEvent event) {
// 查詢訂單
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("訂單不存在:" + orderId);
}
boolean result = false;
try {
// 從訂單中恢復狀態(tài)機當前狀態(tài)
stateMachineMemPersister.restore(stateMachine, order);
// 發(fā)送事件觸發(fā)狀態(tài)變更
Message<OrderEvent> message = MessageBuilder.withPayload(event)
.setHeader("orderId", orderId)
.build();
result = stateMachine.sendEvent(message);
// 狀態(tài)轉(zhuǎn)移成功,更新數(shù)據(jù)庫中的訂單狀態(tài)
if (result) {
// 持久化狀態(tài)機最新狀態(tài)
stateMachineMemPersister.persist(stateMachine, order);
// 更新訂單實體狀態(tài)
order.setStatus(stateMachine.getState().getId());
orderMapper.updateById(order);
}
} catch (Exception e) {
throw new RuntimeException("狀態(tài)變更失?。? + e.getMessage(), e);
}
return result;
}
/**
* 查詢訂單(用于測試)
*/
public Order getOrder(Long orderId) {
return orderMapper.selectById(orderId);
}
}接口層實現(xiàn)
對外暴露HTTP接口,用于創(chuàng)建訂單和觸發(fā)狀態(tài)事件:
@CrossOrigin
@RestController
@RequestMapping("/order")
public class OrderController {
@Resource
private OrderService orderService;
/**
* 創(chuàng)建訂單
*/
@PostMapping("/create")
public Order createOrder(
@RequestParam String userId,
@RequestParam String productId,
@RequestParam BigDecimal amount) {
Order order = new Order();
order.setId(userId);
order.setProductName(productId);
order.setAmount(amount);
order.setStatus(OrderStatus.WAIT_PAY);
return orderService.createOrder(order);
}
/**
* 觸發(fā)訂單狀態(tài)事件
*/
@PostMapping("/event/{orderId}/{event}")
public boolean triggerEvent(
@PathVariable Long orderId,
@PathVariable String event) {
OrderEvent orderEvent = OrderEvent.valueOf(event);
return orderService.triggerEvent(orderId, orderEvent);
}
@GetMapping("/{orderId}")
public Order getOrder(@PathVariable Long orderId) {
Order order = orderService.getOrder(orderId);
if (order == null) {
throw new RuntimeException("訂單不存在");
}
return order;
}
}持久化(可選)
@Slf4j
@Configuration
public class StateMachinePersistConfig {
/**
* 內(nèi)存持久化(基于HashMap)
* 適合單體應(yīng)用,服務(wù)重啟后狀態(tài)會丟失
*/
@Bean(name = "stateMachineMemPersister")
public StateMachinePersister<OrderStatus, OrderEvent, Order> stateMachineMemPersister() {
// 實現(xiàn)StateMachinePersist接口,定義狀態(tài)讀寫邏輯
StateMachinePersist<OrderStatus, OrderEvent, Order> persist = new StateMachinePersist<OrderStatus, OrderEvent, Order>() {
// 用HashMap存儲訂單ID與狀態(tài)機上下文的映射
private final Map<String, StateMachineContext<OrderStatus, OrderEvent>> stateMap = new HashMap<>();
@Override
public void write(StateMachineContext<OrderStatus, OrderEvent> context, Order order) throws Exception {
log.info("內(nèi)存持久化狀態(tài)機 - 寫入,訂單ID: {}, 狀態(tài)上下文: {}", order.getId(), JSON.toJSONString(context));
stateMap.put(order.getId(), context);
}
@Override
public StateMachineContext<OrderStatus, OrderEvent> read(Order order) throws Exception {
StateMachineContext<OrderStatus, OrderEvent> context = stateMap.get(order.getId());
log.info("內(nèi)存持久化狀態(tài)機 - 讀取,訂單ID: {}, 狀態(tài)上下文: {}", order.getId(), JSON.toJSONString(context));
return context;
}
};
// 使用Spring提供的DefaultStateMachinePersister包裝
return new DefaultStateMachinePersister<>(persist);
}
}Redis持久化配置案例:
@Configuration
public class StateMachinePersistConfig {
/**
* Redis持久化(分布式系統(tǒng)適用)
* 狀態(tài)機上下文存儲在Redis中,支持多實例共享狀態(tài)
*/
@Bean(name = "stateMachineRedisPersister")
public StateMachinePersister<OrderStatus, OrderEvent, Long> stateMachineRedisPersister(
RedisConnectionFactory redisConnectionFactory) {
// 創(chuàng)建Redis狀態(tài)機上下文倉庫
RedisStateMachineContextRepository<OrderStatus, OrderEvent> repository =
new RedisStateMachineContextRepository<>(redisConnectionFactory);
// 基于倉庫實現(xiàn)持久化邏輯
RepositoryStateMachinePersist<OrderStatus, OrderEvent, Long> persist =
new RepositoryStateMachinePersist<>(repository);
// 包裝為RedisStateMachinePersister
return new RedisStateMachinePersister<>(persist);
}
}注意事項
- 狀態(tài)機上下文結(jié)構(gòu):
Spring State Machine的StateMachineContext包含當前狀態(tài)、歷史狀態(tài)、擴展變量等信息,持久化時會完整存儲這些內(nèi)容,確保狀態(tài)恢復的準確性。 Redis鍵設(shè)計:Redis持久化默認鍵格式為STATE_MACHINE_CONTEXT:{orderId},可通過自定義RedisStateMachineContextRepository修改鍵前綴,避免與其他業(yè)務(wù)鍵沖突。- 過期策略:對于
Redis持久化,可設(shè)置鍵過期時間(如訂單完成后24小時),避免無效數(shù)據(jù)占用內(nèi)存。 - 分布式鎖:在
Redis持久化的分布式場景中,建議為triggerEvent方法添加分布式鎖(如Redisson),防止并發(fā)狀態(tài)修改導致的數(shù)據(jù)不一致。






























