Spring state machine StateMachine framework---Enterprise development practice (including code)

Spring state machine StateMachine framework—enterprise development practice (including code)

​ Recently, my superior assigned me a task at work and asked me to write down a state machine. I was confused at first when I heard it. I didn’t even know what a state machine was. Later, after listening to the business requirements, I gradually understood that the function of this state machine is to modify the order status in order to enable efficient reuse of business code. This leads to the second question, how to implement the state machine, is it to write a bunch of if-else judgments? This was what I thought at first. Then I checked the state machine online and found out that there is a StateMachine framework, and then I went to read the official documentation https://docs.spring.io/spring-statemachine/docs/2.0.2.RELEASE/ reference/htmlsingle/#with-enablestatemachinefactory. Of course, I also encountered a lot of pitfalls during use, so I wrote down some of my own experiences. Of course, I have never read any source code, so I can only express my superficial opinions. for reference only.

1. Status flow chart

I feel that this flow chart is very important. It is very important to know the flow of each state and what kind of event will trigger what flow of events. The following is a status flow chart of my own work
Insert image description here

2. Understand several basic commonly used components

.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
.target(TradeOrderStateMachineEnum.CLOSED)
.event(TradeOrderEvent.CLOSE).and()

This is a configuration rule, which means that from WAIT_FOR_PAY->CLOSED, the CLOSE event is required to trigger. This is a relatively simple one. There is no guard judgment and it flows directly to the CLOSED state.

.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
.target(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER)
.event(TradeOrderEvent.AUDIT).guard(tradeOrderGuardFactory.new TradeOrderGuard()).and()

The following one has an additional .guard(), which is a judgment, equivalent to the judgment condition of if in Java. This custom judgment class must implement the Guard interface and override the evaluate method inside. This method returns boolean. It is worth mentioning that each rule can be configured with an action. You can add .action() directly after it, or you can use the two annotations @WithStateMachine and @OnTransition to write your own business code. This action indicates that it should be done only after satisfying the entire link rules.

