1、背景描述

        在一家小型主机厂,我们正规划开发监控系统、配套 APP 及订单管理系统。鉴于公司对业务发展有较高期许,还计划搭建自主电商平台。近期需优先完成订单功能开发,而订单业务天然适合采用状态机模式实现。

        在技术选型上,我们评估了三种方案:自主开发状态机、采用 ColaMachine 框架,以及使用 Spring StateMachine。考虑到项目时间紧张,且现有技术栈基于 Spring 生态,最终决定采用 Spring StateMachine 来实现订单功能,力求通过优化设计确保逻辑清晰、状态流转路径明确。

2、代码实现

2.1 配置项

@Configuration
@EnableStateMachine(name = "orderStateMachine")
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStateEnum, OrderStatusConversionEvent> {

    /** 配置状态
     * @param states
     * @throws Exception
    **/
    public void configure(StateMachineStateConfigurer<OrderStateEnum, OrderStatusConversionEvent> states) throws Exception {
        states
                .withStates()
                .initial(OrderStateEnum.AWAITING_PAYMENT)
                .states(EnumSet.allOf(OrderStateEnum.class));
    }

    /**
     * 配置状态转换事件关系
     * @param transitions
     * @throws Exception
    **/
    public void configure(StateMachineTransitionConfigurer<OrderStateEnum, OrderStatusConversionEvent> transitions) throws Exception {
        transitions
                //订单支付成功事件:待支付-》支付完成
                .withExternal().source(OrderStateEnum.AWAITING_PAYMENT).target(OrderStateEnum.PAYMENT_COMPLETED).event(OrderStatusConversionEvent.PAYED)
                .and()
                //订单支付失败事件:待支付-》已取消
                .withExternal().source(OrderStateEnum.AWAITING_PAYMENT).target(OrderStateEnum.CANCELLED).event(OrderStatusConversionEvent.CANCEL)
                .and()
                //开始生产事件:支付完成-》生产中
                .withExternal().source(OrderStateEnum.PAYMENT_COMPLETED).target(OrderStateEnum.PRODUCING).event(OrderStatusConversionEvent.PRODUCTION_START)
                .and()
                //生产完成事件:生产中-》生产完成
                .withExternal().source(OrderStateEnum.PRODUCING).target(OrderStateEnum.PRODUCTION_COMPLETED).event(OrderStatusConversionEvent.PRODUCTION_END)
                .and()
                //发货事件:生产中-》发货中
                .withExternal().source(OrderStateEnum.PRODUCTION_COMPLETED).target(OrderStateEnum.IN_TRANSIT).event(OrderStatusConversionEvent.DELIVERY)
                .and()
                //完成事件:发货中-》已完成
                .withExternal().source(OrderStateEnum.IN_TRANSIT).target(OrderStateEnum.COMPLETED).event(OrderStatusConversionEvent.DELIVERY_END)
                .and()
                //申请退款事件:支付完成-》申请退款
                .withExternal().source(OrderStateEnum.PAYMENT_COMPLETED).target(OrderStateEnum.REFUND_PENDING).event(OrderStatusConversionEvent.REFUND)
                .and()
                //同意退款事件:申请退款-》退款中
                .withExternal().source(OrderStateEnum.REFUND_PENDING).target(OrderStateEnum.REFUND_IN_PROGRESS).event(OrderStatusConversionEvent.REFUND_OK)
                .and()
                //退款完成:退款中-》退款完成
                .withExternal().source(OrderStateEnum.REFUND_IN_PROGRESS).target(OrderStateEnum.REFUNDED).event(OrderStatusConversionEvent.REFUND_COMPLETE);
    }
}
@Configuration
@Slf4j
public class OrderMachinePersist<E, S> {

    /**
            * 创建基于内存Map的状态机持久化器(适用于单机环境)
            *
            * @return StateMachinePersister 状态机持久化实例
    */

