java状态机实现订单状态转移

news2025/2/24 12:19:34

一、状态机

状态机是状态模式的一种应用,相当于上下文角色的一个升级版。在工作流或游戏等各种系统中有大量使用,如各种工作流引擎,它几乎是状态机的子集和实现,封装状态的变化规则。状态机可以帮助开发者简化状态控制的开发过程,让状态机结构更加层次化。

二、状态机要素

把状态机的要素分为4个要素,即:现态、条件、动作、次态。
“现态”和“条件”是因,“动作”和“次态”是果。

(1)现态:是指当前所处状态;
(2)条件:又称为“事件”。当条件被满足时,将会触发一个动作,或者执行一次状态的迁移。
(3)动作:条件满足后执行的动作。动作不是必须的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
(4)次态:条件满足后要迁移往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

三、状态机状态举例

以电商平台的订单状态为例,订单状态的可能有待支付、待发货、待收货、已完成、取消等。每一种状态都和变化前的状态以及执行的操作有关。

状态机图为:

 四、代码步骤

Spring 提供了一个很好的解决方案,Spring Statemachine(状态机)是应用程序开发人员在 Spring 应用程序中使用状态机概念的框架。

1.添加依赖

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

2.创建订单实体类

package com.sky.statemachinedemo.entity;

import lombok.Data;

/**
 * 订单实体类
 */
@Data
public class Order {
    private Integer id;
    private Integer status;
}

3.创建订单状态枚举类

package com.sky.statemachinedemo.constants;

/**
 * 订单状态枚举
 */
public enum OrderStatusEnum {

    /**
     * 待付款
     */
    WAIT_PAYMENT(0),

    /**
     * 待发货
     */
    WAIT_DELIVER(1),

    /**
     * 待收货
     */
    WAIT_RECEIVE(2),

    /**
     * 已完成
     */
    FINISH(3),

    /**
     * 取消
     */
    CANCEL(4),
    ;
    /**
     * 订单状态
     */
    public final int value;

    OrderStatusEnum(int value) {
        this.value = value;
    }

    public static Object getByValue(Integer status) {
        for(OrderStatusEnum OrderStatusEnum:OrderStatusEnum.values()){
            if(OrderStatusEnum.value==status){
                return OrderStatusEnum;
            }
        }
        return null;
    }
}

4.创建状态转换枚举类(事件event)

package com.sky.statemachinedemo.constants;

/**
 * 状态转换枚举类(事件)
 */
public enum OrderStatusChangeEvent {
    // 支付,发货,确认收货
    PAYED, DELIVERY, RECEIVED,CANCEL;
}

5.添加状态流配置

package com.sky.statemachinedemo.config;

import com.sky.statemachinedemo.constants.OrderStatusChangeEvent;
import com.sky.statemachinedemo.constants.OrderStatusEnum;
import com.sky.statemachinedemo.entity.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.persist.DefaultStateMachinePersister;
import org.springframework.statemachine.support.DefaultStateMachineContext;

import java.util.EnumSet;

/**
 * 订单状态机配置
 */
