传统的if-else分支判断在状态较多、转移逻辑复杂时会变得难以维护,而Spring状态机结合状态模式可以很好地解决这个问题,让状态转移逻辑更加清晰、可扩展。
引言
在电商系统中,订单状态的流转是核心业务流程之一。从用户下单、支付、商家发货到用户收货、售后等环节,每个环节对应不同的订单状态,且状态之间的转移需要满足严格的业务规则。
传统的if-else分支判断在状态较多、转移逻辑复杂时会变得难以维护,而Spring状态机结合状态模式可以很好地解决这个问题,让状态转移逻辑更加清晰、可扩展。
实现


案例代码
订单状态枚举
定义订单的所有状态,每个状态对应业务中的一个环节:
@WithStateMachine
public enum OrderStatus {
WAIT_PAY("待支付"),
WAIT_DELIVER("待发货"),
WAIT_RECEIVE("待收货"),
COMPLETED("已完成"),
CLOSED("已关闭"),
AFTER_SALE("售后中");
@Getter
private final String desc;
OrderStatus(String desc) {
this.desc = desc;
}
}
订单事件枚举
事件是触发状态转移的动作,每个事件对应一次状态变更的触发条件:
public enum OrderEvent {
PAY_SUCCESS("支付成功"),
DELIVER("仓库发货"),
RECEIVE("确认收货"),
CANCEL("用户取消/超时"),
APPLY_REFUND("审核退款"),
APPLY_AFTER_SALE("申请售后");
@Getter
private final String desc;
OrderEvent(String desc) {
this.desc = desc;
}
}
状态机配置
配置状态机的状态、事件、转移逻辑及监听器:
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<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
// 待支付 → 待发货:支付成功
.withExternal()
.source(OrderStatus.WAIT_PAY).target(OrderStatus.WAIT_DELIVER)
.event(OrderEvent.PAY_SUCCESS)
.and()
// 待支付 → 已关闭:用户取消/超时
.withExternal()
.source(OrderStatus.WAIT_PAY).target(OrderStatus.CLOSED)
.event(OrderEvent.CANCEL)
.and()
// 待发货 → 待收货:仓库发货
.withExternal()
.source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE)
.event(OrderEvent.DELIVER)
.and()
// 待发货 → 已关闭:审核退款
.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);
}
/**
* 配置状态机的“全局配置”(如监听器)
*/
@Override
public void configure(StateMachineConfigurationConfigurer<OrderStatus, OrderEvent> config) throws Exception {
config.withConfiguration()
.listener(new OrderStateMachineListener()); // 注册状态变更监听器
}
}
@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("订单状态从: " + from.getId().getDesc() + " 变更为: " + to.getId().getDesc());
} else {
log.info("订单初始状态: " + to.getId().getDesc());
}
}
}
业务逻辑层实现
封装状态机触发逻辑与数据库操作,保证事务一致性:
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StateMachine<OrderStatus, OrderEvent> stateMachine;
@Autowired
private StateMachinePersister<OrderStatus, OrderEvent, Order> stateMachineMemPersister;
/**
* 创建订单(初始状态为待支付)
*/
@Transactional
public Order createOrder(Order order) {
order.setStatus(OrderStatus.WAIT_PAY);
orderMapper.insert(order);
return order;
}
/**
* 触发状态事件,更新订单状态
*/
@Transactional
public boolean triggerEvent(Long orderId, OrderEvent event) {
// 查询订单
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在:" + orderId);
}
boolean result = false;
try {
// 从订单中恢复状态机当前状态
stateMachineMemPersister.restore(stateMachine, order);
// 发送事件触发状态变更
Message<OrderEvent> message = MessageBuilder.withPayload(event)
.setHeader("orderId", orderId)
.build();
result = stateMachine.sendEvent(message);
// 状态转移成功,更新数据库中的订单状态
if (result) {
// 持久化状态机最新状态
stateMachineMemPersister.persist(stateMachine, order);
// 更新订单实体状态
order.setStatus(stateMachine.getState().getId());
orderMapper.updateById(order);
}
} catch (Exception e) {
throw new RuntimeException("状态变更失败:" + e.getMessage(), e);
}
return result;
}
/**
* 查询订单(用于测试)
*/
public Order getOrder(Long orderId) {
return orderMapper.selectById(orderId);
}
}
接口层实现
对外暴露HTTP接口,用于创建订单和触发状态事件:
@CrossOrigin
@RestController
@RequestMapping("/order")
public class OrderController {
@Resource
private OrderService orderService;
/**
* 创建订单
*/
@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);
}
/**
* 触发订单状态事件
*/
@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 {
/**
* 内存持久化(基于HashMap)
* 适合单体应用,服务重启后状态会丢失
*/
@Bean(name = "stateMachineMemPersister")
public StateMachinePersister<OrderStatus, OrderEvent, Order> stateMachineMemPersister() {
// 实现StateMachinePersist接口,定义状态读写逻辑
StateMachinePersist<OrderStatus, OrderEvent, Order> persist = new StateMachinePersist<OrderStatus, OrderEvent, Order>() {
// 用HashMap存储订单ID与状态机上下文的映射
private final Map<String, StateMachineContext<OrderStatus, OrderEvent>> stateMap = new HashMap<>();
@Override
public void write(StateMachineContext<OrderStatus, OrderEvent> context, Order order) throws Exception {
log.info("内存持久化状态机 - 写入,订单ID: {}, 状态上下文: {}", 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("内存持久化状态机 - 读取,订单ID: {}, 状态上下文: {}", order.getId(), JSON.toJSONString(context));
return context;
}
};
// 使用Spring提供的DefaultStateMachinePersister包装
return new DefaultStateMachinePersister<>(persist);
}
}
Redis持久化配置案例:
@Configuration
public class StateMachinePersistConfig {
/**
* Redis持久化(分布式系统适用)
* 状态机上下文存储在Redis中,支持多实例共享状态
*/
@Bean(name = "stateMachineRedisPersister")
public StateMachinePersister<OrderStatus, OrderEvent, Long> stateMachineRedisPersister(
RedisConnectionFactory redisConnectionFactory) {
// 创建Redis状态机上下文仓库
RedisStateMachineContextRepository<OrderStatus, OrderEvent> repository =
new RedisStateMachineContextRepository<>(redisConnectionFactory);
// 基于仓库实现持久化逻辑
RepositoryStateMachinePersist<OrderStatus, OrderEvent, Long> persist =
new RepositoryStateMachinePersist<>(repository);
// 包装为RedisStateMachinePersister
return new RedisStateMachinePersister<>(persist);
}
}
注意事项
- 状态机上下文结构:
Spring State Machine的StateMachineContext包含当前状态、历史状态、扩展变量等信息,持久化时会完整存储这些内容,确保状态恢复的准确性。 Redis键设计:Redis持久化默认键格式为STATE_MACHINE_CONTEXT:{orderId},可通过自定义RedisStateMachineContextRepository修改键前缀,避免与其他业务键冲突。- 过期策略:对于
Redis持久化,可设置键过期时间(如订单完成后24小时),避免无效数据占用内存。 - 分布式锁:在
Redis持久化的分布式场景中,建议为triggerEvent方法添加分布式锁(如Redisson),防止并发状态修改导致的数据不一致。
AI大模型学习福利
作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
一、全套AGI大模型学习路线
AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获取
二、640套AI大模型报告合集
这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获
三、AI大模型经典PDF籍
随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获
四、AI大模型商业化落地方案

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获
作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量
3188

被折叠的 条评论
为什么被折叠?



