You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Statemachine状态切换异常求助:ARRIVED→IN_PROGRESS等转换失败

Spring Statemachine状态切换异常排查与修复

问题描述

使用Spring Statemachine时遇到状态转换异常:初始状态为PENDING,**ARRIVED→IN_PROGRESS、IN_PROGRESS→COMPLETED**的转换仅在从PENDING直接切换到对应状态时才能生效;若先从PENDING切换到WAITING,再切换到ARRIVED,后续无法完成ARRIVED→IN_PROGRESS的转换,同理IN_PROGRESS→COMPLETED也无法正常触发。

允许的状态转换规则

/**
 * 仅允许以下状态转换:
 *
 * PENDING -> WAITING / ARRIVED / NO_SHOW / USER_CANCELLED / TEAM_CANCELLED
 * WAITING -> ARRIVED / NO_SHOW
 * ARRIVED -> IN_PROGRESS / USER_CANCELLED / TEAM_CANCELLED
 * IN_PROGRESS -> COMPLETED / USER_CANCELLED / TEAM_CANCELLED
 * COMPLETED -> 无后续转换
 * USER_CANCELLED -> 无后续转换
 * TEAM_CANCELLED -> 无后续转换
 * NO_SHOW -> 无后续转换
 */

当前状态机配置代码

状态配置

public void configure(final StateMachineStateConfigurer<Appointment.Status, Appointment.Status> states) throws Exception {
    states
            .withStates()
            .initial(Appointment.Status.PENDING)
            .states(EnumSet.allOf(Appointment.Status.class))
            .end(Appointment.Status.COMPLETED)
            .end(Appointment.Status.USER_CANCELLED)
            .end(Appointment.Status.TEAM_CANCELLED)
            .end(Appointment.Status.NO_SHOW);
}

转换配置

public void configure(final StateMachineTransitionConfigurer<Appointment.Status, Appointment.Status> transitions) throws Exception {
    transitions
            .withExternal()
            .source(Appointment.Status.PENDING)
            .target(Appointment.Status.WAITING)
            .event(Appointment.Status.WAITING)
            .and()

            .withExternal()
            .source(Appointment.Status.PENDING)
            .target(Appointment.Status.ARRIVED)
            .event(Appointment.Status.ARRIVED)
            .and()

            .withExternal()
            .source(Appointment.Status.PENDING)
            .target(Appointment.Status.NO_SHOW)
            .event(Appointment.Status.NO_SHOW)
            .and()

            .withExternal()
            .source(Appointment.Status.PENDING)
            .target(Appointment.Status.USER_CANCELLED)
            .event(Appointment.Status.USER_CANCELLED)
            .and()

            .withExternal()
            .source(Appointment.Status.PENDING)
            .target(Appointment.Status.TEAM_CANCELLED)
            .event(Appointment.Status.TEAM_CANCELLED)
            .and()

            .withExternal()
            .source(Appointment.Status.WAITING)
            .target(Appointment.Status.ARRIVED)
            .event(Appointment.Status.ARRIVED)
            .and()

            .withExternal()
            .source(Appointment.Status.WAITING)
            .target(Appointment.Status.NO_SHOW)
            .event(Appointment.Status.NO_SHOW)
            .and()

            .withExternal()
            .source(Appointment.Status.ARRIVED)
            .target(Appointment.Status.IN_PROGRESS)
            .event(Appointment.Status.IN_PROGRESS)
            .and()

            .withExternal()
            .source(Appointment.Status.ARRIVED)
            .target(Appointment.Status.USER_CANCELLED)
            .event(Appointment.Status.USER_CANCELLED)
            .and()

            .withExternal()
            .source(Appointment.Status.ARRIVED)
            .target(Appointment.Status.TEAM_CANCELLED)
            .event(Appointment.Status.TEAM_CANCELLED)
            .and()

            .withExternal()
            .source(Appointment.Status.IN_PROGRESS)
            .target(Appointment.Status.COMPLETED)
            .event(Appointment.Status.COMPLETED)
            .and()

            .withExternal()
            .source(Appointment.Status.IN_PROGRESS)
            .target(Appointment.Status.USER_CANCELLED)
            .event(Appointment.Status.USER_CANCELLED)
            .and()

            .withExternal()
            .source(Appointment.Status.IN_PROGRESS)
            .target(Appointment.Status.TEAM_CANCELLED)
            .event(Appointment.Status.TEAM_CANCELLED);
}

问题排查与修复方案

从配置代码来看,状态转换规则的定义完全符合需求,不存在语法或逻辑错误。异常大概率出现在状态机的使用流程中,可按以下步骤排查:

1. 验证状态转换是否真实完成

添加状态监听器,跟踪状态变化和转换失败信息,确认从WAITING到ARRIVED的转换是否成功,状态是否正确更新:

stateMachine.addStateListener(new StateMachineListenerAdapter<Appointment.Status, Appointment.Status>() {
    @Override
    public void stateChanged(State<Appointment.Status, Appointment.Status> from, State<Appointment.Status, Appointment.Status> to) {
        System.out.printf("状态从 %s 切换到 %s%n", from == null ? "初始状态" : from.getId(), to.getId());
    }

    @Override
    public void transitionFailed(Transition<Appointment.Status, Appointment.Status> transition, StateMachine<Appointment.Status, Appointment.Status> stateMachine, Exception exception) {
        System.err.printf("转换失败:%s → %s,异常信息:%s%n", 
            transition.getSource().getId(), transition.getTarget().getId(), exception.getMessage());
    }
});

如果发现WAITING→ARRIVED的转换未触发,需检查事件发送是否正确,或状态机是否处于WAITING状态。

2. 确认事件发送时机与内容

确保在状态机处于ARRIVED状态时,发送的是IN_PROGRESS事件;同理,处于IN_PROGRESS状态时发送COMPLETED事件。避免在状态未更新完成时提前发送事件。

3. 检查状态持久化(若使用)

如果项目中使用了StateMachinePersister实现状态持久化,需确认持久化逻辑是否正确,确保状态变更被正确保存和加载,避免状态机上下文丢失。

4. 验证状态机实例管理

如果状态机是单例实例,需确保每次测试前重置状态机到初始状态(PENDING),避免之前的状态残留影响后续测试;若为多实例,需确认每个实例的状态独立管理。

内容的提问来源于stack exchange,提问作者Viorel Moldovanu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 01:40:34