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