最近好多业务都是流程状态的传递,借此机会写词文章,记录一下,什么样的场景设计需要我们的状态机:

目前市场流行的状态机:
 1.Spring Statemachine 

  2.阿里COLA4.4

状态机框架

  • Spring Statemachine (重量级选手)1.2k+ star.
  • squirrel-foundation(松鼠)1.8k+ star.
  • cola-statemachine (可乐)6.6k+ star.

springboot合cola-statemachine 4.4.0

cola-statemachine(阿里出品)

相比Spring statemachine状态机等的复杂,功能多;我们实际业务员需要常用的功能,简单使用,所以这类就显得不简洁;再看cola-statemachine相比就是小巧、无状态、简单、轻量、性能极高的状态机DSL实现,解决业务中的状态流转问题。
github:
https://github.com/alibaba/COLA/tree/master/cola-components/cola-component-statemachine

学习这玩意什么用:

状态机优势

1、状态机建立的控制中心是跟外界低耦合的,通过event通信;
2、控制中心所有的状态都是预设好的,不会超预料;
3、状态的跳转都是有设定控制条件的,会按照预设的转移路径运动;
4、状态机还非常容易的扩展和变更,支持因业务的发展而变更或扩展复杂业务流程。

Spring Boot StateMachine实现 

<dependency>
		<groupId>org.springframework.statemachine</groupId>
		<artifactId>spring-statemachine-core</artifactId>
		<version>1.2.0.RELEASE</version>
	</dependency>

Spring Boot cola实现 

<dependency>
                <groupId>com.alibaba.cola</groupId>
                <artifactId>cola-component-statemachine</artifactId>
                <version>4.4.0-SNAPSHOT</version>
            </dependency>

状态机实现步骤:

         第一步生成一个状态机

        第二步:设置一个外部状态转移类型的builder 
        第三步:设置状态机的id和ready
        第四步:触发状态机

概念:
State:状态

Event:事件,状态由事件触发,引起变化

Transition:流转,表示从一个状态到另一个状态

External Transition:外部流转,两个不同状态之间的流转

Internal Transition:内部流转,同一个状态之间的流转

Condition:条件,表示是否允许到达某个状态

Action:动作,到达某个状态之后,可以做什么

StateMachine:状态机

完成状态机配置:状态机的初始状态和所有状态机的转移规则

整个状态的调度逻辑主要依靠配置方式的定义,而所有的业务逻辑操作都被定义在了状态监听器中,其实状态监听器可以实现的功能远不止上面我们所述的内容,它还有更多的事件捕获

都是技术人员介绍那么多都是废话,直接上代码它不香

好多代码都是demo根本没有实际试用起来。今天给大家上点干活。

无脑选择spring自带的状态机:

场景一:电商的订单,和发货,退货的状态

企业无商城,你玩什么。因此我们的实际场景就是我们经常看到的订单状态的流转

状态机定义三件事:

1.定义枚举状态

2.定义枚举事件

3.定义事件流转的触发条件

有人就说了,为什么定义枚举呢?

答:你想定义啥,定义啥,你有本事,可以直接定义字典,定义接口,定义静态字段。

1.先给我们的业务订单定义状态

public enum OrderState implements IEnum<String> {
    CREATED(0, ""),//新创建订单
    PENDING(10, "Pending"),//支付待确认
    WAIT_SHIP(20, "Preparing"),//待发货
    REFUND_CHOICE(30, ""),//订单退款逻辑选择器,伪状态,数据库中不存在

    DELIVERING(40, "Shipped"),//已发货[运输中],所有的子单都发货,订单变更为已发货状态
    DELIVERED(50, "Shipped"),//已妥投[运输中],所有的子单都妥投,订单变更为妥投状态

    RECEIVED(60, "Received"),//已签收,所有的子单都签收(刨除逆向订单),订单变更为已签收
    COMPLETED(70, "Completed"),//完成,所有的子单都完成(刨除逆向订单),订单变更为已完成
    CANCELED(80, "Canceled"),//已取消,未支付订单、取消订单