    @Bean(name = "stateMachineMemPersister")
    public static StateMachinePersister getPersister() {
        return new DefaultStateMachinePersister(new StateMachinePersist() {
            // 使用HashMap存储状态机上下文
            private Map<Object, StateMachineContext> map = new HashMap<Object, StateMachineContext>();
            /**
                    * 持久化状态机上下文到内存Map
             * @param context 状态机上下文
             * @param contextObj 业务上下文对象(如订单ID)
             */
            @Override
            public void write(StateMachineContext context, Object contextObj) throws Exception {
                log.info("持久化状态机, context:{}, contextObj:{}",
                        JSON.toJSONString(context),
                        JSON.toJSONString(contextObj));
                map.put(contextObj, context);
            }
            /**
            * 从内存Map恢复状态机上下文
            * @param contextObj 业务上下文对象(如订单ID)
            * @return 状态机上下文
             */
            @Override
            public StateMachineContext read(Object contextObj) throws Exception {
                log.info("获取状态机, contextObj:{}", JSON.toJSONString(contextObj));
                StateMachineContext stateMachineContext = map.get(contextObj);
                log.info("获取状态机结果, stateMachineContext:{}",
                        JSON.toJSONString(stateMachineContext));
                return stateMachineContext;
            }
        });
    }
}

2.2 事件状态枚举项

/**
 * 订单状态枚举
 */
@Getter
@AllArgsConstructor
public enum OrderStateEnum {

    /**
     * 待付款
     */
    AWAITING_PAYMENT(1, "待付款"),

    /**
     * 已支付
     */
    PAYMENT_COMPLETED(2, "已支付"),

    /**
     * 生产中
     */
    PRODUCING(3, "生产中"),

    /**
     * 生产完成
     */
    PRODUCTION_COMPLETED(4, "生产完成"),

    /**
     * 运输中
     */
    IN_TRANSIT(5, "运输中"),

    /**
     * 已完成
     */
    COMPLETED(6, "已完成"),

    /**
     * 申请退款
     */
    REFUND_PENDING(7, "申请退款"),

    /**
     * 退款中
     */
    REFUND_IN_PROGRESS(8, "退款中"),

    /**
     * 退款完成
     */
    REFUNDED(9, "退款完成"),

    /**
     * 已取消
     */
    CANCELLED(10, "已取消"),
    ;

    final Integer status;

    final String describe;

    public static OrderStateEnum fromStatus(Integer status) {
        return Arrays.stream(OrderStateEnum.values())
                .filter(e -> e.getStatus().equals(status))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("Unknown order status: " + status));
    }

}
public enum OrderStatusConversionEvent {
    // 支付
    PAYED,
    // 开始生产
    PRODUCTION_START,
    // 生产完成
    PRODUCTION_END,
    // 开始发货
    DELIVERY,
    // 运输完成
    DELIVERY_END,
    //退款
    REFUND,
    //同意退款
    REFUND_OK,
    //同意拒绝
    REFUND_REJECTED,
    //退款完成
    REFUND_COMPLETE,
    //取消
    CANCEL;
}

2.3 处理项

@Component("orderStateListener")
@WithStateMachine(name = "orderStateMachine")
@Slf4j
public class OrderStateListener{


    @Autowired
    private OrderService orderService;


    /**
     * 支付完成-已支付
     */
    @OnTransition(source = "AWAITING_PAYMENT", target = "PAYMENT_COMPLETED")
    @EventListenerResult(key = payTransition)
    public void payTransition(Message<OrderStatusConversionEvent> message) {
        OrderDO order = (OrderDO) message.getHeaders().get("order");
        log.info("支付,状态机反馈信息:{}",  message.getHeaders().toString());
        // 调用订单服务 修改订单信息
        orderService.updateByDTO(OrderDTO.builder().id(order.getId()).status(OrderStateEnum.PAYMENT_COMPLETED.getStatus()).build());

    }

    /**
     * 生产中
     */
    @OnTransition(source = "PAYMENT_COMPLETED", target = "PRODUCING")
    @EventListenerResult(key = OrderConstants.producingTransition)
    public void  producingTransition(Message<OrderStatusConversionEvent> message){
        OrderDO order = (OrderDO) message.getHeaders().get("order");
        log.info("生产中,状态机反馈信息:{}",  message.getHeaders().toString());
        // 调用订单服务 修改订单信息
        orderService.updateByDTO(OrderDTO.builder().id(order.getId()).status(OrderStateEnum.PRODUCING.getStatus()).build());
    }

