华东檀梦工艺品有限责任公司

您的位置:首頁 >寬帶 > 正文

今日熱聞!聊聊Cola-StateMachine輕量級狀態機的實現

來源:博客園 時間:2023-06-07 12:57:34
背景

在分析Seata的saga模式實現時,實在是被其復雜的 json 狀態語言定義文件勸退,我是有點沒想明白為啥要用這么來實現狀態機;盲猜可能是基于可視化的狀態機設計器來定制化流程,更方便快捷且上手快吧,畢竟可以通過UI直接操作,設計狀態流轉圖,但我暫時不太能get到。對于Saga模式的實現,之前的博文中已經闡述了基于狀態機模式實現Saga,是比較常見且合適的做法,因此了解了下Java中的狀態機實現方案,以后有相關的業務場景也可以直接上手使用狀態機。

Cola-StateMachine

Cola-StateMachine組件是一種輕量級的、無狀態的、基于注解的狀態機實現,可以方便地管理訂單等業務對象的狀態轉換。COLA框架的狀態機使用了連貫接口(Fluent Interfaces)來定義狀態和事件,以及對應的動作和檢查。COLA框架的狀態機是COLA 4.0應用架構的一部分,旨在控制復雜度,提高開發效率。開發背景可見實現一個狀態機引擎,教你看清DSL的本質。


(相關資料圖)

基礎模型

在Cola-StateMachine組件中有如下的抽象概念模型:

1.State:狀態2.Event:事件,狀態由事件觸發,引起變化3.Transition:流轉,表示從一個狀態到另一個狀態4.External Transition:外部流轉,兩個不同狀態之間的流轉5.Internal Transition:內部流轉,同一個狀態之間的流轉6.Condition:條件,表示是否允許到達某個狀態7.Action:動作,到達某個狀態之后,可以做什么8.StateMachine:狀態機

Cola-StateMachine鏈路圖業務應用示例

基于訂單業務的場景,做一個簡單的demo。

關閉訂單的簡單流程圖

關閉訂單簡單的狀態流轉圖

添加依賴

    com.alibaba.cola    cola-component-statemachine    4.3.1

定義一個訂單的實體類、訂單狀態的枚舉值、訂單事件的枚舉值

@Data@Builderpublic class Order {    public OrderStatusEnum orderStatusEnum;    public Integer orderId;    public String orderName;}public enum OrderStatusEnum {    INIT("0", "待付款"),    WAITING_FOR_DELIVERY("1", "待發貨"),    HAVE_BEEN_DELIVERY("2", "已發貨"),    CLOSE("3", "已取消");    private final String code;    private final String info;    OrderStatusEnum(String code, String info)    {        this.code = code;        this.info = info;    }    public String getCode()    {        return code;    }    public String getInfo()    {        return info;    }}public enum OrderEvent {    /**     * 用戶關閉     */    USER_CLOSE("0", "用戶取消"),    /**     * 管理員關閉     */    ADMIN_CLOSE("1", "后臺取消"),    /**     * 超時關閉     */    OVERTIME_CLOSE("2", "超時取消"),    /**     * 檢查錯誤關閉     */    CHECK_ERROR_CLOSE("3", "上級審核取消"),    /**     * 用戶付費     */    USER_PAY("4", "用戶支付");    /**     * 密碼     */    private final String code;    /**     * 信息     */    private final String info;    /**     * 訂單事件     *     * @param code 密碼     * @param info 信息     */    OrderEvent(String code, String info) {        this.code = code;        this.info = info;    }    /**     * 獲取代碼     *     * @return {@link String}     */    public String getCode() {        return code;    }    /**     * 獲取信息     *     * @return {@link String}     */    public String getInfo() {        return info;    }}

在容器啟動的時候注冊一個訂單狀態變更的工廠

