大数跨境
0
0

订单履约系统的状态机设计

订单履约系统的状态机设计 跨境Amy
2025-10-17
16

订单系统的核心,是状态管理。

一个订单从生到死,流经几十个状态。管不好,库存、财务都会乱套。

要理顺这一切,就需要:状态机。

状态机是什么

理解状态机,可以当成一张地铁路线图,标出了从起点到终点的所有站点和换乘路线。由四个元素构成:

  • 状态(State):地图上的一个个站点,比如待支付、待发货。
  • 事件(Event):促使你从一个站点到另一个站点的动作,比如成功付款这个动作让你从待支付站到了待发货站。
  • 迁移(Transition):连接两个站点的线路,代表一次具体的状态变化。
  • 动作(Action):当状态发生改变(像到站或换乘)时,需要做的具体事项。以订单为例,进入待发货状态时,就进行通知仓库打包
状态机基本模型

订单履约的状态

了解了状态机,咱们就来看看订单履约的路线图。首先是主干线,也就是订单的理想流程:

待支付--(支付成功)--> 待发货--(仓库发货)--> 待收货--(确认收货)--> 已完成

线路看起来简单清晰,但实际情况会复杂得多,需要把各种换乘和支线情况都考虑进来。

  • 待支付站,乘客可能不想坐了(用户取消),或错过了(支付超时),需要一条通往已关闭站的支线。
  • 待发货站,乘客可能突然反悔(申请退款),要引入退款中这类的临时换乘站。
  • 已完成站,旅程看着像结束了,但乘客可能发现行李有问题(申请售后),要开启另一条名为售后的新线路。

把所有可能性都画上去,就得到了一张完整的订单履约全景图。

订单状态机全景图

状态机的实现

地图画好了,作为工程师,我们如何用代码把它构建出来?这是一段有趣的探索之旅。

最简单的实现:if-else

第一反应,可能就是用 if-else或 switch来实现。

public void process(Order order, Event event) {
    if (order.getStatus() == OrderStatus.PENDING_PAYMENT) {
        if (event == Event.PAY_SUCCESS) {
            order.setStatus(OrderStatus.PENDING_SHIPMENT);
            // ...其他业务逻辑
        } else if (event == Event.CANCEL) {
            order.setStatus(OrderStatus.CLOSED);
        }
    } else if (order.getStatus() == OrderStatus.PENDING_SHIPMENT) {
        // ...
    }
}

在只有两三个站点的简单线路上,这很管用。但当地图变得复杂,if-else就会迅速变成一团乱麻。每当产品经理想增加一个新站点或新线路,我们都得硬着头皮去修改这个庞大而脆弱的结构,一不小心就会引发全线交通瘫痪。

进阶之路:状态模式

if-else显然是条死胡同。我们应该把每个站点的逻辑独立开来。于是,状态模式(State Pattern)进入了我们的视野。

// 状态接口
publicinterface OrderState {
    void handle(Order context, Event event);
}

// 具体状态类:待支付
publicclass PendingPaymentState implements OrderState {
    @Override
    public void handle(Order context, Event event) {
        if (event == Event.PAY_SUCCESS) {
            context.setState(new PendingShipmentState());
            // ...业务动作
        }
    }
}

每个状态的职责变得清晰,添加新状态也更容易了。但新的问题随之而来:当地图上有几十个站点时,我们就要维护几十个 State类。系统的复杂性从一个巨大的 if-else转移到了数量庞大的类上。

表驱动引擎

这张地铁图的逻辑,能否不写死在代码里?如果它本身就是一份配置,那该多好?

这就是表驱动思想。把状态流转的所有规则,定义在一张配置表里。

状态驱动表设计

这张表就是我们的线路图数据。然后,a只需要一个通用的列车调度中心(状态机引擎)来读取这张图。

// 伪代码:状态机引擎
publicclass StateMachineEngine {
    // 加载上面的状态驱动表
    private Map<CurrentState, Map<Event, Transition>> transitions;

    public void fire(StatefulObject obj, Event event) {
        // 根据当前状态和事件,查表找到对应的线路
        Transition transition = findTransition(obj.getCurrentState(), event);
        if (transition != null) {
            // 执行动作
            transition.getAction().execute();
            // 驶向下一站
            obj.setCurrentState(transition.getNextState());
        } else {
            // 没有这条线路,报告异常
            thrownew IllegalStateException("Invalid transition.");
        }
    }
}

这才是我们想要的优雅!现在,无论是修改线路,还是增加站点,都只是修改配置数据而已,核心的调度中心代码岿然不动。系统获得了前所未有的灵活性和可维护性。

开源方案

自己动手构建一个简单的引擎很有趣,但在真实的生产环境中,我们完全可以站在巨人的肩膀上。

业界有许多成熟的开源状态机框架,比如 Spring Statemachine、Squirrel-foundation 等。它们就是我们设想的那个列车调度中心的专业、豪华版,提供了更完善的功能:

  • 声明式配置:用更简洁的方式定义线路图。
  • 持久化支持:能记住每列火车当前所在的站点,即使系统重启也不会丢失。
  • 监听与拦截:方便地在列车进出站的各个节点挂载额外任务。
  • 分布式支持:在庞大的铁路网中确保状态的准确无误。

使用 Spring Statemachine,定义上面那张图的核心逻辑,代码会非常清晰:

@Configuration
@EnableStateMachine
publicclass OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStatusOrderEvent{

    @Override
    public void configure(StateMachineStateConfigurer<OrderStatus, OrderEvent> states) throws Exception {
        states.withStates()
            .initial(OrderStatus.PENDING_PAYMENT) // 定义起点站
            .states(EnumSet.allOf(OrderStatus.class))// 定义所有站点
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvent> transitions) throws Exception {
        transitions
            .withExternal() // 定义一条线路
                .source(OrderStatus.PENDING_PAYMENT).target(OrderStatus.PENDING_SHIPMENT) // 从..到..
                .event(OrderEvent.PAY_SUCCESS) // 需要支付成功这张票
                .and()
            .withExternal() // 再定义一条
                .source(OrderStatus.PENDING_SHIPMENT).target(OrderStatus.TO_BE_RECEIVED)
                .event(OrderEvent.SHIP);
    }
}

写在最后

从 if-else到表驱动引擎的演进,本质是思维方式的转变:把流程当数据来管理。

状态机就是这个思想的载体。它是一种将复杂抽象为简单、将混乱转化为秩序的工具。

手握此图,万变不惊。


图片


▲ 添加微信:JiteStart,获取架构资料


图片


  
往期推荐

 


AI提效

别再用手画图了!看看字节研发怎么用AI画图

不懂设计?3步用AI生成大厂级交互原型

AI秒出高质量架构图,打工人必备神器!

架构设计

SaaS架构:身份认证与授权体系设计

一文吃透多租户SaaS设计精髓

一文吃透SaaS应用层架构设计

【声明】内容源于网络
0
0
跨境Amy
跨境分享站 | 每日更新跨境知识
内容 44207
粉丝 2
跨境Amy 跨境分享站 | 每日更新跨境知识
总阅读258.9k
粉丝2
内容44.2k