@Slf4j
@Configuration
@EnableStateMachine(name = "OrderStateMachine")
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStatusEnum, OrderStatusChangeEvent> {
    /**
     * 配置状态
     *
     * @param states
     * @throws Exception
     */
    public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderStatusChangeEvent> states) throws Exception {
        states
                .withStates()
                .initial(OrderStatusEnum.WAIT_PAYMENT)
                .states(EnumSet.allOf(OrderStatusEnum.class));
    }

    /**
     * 配置状态转换事件关系
     *
     * @param transitions
     * @throws Exception
     */
    public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderStatusChangeEvent> transitions) throws Exception {
        transitions
                .withExternal().source(OrderStatusEnum.WAIT_PAYMENT).target(OrderStatusEnum.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED)
                .and()
                .withExternal().source(OrderStatusEnum.WAIT_PAYMENT).target(OrderStatusEnum.CANCEL).event(OrderStatusChangeEvent.CANCEL)
                .and()
                .withExternal().source(OrderStatusEnum.WAIT_DELIVER).target(OrderStatusEnum.WAIT_RECEIVE).event(OrderStatusChangeEvent.DELIVERY)
                .and()
                .withExternal().source(OrderStatusEnum.WAIT_RECEIVE).target(OrderStatusEnum.FINISH).event(OrderStatusChangeEvent.RECEIVED);
    }

    /**
     * 持久化配置
     * 实际使用中,可以配合redis等,进行持久化操作
     *
     * @return
     */
    @Bean
    public DefaultStateMachinePersister persister() {
        return new DefaultStateMachinePersister<>(new StateMachinePersist<OrderStatusEnum, OrderStatusChangeEvent, Order>() {
            @Override
            public void write(StateMachineContext<OrderStatusEnum, OrderStatusChangeEvent> context, Order Order) throws Exception {
                //此处并没有进行持久化操作
                //此处可调用更新状态流转接口(如:更新时间)状态不需要在此进行调用
            }

            @Override
            public StateMachineContext<OrderStatusEnum, OrderStatusChangeEvent> read(Order Order) throws Exception {
                //此处直接获取Order中的状态,其实并没有进行持久化读取操作
                //实际生产中我们会查询出订单信息,根据订单状态获取状态机状态
                return new DefaultStateMachineContext(OrderStatusEnum.getByValue(Order.getStatus()), null, null, null);
            }
        });
    }
}

6.添加订单状态监听器 

package com.sky.statemachinedemo.listener;

import com.sky.statemachinedemo.constants.OrderStatusChangeEvent;
import com.sky.statemachinedemo.constants.OrderStatusEnum;
import com.sky.statemachinedemo.entity.Order;
import org.springframework.messaging.Message;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;
import org.springframework.stereotype.Component;

/**
 * 订单状态监听器
 */
@Component("OrderStateListener")
@WithStateMachine(name = "OrderStateMachine")
public class OrderStateListener {

    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
    public boolean payTransition(Message<OrderStatusChangeEvent> message) {
        Order Order = (Order) message.getHeaders().get("order");
        Order.setStatus(OrderStatusEnum.WAIT_DELIVER.value);
        System.out.println("支付,状态机反馈信息:" + message.getHeaders().toString());
        return true;
    }
    @OnTransition(source = "WAIT_PAYMENT", target = "CANCEL")
    public boolean cancelTransition(Message<OrderStatusChangeEvent> message) {
        Order Order = (Order) message.getHeaders().get("order");
        Order.setStatus(OrderStatusEnum.CANCEL.value);
        System.out.println("支付,状态机反馈信息:" + message.getHeaders().toString());
        return true;
    }
    @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
    public boolean deliverTransition(Message<OrderStatusChangeEvent> message) {
        Order Order = (Order) message.getHeaders().get("order");
        Order.setStatus(OrderStatusEnum.WAIT_RECEIVE.value);
        System.out.println("发货,状态机反馈信息:" + message.getHeaders().toString());
        return true;
    }

    @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
    public boolean receiveTransition(Message<OrderStatusChangeEvent> message) {
        Order Order = (Order) message.getHeaders().get("order");
        Order.setStatus(OrderStatusEnum.FINISH.value);
        System.out.println("收货,状态机反馈信息:" + message.getHeaders().toString());
        return true;
    }
}

7.创建OrderService接口

package com.sky.statemachinedemo.service;

import com.sky.statemachinedemo.entity.Order;

import java.util.Map;

public interface OrderService {
    //创建订单
    Order create();

    //发起支付
    Order pay(Integer id);
    //发起支付
    Order cancel(Integer id);

    //订单发货
    Order deliver(Integer id);

    //订单收货
    Order receive(Integer id);

    //获取所有订单信息
    //模拟数据存储的订单信息,生产中用数据库存储
    Map<Integer, Order> getOrders();
}

8.在 Service 业务逻辑中应用

package com.sky.statemachinedemo.service;