@Componentpublic class StateMachineBuilderConfig {    @Autowired    UserCloseAction userCloseAction;    @Bean("orderOperaMachine")    public StateMachine orderOperaMachine() {        String ORDER_OPERA = "order_opera";        StateMachineBuilder builder = StateMachineBuilderFactory.create();        //訂單從初始化狀態-待發貨-狀態-轉到-關閉訂單狀態--用戶關閉        builder.externalTransitions()                .fromAmong(OrderStatusEnum.INIT, OrderStatusEnum.WAITING_FOR_DELIVERY)                .to(OrderStatusEnum.CLOSE)                .on(OrderEvent.USER_CLOSE)                .when(checkCondition())                .perform(userCloseAction);        //訂單從-初始化狀態-已發貨-待發貨--轉到-關閉訂單狀態--后臺操作人員關閉        builder.externalTransitions()                .fromAmong(OrderStatusEnum.INIT, OrderStatusEnum.HAVE_BEEN_DELIVERY, OrderStatusEnum.WAITING_FOR_DELIVERY)                .to(OrderStatusEnum.CLOSE)                .on(OrderEvent.ADMIN_CLOSE)                .when(checkCondition())                .perform(doAction());        //訂單從等待發貨狀態-轉為-訂單關閉狀態-超時關閉        builder.externalTransition()                .from(OrderStatusEnum.WAITING_FOR_DELIVERY)                .to(OrderStatusEnum.CLOSE)                .on(OrderEvent.OVERTIME_CLOSE)                .when(checkCondition())                .perform(doAction());        //訂單從待發貨狀態--轉為-訂單關閉狀態-上級審批不通過關閉        builder.externalTransition()                .from(OrderStatusEnum.WAITING_FOR_DELIVERY)                .to(OrderStatusEnum.CLOSE)                .on(OrderEvent.CHECK_ERROR_CLOSE)                .when(checkCondition())                .perform(doAction());        //訂單從初始化狀態--轉為待發貨狀態--用戶支付完畢動        builder.externalTransition()                .from(OrderStatusEnum.INIT)                .to(OrderStatusEnum.WAITING_FOR_DELIVERY)                .on(OrderEvent.USER_PAY)                .when(checkCondition())                .perform(doAction());        StateMachine orderOperaMachine = builder.build(ORDER_OPERA);        //打印uml圖        String plantUML = orderOperaMachine.generatePlantUML();        System.out.println(plantUML);        return orderOperaMachine;    }    private Condition checkCondition() {        return (ctx) -> {            return true;        };    }    private Action doAction() {        return (from, to, event, ctx) -> {            System.out.println(ctx.getOrderName() + " 正在操作 " + ctx.getOrderId() + " from:" + from + " to:" + to + " on:" + event);        };    }}

在定義一個特殊的,只是舉個例子,可以通過集成的方式集成實現一個用戶關單的具體操作

@Componentpublic class UserCloseAction implements Action {    @Override    public void execute(OrderStatusEnum from, OrderStatusEnum to, OrderEvent event, Order context) {        System.out.println("用戶關閉流程開始走了");        System.out.println("從這個狀態-【" + from.getInfo() + "】-轉為+【" + to.getInfo() + "】 的狀態");        System.out.println("上下文信息:" + context.toString());        System.out.println("中間執行的一些操作.......");        System.out.println("用戶關閉流程完畢了");    }}

定義一個 controller 的操作接口

@RestControllerpublic class OrderOperaController {    @Autowired    @Qualifier("orderOperaMachine")    StateMachine orderOperaMachine;    /**     * 場景1-用戶關閉訂單     *     * @return {@link Boolean}     */    @RequestMapping("userclose")    public Boolean userCloseOrder() {        //把訂單狀態改為關閉        String machineId = orderOperaMachine.getMachineId();        System.out.println(machineId);        Order order = Order.builder().orderId(1).orderName("用戶").orderStatusEnum(OrderStatusEnum.INIT).build();        OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.INIT,OrderEvent.USER_CLOSE, order);        System.out.println(orderStatusEnum.toString());        return true;    }    /**     * 場景2-管理員關閉訂單     *     * @return {@link Boolean}     */    @RequestMapping("adminClose")    public Boolean adminCloseOrder() {        //把訂單狀態改為關閉        Order order = Order.builder().orderId(1).orderName("后臺操作人員").orderStatusEnum(OrderStatusEnum.HAVE_BEEN_DELIVERY).build();        OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.HAVE_BEEN_DELIVERY, OrderEvent.ADMIN_CLOSE, order);        System.out.println(orderStatusEnum.toString());        return true;    }    /**     * 場景3-超時關閉訂單     *     * @return {@link Boolean}     */    @RequestMapping("overTimeclose")    public Boolean overTimeCloseOrder() {        //把訂單狀態改為關閉        Order order = Order.builder().orderId(1).orderName("超時了關閉訂單")                .orderStatusEnum(OrderStatusEnum.WAITING_FOR_DELIVERY).build();        //OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.CLOSE, OrderEvent.OVERTIME_CLOSE, order);        OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.WAITING_FOR_DELIVERY, OrderEvent.OVERTIME_CLOSE, order);        System.out.println(orderStatusEnum.toString());        return true;    }    /**     * 場景4-檢查錯誤關閉訂單     *     * @return {@link Boolean}     */    @RequestMapping("checkErrorClose")    public Boolean checkErrorCloseOrder() {        //把訂單狀態改為關閉        Order order = Order.builder().orderId(1).orderName("上級檢查錯誤").orderStatusEnum(OrderStatusEnum.WAITING_FOR_DELIVERY).build();        OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.WAITING_FOR_DELIVERY, OrderEvent.CHECK_ERROR_CLOSE, order);        System.out.println(orderStatusEnum.toString());        return true;    }}

啟動程序

安裝UML

隨便新建一個uml文件,然后將啟動程序的控制臺輸出內容復制到uml中

最后運行下

標簽:

相關閱讀