Java状态机实战:打造高扩展性的订单流程引擎(含源码详解与快照设计)

Java状态机打造订单流程引擎及源码详解

在业务开发中,订单、支付、审批等场景往往都涉及“状态变更”这一核心需求。随着流程复杂化,仅靠 if-else 处理状态流转不仅维护困难,还容易出错。因此,我们团队基于实际项目需求,设计并实现了一套可复用的状态机框架

本文将系统讲解:

  • 为什么要写状态机?

  • 状态机的业务场景适用性;

  • 我们自研状态机模块的核心架构;

  • 如何在业务中接入使用;

  • 核心源码详解(含快照与缓存机制)。


一、为什么要写状态机?

简单来说,状态机的目标是:让状态变更有据可依、逻辑可控、行为可复用。

✅ 状态机的优势

作用说明
明确状态流转限定合法状态变化路径,防止“越级跳转”
避免状态错乱禁止非法状态变更,提高系统健壮性
解耦处理逻辑状态控制与业务处理解耦,逻辑更清晰
快照审计回溯每次状态变更都可记录快照,实现审计和回滚
易于维护扩展统一管理所有状态逻辑,提升可维护性
可视化流程状态图可辅助产品/测试理解流程

二、什么场景适合用状态机?

以下场景强烈建议引入状态机:

  • 📦 订单、支付等具有生命周期控制的业务;

  • 🔁 审核、审批、分阶段流程;

  • 🔐 需严控状态安全、防止非法状态跳转;

  • 🧾 快照记录、状态回溯需求;

  • 📈 状态驱动型架构(事件+状态控制);

而对于一些只存在“启用/禁用”这类两状态的小功能模块,状态机反而会增加不必要的复杂度。


三、我们自研状态机模块设计

1. 状态定义接口

public interface StatusDefine {
    Integer getStatus();
    String getDesc();
    String getCode();
}

订单状态示例:

public enum OrderStatusEnum implements StatusDefine {
    WAIT_PAY(0, "待支付", "WAIT_PAY"),
    PAID(1, "已支付", "PAID"),
    DELIVERING(2, "发货中", "DELIVERING"),
    FINISHED(3, "已完成", "FINISHED");
}

2. 事件定义接口

public interface StatusChangeEvent {
    StatusDefine getSourceStatus();
    StatusDefine getTargetStatus();
    String getDesc();
    String getCode();
}

订单事件示例:

public enum OrderStatusEventEnum implements StatusChangeEvent {
    PAY(WAIT_PAY, PAID, "支付成功", "PAY"),
    DELIVER(PAID, DELIVERING, "发货", "DELIVER"),
    COMPLETE(DELIVERING, FINISHED, "完成订单", "COMPLETE");
}

3. 快照抽象类

public abstract class StateMachineSnapshot {
    public abstract String getSnapshotId();
    public abstract void setSnapshotId(String id);
    public abstract Integer getSnapshotStatus();
    public abstract void setSnapshotStatus(Integer status);
}

订单快照示例:

@Data
public class OrderSnapshot extends StateMachineSnapshot {
    private String snapshotId;
    private Integer snapshotStatus;
    private String customerName;
    private String address;
    private List<String> itemList;
}

4. 状态处理器(可选)

@Component("order_PAY")
public class PayHandler implements StatusChangeHandler<OrderSnapshot> {
    public void handler(String bizId, StatusChangeEvent event, OrderSnapshot snapshot) {
        // 扣库存、发消息等
    }
}

5. 状态机子类

@Component
public class OrderStateMachine extends AbstractStateMachine<OrderSnapshot> {
    public OrderStateMachine(StateMachinePersister p, BizSnapshotService s, RedisTemplate r) {
        super(p, s, r);
    }

    protected String getName() { return "order"; }
    protected StatusDefine getInitState() { return OrderStatusEnum.WAIT_PAY; }
    protected void postProcessor(OrderSnapshot snapshot) {
        log.info("订单状态变更为:{}", snapshot.getSnapshotStatus());
    }
}

四、实际使用示例

@Autowired
private OrderStateMachine orderStateMachine;

public void 创建订单(String orderId) {
    OrderSnapshot snapshot = new OrderSnapshot();
    snapshot.setSnapshotId(orderId);
    snapshot.setCustomerName("张三");
    orderStateMachine.start(orderId, OrderStatusEnum.WAIT_PAY, snapshot);
}

public void 支付订单(String orderId) {
    orderStateMachine.changeStatus(orderId, OrderStatusEventEnum.PAY, new OrderSnapshot());
}

五、为什么要保存业务快照?

状态表示“发生了什么”,快照表示“发生时的详细数据”。

以订单为例,不同状态下的快照记录不同数据:

状态快照信息
待支付商品列表、价格、地址
已支付支付流水号、金额、支付时间
发货中快递公司、运单号
已完成签收时间、评价信息

📌 快照的用途

  • ✅ 审计回溯:出问题时可还原状态当时的数据;

  • ✅ 支持缓存:提升快照查询性能;

  • ✅ 保留历史记录:用于日志、数据分析;


六、源码详解:AbstractStateMachine

AbstractStateMachine<T> 是整个状态机的“大脑”,封装了初始化、变更、缓存、快照存储等功能。

🔁 changeStatus 流程简述:

  1. 查询当前状态 → 校验合法性;

  2. 加载事件处理器 → 执行业务逻辑(可选);

  3. 状态入库 → 快照入库;

  4. 清除 Redis 缓存;

  5. 调用 postProcessor() 执行收尾逻辑。

🧠 核心机制亮点:

  • 状态合法校验:防止越级跳转;

  • 事件处理器动态注入:order_PAY 命名方式插件化;

  • 快照深合并机制:保留旧值,叠加新数据;

  • Redis 缓存机制:避免频繁查库。


七、小项目是否适合使用?

情况是否适用理由
状态极少、流程简单❌ 不建议if-else 足够
状态有多个且需审计快照✅ 推荐状态机更规范
未来状态扩展可能性大✅ 推荐易维护扩展

✅ 总结

状态机框架适合中大型系统,将状态流转逻辑结构化、业务处理解耦、数据可回溯,是复杂系统必备的技术选项之一。

欢迎点赞、收藏、评论交流你在项目中的状态管理经验!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值