
为什么要单独设计订单状态机很多同学写订单模块第一反应是用一个 Integer 字段存状态然后在业务代码里写一堆 if-else 判断“当前状态能不能执行这个操作”。订单少时没问题一旦状态变多、角色变多用户、司机、系统定时任务都能改状态这种写法很快会失控非法状态跳转难以排查新加一个状态要改动十几处。本文分享一套开源代驾系统的订单状态机设计。源码地址https://gitee.com/zhoujian6666/biaoma-ride-car-service代驾订单有哪些状态代驾业务的完整生命周期待接单用户已下单等待附近司机抢单已接单司机抢单成功正前往用户位置司机已到达司机抵达上车点行程中用户上车开始计费待支付行程结束等待用户付款已支付支付成功若有垫付/余额支付可能直接流转待评价等待用户评价已完成订单闭环已取消下单后未接单可取消接单后取消涉及违约金判断核心设计状态 事件 转移状态机三要素State状态枚举列出所有合法状态Event事件触发状态变更的动作如“司机接单”“到达上车点”“支付成功”“用户取消”Transition转移定义“当前状态 事件 → 目标状态”的合法映射关键思想是把“能不能跳转”的规则集中声明而不是散落在业务代码里。落地方式1. 状态枚举集中管理用枚举定义状态码、状态描述避免魔法数字。订单实体里的状态字段只允许取枚举值。2. 事件与转移表用一张转移映射表声明规则例如待接单 接单事件 → 已接单已接单 到达事件 → 司机已到达司机已到达 开始行程事件 → 行程中行程中 结束行程事件 → 待支付待支付 支付成功事件 → 待评价任意未完成态 取消事件 → 已取消带条件校验3. 统一的状态变更入口所有状态修改走同一个方法传入订单、事件方法内部查转移表找不到合法映射就抛异常找到则更新状态并记录变更日志。这样非法跳转在入口就被拦截不会污染数据。事件驱动状态变更后要做什么状态变更本身和“变更后的副作用”要解耦。例如订单变为“已接单”后要推送通知给用户通知其他抢单司机该单已被接给接单侧写一条数据这些副作用不写在状态机里而是状态变更成功后发布领域事件由各自的监听者处理推送走 Netty异步任务走 RabbitMQ。状态机保持纯粹只负责“状态对不对”。取消场景的特殊处理取消是最复杂的事件待接单时取消直接关闭无费用已接单后取消根据司机是否已到达、等待时长判断是否收空驶费/违约金行程中一般不允许取消需走异常订单流程这类带业务条件的判断放在事件处理层状态机只接收“已经算好的、合法的取消结果”。状态流转的可观测性每次状态变更写一条流转日志订单 ID、原状态、新状态、触发事件、操作人、时间。线上排查“订单怎么变成这个状态了”时这张表是最重要的依据也方便后台给客服展示订单时间轴。项目其他模块除订单状态机外系统还包含智能派单、Netty WebSocket 实时推送、计价引擎、Redis 在线司机管理、微信/支付宝支付、分润对账。后端 管理后台已开源Java 17 Spring Boot 2.7 Vue可直接部署。源码https://gitee.com/zhoujian6666/biaoma-ride-car-service欢迎评论区交流状态机设计觉得有用欢迎 Star。