状态机的基本使用

状态机

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.mzph.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…