    REFUND_AUDIT(90, "Reviewing"),//退货审核,所有子单都处于该状态
    REFUND_AUDIT_CHOICE(95, ""),//订单退款审核逻辑选择器,伪状态,数据库中不存在
    REFUNDING(100, "Refunding"),//退款中,所有子单都处于该状态
    REFUNDED(110, "Refunded");//已退款,所有子单都处于该状态

    /**
     * 此值为了解决父订单的状态取值而设,越大说明业务流程越深,时间离现在越近,订单的状态取此值最小的子单的状态
     */
    @Getter
    private final int logicDepth;

    @Getter
    @Setter
    private String customerDesc;

    OrderState(int depth, String desc) {
        this.logicDepth = depth;
        this.customerDesc = desc;
    }

    @Override
    public String getValue() {
        return name();
    }
}

2.定义事件

public enum OrderEvent implements IEnum<String> {
    COD_CHECKOUT,//创建订单后选择COD支付
    COD_CONFIRM,//支付确认
    COD_CANCEL,//支付取消
    COD_TIMEOUT,//支付确认超时

    PAY_SUCCESS,//PPD支付成功
    PAY_TIMEOUT,//支付超时
    PAY_CANCEL,//取消支付

    USER_CANCEL,//支付后/支付确认后用户取消
    PLATFORM_CANCEL,//支付后平台取消
    TIMEOUT_CANCEL,//订单匹配超时取消

    DO_SHIP,//发货
    ORDER_ARRIVE,//妥投
    CONFIRM_RECEIVE,//用户签收
    ORDER_REVIEW,//订单评价

    REJECT_ORDER,//用户拒签
    APPLY_RETURN,//申请退货退款

    RETURN_AGGREE,//退货退款审核通过
    RETURN_REJECT,//退货退款审核拒绝

    GOODS_RETURNED,//平台收到退回货物
    DO_REFUND,//收到货后,平台操作发起退款

    PPD_REFUND_SUCCESS,;//退款成功回调
    COD_REFUND_SUCCESS,//退款成功回调

    @Override
    public String getValue() {
        return name();
    }
}

3.定义状态机配置

@Configuration
public class OrderStateMachineConfig {

    final static String MACHINEID = "orderItemStateMachine";

    @Bean
    public StateMachinePersister<OrderState, OrderEvent, OrderStateAware> orderItemPersister(
            StateMachinePersist<OrderState, OrderEvent, OrderStateAware> orderItemStateMachinePersist) {
        return new DefaultStateMachinePersister<>(orderItemStateMachinePersist);
    }

