松鼠状态机流转-@Transit
疑问
状态from to合法性校验,都是在代码中手动进行的吗,不是状态机自动进行的?
注解中from状态,代表当前状态
和谁校验:上下文中初始状态
怎么根据注解找到执行方法的
分析代码,创建运单,怎么关联到注解 @Transit(from = "unsigned", to = "unsigned", on = "CREATE"),的,怎么根据注解找到执行方法的?
package com.smcv.mall.tasks.statemachine;import com.alibaba.fastjson.JSON;
import com.smcv.mall.api.tasks.model.JghcTaskDTO.JghcTaskCreateForDP;
import com.smcv.mall.tasks.constant.ErrConstant;
import com.smcv.mall.tasks.ddd.domain.impl.TaskStatusTriggerService;
import com.smcv.mall.tasks.ddd.domain.inf.IDPStatusService;
import com.smcv.mall.tasks.exception.YlTaskException;
import com.smcv.mall.tasks.model.dto.DPTaskStatusChangeReqDTO;
import com.smcv.mall.tasks.statemachine.constant.DPEventEnum;
import com.smcv.mall.tasks.statemachine.constant.DPStateEnum;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.squirrelframework.foundation.fsm.annotation.StateMachineParameters;
import org.squirrelframework.foundation.fsm.annotation.Transit;
import org.squirrelframework.foundation.fsm.annotation.Transitions;
import org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine;/**
*/
@Transitions({
//创建运单
@Transit(from = "unsigned", to = "unsigned", on = "CREATE"),/*** 未指派 - 执行中* 1.扫码抢单* 2.派单员指派* 3.自提单*/@Transit(from = "unsigned", to = "executing", on = "ASSIGN_EXCUTING", callMethod = "assignExcuting"),/*** 已指派 - 执行中* 1.扫码抢单* 2.派单员指派*/@Transit(from = "signed", to = "executing", on = "ASSIGN_EXCUTING", callMethod = "assignExcuting"),/*** 未指派 - 已指派* 1.超时指派* 2.司机抢单*/@Transit(from = "unsigned", to = "signed", on = "ASSIGN_SIGNED"),//执行运单@Transit(from = "signed", to = "executing", on = "EXECUTING"),//前往途经点@Transit(from = "waitStart", to = "toStart", on = "CHILD_TOSTART", callMethod = "childTostart"),//前往途经点 兼容老版本@Transit(from = "contacted", to = "toStart", on = "CHILD_TOSTART", callMethod = "childTostart"),//到达途经点@Transit(from = "toStart", to = "toEnd", on = "CHILD_TOEND"),//待出发 - 到达途经点@Transit(from = "waitStart", to = "toEnd", on = "CHILD_WAITTOEND"),//前往下一个途经点@Transit(from = "toEnd", to = "toNextStart", on = "CHILD_TONEXTSTART", callMethod = "childTonextstart"),//前往下一个途经点 兼容老版本@Transit(from = "payed", to = "toNextStart", on = "CHILD_TONEXTSTART", callMethod = "childTonextstart"),//运单完成@Transit(from = "executing", to = "finished", on = "FINISH"),//运单取消@Transit(from = "unsigned", to = "canceled", on = "CANCEL", callMethod = "cancel"),@Transit(from = "signed", to = "canceled", on = "CANCEL", callMethod = "cancel"),@Transit(from = "executing", to = "canceled", on = "CANCEL", callMethod = "cancel"),@Transit(from = "canceled", to = "canceled", on = "CANCEL", callMethod = "cancel"),//幂等性
})
@StateMachineParameters(stateType = DPStateEnum.class, eventType = DPEventEnum.class, contextType = DPContext.class)
@Slf4j(topic = "task")
public class DPStateMachine extends AbstractUntypedStateMachine {private static final String LOG_PRE = "【{}】地跑状态流转,orderNumber=[{}],fromState=[{}],toState=[{}],event=[{}],context=[{}]";protected ApplicationContext applicationContext;private IDPStatusService idpStatusService;/*** 初始化时候会调用,类似后置处理器** @param applicationContext*/
public void postConstruct(ApplicationContext applicationContext) {this.applicationContext = applicationContext;
}/*** 开始执行之前的方法** @param fromState* @param event* @param context*/
@Override
protected void beforeTransitionBegin(Object fromState, Object event, Object context) {log.info(LOG_PRE, Thread.currentThread().getStackTrace()[1].getMethodName(), ((DPContext) context).getOrderNumber(), fromState, null, event, context);//获取状态机服务类idpStatusService = (IDPStatusService) ((DPContext) context).getIStatusService();
}/*** 事件触发之前被调用** @param fromState* @param toState* @param event* @param context*/
@Override
protected void beforeActionInvoked(Object fromState, Object toState, Object event, Object context) {log.info(LOG_PRE, Thread.currentThread().getStackTrace()[1].getMethodName(), ((DPContext) context).getOrderNumber(), fromState, toState, event, context);/*** 状态合理性校验*/DPContext dpContext = (DPContext) context;if (!dpContext.isReentrantLock()) {//重入锁,不需要状态合理性校验DPEventEnum dpEventEnum = (DPEventEnum) event;((TaskStatusTriggerService) dpContext.getIStatusService()).checkFromState(fromState, toState, context, dpEventEnum);}
}/*** 非法状态流转** @param fromState* @param event* @param context*/
@Override
protected void afterTransitionDeclined(Object fromState, Object event, Object context) {log.info(LOG_PRE, Thread.currentThread().getStackTrace()[1].getMethodName(), ((DPContext) context).getOrderNumber(), fromState, null, event, context);throw new YlTaskException(ErrConstant.BUSI_RETURN_ERR, "状态流转异常");
}/*** 正常执行完毕后执行的方法** @param fromState* @param toState* @param event* @param context*/
@Override
protected void afterTransitionCompleted(Object fromState, Object toState, Object event, Object context) {log.info(LOG_PRE, Thread.currentThread().getStackTrace()[1].getMethodName(), ((DPContext) context).getOrderNumber(), fromState, toState, event, context);
}/*** 执行异常** @param fromState* @param toState* @param event* @param context*/
@SneakyThrows
@Override
protected void afterTransitionCausedException(Object fromState, Object toState, Object event, Object context) {log.info(LOG_PRE, Thread.currentThread().getStackTrace()[1].getMethodName(), ((DPContext) context).getOrderNumber(), fromState, toState, event, context);//此处不调用父类方法,异常不会被抛出//super.afterTransitionCausedException(fromState, toState, event, context);if (getLastException().getTargetException() != null) {//重写父类方法,向外抛出自定义异常throw getLastException().getTargetException();} else {super.afterTransitionCausedException(fromState, toState, event, context);}
}/*** 状态机执行完毕必须执行的方法,不管成功/失败与否** @param fromState* @param toState* @param event* @param context*/
@Override
protected void afterTransitionEnd(Object fromState, Object toState, Object event, Object context) {log.info(LOG_PRE, Thread.currentThread().getStackTrace()[1].getMethodName(), ((DPContext) context).getOrderNumber(), fromState, toState, event, context);super.afterTransitionEnd(fromState, toState, event, context);
}/*** 创建运单** @param fromState* @param toState* @param dpEventEnum* @param dpContext*/
public void transitFromunsignedTounsignedOnCREATE(DPStateEnum fromState, DPStateEnum toState, DPEventEnum dpEventEnum, DPContext dpContext) {log.info(LOG_PRE, Thread.currentThread().getStackTrace()[1].getMethodName(), dpContext.getOrderNumber(), fromState, toState, dpEventEnum, dpContext);JghcTaskCreateForDP jghcTaskCreateForDP = dpContext.getJghcTaskCreateForDP();jghcTaskCreateForDP.setServiceCode(dpContext.getCreateChannel());idpStatusService.createTask(jghcTaskCreateForDP);
}/*** 指派运单[状态流转到执行中]** @param fromState* @param toState* @param dpEventEnum* @param dpContext*/
public void assignExcuting(DPStateEnum fromState, DPStateEnum toState, DPEventEnum dpEventEnum, DPContext dpContext) {log.info(LOG_PRE, Thread.currentThread().getStackTrace()[1].getMethodName(), dpContext.getOrderNumber(), fromState, toState, dpEventEnum, dpContext);DPTaskStatusChangeReqDTO dpTaskStatusChangeReqDTO = dpContext.getDpTaskStatusChangeReqDTO();dpTaskStatusChangeReqDTO.setFromState(fromState);dpTaskStatusChangeReqDTO.setToState(toState);idpStatusService.assignExcuting(dpTaskStatusChangeReqDTO);
}/*** 指派运单[状态流转到已指派]** @param fromState* @param toState* @param dpEventEnum* @param dpContext*/
public void transitFromunsignedTosignedOnASSIGN_SIGNED(DPStateEnum fromState, DPStateEnum toState, DPEventEnum dpEventEnum, DPContext dpContext) {log.info(LOG_PRE, Thread.currentThread().getStackTrace()[1].getMethodName(), dpContext.getOrderNumber(), fromState, toState, dpEventEnum, dpContext);DPTaskStatusChangeReqDTO dpTaskStatusChangeReqDTO = dpContext.getDpTaskStatusChangeReqDTO();dpTaskStatusChangeReqDTO.setFromState(fromState);dpTaskStatusChangeReqDTO.setToState(toState);idpStatusService.assignSigned(dpTaskStatusChangeReqDTO);
}/*** 执行运单** @param fromState* @param toState* @param dpEventEnum* @param dpContext*/
public void transitFromsignedToexecutingOnEXECUTING(DPStateEnum fromState, DPStateEnum toState, DPEventEnum dpEventEnum, DPContext dpContext) {log.info(LOG_PRE, Thread.currentThread().getStackTrace()[1].getMethodName(), dpContext.getOrderNumber(), fromState, toState, dpEventEnum, dpContext);DPTaskStatusChangeReqDTO dpTaskStatusChangeReqDTO = dpContext.getDpTaskStatusChangeReqDTO();dpTaskStatusChangeReqDTO.setFromState(fromState);dpTaskStatusChangeReqDTO.setToState(toState);
// dpContext.setDpTaskStatusChan
在给定的代码中,状态机的行为是通过 @Transit
注解定义的,这些注解指明了状态转移的规则和事件,并且关联了执行的方法。具体来说,@Transit
注解配置了状态从 from
到 to
的转移条件,以及触发该状态转移的事件 (on
)。有些 @Transit
注解还指定了 callMethod
属性,表示当该转移发生时,应该调用哪个方法来执行相关逻辑。
注解分析
java
@Transit(from = "unsigned", to = "unsigned", on = "CREATE")
这个注解表示当事件 CREATE
发生时,状态从 unsigned
转移到 unsigned
。这是一种状态机的事件与状态的定义。
方法执行关联
-
方法名与注解中的
callMethod
对应: 在注解中,可以通过callMethod
属性指定状态转换时需要调用的方法。如果callMethod
被指定,状态机在状态转换时会执行这个方法。例如:java
@Transit(from = "unsigned", to = "executing", on = "ASSIGN_EXCUTING", callMethod = "assignExcuting")
这个注解表示当事件
ASSIGN_EXCUTING
发生时,状态从unsigned
转移到executing
,并且会调用assignExcuting
方法。 -
方法调用的方式: 对于状态机中的状态转换,当事件触发时,注解框架(在这个例子中是
Squirrel
状态机库)会通过反射查找并调用注解中指定的callMethod
方法。具体来说,框架会使用事件、状态和上下文信息来找到正确的方法并执行。 -
如何根据注解找到执行方法:
- 在代码执行的过程中,当事件发生并触发状态机的状态转换时,状态机框架会检查该状态转换是否带有
callMethod
。 - 如果
callMethod
指定了方法名,框架会使用反射机制去查找与该方法名匹配的实例方法,并调用它。 - 例如,当
on = "CREATE"
事件发生时,如果没有指定callMethod
,那么该状态转移会自动被处理(状态变化本身不需要调用方法)。但如果callMethod
被指定,状态转移发生时就会调用指定的方法(如transitFromunsignedTounsignedOnCREATE
方法)。
- 在代码执行的过程中,当事件发生并触发状态机的状态转换时,状态机框架会检查该状态转换是否带有
-
如何定义方法:
- 每个方法签名都应该与状态机的定义保持一致,即方法应该接受
fromState
、toState
、event
、context
等参数。 - 例如,在
transitFromunsignedTounsignedOnCREATE
方法中,签名是:java
public void transitFromunsignedTounsignedOnCREATE(DPStateEnum fromState, DPStateEnum toState, DPEventEnum dpEventEnum, DPContext dpContext)
这个方法的作用是处理从unsigned
到unsigned
状态的转换,并且根据CREATE
事件的触发执行特定的操作。其实现逻辑可以是创建运单等操作。
- 每个方法签名都应该与状态机的定义保持一致,即方法应该接受
事件触发与方法调用的流程
- 触发事件:当事件(如
CREATE
)被触发时,状态机会根据状态机的配置来查找与该事件相关的状态转移。 - 查找对应的
@Transit
注解:框架会检查所有的@Transit
注解,找出符合当前事件的注解。例如,如果当前事件是CREATE
,它会检查所有定义了on = "CREATE"
的注解。 - 调用指定的方法:如果某个
@Transit
注解指定了callMethod
属性(如transitFromunsignedTounsignedOnCREATE
),框架会使用反射来调用相应的方法。
例如,下面这个注解:
java
@Transit(from = "unsigned", to = "unsigned", on = "CREATE")
当 CREATE
事件发生时,框架会寻找与该事件相关的状态转移逻辑。如果没有指定 callMethod
,则仅处理状态转移。如果有指定方法(如 transitFromunsignedTounsignedOnCREATE
),它会调用该方法来处理相关逻辑。
总结
- 注解中的
@Transit
定义了事件与状态的转移规则。 callMethod
属性允许开发者为状态转移指定自定义的处理方法。- 状态机框架使用反射来根据注解动态调用对应的方法。
callMethod
指定的方法需要具备一定的签名(通常包括fromState
、toState
、event
和context
)。
通过这种方式,状态机可以动态地根据事件和状态转移配置,执行预定的操作。
如果直接根据事件on,callMethod 找到对应的方法,状态from to,有什么用
你提到的问题是关于 状态机 中 @Transit
注解中的 from
和 to
参数,如果在事件处理时,已经通过 on
和 callMethod
找到对应的方法,那么 from
和 to
的作用是什么?这两个参数的意义其实是非常重要的,下面我会逐一解释它们在状态机中的作用。
1. 状态 (from
和 to
) 用于验证状态转换
尽管 callMethod
指定了方法来执行特定的逻辑,但是 from
和 to
这两个参数仍然有它们的关键作用,尤其是在 验证和控制状态流转的合法性 方面。
主要作用:
- 状态的合法性检查:
from
和to
定义了状态机中状态的合法转换路径。它们确保了事件不会导致非法的状态跳跃,保证了状态流转的一致性。 - 保障状态流转的一致性:通过定义明确的状态流转路径(从
from
到to
),状态机可以防止不符合逻辑的状态转移。例如,如果状态机中定义了只能从 "未指派" (unsigned
) 到 "已指派" (signed
) 的状态流转,那么如果你尝试从 "执行中" (executing
) 到 "已指派" (signed
) 的转移,状态机应该阻止该转移。
示例:
java
@Transit(from = "unsigned", to = "signed", on = "ASSIGN_SIGNED")
这里,from = "unsigned"
和 to = "signed"
表示只有在状态是 unsigned
时,事件 ASSIGN_SIGNED
才能触发状态从 unsigned
到 signed
的转移。如果状态已经是 signed
,即使事件 ASSIGN_SIGNED
被触发,状态机会拒绝该转移。
2. 事件的路由控制
在复杂的状态机中,可能存在多个事件和方法都可以引发相似的状态转换。例如,你可能有多个事件(如 ASSIGN_EXCUTING
)可以从不同的源状态(如 unsigned
和 signed
)转换到同一个目标状态(如 executing
)。在这种情况下,from
和 to
就起到了关键作用,它们有助于确保事件在正确的上下文中被触发,并确保事件与状态之间的关系是明确的。
例如:
java
@Transit(from = "unsigned", to = "executing", on = "ASSIGN_EXCUTING", callMethod = "assignExcuting") @Transit(from = "signed", to = "executing", on = "ASSIGN_EXCUTING", callMethod = "assignExcuting")
这两个注解表明,ASSIGN_EXCUTING
事件可以发生在两种不同的源状态 (unsigned
或 signed
) 下,转移到相同的目标状态 (executing
)。但是,实际的状态流转只有在这些源状态下才会发生。因此,from
和 to
参数确保了事件和状态之间的明确关系。
3. 帮助调试和日志记录
from
和 to
的状态信息在调试和日志记录中也非常有用。在实际的运行时,日志会记录状态机的状态流转,包括当前的源状态 (fromState
) 和目标状态 (toState
),这些信息帮助开发者了解状态机的运行轨迹。
例如,在方法中,通常会记录每个状态转换的日志:
java
log.info("Transitioning from {} to {} via event {}. Context: {}", fromState, toState, event, context);
这种日志信息有助于在问题出现时快速定位问题并排查逻辑错误。
4. 确保不同的状态转换逻辑
from
和 to
不仅帮助确定状态转移是否合法,还能在不同的状态之间实现不同的逻辑。例如,你可能希望根据当前状态 (fromState
) 的不同执行不同的操作或方法。虽然 callMethod
指定了通用的执行方法,但你仍然可能希望根据不同的状态执行不同的逻辑。
例如:
java
@Transit(from = "unsigned", to = "signed", on = "ASSIGN_SIGNED", callMethod = "assignUnsignedToSigned") @Transit(from = "signed", to = "executing", on = "ASSIGN_EXCUTING", callMethod = "assignSignedToExecuting")
在这个例子中,尽管事件 ASSIGN_SIGNED
和 ASSIGN_EXCUTING
看起来可能是类似的动作,但它们发生在不同的状态下,因此你可能会希望它们触发不同的处理逻辑。from
和 to
的定义确保了在正确的上下文中执行正确的逻辑。
5. 保持代码清晰和可维护
from
和 to
提供了清晰的状态流转图,开发者可以从注解中快速了解状态机的行为和状态转换路径。这对于团队协作、文档编写以及代码的长期维护都非常有帮助。
总结
尽管 callMethod
可以直接指定要调用的方法,但是 from
和 to
参数在状态机中扮演着不可或缺的角色:
- 状态合法性验证:确保状态流转是符合预定义的规则的,避免出现不符合逻辑的状态跳跃。
- 路由控制:帮助事件在正确的上下文中触发,从正确的源状态转换到目标状态。
- 调试与日志记录:提供调试信息,帮助开发者追踪状态转移的路径。
- 不同状态的逻辑控制:根据
from
和to
确定具体的业务逻辑。
因此,from
和 to
并非多余,它们在状态机的结构化和规则验证中发挥了重要作用,确保状态转移的过程是可控和符合业务逻辑的。
具体如何状态合法性验证
状态合法性验证:确保状态流转是符合预定义的规则的,避免出现不符合逻辑的状态跳跃。 这个具体如何实现的?from代表当前状态?这个参数怎么传入的
在状态机的实现中,状态合法性验证是确保状态转移符合预定义的规则、避免非法的状态跳跃的关键。具体来说,from
代表当前的状态,to
代表目标状态,验证这两个状态的合法转移可以通过一系列的机制来实现。
1. from
代表当前状态,to
代表目标状态
from
:表示当前的状态,即事件触发时所处的状态。to
:表示目标状态,即状态转换后将进入的状态。
状态合法性验证的核心任务就是在事件发生时,检查当前状态(from
)和目标状态(to
)是否符合预定义的合法转换规则。如果不合法,状态机应该拒绝该转换并抛出异常或返回错误。
2. 如何实现状态合法性验证?
实现状态合法性验证的方式可以分为几种,不同的状态机框架或自定义实现可能会有不同的方法。以下是常见的几种实现方式:
2.1. 手动映射与检查
一种最简单的实现方式是手动维护一个合法的状态转换表,在处理事件时检查当前状态和目标状态是否允许转换。
例如:
- 使用一个状态转换表来存储所有合法的
from -> to
映射。 - 当事件发生时,根据当前状态(
from
)和目标状态(to
)查表,检查转换是否合法。
假设我们有如下状态和状态转换表:
java
enum State { UNSIGNED, SIGNED, EXECUTING, COMPLETED } Map<State, Set<State>> validTransitions = new HashMap<>(); validTransitions.put(State.UNSIGNED, new HashSet<>(Arrays.asList(State.SIGNED))); validTransitions.put(State.SIGNED, new HashSet<>(Arrays.asList(State.EXECUTING))); validTransitions.put(State.EXECUTING, new HashSet<>(Arrays.asList(State.COMPLETED)));
当事件触发时,我们可以检查状态是否合法:
java
public boolean isValidTransition(State fromState, State toState) { Set<State> validToStates = validTransitions.get(fromState); return validToStates != null && validToStates.contains(toState); }
在状态转移时,isValidTransition
方法就会验证 from
和 to
是否匹配预定义的合法转换。
2.2. 通过注解与反射动态绑定
在使用注解驱动的状态机框架中,状态转移规则通常通过注解(如 @Transit
)来定义。状态机会通过反射机制读取这些注解,并验证事件发生时的状态转移是否合法。
例如,在基于注解的状态机实现中,可能会有如下的代码:
java
@Transit(from = "UNSIGNED", to = "SIGNED", on = "ASSIGN_SIGNED") @Transit(from = "SIGNED", to = "EXECUTING", on = "ASSIGN_EXECUTING")
在处理事件时,框架会根据注解定义的 from
和 to
来验证当前状态和目标状态是否匹配。可以通过以下方式实现:
- 读取注解:通过反射获取方法上的注解,提取出
from
和to
状态。 - 检查合法性:比对当前状态与目标状态是否符合注解中定义的合法状态流转。
假设我们有一个 StateMachine
类,在事件发生时,我们通过反射查找匹配的转移规则:
java
public void handleEvent(String event, State currentState) throws IllegalStateException { // 获取所有标注了 @Transit 注解的方法 Method[] methods = StateMachine.class.getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(Transit.class)) { Transit transit = method.getAnnotation(Transit.class); if (transit.from().equals(currentState.name()) && transit.on().equals(event)) { // 验证目标状态是否合法 if (validTransitions(currentState, transit.to())) { // 执行状态转换方法 method.invoke(this); } else { throw new IllegalStateException("Invalid state transition from " + currentState + " to " + transit.to()); } } } } }
在这种方式下,from
和 to
都是通过注解定义的,在事件处理时,框架会动态检查当前状态和目标状态是否符合合法转移规则。
2.3. 状态机框架中的自动检查
一些状态机框架(如 Spring State Machine、Akka FSM 等)已经内建了状态转移规则的验证机制。它们通过状态机的图结构自动验证 from
和 to
之间的合法性,开发者只需要通过配置或者注解来定义状态转移,而框架会在运行时执行验证。
例如,Spring State Machine 提供了一些 API 来配置状态转换:
java
StateMachine<State, Event> stateMachine = new StateMachineBuilder.Builder<State, Event>() .configure() .withStates() .initial(State.UNSIGNED) .state(State.SIGNED) .state(State.EXECUTING) .state(State.COMPLETED) .and() .withTransitions() .between(State.UNSIGNED, State.SIGNED).perform(assignSigned()) .between(State.SIGNED, State.EXECUTING).perform(assignExecuting()) .between(State.EXECUTING, State.COMPLETED).perform(assignCompleted()) .and() .build();
在这里,框架会在 StateMachine
被激活后自动检查每个状态转移是否合法。
3. from
参数如何传入
from
代表当前状态,它通常是从当前的 状态机实例 中获取的。具体来说:
- 如果状态机是基于事件驱动的,那么
from
状态通常是触发事件时状态机当前的状态。 - 在基于注解的状态机中,
from
状态会在处理事件时动态传入。 - 在手动配置的状态机中,
from
状态会随着事件的触发而改变,通常由当前的state
属性表示。
例如,在事件处理方法中,from
通常是方法的参数或者是类中的实例变量:
java
public void handleEvent(State currentState, Event event) { // 当前状态:currentState if (isValidTransition(currentState, event)) { // 执行状态转移 } }
在注解驱动的框架中,from
可能会自动由框架通过反射从注解中获取,或者从事件的上下文中获取当前状态。
4. 总结
状态合法性验证的关键是在事件触发时检查当前状态(from
)和目标状态(to
)是否符合预定义的合法转换规则。具体实现可以通过手动维护转换表、反射检查注解或使用状态机框架的自动验证来实现。通过这些方法,我们能够确保状态机的状态流转始终是符合业务逻辑的,并避免发生不符合规则的状态跳跃。
状态合法性校验实现例子
触发点,方法执行前
/*** 事件触发之前被调用** @param fromState* @param toState* @param event* @param context*/@Overrideprotected void beforeActionInvoked(Object fromState, Object toState, Object event, Object context) {log.info(LOG_PRE, Thread.currentThread().getStackTrace()[1].getMethodName(), ((DPContext) context).getOrderNumber(), fromState, toState, event, context);/*** 状态合理性校验*/DPContext dpContext = (DPContext) context;if (!dpContext.isReentrantLock()) {//重入锁,不需要状态合理性校验DPEventEnum dpEventEnum = (DPEventEnum) event;((TaskStatusTriggerService) dpContext.getIStatusService()).checkFromState(fromState, toState, context, dpEventEnum);}}
public void checkFromState(DPStateEnum dpStateEnum_from, DPStateEnum dpStateEnum_to, DPContext dpContext, DPEventEnum eventEnum) {
// from statusgetInitialState(dpContext.getEvent(), Optional.ofNullable(dpContext.getDpTaskStatusChangeReqDTO()).map(dpTaskStatusChangeReqDTO -> dpTaskStatusChangeReqDTO.getWayBill()).orElse(""), dpContext);if (!dpContext.getInitialState().equals(dpStateEnum_from)) {log.error("状态异常");throw new YlTaskException(ErrConstant.BUSI_RETURN_ERR, "状态异常");}// to status//幂等性处理if (Arrays.asList(DPEventEnum.CANCEL).contains(eventEnum)) {String taskStatus_db = ttTask.getTaskStatus();if (taskStatus_db.equals(dpStateEnum_to.getStatusCode())) {log.warn("当前运单已处理");dpContext.setIdempotent(true);return;}}}