.withChoice()
.source(TradeOrderStateMachineEnum.AUDIT_CHOICE)
.first(TradeOrderStateMachineEnum.COMPLETED, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard2(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_SIGN, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard3(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard4(),new TradeOrderChoiceAction())
.last(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT).and()

The first two are relatively simple. An event will only flow to one state. The above one is more complicated and is often used in business. An event can have several states. First-then-last is equivalent to if- else if, as long as a guard judgment is met, it will not flow downward. Note that there are several pitfalls here, which will be discussed later.

3. Code

3.1 Introduce dependency packages
<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-starter</artifactId>
    <version>2.0.1.RELEASE</version>
</dependency>

3.2 Define status enumeration and event enumeration

TradeOrderStateMachineEnum
public enum TradeOrderStateMachineEnum {

    WAIT_FOR_PAY(10, "待付款"),
    WAIT_FOR_AUDIT(20, "待评审"),
    WAIT_FOR_DELIVER(30, "待发货"),
    WAIT_FOR_SIGN(40, "待签收"),
    WAIT_FOR_EVALUATE(45, "待评价"),
    COMPLETED(98, "完成"),
    CLOSED(99, "关闭"),
    AUDIT_CHOICE(1000, "评审选择态"),
    SIGN_CHOICE(1001, "签收选择态");

    private final Integer value;
    private final String desc;
    private static final Map<Integer, TradeOrderStateMachineEnum> valueMap = (Map) Arrays.stream(values()).collect(Collectors.toMap(TradeOrderStateMachineEnum::getValue, Function.identity()));

    private TradeOrderStateMachineEnum(Integer value, String desc) {
        this.value = value;
        this.desc = desc;
    }

    public Integer getValue() {
        return this.value;
    }

    public String getDesc() {
        return this.desc;
    }

    public static TradeOrderStateMachineEnum fromValue(Integer value) {
        return (TradeOrderStateMachineEnum) Optional.ofNullable(valueMap.get(value)).orElseThrow(() -> {
            return new RuntimeException("can not find the enum for this value: " + value);
        });
    }
}
TradeOrderEvent
public enum TradeOrderEvent {
    PAY,//付款
    CLOSE,//关闭订单
    CANCEL,//取消数量
    AUDIT,//评审
    DELIVER,//发货
    SIGN,//签收
    EVALUATE;//评价
}
3.2 Define state machine rules and configure state machine
TradeOrderStateMachineBuilder
@Component
@EnableStateMachine(name= TradeOrderStateMachineBuilder.MACHINEID_TO)
public class TradeOrderStateMachineBuilder {

    private static final TradeOrderGuardFactory tradeOrderGuardFactory= new TradeOrderGuardFactory();

    @Autowired
    private BeanFactory beanFactory;
    private Logger logger = LoggerFactory.getLogger(getClass());
    public final static String MACHINEID_TO = "MACHINEID_TO";//TO状态机
    public StateMachine<TradeOrderStateMachineEnum, TradeOrderEvent> build() throws Exception {
        StateMachine<TradeOrderStateMachineEnum, TradeOrderEvent> stateMachine = build(beanFactory);
        logger.info("状态机ID:"+stateMachine.getId());
        stateMachine.start();
        return stateMachine;
    }
    /**
     * 构建状态机
     * -构建TO单状态机
     * @param beanFactory
     * @return
     * @throws Exception
     */
    public StateMachine<TradeOrderStateMachineEnum, TradeOrderEvent> build(BeanFactory beanFactory) throws Exception {
        StateMachineBuilder.Builder<TradeOrderStateMachineEnum, TradeOrderEvent> builder = StateMachineBuilder.builder();
        builder.configureConfiguration()
                .withConfiguration()
                .machineId(MACHINEID_TO)
                .beanFactory(beanFactory);

        builder.configureStates()
                .withStates()
                .initial(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
                .choice(TradeOrderStateMachineEnum.AUDIT_CHOICE)
                .choice(TradeOrderStateMachineEnum.SIGN_CHOICE)
                .states(EnumSet.allOf(TradeOrderStateMachineEnum.class));

        builder.configureTransitions()
                //支付后,从待付款到待审核
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
                .target(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
                .event(TradeOrderEvent.PAY).and()
                //取消订单,从待付款到关闭
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
                .target(TradeOrderStateMachineEnum.CLOSED)
                .event(TradeOrderEvent.CLOSE).and()
                //取消数量,从待审核到审核选择态
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
                .target(TradeOrderStateMachineEnum.AUDIT_CHOICE)
                .event(TradeOrderEvent.CANCEL).and()
                //取消数量,从审核选择态->待发货,待签收,待评价,完成任意一种状态
                .withChoice()
                .source(TradeOrderStateMachineEnum.AUDIT_CHOICE)
                .first(TradeOrderStateMachineEnum.COMPLETED, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard(),new TradeOrderChoiceAction())
                .then(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard2(),new TradeOrderChoiceAction())
                .then(TradeOrderStateMachineEnum.WAIT_FOR_SIGN, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard3(),new TradeOrderChoiceAction())
                .then(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard4(),new TradeOrderChoiceAction())
                .last(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT).and()
                //审核后,从待审核到待发货
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
                .target(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER)
                .event(TradeOrderEvent.AUDIT).guard(tradeOrderGuardFactory.new TradeOrderGuard()).and()
                //发货后,从待发货到待签收
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER)
                .target(TradeOrderStateMachineEnum.WAIT_FOR_SIGN)
                .event(TradeOrderEvent.DELIVER).guard(tradeOrderGuardFactory.new TradeOrderGuard()).and()
                //签收后,从待签收到待签收选择态
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_SIGN)
                .target(TradeOrderStateMachineEnum.SIGN_CHOICE)
                .event(TradeOrderEvent.SIGN).and()
                //签收后,从待签收选择态到待评价或者到已完成
                .withChoice()
                .source(TradeOrderStateMachineEnum.SIGN_CHOICE)
                .first(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE, tradeOrderGuardFactory.new TradeOrderSignChoiceGuard(),new TradeOrderChoiceAction())
                .then(TradeOrderStateMachineEnum.COMPLETED, tradeOrderGuardFactory.new TradeOrderSignChoiceGuard2(),new TradeOrderChoiceAction())
                .last(TradeOrderStateMachineEnum.WAIT_FOR_SIGN).and()
                //评价后,从待评价到已完成
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE)
                .target(TradeOrderStateMachineEnum.COMPLETED)
                .event(TradeOrderEvent.EVALUATE);
        return builder.build();
    }

    @Bean(name = "tradeOrderStateMachinePersister")
    public StateMachinePersister<TradeOrderStateMachineEnum, TradeOrderEvent, TradeOrder> getOrderPersister() {
        return new DefaultStateMachinePersister<>(new StateMachinePersist<TradeOrderStateMachineEnum, TradeOrderEvent, TradeOrder>() {
            @Override
            public void write(StateMachineContext<TradeOrderStateMachineEnum, TradeOrderEvent> context, TradeOrder contextObj) {
            }
            @Override
            public StateMachineContext<TradeOrderStateMachineEnum, TradeOrderEvent> read(TradeOrder contextObj) {
                StateMachineContext<TradeOrderStateMachineEnum, TradeOrderEvent> result = new DefaultStateMachineContext(TradeOrderStateMachineEnum.fromValue(contextObj.getOrderState()),
                        null, null, null, null, MACHINEID_TO);
                return result;
            }
            ;
        });


    }
}
3.3 Configure guard judgment class

This part can be optimized, because according to the documentation on the official website, every judgment must create a new guard class and override the evaluate method. I don’t want to create too many classes, so I just integrate them into one class. My original intention was to use factory mode to create a new Guard class, but my development experience is not very rich.

TradeOrderGuardFactory
public class TradeOrderGuardFactory {

    private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;

    public class TradeOrderAuditChoiceGuard implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {

        @Override
        public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder) && !StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                return true;
            }
            return false;
        }
    }
    public class TradeOrderAuditChoiceGuard2 implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {

        @Override
        public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder) && StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                return true;
            }
            return false;
        }
    }
    public class TradeOrderAuditChoiceGuard3 implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {

        @Override
        public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllDeliver(tradeOrder)){
                return true;
            }
            return false;
        }
    }

    public class TradeOrderAuditChoiceGuard4 implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {

        @Override
        public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllAudit(tradeOrder) ){
                return true;
            }
            return false;
        }
    }

    public class TradeOrderSignChoiceGuard implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {

        @Override
        public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder) && StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                return true;
            }
            return false;
        }
    }

    public class TradeOrderSignChoiceGuard2 implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {

        @Override
        public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder) && !StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                return true;
            }
            return false;
        }
    }

    public class TradeOrderGuard implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
        @Override
        public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
            boolean result=false;

            System.out.println(context.getSource().getId());
            System.out.println(context.getTarget().getId());
            switch (context.getTarget().getId()) {
                case WAIT_FOR_DELIVER:
                    return WAIT_FOR_DELIVER(context);
                case WAIT_FOR_SIGN:
                    return WAIT_FOR_SIGN(context);
                case SIGN_CHOICE:
                    return SIGN_CHOICE(context);
                case WAIT_FOR_EVALUATE:
                    return WAIT_FOR_EVALUATE(context);
                case COMPLETED:
                    return COMPLETED(context);
                default:
                    break;
            }
            return result;
        }

        private boolean WAIT_FOR_DELIVER(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllAudit(tradeOrder)){
                return true;
            }
            return false;
        }

        private boolean WAIT_FOR_SIGN(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllDeliver(tradeOrder)){
                return true;
            }
            return false;
        }
        private boolean SIGN_CHOICE(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder)){
                return true;
            }
            return false;
        }

        private boolean WAIT_FOR_EVALUATE(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder)&& StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                return true;
            }
            return false;
        }

        private boolean COMPLETED(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder)&& !StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                return true;
            }
            return false;
        }

    }

    private boolean isAllAudit(TradeOrder tradeOrder) {
        for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
                tradeOrderDetail.setCancelQty(0);
            }
            if(ObjectUtils.isEmpty(tradeOrderDetail.getAuditQty())){
                tradeOrderDetail.setAuditQty(0);
            }
            //待评审的数量
            if(tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getAuditQty()!=0){
                return false;
            }
        }
        return true;
    }

    private boolean isAllDeliver(TradeOrder tradeOrder) {
        for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
                tradeOrderDetail.setCancelQty(0);
            }
            if(ObjectUtils.isEmpty(tradeOrderDetail.getDeliverQty())){
                tradeOrderDetail.setDeliverQty(0);
            }
            //待评审的数量
            if(tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getDeliverQty()!=0){
                return false;
            }
        }
        return true;
    }

    private boolean isAllSign(TradeOrder tradeOrder) {
        for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
                tradeOrderDetail.setCancelQty(0);
            }
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCustSignQty())){
                tradeOrderDetail.setCustSignQty(0);
            }
            //代签收的数量
            if((tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getCustSignQty()!=0)){
                return false;
            }
        }
        return true;
    }
}
3.4 action class