import com.sky.statemachinedemo.constants.OrderStatusChangeEvent;
import com.sky.statemachinedemo.constants.OrderStatusEnum;
import com.sky.statemachinedemo.entity.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.persist.StateMachinePersister;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class OrderServiceImpl implements OrderService{
    @Autowired
    private StateMachine<OrderStatusEnum, OrderStatusChangeEvent> orderStateMachine;

    @Autowired
    private StateMachinePersister<OrderStatusEnum, OrderStatusChangeEvent, Order> persister;
    private int id = 1;
    private Map<Integer, Order> orders = new HashMap<>();

    @Override
    public Order create() {
        Order order = new Order();
        order.setStatus(OrderStatusEnum.WAIT_PAYMENT.value);
        order.setId(id++);
        orders.put(order.getId(), order);
        return order;
    }

    @Override
    public Order pay(Integer id) {
        Order order = orders.get(id);
        System.out.println("线程名称:" + Thread.currentThread().getName() + " 尝试支付,订单号:" + id);
        Message message = MessageBuilder.withPayload(OrderStatusChangeEvent.PAYED).setHeader("order", order).build();
        if (!sendEvent(message, order)) {
            System.out.println("线程名称:" + Thread.currentThread().getName() + " 支付失败, 状态异常,订单号:" + id);
        }
        return orders.get(id);
    }

    @Override
    public Order cancel(Integer id) {
        Order order = orders.get(id);
        System.out.println("线程名称:" + Thread.currentThread().getName() + " 尝试取消,订单号:" + id);
        Message message = MessageBuilder.withPayload(OrderStatusChangeEvent.CANCEL).setHeader("order", order).build();
        if (!sendEvent(message, order)) {
            System.out.println("线程名称:" + Thread.currentThread().getName() + " 取消失败, 状态异常,订单号:" + id);
        }
        return orders.get(id);
    }

    @Override
    public Order deliver(Integer id) {
        Order order = orders.get(id);
        System.out.println("线程名称:" + Thread.currentThread().getName() + " 尝试发货,订单号:" + id);
        if (!sendEvent(MessageBuilder.withPayload(OrderStatusChangeEvent.DELIVERY).setHeader("order", order).build(), orders.get(id))) {
            System.out.println("线程名称:" + Thread.currentThread().getName() + " 发货失败,状态异常,订单号:" + id);
        }
        return orders.get(id);
    }

    @Override
    public Order receive(Integer id) {
        Order order = orders.get(id);
        System.out.println("线程名称:" + Thread.currentThread().getName() + " 尝试收货,订单号:" + id);
        if (!sendEvent(MessageBuilder.withPayload(OrderStatusChangeEvent.RECEIVED).setHeader("order", order).build(), orders.get(id))) {
            System.out.println("线程名称:" + Thread.currentThread().getName() + " 收货失败,状态异常,订单号:" + id);
        }
        return orders.get(id);
    }

    @Override
    public Map<Integer, Order> getOrders() {
        return orders;
    }

    /**
     * 发送订单状态转换事件
     *
     * @param message
     * @param order
     * @return
     */
    private synchronized boolean sendEvent(Message<OrderStatusChangeEvent> message, Order order) {
        boolean result = false;
        try {
            orderStateMachine.start();
            //尝试恢复状态机状态
            persister.restore(orderStateMachine, order);
            //添加延迟用于线程安全测试
            Thread.sleep(1000);
            result = orderStateMachine.sendEvent(message);
            //持久化状态机状态
            persister.persist(orderStateMachine, order);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }
}

9.编写单元测试代码

package com.sky.statemachinedemo.service;

import com.sky.statemachinedemo.StateMachineDemoApplication;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = StateMachineDemoApplication.class,
        webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class OrderServiceTest extends TestCase {
    @Autowired
    OrderService orderService;
    @Test
    public void stateTest(){
        orderService.create();
        orderService.pay(1);
        orderService.deliver(1);
        orderService.receive(1);
        orderService.cancel(1);

    }
}

10.也可以用controller测试,更方便

package com.sky.statemachinedemo.controller;

import com.sky.statemachinedemo.entity.Order;
import com.sky.statemachinedemo.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    OrderService orderService;
    @RequestMapping("/create")
    public Order create(){
        return orderService.create();
    }

    @RequestMapping("/cancel")
    public Order cancel(Integer orderId){
        return orderService.cancel(orderId);
    }
    @RequestMapping("/pay")
    public Order pay(Integer orderId){
        return orderService.pay(orderId);
    }
    @RequestMapping("/deliver")
    public Order deliver(Integer orderId){
        return orderService.deliver(orderId);
    }
    @RequestMapping("/receive")
    public Order receive(Integer orderId){
        return orderService.receive(orderId);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/506988.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

APP界面设计都有哪些好用的软件推荐

基于APP界面的不同功能&#xff0c;所选择的APP界面设计软件也会有所不同。然而&#xff0c;并不是说所有的APP界面设计软件都非常精通&#xff0c;熟练地学习几个常用的APP界面设计软件。以下10个APP界面设计软件将为您的团队提供绘制APP界面所需的必要功能。 1.即时设计 即…

OpenCV-Python实战(7) —— OpenCV 实现抖音视频倒放效果

1. 需求分析 参考&#xff1a;十行Python代码制作一个视频倒放神器&#xff0c;由于最近在学习 OpenCV &#xff0c;因此试着使用 OpenCV 进行实现&#xff0c;学以致用&#xff08;胡乱折腾&#xff09;。 需要视频倒放&#xff0c;因此需要读取视频cv.VideoCapture&#xff1…

一键docker搭建mysql主从环境

一键docker搭建mysql主从环境 初衷准备阶段操作阶段注意事项 初衷 一开始为了玩一下shared-jdbc&#xff0c;要搭Mysql主从环境&#xff0c;这玩意虽然搭好&#xff0c;之后使用要是网络问题&#xff0c;或者sql执行出错&#xff0c;还得重新调Binlog位置&#xff0c;麻烦得很…

接口自动化测试之HTTP协议详解(敢称全网最全)

目录 协议 OSI模型 HTTP URL 报文 响应报文 HTTP扩展 协议 简单理解&#xff0c;计算机与计算机之间的通讯语言就叫做协议&#xff0c;不同的计算机之间只有使用相同的协议才能通信。所以网络协议就是为计算机网络中进行数据交换而建立的规则&#xff0c;标准或约定的集…

Node.js 使用RSA加密/解密

在本文中&#xff0c;我们将探讨如何在 Node.js 中使用 RSA 加密和解密。RSA 是一种非对称加密算法&#xff0c;它可以确保数据的安全传输。使用 RSA&#xff0c;我们可以在不直接传输密钥的情况下安全地加密和解密数据。 一、安装依赖 我们将使用 node-rsa 库来执行加密和解密…

2023 年Java经典面试题,基础篇01(持续更新)

本篇文章主要讲的是 2023 年Java最新面试题&#xff0c;持续更重中 基础概念与常识 原文地址&#xff1a;https://github.com/Snailclimb/JavaGuide Java 语言有哪些特点? 简单易学&#xff1b;面向对象&#xff08;封装&#xff0c;继承&#xff0c;多态&#xff09;&#…

《LeetCode》—— LeetCode刷题日记

本期&#xff0c;我给大家讲述的是关于 n数之和这类题目的讲解&#xff0c;我会给大家讲解两数之和&#xff0c;三数之和和四数之和这三道题目。 目录 &#xff08;一&#xff09;两数之和 &#xff08;二&#xff09;三数之和 &#xff08;三&#xff09;四数之和 &#xf…

NodeJs 最近各版本特性汇总

&#xff08;预测未来最好的方法就是把它创造出来——尼葛洛庞帝&#xff09; NodeJs 官方链接 github链接 V8链接 Node.js发布于2009年5月&#xff0c;由Ryan Dahl开发&#xff0c;是一个基于Chrome V8引擎的JavaScript运行环境&#xff0c;使用了一个事件驱动、非阻塞式I/O模…

对象应用:C++字符串和vector,对象的new与delete重构

对象应用 C字符串和vector字符串创建方式字符串拼接字符串追加 字符串截断autovector创建方式vector操作 new与delete重构new与delete的工作步骤new与delete重构应用只能生成栈对象只能生成堆对象 C字符串和vector C的字符串是一个对象&#xff0c;存在于std标准库中&#xff0…

Python基础入门(4)—— 什么是偷懒编程法?是类、对象和继承

文章目录 00 | &#x1f603;为什么学习类&#xff1f;&#x1f603;01 | &#x1f604;创建类&#x1f604;02 | &#x1f606;创建对象&#x1f606;03 | &#x1f609;访问对象属性和方法&#x1f609;04 | &#x1f60a;构造函数&#x1f60a;05 | &#x1f60b;继承&#…

Shell编程之数组

目录 一、数组的基本概念 二、定义数组的方法 方法一&#xff1a; ​编辑 方法二&#xff1a; 方法三&#xff1a; ​编辑 方法四&#xff1a; 三、 数组的输出&#xff0c;删除和长度统计 1&#xff09;数组元素的输出 2&#xff09;数组全部元素输出 3&#xff0…

一种用于提高无线传感器网络寿命的改进LEACH协议(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 无线传感器网络具有网络灵活性强、网络规模可变等优点&#xff0c;广泛应用于军事、工业等领域。无线传感器网络的基本网络路由…

Mybatis一级缓存详解

目录 一级缓存 一级缓存的组织 一级缓存的生命周期 一级缓存的工作流程 Cache接口的设计以及CacheKey的定义 一级缓存的性能分析 一级缓存与Spring 事务一级缓存存在的弊端 官方文档分析 Spring通过Mybatis调用数据库的过程 一级缓存 对于会话&#xff08;Session&am…

Nacos-01-Nacos基本介绍

背景 ​ 服务发现是⼀个古老的话题&#xff0c;当应用开始脱离单机运行和访问时&#xff0c;服务发现就诞生了。目前的网络架构是每个主机都有⼀个独立的 IP 地址&#xff0c;那么服务发现基本上都是通过某种方式获取到服务所部署的 IP 地址。DNS 协议是最早将⼀个网络名称翻译…

让AI帮忙写个需求,AI写出来了,只是有bug而已

需求 使用原生JS和iframe&#xff0c;嵌入网页进行轮播&#xff0c;需要可以点击暂停、上一页、下一页。 AI的答案 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>轮播图</title><style>* {margin: 0;padd…

看板项目管理:如何可视化工作以提高生产力?

如果你一直关心优化工作流程&#xff0c;提高你或团队的生产力&#xff0c;你肯定听说过看板这个词。 看板是一种工作管理方法&#xff0c;可以将整个工作流程以及构成工作流程的每个单独活动可视化&#xff0c;从而可以识别瓶颈和优化整体流程。 在这方面&#xff0c;看板的…

Python基础(二)

目录 一、类型转换 1、为什么需要数据类型转换 2、数据类型转化的函数 3、str()函数类型转换使用 4、int()函数类型转换使用 4.1int()不能将str类型数据转换成int 4.2int()将bool类型转换成int 4.3int()将float转换成int 5、Float()函数类型转换使用 5.1Float()函数不…

ros imu可视化(ubantu)

可以用下面的链接安装ros 安装ros 在home下建立workspace&#xff0c;添加环境变量 export $ROS_PACKAGE_PATHROS_PACKAGE_PATH:/home/workspace在workspace下建立src文件夹&#xff0c;将fdilink_ahrs_ROS1解压在src目录下面 在workspace下运行以下命令&#xff1a; catkin_…

【牛客面试必刷TOP101】有效括号序列、滑动窗口的最大值

BM44 有效括号序列 点击进入该题 描述&#xff1a; 给出一个仅包含字符’(‘,’)‘,’{‘,’}‘,’[‘和’],的字符串&#xff0c;判断给出的字符串是否是合法的括号序列。 括号必须以正确的顺序关闭&#xff0c;"()“和”()[]{}“都是合法的括号序列&#xff0c;但”(]“…

(七)如何实现即时通信系统中用户的文字聊天功能?

文章目录 一、引言二、实现用户文字功能2.1 使用QTextEdit控件实现用户输入文字消息2.2 实现文字消息的发送和接收2.3 实现文字消息的展示和管理 三、解码接收到的文字消息3.1 接收数据并解码3.2 在客户端展示文字消息3.3 客户端关键代码展示3.4 服务端关键代码展示 四、效果展…