狀態機的基本使用

狀態機

1. 什么是狀態機

1.1 場景

在業務代碼中對一些業務狀態進行硬編碼,如果有一天更改了業務邏輯就需要更改代碼,不方便進行系統擴展和維護。

if (status == 狀態1) {// TODO
} else if(status == 狀態2) {// TODO
} ...

另外對訂單狀態的管理是散落在很多地方不方便對訂單狀態進行統一管理和維護。

1.2 使用狀態機解決問題

使用狀態機對業務狀態進行統一管理

理解狀態機設計模式需要理解四個要素:

  1. 現態:是指當前所處的狀態;
  2. 事件:觸發狀態變更的事件;
  3. 動作:當事件被觸發時,執行的操作;
  4. 次態:條件滿足后要遷往的新狀態。

例如:拿待支付狀態到派單中狀態舉例:

  1. 現態:訂單當前處于待支付狀態那么現態為待支付。
  2. 事件:用戶支付成功為事件,支付成功是條件,當條件滿足進行狀態遷移。
  3. 動作:將訂單狀態由待支付更改為派單中。
  4. 次態:派單中。

2. 實現狀態機

基于設計模式開發狀態機組件,參考代碼:statemachine.zip

在需要使用的模塊中添加狀態機依賴

<dependency><groupId>com.jzo2o</groupId><artifactId>jzo2o-statemachine</artifactId><version>1.0-SNAPSHOT</version>
</dependency>

下面以訂單業務進行舉例

2.1 狀態枚舉類

閱讀訂單狀態枚舉類,實現了 StatusDefine 狀態接口,不論是現態還是次態都需要實現狀態接口。

@Getter
@AllArgsConstructor
public enum OrderStatusEnum implements StatusDefine {NO_PAY(0, "待支付", "NO_PAY"),DISPATCHING(100, "派單中", "DISPATCHING"),NO_SERVE(200, "待服務", "NO_SERVE"),SERVING(300, "服務中", "SERVING"),FINISHED(500, "已完成", "FINISHED"),CANCELED(600, "已取消", "CANCELED"),CLOSED(700, "已關閉", "CLOSED");private final Integer status;private final String desc;private final String code;// 根據狀態值獲得對應枚舉public static OrderStatusEnum codeOf(Integer status) {for (OrderStatusEnum orderStatusEnum : values()) {if (orderStatusEnum.status.equals(status)) { return orderStatusEnum; }}return null;}
}

2.2 狀態變更事件枚舉類

所有狀態之間存在的變更都需要定義狀態變更事件,它實現了 StatusChangeEvent 狀態變更事件接口,事件對應狀態機四要素的事件

@Getter
@AllArgsConstructor
public enum OrderStatusChangeEventEnum implements StatusChangeEvent {PAYED(OrderStatusEnum.NO_PAY, OrderStatusEnum.DISPATCHING, "支付成功", "payed"),DISPATCH(OrderStatusEnum.DISPATCHING, OrderStatusEnum.NO_SERVE, "接單/搶單成功", "dispatch"),START_SERVE(OrderStatusEnum.NO_SERVE, OrderStatusEnum.SERVING, "開始服務", "start_serve"),COMPLETE_SERVE(OrderStatusEnum.SERVING, OrderStatusEnum.FINISHED, "完成服務", "complete_serve"),EVALUATE(OrderStatusEnum.NO_EVALUATION, OrderStatusEnum.FINISHED, "評價完成", "evaluate"),CANCEL(OrderStatusEnum.NO_PAY, OrderStatusEnum.CANCELED, "取消訂單", "cancel"),SERVE_PROVIDER_CANCEL(OrderStatusEnum.NO_SERVE, OrderStatusEnum.DISPATCHING, "服務人員/機構取消訂單", "serve_provider_cancel"),CLOSE_DISPATCHING_ORDER(OrderStatusEnum.DISPATCHING, OrderStatusEnum.CLOSED, "派單中訂單關閉", "close_dispatching_order"),CLOSE_NO_SERVE_ORDER(OrderStatusEnum.NO_SERVE, OrderStatusEnum.CLOSED, "待服務訂單關閉", "close_no_serve_order"),CLOSE_SERVING_ORDER(OrderStatusEnum.SERVING, OrderStatusEnum.CLOSED, "服務中訂單關閉", "close_serving_order"),CLOSE_NO_EVALUATION_ORDER(OrderStatusEnum.NO_EVALUATION, OrderStatusEnum.CLOSED, "待評價訂單關閉", "close_no_evaluation_order"),CLOSE_FINISHED_ORDER(OrderStatusEnum.FINISHED, OrderStatusEnum.CLOSED, "已完成訂單關閉", "close_finished_order");// 源狀態private final OrderStatusEnum sourceStatus;// 目標狀態private final OrderStatusEnum targetStatus;// 描述private final String desc;// 代碼private final String code;
}