    /**
     * 生产完成
     */
    @OnTransition(source = "PRODUCING", target = "PRODUCTION_COMPLETED")
    @EventListenerResult(key = OrderConstants.productionCompleted)
    public void  productionCompleted(Message<OrderStatusConversionEvent> message){
        OrderDO order = (OrderDO) message.getHeaders().get("order");
        log.info("生产完成,状态机反馈信息:{}",  message.getHeaders().toString());
        // 调用订单服务 修改订单信息
        orderService.updateByDTO(OrderDTO.builder().id(order.getId()).status(OrderStateEnum.PRODUCTION_COMPLETED.getStatus()).build());
    }

    /**
     * 运输中
     */
    @OnTransition(source = "PRODUCTION_COMPLETED", target = "IN_TRANSIT")
    @EventListenerResult(key = OrderConstants.inTransit)
    public void  inTransit(Message<OrderStatusConversionEvent> message){
        OrderDO order = (OrderDO) message.getHeaders().get("order");
        log.info("运输中,状态机反馈信息:{}",  message.getHeaders().toString());
        // 调用订单服务 修改订单信息
        orderService.updateByDTO(OrderDTO.builder().id(order.getId()).status(OrderStateEnum.IN_TRANSIT.getStatus()).build());
    }

    /**
     * 已完成
     */
    @OnTransition(source = "IN_TRANSIT", target = "COMPLETED")
    @EventListenerResult(key = OrderConstants.completed)
    public void  completed(Message<OrderStatusConversionEvent> message){
        OrderDO order = (OrderDO) message.getHeaders().get("order");
        log.info("已完成,状态机反馈信息:{}",  message.getHeaders().toString());
        // 调用订单服务 修改订单信息
        orderService.updateByDTO(OrderDTO.builder().id(order.getId()).status(OrderStateEnum.COMPLETED.getStatus()).build());
    }

    /**
     * 申请退款
     */
    @OnTransition(source = "WAIT_PAYMENT", target = "REFUND_PENDING")
    @EventListenerResult(key = OrderConstants.refundPending)
    public void  refundPending(Message<OrderStatusConversionEvent> message){
        OrderDO order = (OrderDO) message.getHeaders().get("order");
        log.info("申请退款,状态机反馈信息:{}",  message.getHeaders().toString());
        // 调用订单服务 修改订单信息
        orderService.updateByDTO(OrderDTO.builder().id(order.getId()).status(OrderStateEnum.REFUND_PENDING.getStatus()).build());
    }

    /**
     * 退款中
     */
    @OnTransition(source = "REFUND_PENDING", target = "REFUND_IN_PROGRESS")
    @EventListenerResult(key = OrderConstants.refundInProgress)
    public void  refundInProgress(Message<OrderStatusConversionEvent> message){
        OrderDO order = (OrderDO) message.getHeaders().get("order");
        log.info("退款中,状态机反馈信息:{}",  message.getHeaders().toString());
        // 调用订单服务 修改订单信息
        orderService.updateByDTO(OrderDTO.builder().id(order.getId()).status(OrderStateEnum.REFUND_IN_PROGRESS.getStatus()).build());
    }

    /**
     * 退款完成
     */
    @OnTransition(source = "REFUND_IN_PROGRESS", target = "REFUNDED")
    @EventListenerResult(key = OrderConstants.refunded)
    public void  refunded(Message<OrderStatusConversionEvent> message){
        OrderDO order = (OrderDO) message.getHeaders().get("order");
        log.info("退款完成,状态机反馈信息:{}",  message.getHeaders().toString());
        // 调用订单服务 修改订单信息
        orderService.updateByDTO(OrderDTO.builder().id(order.getId()).status(OrderStateEnum.REFUNDED.getStatus()).build());
    }

    /**
     * 已取消
     */
    @OnTransition(source = "AWAITING_PAYMENT", target = "CANCELLED")
    @EventListenerResult(key = OrderConstants.cancelled)
    public void  cancelled(Message<OrderStatusConversionEvent> message){
        OrderDO order = (OrderDO) message.getHeaders().get("order");
        log.info("已取消,状态机反馈信息:{}",  message.getHeaders().toString());
        // 调用订单服务 修改订单信息
        orderService.updateByDTO(OrderDTO.builder().id(order.getId()).status(OrderStateEnum.CANCELLED.getStatus()).build());
    }
}
@Service("OrderStatusChangeService")
@AllArgsConstructor
public class OrderStatusChangeServiceImpl implements OrderStateService {

