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



所有评论(0)