There are two methods used here, one is annotation, and the other is to implement the Action class and override the execute method. As for why two methods are used, I will talk about it later. This is also one of the pitfalls.

TradeOrderChoiceAction
@Slf4j
public class TradeOrderChoiceAction implements Action<TradeOrderStateMachineEnum, TradeOrderEvent> {


    private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;

    @Override
    public void execute(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
        System.out.println(context.getTarget().getId());
        switch (context.getTarget().getId()) {
            case AUDIT_CHOICE:
                AUDIT_CHOICE(context);
                break;
            case SIGN_CHOICE:
                SIGN_CHOICE(context);
                break;
            default:
                break;
        }
    }

    private void SIGN_CHOICE(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
        TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
        log.info("签收事件之前,订单的状态为:{}"+tradeOrder.getOrderState());
        if(isAllSign(tradeOrder)){
            //全部签收,并且是2C,则为待评价状态
            if(StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE.getValue());
            }else{
                //全部签收,并且是2B,则为完成状态
                tradeOrder.setOrderState(TradeOrderStateMachineEnum.COMPLETED.getValue());
            }
        }
        log.info("签收事件之后,订单的状态为:{}"+tradeOrder.getOrderState());
    }

    private void AUDIT_CHOICE(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
        TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
        log.info("取消数量事件之前,订单的状态为:{}"+tradeOrder.getOrderState());
        //如果全部签收,则可能是待评价状态或者是完成状态
        if(isAllSign(tradeOrder)){
            //2C,则为待评价状态
            if(StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE.getValue());
            }else{
                //2B,则为完成状态
                tradeOrder.setOrderState(TradeOrderStateMachineEnum.COMPLETED.getValue());
            }
        }else if(isAllDeliver(tradeOrder)){
            //如果全部发货,则为代签收状态
            tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_SIGN.getValue());
        }else if(isAllAudit(tradeOrder)){
            //如果全部审核,则为待发货状态
            tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getValue());
        }
        log.info("取消数量事件之后,订单的状态为:{}"+tradeOrder.getOrderState());
    }


    private boolean isAllAudit(TradeOrder tradeOrder) {
        for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
                tradeOrderDetail.setCancelQty(0);
            }
            if(ObjectUtils.isEmpty(tradeOrderDetail.getAuditQty())){
                tradeOrderDetail.setAuditQty(0);
            }
            //待评审的数量
            if(tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getAuditQty()!=0){
                return false;
            }
        }
        return true;
    }

    private boolean isAllDeliver(TradeOrder tradeOrder) {
        for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
                tradeOrderDetail.setCancelQty(0);
            }
            if(ObjectUtils.isEmpty(tradeOrderDetail.getDeliverQty())){
                tradeOrderDetail.setDeliverQty(0);
            }
            //待评审的数量
            if(tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getDeliverQty()!=0){
                return false;
            }
        }
        return true;
    }

    private boolean isAllSign(TradeOrder tradeOrder) {
        for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
                tradeOrderDetail.setCancelQty(0);
            }
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCustSignQty())){
                tradeOrderDetail.setCustSignQty(0);
            }
            //代签收的数量
            if((tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getCustSignQty()!=0)){
                return false;
            }
        }
        return true;
    }
}
TradeOrderAction
@WithStateMachine(id= TradeOrderStateMachineBuilder.MACHINEID_TO)
public class TradeOrderAction {

