基于 Spring 状态机实现电商订单状态流转管理

传统的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 MachineStateMachineContext包含当前状态、历史状态、扩展变量等信息,持久化时会完整存储这些内容,确保状态恢复的准确性。
  • 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大模型商业化落地方案

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值