    @Bean
    public StateMachine<OrderState, OrderEvent> orderItemStateMachine() throws Exception {
        // @formatter:off
        StateMachineBuilder.Builder<OrderState, OrderEvent> builder = StateMachineBuilder.builder();

        builder.configureConfiguration()
                .withConfiguration()
                .machineId(MACHINEID)
                .beanFactory(beanFactory);

        builder.configureStates()
                .withStates()
                .initial(OrderState.CREATED)
                .choice(OrderState.REFUND_CHOICE)
                .choice(OrderState.REFUND_AUDIT_CHOICE)
                .end(OrderState.COMPLETED)
                .end(OrderState.CANCELED)
                .end(OrderState.REFUNDED)
                .states(EnumSet.allOf(OrderState.class));

        builder.configureTransitions()
                //CREATED 状态流转
                .withExternal()
                .source(OrderState.CREATED).target(OrderState.WAIT_SHIP).event(OrderEvent.PAY_SUCCESS)
                .and().withExternal()
                .source(OrderState.CREATED).target(OrderState.CANCELED).event(OrderEvent.PAY_CANCEL)
                .and().withExternal()
                .source(OrderState.CREATED).target(OrderState.CANCELED).event(OrderEvent.PAY_TIMEOUT)
                .and().withExternal()
                .source(OrderState.CREATED).target(OrderState.PENDING).event(OrderEvent.COD_CHECKOUT)

                //PENDING 状态流转
                .and().withExternal()
                .source(OrderState.PENDING).target(OrderState.WAIT_SHIP).event(OrderEvent.COD_CONFIRM)
                .and().withExternal()
                .source(OrderState.PENDING).target(OrderState.CANCELED).event(OrderEvent.COD_CANCEL)
                .and().withExternal()
                .source(OrderState.PENDING).target(OrderState.CANCELED).event(OrderEvent.COD_TIMEOUT)

                //WAIT_SHIP 状态流转
                .and().withExternal()
                .source(OrderState.WAIT_SHIP).target(OrderState.DELIVERING).event(OrderEvent.DO_SHIP)
                .and().withExternal()
                .source(OrderState.WAIT_SHIP).target(OrderState.REFUND_CHOICE).event(OrderEvent.USER_CANCEL)
                .and().withExternal()
                .source(OrderState.WAIT_SHIP).target(OrderState.REFUND_CHOICE).event(OrderEvent.PLATFORM_CANCEL)
                .and().withExternal()
                .source(OrderState.WAIT_SHIP).target(OrderState.REFUND_CHOICE).event(OrderEvent.TIMEOUT_CANCEL)

                //退货退款/仅退款/cancel choice流转
                .and().withChoice()
                .source(OrderState.REFUND_CHOICE)
                //如果为COD支付[wait_ship,delivered],状态变为Canceled,事物提交后返还库存、退还限购,发送异步消息通知A端
                //COD received状态 不允许退款
                .first(OrderState.CANCELED, codRefundChoiceGuard,codRefundChoiceAction)
                //否则 !cod & wait_ship & 实付=0,状态变为Refunded,事物提交后返还库存、退还限购,退还cash,发送异步消息通知A端
                .then(OrderState.REFUNDED,cashRefundChoiceGuard,cashRefundChoiceAction)
                //否则 !cod & wait_ship & 实付>0, 状态变为Refunding,事物提交后返还库存、退还限购,退还cash,调用三方支付退款api,发送异步消息通知A端
                .then(OrderState.REFUNDING, ppdRefundOnlyChoiceGuard,ppdRefundOnlyChoiceAction)
                //否则 (!cod & received)(可能包含cash支付,此时也需要审核)
                .then(OrderState.REFUND_AUDIT, ppdRefundChoiceGuard,ppdRefundChoiceAction)

                //DELIVERING 状态流转
                .and().withExternal()
                .source(OrderState.DELIVERING).target(OrderState.DELIVERED).event(OrderEvent.ORDER_ARRIVE)

                //DELEVERED 状态流转
                .and().withExternal()
                .source(OrderState.DELIVERED).target(OrderState.RECEIVED).event(OrderEvent.CONFIRM_RECEIVE)
                .and().withExternal()
                .source(OrderState.DELIVERED).target(OrderState.REFUND_CHOICE).event(OrderEvent.REJECT_ORDER)

                //RECEIVED 状态流转
                .and().withExternal()
                .source(OrderState.RECEIVED).target(OrderState.COMPLETED).event(OrderEvent.ORDER_REVIEW)
                .and().withExternal()
                .source(OrderState.RECEIVED).target(OrderState.REFUND_CHOICE).event(OrderEvent.APPLY_RETURN)

                //RETURN_AUDIT 状态流转
                .and().withExternal()
                .source(OrderState.REFUND_AUDIT).target(OrderState.COMPLETED).event(OrderEvent.RETURN_REJECT)
                .and().withExternal()
                .source(OrderState.REFUND_AUDIT).target(OrderState.REFUND_AUDIT_CHOICE).event(OrderEvent.RETURN_AGGREE)

                //审核通过后
                .and().withChoice()
                .source(OrderState.REFUND_AUDIT_CHOICE)
                //实付=0
                .first(OrderState.REFUNDED,refundAuditChoiceGuard)
                //实付>0
                .last(OrderState.REFUNDING)

                //REFUNDING 状态流转
                .and().withExternal()
                .source(OrderState.REFUNDING).target(OrderState.REFUNDED).event(OrderEvent.PPD_REFUND_SUCCESS)
//                .and().withExternal()
//                .source(OrderState.REFUNDING).target(OrderState.REFUNDED).event(OrderEvent.COD_REFUND_SUCCESS)
        ;
        // @formatter:on
        return builder.build();
    }