2.3 定義訂單快照類

快照是訂單變化瞬間的狀態及相關信息。

快照基礎類型是 StateMachineSnapshot,如果我們要實現訂單快照則需要定義一個訂單快照類 OrderSnapshotDTO 去繼承 StateMachineSnapshot 類型,代碼如下:

// 訂單快照@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderSnapshotDTO extends StateMachineSnapshot {// ...原來的內容保持不變,添加以下代碼@Overridepublic String getSnapshotId() { return String.valueOf(id); }@Overridepublic Integer getSnapshotStatus() { return ordersStatus; }@Overridepublic void setSnapshotId(String snapshotId) {this.id = Long.parseLong(snapshotId);}@Overridepublic void setSnapshotStatus(Integer snapshotStatus) {this.ordersStatus = snapshotStatus;}
}

2.4 定義事件變更動作類

當執行狀態變更事件會伴隨著執行具體的動作,此部分對應狀態機四要素中的動作。

// 訂單支付成功處理器
@Slf4j
@Component("order_payed")
public class OrderPayedHandler implements StatusChangeHandler<OrderSnapshotDTO> {@Resourceprivate IOrdersCommonService ordersService;/*** 訂單支付處理邏輯** @param bizId   業務id* @param bizSnapshot 快照*/@Overridepublic void handler(String bizId, StatusChangeEvent statusChangeEventEnum, OrderSnapshotDTO bizSnapshot) {log.info("支付成功事件處理邏輯開始,訂單號:{}", bizId);}
}

2.5 定義訂單狀態機類

AbstractStateMachine 狀態機抽象類是狀態機的核心類,是具體的狀態機要繼承的抽象類,比如我們實現訂單狀態機就需要繼承 AbstractStateMachine 抽象類。

// 訂單狀態機
@Component
public class OrderStateMachine extends AbstractStateMachine<OrderSnapshotDTO> {public OrderStateMachine(StateMachinePersister stateMachinePersister, BizSnapshotService bizSnapshotService, RedisTemplate redisTemplate) {super(stateMachinePersister, bizSnapshotService, redisTemplate);}/*** 設置狀態機名稱** @return 狀態機名稱*/@Overrideprotected String getName() { return "order"; }@Overrideprotected void postProcessor(OrderSnapshotDTO orderSnapshotDTO) { }/*** 設置狀態機初始狀態** @return 狀態機初始狀態*/@Overrideprotected OrderStatusEnum getInitState() { return OrderStatusEnum.NO_PAY; }
}