    private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;

    @OnTransition(source = "WAIT_FOR_PAY", target = "WAIT_FOR_AUDIT")
    public void CUSTOMER_PAY(Message<TradeOrderEvent> message) {
        TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
        tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT.getValue());
    }

    @OnTransition(source = "WAIT_FOR_PAY", target = "CLOSED")
    public void CUSTOMER_CLOSE(Message<TradeOrderEvent> message) {
        TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
        tradeOrder.setOrderState(TradeOrderStateMachineEnum.CLOSED.getValue());
    }

    @OnTransition(source = "WAIT_FOR_AUDIT", target = "WAIT_FOR_DELIVER")
    public void CUSTOMER_AUDIT(Message<TradeOrderEvent> message) {
        TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
        tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getValue());
    }
    @OnTransition(source = "WAIT_FOR_DELIVER", target = "WAIT_FOR_SIGN")
    public void CUSTOMER_DELIVER(Message<TradeOrderEvent> message) {
        TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
        tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_SIGN.getValue());
    }

    @OnTransition(source = "WAIT_FOR_EVALUATE", target = "COMPLETED")
    public void CUSTOMER_EVALUATE(Message<TradeOrderEvent> message) {
        TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
        tradeOrder.setOrderState(TradeOrderStateMachineEnum.COMPLETED.getValue());
    }


}
3.5 Other categories