    @Bean
    public StateMachinePersist<OrderState, OrderEvent, OrderStateAware> orderItemStateMachinePersist() {
        return new StateMachinePersist<OrderState, OrderEvent, OrderStateAware>() {
            @Override
            public void write(StateMachineContext<OrderState, OrderEvent> context, OrderStateAware contextObj) {
            }

            @Override
            public StateMachineContext<OrderState, OrderEvent> read(OrderStateAware contextObj) {
                return new DefaultStateMachineContext<>(
                        contextObj.getState(),
                        null, null, null, null, MACHINEID);
            }
        };
    }
}

 4.定义状态机

public interface OrderEventFirer extends OrderStateAware {

    default void fireEvent(StateMachineRestorer restorer, OrderEvent event) {
        StateMachine<OrderState, OrderEvent> stateMachine = restorer.restore(this);
        stateMachine.sendEvent(event);
        OrderState newState = stateMachine.getState().getId();
        changeState(newState,event);
    }

    default void fireEvent(StateMachineRestorer restorer, OrderEvent event, Map<String,Object> headers) {
        StateMachine<OrderState, OrderEvent> stateMachine = restorer.restore(this);

        MessageBuilder<OrderEvent> builder = MessageBuilder.withPayload(event);
        for (Map.Entry<String, Object> entry : headers.entrySet()) {
            builder.setHeader(entry.getKey(), entry.getValue());
        }
        Message<OrderEvent> message = builder.build();
        stateMachine.sendEvent(message);
        OrderState newState = stateMachine.getState().getId();
        changeState(newState,event);
    }

    default void changeState(OrderState newState, OrderEvent event){
        OrderState currentState = this.getState();
        Assert.state(currentState != newState,"Undefined state transition.state=" + currentState + ",event=" + event);
        this.setBackState(currentState);
        this.setState(newState);
        this.setLatestAction(event);
    }

    void setState(OrderState state);

    void setBackState(OrderState state);

    void setLatestAction(OrderEvent event);
}

5.业务代码使用

@Override
    public void doDelivered(String orderItemUuid, TrackDetailsDto tracking) {
        OrderItem orderItem = baseMapper.findByUuid(orderItemUuid);
        orderItem.setDeliverAt(tracking.getDeliverDate());
        orderItem.fireEvent(stateMachineRestorer, OrderEvent.ORDER_ARRIVE);
        if(null == orderItem.getPaymentAt()) {
            orderItem.setPaymentAt(orderItem.getDeliverAt());
        }
        baseMapper.updateById(orderItem);

        Order order = orderDao.findByUuid(orderItem.getOrderUuid());
        if (null == order.getDeliveredCount()) {
            order.setDeliveredCount(0);
        }
        order.setDeliveredCount(order.getDeliveredCount() + 1 );

        List<OrderState> subStates = baseMapper.findByOrderUuid(order.getUuid()).stream().map(OrderItem::getState).collect(Collectors.toList());
        if (order.stateModified(subStates)) {
            smsSender.sendSms(
                    SmsBizType.ORDER_SINGED,
                    order.getLanguage(),
                    order.getShippingAddressMobile());
        }
        orderDao.updateById(order);
    }

写完收工:

好多网友看了,欲哭无泪,看了这么久不能运行。白扯半天。😄 里面缺几个实现类,缺数据库表设计。这是实际代码,能贴出来嘛。根据思路去实现才是王道,比一些demo强太多了。