    private static final Logger log = LoggerFactory.getLogger(OrderStatusChangeServiceImpl.class);

    @Autowired
    private OrderService orderService;

    @Autowired
    private OrderPaymentService orderPaymentService;

    @Resource
    private StateMachine<OrderStateEnum, OrderStatusConversionEvent> orderStateMachine;

    @Resource
    private StateMachinePersister<OrderStateEnum, OrderStatusConversionEvent, String> stateMachineMemPersister;


    @Override
    public boolean paymentCompleted(Long orderId, String number, BigDecimal amount) {
        // 通过订单编号 获取订单id
        OrderDO orderDO = orderService.tryGetById(orderId);
        log.info("线程名称:{},,订单号:{}" ,Thread.currentThread().getName() , orderId);
        if (!sendEvent(OrderStatusConversionEvent.PAYED, orderDO, OrderConstants.payTransition)) {
            log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), orderDO);
            return false;
        }
        return true;
    }

    @Override
    public boolean producing(Long orderId) {
        // 通过订单编号 获取订单id
        OrderDO orderDO = orderService.tryGetById(orderId);
        log.info("线程名称:{},,订单号:{}" ,Thread.currentThread().getName() , orderId);
        if (!sendEvent(OrderStatusConversionEvent.PRODUCTION_START, orderDO, OrderConstants.producingTransition)) {
            log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), orderDO);
            return false;
        }
        return true;
    }

    @Override
    public boolean productionCompleted(Long orderId) {
        // 通过订单编号 获取订单id
        OrderDO orderDO = orderService.tryGetById(orderId);
        log.info("线程名称:{},,订单号:{}" ,Thread.currentThread().getName() , orderId);
        if (!sendEvent(OrderStatusConversionEvent.PRODUCTION_END, orderDO, OrderConstants.productionCompleted)) {
            log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), orderDO);
            return false;
        }
        return true;
    }

    @Override
    public boolean inTransit(Long orderId, String waybillNo) {
        // 通过订单编号 获取订单id
        OrderDO orderDO = orderService.tryGetById(orderId);
        log.info("线程名称:{},,订单号:{}" ,Thread.currentThread().getName() , orderId);
        if (!sendEvent(OrderStatusConversionEvent.DELIVERY, orderDO, OrderConstants.inTransit)) {
            log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), orderDO);
            return false;
        }
        return true;
    }

    @Override
    public boolean completed(Long orderId) {
        // 通过订单编号 获取订单id
        OrderDO orderDO = orderService.tryGetById(orderId);
        log.info("线程名称:{},,订单号:{}" ,Thread.currentThread().getName() , orderId);
        if (!sendEvent(OrderStatusConversionEvent.DELIVERY_END, orderDO, OrderConstants.completed)) {
            log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), orderDO);
            return false;
        }
        return true;
    }

    @Override
    public boolean refundPending(Long orderId) {
        // 通过订单编号 获取订单id
        OrderDO orderDO = orderService.tryGetById(orderId);
        log.info("线程名称:{},,订单号:{}" ,Thread.currentThread().getName() , orderId);
        if (!sendEvent(OrderStatusConversionEvent.REFUND, orderDO, OrderConstants.refundPending)) {
            log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), orderDO);
            return false;
        }
        return true;
    }

    @Override
    public boolean refundInProgress(Long orderId) {
        // 通过订单编号 获取订单id
        OrderDO orderDO = orderService.tryGetById(orderId);
        log.info("线程名称:{},,订单号:{}" ,Thread.currentThread().getName() , orderId);
        if (!sendEvent(OrderStatusConversionEvent.REFUND_OK, orderDO, OrderConstants.refundInProgress)) {
            log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), orderDO);
            return false;
        }
        return true;
    }

    @Override
    public boolean refunded(String number, BigDecimal amount) {
        // 通过订单编号 获取订单id
        OrderDO orderDO = orderService.tryGetByNumber(number);
        log.info("线程名称:{},,订单号:{}" ,Thread.currentThread().getName() , orderDO.getId());
        if (!sendEvent(OrderStatusConversionEvent.REFUND_COMPLETE, orderDO, OrderConstants.refunded)) {
            log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), orderDO);
            return false;
        }
        return true;
    }

    @Override
    public boolean cancelled(Long orderId) {
        // 通过订单编号 获取订单id
        OrderDO orderDO = orderService.tryGetById(orderId);
        log.info("线程名称:{},,订单号:{}" ,Thread.currentThread().getName() , orderId);
        if (!sendEvent(OrderStatusConversionEvent.CANCEL, orderDO, OrderConstants.cancelled)) {
            log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), orderDO);
            return false;
        }
        return true;
    }

    @Override
    public OrderStateEnum getStateEnum() {
        return null;
    }


     /**
     * 发送订单状态转换事件(持久化到内存)
     * synchronized修饰保证这个方法是线程安全的
     * @param changeEvent
     * @param order
     * @return
     **/
      private synchronized boolean sendEvent(OrderStatusConversionEvent changeEvent, OrderDO order,String key) {
        boolean result = false;
        try {
            //启动状态机
            orderStateMachine.start();
            //尝试恢复状态机状态
            stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
            Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
            result = orderStateMachine.sendEvent(message);
            if(!result){
                return false;
            }
            //获取到监听的结果信息
            Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(key  + order.getId());
            //操作完成之后,删除本次对应的key信息
            orderStateMachine.getExtendedState().getVariables().remove(key +order.getId());
            //如果事务执行成功,则持久化状态机
            if(Objects.equals(1,Integer.valueOf(o))){
                //持久化状态机状态
                stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
            }else {
                //订单执行业务异常
                return false;
            }
        } catch (Exception e) {
            log.error("订单操作失败:{}", e);
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }
}