This class is used to send messages to ensure that the headers are consistent. Of course, it can also be hard-coded to ensure consistency.

StateMachineHeaderNameConstants
public class StateMachineHeaderNameConstants {

    //交易订单
    public static final String TRADE_ORDER = "tradeOrder";
}
3.6 Using tools
StateMachineUtils
@Slf4j
public class StateMachineUtils {
    private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;

    @Autowired
    private TradeOrderStateMachineBuilder tradeOrderStateMachineBuilder;
    @Resource(name = "tradeOrderStateMachinePersister")
    private StateMachinePersister tradeOrderStateMachinePersister;

    public void execute(TradeOrder tradeOrder, TradeOrderEvent event) throws Exception{
        log.debug("调用状态机前的订单状态为>>>>>>>>>>{}"+tradeOrder.getOrderState());
        //获取TO状态机
        StateMachine<TradeOrderStateMachineEnum, TradeOrderEvent> stateMachine = tradeOrderStateMachineBuilder.build();
        Message message = MessageBuilder.withPayload(event).setHeader(TRADE_ORDER, tradeOrder).build();
        //初始化状态机
        tradeOrderStateMachinePersister.restore(stateMachine,tradeOrder);
        stateMachine.sendEvent(message);
        log.debug("调用状态机后的订单状态为>>>>>>>>>>{}"+tradeOrder.getOrderState());
    }
}

Let’s talk about why StateMachinePersister is used. This main function is for persistence, and of course for redis persistence. I haven’t written it since I haven’t used it here. The reference here is to initialize the state of the state machine, so that the state machine can be in the desired state as soon as it comes in. Otherwise, the transfer rules will not be triggered.

4. Some problems that arise (pitfalls that I have stepped on)

4.1 When using withChoice, you must add choice to the initialization, otherwise it will not take effect.

[External link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-VJjSM7rM-1631519370359) (D:\MyData\ligy112\AppData\Roaming\Typora\typora-user-images\ image-20210913151753492.png)]

Be sure to remember to configure the corresponding status here.

4.2 Triggering of withChoice

WithChoice takes effect after the last withExternal process. If you directly set the status in withChoice, guard and action cannot be executed. That's why I have an additional intermediate state here, just to trigger the flow judgment.

[External link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-IN3DCLWI-1631519370361) (D:\MyData\ligy112\AppData\Roaming\Typora\typora-user-images\ image-20210913152331982.png)]

4.3 Target issues in guard.

Under normal circumstances, we can switch to judge without creating a bunch of guard classes. However, the status in first, then, and lat of withChoice is after the transfer, but this judgment cannot be used. You can print the log and try it yourself. This is why I have many internal classes in GuardFactory.

4.4 The withChoice action event cannot be triggered by annotations.

In fact, this question is a bit similar to the previous question. If you use annotation, there will usually be a target, but the target in withChoice is the target of the previous withExternal, so it will not take effect. The figure below shows the general action configuration annotations.

@OnTransition(source = "WAIT_FOR_PAY", target = "WAIT_FOR_AUDIT")

So this is why there are two ways of Action.

5. Summary

StateMachine is still very good, it can save a lot of ifelse, and more importantly, it decouples the business. Of course, the above are the problems I encountered in actual development, and I just wanted to record them. If there are any mistakes, please point them out. After all, I am also a novice, so these are just my personal notes.

Guess you like

Origin blog.csdn.net/qq798867485/article/details/120268675