天天看點

Spring狀态機StateMachine架構---企業開發實戰(含代碼)

Spring狀态機StateMachine架構—企業開發實戰(含代碼)

​ 最近,在工作中上級派了一個任務給我,叫我寫下狀态機。一開始我聽了是直接懵逼的,我都不知道什麼是狀态機。後來聽了業務需求後才慢慢弄懂,這個狀态機的作用是修改訂單狀态,為了讓業務代碼能夠高效複用。這又引出了第二問題了,要怎樣實作狀态機,是寫一堆if-else判斷嗎?一開始我的想法就是這樣,後來上網查了狀态機,發現有個StateMachine架構,然後就去看了官方文檔https://docs.spring.io/spring-statemachine/docs/2.0.2.RELEASE/reference/htmlsingle/#with-enablestatemachinefactory。當然期間在使用過程中也踩了不少的坑,就把自己一些心得寫下來,當然自己沒閱讀過什麼源碼,隻能是自己一些粗淺的見解而已。僅供參考。

一、狀态流程圖

這個流程圖我是感覺非常重要的,要清楚每個狀态的流轉,和哪種事件會觸發什麼事件流轉這都是很重要的。下面是我自己工作的一個狀态流程圖

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-Olhxfhjh-1631519370357)(D:\MyData\ligy112\AppData\Roaming\Typora\typora-user-images\image-20210913135419684.png)]

二、了解下幾個基本常用的元件

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

這是配置規則,表示從WAIT_FOR_PAY->CLOSED,需要CLOSE事件來觸發。這是比較簡單的一種,沒有guard判斷,直接流轉到CLOSED狀态。

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

下面一種,則是多了一個.guard(),這是判斷,相當于java裡面if的判斷條件,這個自定義判斷的類必須實作Guard接口,重寫裡面evaluate方法,這個方法就是傳回boolean。值得一提的是,每個規則都可以配置action,可以直接在後面加上.action(),也可以用@WithStateMachine和@OnTransition兩個注解配合用寫下自己業務代碼。這個action表示,滿足整個鍊路規則後才要做的是。

.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()
           

前面兩種是比較簡單的,一個事件隻會流轉到一個狀态,上面這個就是比較複雜點,也是業務上經常會用,一個event會有幾種狀态,first-then-last,就相當于if-else if,隻要滿足一個guard判斷就不會往下流轉,注意這裡有幾個坑,後面說下的。

三、代碼

3.1 引入依賴包

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

3.2 定義狀态枚舉和事件枚舉

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 定義狀态機規則和配置狀态機

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 配置guard判斷類

這部分是可以優化,因為我看官網的文檔,每個判斷都要建立guard類重寫evaluate方法。我不想建太多類,就內建一個類裡面了。我本意是想用工廠模式想建立Guard類,但是本人開發經驗不是很豐富。

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類

這裡有用兩種方式,一種是注解,另外一種是實作Action類重寫execute方法,至于為什麼用兩種,後面會有說的,這也是其中一個坑。

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 其他類

這個類是為了發message保證header一緻,當然也可以寫死保證一緻。

StateMachineHeaderNameConstants
           
public class StateMachineHeaderNameConstants {

    //交易訂單
    public static final String TRADE_ORDER = "tradeOrder";
}
           

3.6 使用工具類

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());
    }
}
           

這裡說一下,為什麼要用StateMachinePersister,這個主要作用是做持久化的,當然還有做redis持久化,我這邊沒用到就沒寫。而在這裡引用是為了初始化狀态機的狀态,能讓狀态機一進來就是自己想要的狀态的。不然觸發不了流轉規則。

四、出現的一些問題(自己踩過的坑)

4.1 使用withChoice,一定要在在初始化上加上choice,不然的話不生效。

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-VJjSM7rM-1631519370359)(D:\MyData\ligy112\AppData\Roaming\Typora\typora-user-images\image-20210913151753492.png)]

這裡一定要記得配置對應的狀态。

4.2 withChoice的觸發

withChoice的生效,是又上一個withExternal流程後,如果直接設定withChoice裡面的狀态,是不能執行guard和action。是以我這邊才會多出一個中間态,就是為了觸發流轉判斷的。

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-IN3DCLWI-1631519370361)(D:\MyData\ligy112\AppData\Roaming\Typora\typora-user-images\image-20210913152331982.png)]

4.3 guard裡面的target問題。

在一般情況下,我們都可以switch來判斷,不用建立一堆guard類,但是在withChoice的first,then,lat裡面的狀态是流轉後,但是不能用這個判斷,自己可以列印下日志試下。這也是我為什麼在GuardFactory裡現有許多内部類的原因。

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-kDN8BvIp-1631519370362)(D:\MyData\ligy112\AppData\Roaming\Typora\typora-user-images\image-20210913152927415.png)]

4.4 withChoice的action事件,不能用注解觸發。

其實這個問題跟上一個問題有點像,用注解方式的話,一般都會有target,但是在withChoice裡的target是上一個withExternal的target,是以是不會生效的。下圖是一般情況的action配置注解。

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

是以這也是為什麼會有兩種方式的Action。

五、總結

StateMachine還是挺好的,能夠聲調很多ifelse,更重要是讓業務解耦。當然以上是我個人實際開發遇到的問題,就想記錄下來而已。有什麼錯誤,請指出,畢竟我也是個菜鳥,這就當是我個人的筆記而已。