2.4 针对异常的捕获

@Component
@Aspect
@Slf4j
public class EventListenerResultAspect {

    @Resource
    private StateMachine<OrderStateEnum, OrderStatusConversionEvent> orderStateMachine;


    @Around("@annotation(cn.bool.tsp.cloud.mall.modular.order.listener.annotation.EventListenerResult)")
    public Object logResultAround(ProceedingJoinPoint pjp) throws Throwable {
        //获取参数
        Object[] args = pjp.getArgs();
        log.info("参数args:{}", args);
        Message message = (Message) args[0];
        OrderDO order = (OrderDO) message.getHeaders().get("order");
        //获取方法
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        // 获取DemoEventListenerResult注解
        EventListenerResult eventListenerResult = method.getAnnotation(EventListenerResult.class);
        String key = eventListenerResult.key();
        Object returnVal = null;
        try {
            //执行方法
            log.info("执行代理方法");
            returnVal = pjp.proceed();
            //如果业务执行正常,则保存信息
            //成功 则为1
            log.info("代理方法执行完毕。保存ExtendedState状态为正常。");
            orderStateMachine.getExtendedState().getVariables().put(key + order.getId(), 1);
        } catch (Throwable e) {
            log.error("e:{}", e.getMessage());
            //如果业务执行异常,则保存信息
            //将异常信息变量信息中,失败则为0
            orderStateMachine.getExtendedState().getVariables().put(key + order.getId(), 0);
            throw e;
        }
        return returnVal;
    }
}

3、相关说明体验

  1. 团队技术要求较高:Spring State Machine 对团队的技术能力有较高要求,如果团队成员在编码实现或调试排错方面经验不足,建议谨慎评估后再采用,以避免项目风险。
  2. 适用场景为复杂流程:Spring State Machine 更适合处理复杂的状态流转逻辑(如多层级状态或事件驱动流程);对于简单流程,推荐使用轻量级替代方案(如 COLA 状态机框架)或自行实现状态模式代码,以降低复杂度。
  3. 资源支持有限:在使用 Spring State Machine 过程中,可能会遇到一些实现或配置问题,且网上相关文档和社区资源相对较少,因此建议在使用前充分调研和测试。

更多推荐