2.6 狀態機表設計

  1. 狀態機持久化表

    每個訂單對應狀態機表中的一條記錄。

    CREATE TABLE `state_persister` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵',`state_machine_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '狀態機名稱',`biz_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '業務id',`state` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '狀態',`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',PRIMARY KEY (`id`) USING BTREE,UNIQUE KEY `唯一索引` (`state_machine_name`,`biz_id`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=1908702574605910019 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='狀態機持久化表';
    
  2. 狀態機快照表

    一個訂單在快照表有多條記錄,每變一個狀態會記錄該狀態下的快照信息(即訂單相關的詳細信息)便于查詢訂單變化的歷史記錄。

    CREATE TABLE `biz_snapshot` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵',`state_machine_name` varchar(50) DEFAULT NULL COMMENT '狀態機名稱',`biz_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '業務id',`db_shard_id` bigint DEFAULT NULL COMMENT '分庫鍵',`state` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '狀態代碼',`biz_data` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '業務數據',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',PRIMARY KEY (`id`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=1908702660589142017 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='業務數據快照';
    

3. 使用

在配置文件中導入狀態機

@Configuration
@ComponentScan({"com.jzo2o.orders.base.service","com.jzo2o.orders.base.handler"})
@MapperScan("com.jzo2o.orders.base.mapper")
@Import({OrderStateMachine.class}) // 導入狀態機
@EnableConfigurationProperties({DispatchProperties.class, ExecutorProperties.class})
public class AutoImportConfiguration { }

啟用訂單狀態機:

// 創建訂單快照對象
OrderSnapshotDTO orderSnapshotDTO = BeanUtils.toBean(orders, OrderSnapshotDTO.class);
/** 啟動訂單狀態機** Long dbShardId :分庫ID* String bizId :訂單ID* StatusDefine statusDefine :訂單狀態定義(默認 NO_PAY,可省略)* T bizSnapshot :訂單快照*/
orderStateMachine.start(ordersId.toString(), OrderStatusEnum.NO_PAY, orderSnapshotDTO);

調用狀態機:

String bizId = orders.getId().toString();
// 創建快照對象,可配置需要的數據
OrderSnapshotDTO orderSnapshotDTO = new OrderSnapshotDTO();
// orderSnapshotDTO.setPayTime(DateUtils.now());
// ...
// 調用狀態機,更新訂單狀態
orderStateMachine.changeStatus(bizId, OrderStatusChangeEventEnum.PAYED, orderSnapshotDTO);

說明:這里使用狀態變更事件未 PAYED,參考 事件變更枚舉類 可以看到運行的代碼為 payed,于是就可以找到對于事件處理器 order_payed ,從而處理對應的事件。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/76085.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/76085.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/76085.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

22 | 如何繼續提升 Go 開發技術?

提示&#xff1a; 所有體系課見專欄&#xff1a;Go 項目開發極速入門實戰課&#xff1b;歡迎加入 云原生 AI 實戰營 星球&#xff0c;12 高質量體系課、20 高質量實戰項目助你在 AI 時代建立技術競爭力&#xff08;聚焦于 Go、云原生、AI Infra&#xff09;。 「Go 項目開發極速…

LLM Agents項目推薦:MetaGPT、AutoGen、AgentVerse詳解

這一部分我們將深入介紹三大備受關注的LLM Agents項目&#xff1a;MetaGPT、AutoGen和AgentVerse&#xff0c;包括它們的背景、設計思路、主要功能、技術亮點以及典型應用場景。 1. MetaGPT&#xff1a;讓AI像軟件工程團隊一樣協作 項目背景 MetaGPT由Huang et al.于2023年提…

好數(藍橋杯2024省賽B組)

題目描述 一個整數如果按從低位到高位的順序&#xff0c;奇數位&#xff08;個位、百位、萬位……&#xff09;上的數字是奇數&#xff0c;偶數位&#xff08;十位、千位、十萬位……&#xff09;上的數字是偶數&#xff0c;我們就稱之為“好數”。 給定一個正整數 N&#xf…

STM32單片機入門學習——第26節: [9-2] USART串口外設

寫這個文章是用來學習的,記錄一下我的學習過程。希望我能一直堅持下去,我只是一個小白,只是想好好學習,我知道這會很難&#xff0c;但我還是想去做&#xff01; 本文寫于&#xff1a;2025.04.08 STM32開發板學習——第26節: [9-2] USART串口外設 前言開發板說明引用解答和科普…

【學Rust寫CAD】31 muldiv255函數(muldiv255.rs,已經取消)

源碼 // Calculates floor(a*b/255 0.5) #[inline] pub fn muldiv255(a: u32, b: u32) -> u32 {// The deriviation for this formula can be// found in "Three Wrongs Make a Right" by Jim Blinn.let tmp a * b 128;(tmp (tmp >> 8)) >> 8 }代…

LLM+js實現大模型對話

代碼運行效果圖&#xff1a;前提是你有一個可用的openai服務&#xff0c;然后用下面一個html頁即可啟動 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthd…

用claude3.7,不到1天寫了一個工具小程序(11個工具6個游戲)

一、功能概覽和本文核心 本次開發&#xff0c;不是1天干擼&#xff0c;而是在下班后或早起搞的&#xff0c;總體加和計算了一下&#xff0c;大概1天的時間&#xff08;12個小時&#xff09;&#xff0c;平常下班都是9點的衰仔&#xff0c;好在還有雙休&#xff0c;謝天謝地。 …

C++實現文件斷點續傳:原理剖析與實戰指南

文件傳輸示意圖 一、斷點續傳的核心價值 1.1 大文件傳輸的痛點分析 網絡閃斷導致重復傳輸&#xff1a;平均重試3-5次。 傳輸進度不可回溯&#xff1a;用戶無法查看歷史進度。 帶寬利用率低下&#xff1a;每次中斷需從頭開始。 1.2 斷點續傳技術優勢 指標傳統傳輸斷點續傳…

升級 SAP S/4 HANA 之 EWM 攻略

目錄 簡介 知識點 數據遷移 簡介 倉庫管理&#xff0c;SAP 升級不管是否啟動 EWM 功能&#xff0c;評估 EWM 是必經之路&#xff0c;不僅是因為 EWM 是 SAP 主推的倉庫解決方案&#xff0c;更是其功能強大而便捷&#xff0c;不管是簡易倉庫、復雜倉庫、立體倉庫、高架倉庫、…

知識表示方法之六:過程表示法(Procedural Representation)

在人工智能的發展史中&#xff0c;關于知識的表示方法曾存在兩種不同的觀點。一種觀點認為知識主要是陳述性的&#xff0c;其表示方法應著重將其靜態特性&#xff0c;即事物的屬性以及事物間的關系表示出來&#xff0c;稱以這種觀點表示知識的方法為陳述式或說明式表示法&#…

綠色供應鏈管理體系認證:開啟企業可持續發展的綠色新篇章

在全球“雙碳”目標驅動下&#xff0c;綠色供應鏈管理已成為企業高質量發展的核心議題。據國際權威機構預測&#xff0c;到2030年&#xff0c;綠色供應鏈相關市場規模將突破萬億美元。在此背景下&#xff0c;綠色供應鏈管理體系認證不僅是企業合規的“通行證”&#xff0c;更是…

MATLAB如何打印一個桃心形狀

在MATLAB中打印一個桃心形狀&#xff0c;您可以使用繪圖函數來創建一個心形圖案。以下是一個簡單的例子&#xff0c;展示了如何使用MATLAB繪制一個心形&#xff1a; 定義心形的參數方程&#xff1a;心形可以通過一組參數方程來描述。 使用MATLAB的繪圖函數&#xff1a;plot函…

前端知識(vue3)

1.Vue3 1.1 介紹 Vue&#xff08;讀音 /vju?/, 類似于 view&#xff09;是一款用于構建用戶界面的漸進式的JavaScript框架 官網&#xff1a;https://cn.vuejs.org 1.2 常見指令 指令&#xff1a;指的是HTML 標簽上帶有 v- 前綴的特殊屬性&#xff0c;不同指令具有不同含義…

狀態機思想編程

1. LED流水燈的FPGA代碼 一個使用狀態機思想來實現LED流水燈的FPGA代碼 這個例子采用VHDL編寫 VHDL代碼示例&#xff1a; library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL;entity led_flowing isPort ( clk …

網絡安全小知識課堂(五)

病毒與蠕蟲&#xff1a;你的電腦為何會 “生病” 和 “傳染”&#xff1f; 引言 你是否見過這樣的場景&#xff1a;電腦突然彈窗廣告暴增&#xff0c;文件莫名消失&#xff0c;甚至整個公司網絡集體癱瘓&#xff1f;這些癥狀背后&#xff0c;可能是 ** 病毒&#xff08;Virus…

RVOS-1.環境搭建與系統引導

0.環境搭建 riscv-operating-system-mooc: 開放課程《循序漸進&#xff0c;學習開發一個 RISC-V 上的操作系統》配套教材代碼倉庫。 mirror to https://github.com/plctlab/riscv-operating-system-mooc 在 Ubuntu 20.04 以上環境下我們可以直接使用官方提供的 GNU工具鏈和 QEM…

UNet 改進(5):結合SE模塊提升圖像分割性能

U-Net是醫學圖像分割領域最成功的架構之一&#xff0c;其對稱的編碼器-解碼器結構和跳躍連接使其能夠有效捕捉多尺度特征。本文將解析一個改進版的U-Net實現&#xff0c;該版本通過引入Squeeze-and-Excitation(SE)模塊進一步提升了模型性能。 一、架構概覽 這個改進的U-Net保持…

機器人擰螺絲緊固裝配(Robot screw fastening assembly)

機器人擰螺絲緊固裝配技術正以其高精度、高效率和高靈活性&#xff0c;重塑著傳統制造業的生產范式。這項融合了機械臂定位、扭矩控制、視覺引導與數據分析的自動化解決方案&#xff0c;不僅將工人從重復性高強度勞動中解放出來&#xff0c;更通過實時數據反饋與精準執行&#…

圖像處理中的 Gaussina Blur 和 SIFT 算法

Gaussina Blur 高斯模糊 高斯模糊的數學定義 高斯模糊是通過 高斯核(Gaussian Kernel) 對圖像進行卷積操作實現的. 二維高斯函數定義為 G ( x , y , σ ) 1 2 π σ 2 e ? x 2 y 2 2 σ 2 G(x, y, \sigma) \frac{1}{2\pi \sigma^2} e^{-\frac{x^2 y^2}{2\sigma^2}} G(x…

在Unity中實現《幽靈行者》風格的跑酷動作

基礎設置 角色控制器選擇&#xff1a; 使用Character Controller組件或Rigidbody Capsule Collider 推薦使用Character Controller以獲得更精確的運動控制 輸入系統&#xff1a; 使用Unity的新輸入系統(Input System Package)處理玩家輸入 滑鏟實現 public class Slide…