Spring StateMachine简介
Spring StateMachine有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。在电商场景(订单、物流、售后)、社交(IM消息投递)、分布式集群管理(分布式计算平台任务编排)等场景都有大规模的使用。
有限状态机(英语:finite-state machine,缩写:FSM),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
应用 FSM 模型可以帮助对象生命周期的状态的顺序以及导致状态变化的事件进行管理。将状态和事件控制从不同的业务 Service 方法的 if else 中抽离出来。FSM 的应用范围很广,对于有复杂状态流,扩展性要求比较高的场景都可以使用该模型。
有限状态机概念
有限状态机简称就是状态机,因为一般的状态机的状态都是离散和可举的,即为有限,所以后面的介绍都不加有限二字。状态机表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。通俗的描述状态机就是定义了一套状态変更的流程:状态机包含一个状态集合,定义当状态机处于某一个状态的时候它所能接收的事件以及可执行的行为,执行完成后,状态机所处的状态。所以状态机会包含以下几个重要的元素:
State:状态。一个标准的状态机最少包含两个状态:初始和终态。初态是状态机初始化后所处的状态,而终态顾名思义就是状态机结束时所处的状态。其他的状态都是一些流转中停留的状态。标准的状态机还会涉及到一些中间态,存在中间态的状态机流程就会比较复杂(用处也不是特别大,而且可以通过其他方式实现),所以在目标实现的状态机里不会引入这个概念。
Event:事件。还有中描述叫Trigger,表达的意思都一样,就是要执行某个操作的触发器或口令:当状态机处于某个状态时,只有外界告诉状态机要干什么事情的时候,状态机才会去执行具体的行为,来完成外界想要它完成的操作。比如出去吃饭,说“点菜”,服务员才会拿着小本过来记录你要吃的菜,说的那句“点菜”,就相当于Event。
Action:行为。状态变更索要执行的具体行为。还是拿上面点菜的例子,服务员拿小本记录你定的菜的过程就是Action
Transition:变更。一个状态接收一个事件执行了某些行为到达了另外一个状态的过程就是一个Transition。定义Transition就是在定义状态机的运转流程。
状态机的要素
状态机可归纳为4个要素,现态、条件、动作、次态。“现态”和“条件”是因,“动作”和“次态”是果。
现态:指当前所处的状态
条件:又称“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移
动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必须的,当条件满足后,也可以不执行任何动作,直接迁移到新的状态。
次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转换成“现态”。
状态机领域模型
整个状态机的核心语义模型(Semantic Model)也很简单,就是如下图所示:
四大概念:
状态(state) | 一个状态机至少要包含两个状态。分为:现态(源状态)、次态(目标状态)状态可以理解为一种结果,一种稳态形式,没有扰动会保持不变的。 状态命名形式:1.副词+动词;例如:待审批、待支付、待收货这种命名方式体现了:状态机就是事件触发状态不断迁徙的本质。表达一种待触发的感觉。2.动词+结果;例如:审批完成、支付完成3.已+动词形式;例如:已发货、已付款以上两种命名方式体现了:状态是一种结果或者稳态的本质。表达了一种已完成的感觉。 角色很多的时候,为了表示清晰,可以加上角色名:例如:待财务审批、主管已批准 命名考虑从用户好理解角度出发。 |
---|---|
事件(event)or触发条件 | 又称为“条件”,就是某个操作动作的触发条件或者口令。当一个条件满足时,就会触发一个动作,或者执行一次状态迁徙这个事件可以是外部调用、监听到消息、或者各种定时到期等触发的事件。对于灯泡,“打开开关”就是一个事件。条件命名形式:动词+结果;例如:支付成功、下单时间>5分钟 |
动作(action) | 事件发生以后要执行动作。例如:事件=“打开开关指令”,动作=“开灯”。一般就对应一个函数。条件满足后执行动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。那么如何区分“动作”和“状态”?“动作”是不稳定的,即使没有条件的触发,“动作”一旦执行完毕就结束了;而“状态”是相对稳定的,如果没有外部条件的触发,一个状态会一直持续下去。 |
变换(transition) | 即从一个状态变化到另外一个状态例如:“开灯过程”就是一个变化 |
状态机动作类型
进入动作:在进入状态时进行
退出动作:在退出状态时进行
输入动作:依赖于当前状态和输入条件进行
转移动作:在进行特定转移时进行
spring statemachine是使用 Spring框架下的状态机概念创建的一种应用程序开发框架。它使得状态机结构层次化,简化了配置状态机的过程。
登录场景设计一个状态机
设计一张状态机表。
横轴是动作,纵轴是状态
此时它的二维数组,如下所示
在启动 springboot 时,需要注入状态机的状态,事件的配置。起主要涉及到以下两个类:
StateMachineStateConfigurer < S, E> 配置状态集合以及初始状态,泛型参数 S 代表状态,E 代表事件。
StateMachineTransitionConfigurer 配置状态流的转移,可以定义状态转换接受的事件。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-boot-parent</artifactId>
<groupId>cn.spring.boot</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>springboot-statemachine</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-statemachine</name>
<description>springboot-statemachine</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-bom</artifactId>
<version>2.3.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-starter</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
public enum RegStatusEnum {
// 未连接
UNCONNECTED,
// 已连接
CONNECTED,
// 正在登录
LOGINING,
// 登录进系统
LOGIN_INTO_SYSTEM;
}
public enum RegEventEnum {
// 连接
CONNECT,
// 开始登录
BEGIN_TO_LOGIN,
// 登录成功
LOGIN_SUCCESS,
// 登录失败
LOGIN_FAILURE,
// 注销登录
LOGOUT;
}
import org.springframework.context.annotation.Configuration;
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 java.util.EnumSet;
/**
* @Descripton:
* @Version:
**/
@Configuration
@EnableStateMachine // 开启状态机配置
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<RegStatusEnum, RegEventEnum> {
/**
* 配置状态机状态
*/
@Override
public void configure(StateMachineStateConfigurer states) throws Exception {
states.withStates()
// 初始化状态机状态
.initial(UNCONNECTED)
// 指定状态机的所有状态
.states(EnumSet.allOf(RegStatusEnum.class));
}
/**
* 配置状态机状态转换
*/
@Override
public void configure(StateMachineTransitionConfigurer<RegStatusEnum, RegEventEnum> transitions) throws Exception {
// 1. connect UNCONNECTED -> CONNECTED
transitions.withExternal()
.source(UNCONNECTED).target(CONNECTED)
.event(CONNECT)
// 2. beginToLogin CONNECTED -> LOGINING
.and().withExternal()
.source(CONNECTED)
.target(LOGINING)
.event(BEGIN_TO_LOGIN)
// 3. login failure LOGINING -> UNCONNECTED
.and().withExternal()
.source(LOGINING)
.target(UNCONNECTED)
.event(LOGIN_FAILURE)
// 4. login success LOGINING -> LOGIN_INTO_SYSTEM
.and().withExternal()
.source(LOGINING)
.target(LOGIN_INTO_SYSTEM)
.event(LOGIN_SUCCESS)
// 5. logout LOGIN_INTO_SYSTEM -> UNCONNECTED
.and().withExternal()
.source(LOGIN_INTO_SYSTEM)
.target(UNCONNECTED)
.event(LOGOUT);
}
}
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;
/**
* @Descripton:
* @Version:
**/
@Configuration
@WithStateMachine
public class StateMachineEventConfig {
@OnTransition(source = "UNCONNECTED", target = "CONNECTED")
public void connect() {
System.out.println("Switch state from UNCONNECTED to CONNECTED: connect");
}
@OnTransition(source = "CONNECTED", target = "LOGINING")
public void beginToLogin() {
System.out.println("Switch state from CONNECTED to LOGINING: beginToLogin");
}
@OnTransition(source = "LOGINING", target = "LOGIN_INTO_SYSTEM")
public void loginSuccess() {
System.out.println("Switch state from LOGINING to LOGIN_INTO_SYSTEM: loginSuccess");
}
@OnTransition(source = "LOGINING", target = "UNCONNECTED")
public void loginFailure() {
System.out.println("Switch state from LOGINING to UNCONNECTED: loginFailure");
}
@OnTransition(source = "LOGIN_INTO_SYSTEM", target = "UNCONNECTED")
public void logout() {
System.out.println("Switch state from LOGIN_INTO_SYSTEM to UNCONNECTED: logout");
}
}
@RestController
public class WebApiController {
@Autowired
private StateMachine stateMachine;
@GetMapping(value = "/testStateMachine")
public void testStateMachine()
{
stateMachine.start();
stateMachine.sendEvent(RegEventEnum.CONNECT);
stateMachine.sendEvent(RegEventEnum.BEGIN_TO_LOGIN);
stateMachine.sendEvent(RegEventEnum.LOGIN_FAILURE);
stateMachine.sendEvent(RegEventEnum.LOGOUT);
}
}
Spring StateMachine 让状态机结构更加层次化
第一步,定义状态枚举。
第二步,定义事件枚举。
第三步,定义状态机配置,设置初始状态,以及状态与事件之间的关系。
第四步,定义状态监听器,当状态变更时,触发方法。
状态转移的监听器
状态转移过程中,可以通过监听器(Listener)来处理一些持久化或者业务监控等任务。在需要持久化的场景中,可以在状态机模式中的监听器中添加持久化的处理。
StateMachineListener 事件监听器(通过 Spring 的 event 机制实现)。
监听 stateEntered(进入状态)、stateExited(离开状态)、eventNotAccepted(事件无法响应)、transition(转换)、transitionStarted(转换开始)、transitionEnded(转换结束)、stateMachineStarted(状态机启动)、stateMachineStopped(状态机关闭)、stateMachineError(状态机异常)等事件,借助 listener 可以跟踪状态转移。
StateChangeInterceptor 拦截器接口,不同于 Listener。其可以改变状态转移链的变化。主要在 preEvent(事件预处理)、preStateChange(状态变更的前置处理)、postStateChange(状态变更的后置处理)、preTransition(转化的前置处理)、postTransition(转化的后置处理)、stateMachineError(异常处理)等执行点生效。
StateMachine 状态机实例,spring statemachine 支持单例、工厂模式两种方式创建,每个 statemachine 有一个独有的 machineId 用于标识 machine 实例;需要注意的是 statemachine 实例内部存储了当前状态机等上下文相关的属性,因此这个实例不能够被多线程共享。
订单流转业务
状态流程图
理解下几个基本常用的组件
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
.target(TradeOrderStateMachineEnum.CLOSED)
.event(TradeOrderEvent.CLOSE).and()
这是配置规则,表示从WAIT_FOR_PAY->CLOSED,需要CLOSE事件来触发。这是比较简单的一种,没有guard判断,直接流转到CLOSED状态。
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
.target(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER)
.event(TradeOrderEvent.AUDIT).guard(tradeOrderGuardFactory.new TradeOrderGuard()).and()
下面一种,则是多了一个.guard(),这是判断,相当于java里面if的判断条件,这个自定义判断的类必须实现Guard接口,重写里面evaluate方法,这个方法就是返回boolean。值得一提的是,每个规则都可以配置action,可以直接在后面加上.action(),也可以用@WithStateMachine和@OnTransition两个注解配合用写下自己业务代码。这个action表示,满足整个链路规则后才要做的是。
.withChoice()
.source(TradeOrderStateMachineEnum.AUDIT_CHOICE)
.first(TradeOrderStateMachineEnum.COMPLETED, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard2(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_SIGN, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard3(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard4(),new TradeOrderChoiceAction())
.last(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT).and()
前面两种是比较简单的,一个事件只会流转到一个状态,上面这个就是比较复杂点,也是业务上经常会用,一个event会有几种状态,first-then-last,就相当于if-else if,只要满足一个guard判断就不会往下流转,注意这里有几个坑,后面说下的。
持久化
在实际业务中,状态机可能需要在某个环节停留,等待其他业务的触发,然后再继续下面的流程。比如订单,可能在支付环节需要等待一个剁手的用户隔天再下单,所以这里面涉及到一个创建的状态机该何去何从的问题。在spring statemachine中,给出来的办法就是保存起来,到需要的时候取出来用。
目前Spring状态机通过StateMachinePersister接口的实现,完成状态机状态的存储
目前已经提供的存储方式包含Redis,mongoDB,Jpa三种实现,分别对应JpaStateMachineRuntimePersister,RedisStateMachineRuntimePersister 和MongoDbStateMachineRuntimePersister
其中StateContext序列化的方式为kryo,序列化和反序列化都在实现类中自行实现了
注意 需要
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-redis</artifactId>
<version>1.2.14.RELEASE</version>
</dependency>
// 初始化redis连接工厂
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
redisConfig.setHostName("1.1.1.1");
redisConfig.setPassword(RedisPassword.of("xxxx"));
redisConfig.setPort(6379);
return new JedisConnectionFactory(redisConfig);
}
// 通过名称进行区分(带有不同枚举导致不能混用)
@Bean(name = "newStateMachinePersister")
public RedisStateMachinePersister<NewStates, NewEvents> redisStateMachinePersister(RedisConnectionFactory connectionFactory) {
RedisStateMachineContextRepository<NewStates, NewEvents> repository = new RedisStateMachineContextRepository<>(connectionFactory);
return new RedisStateMachinePersister<>(new RepositoryStateMachinePersist<>(repository));
}
//状态数据实例化组件
@Resource(name = "newStateMachinePersister")
private StateMachinePersister<NewStates, NewEvents, String> newStateMachinePersister;
// 状态机状态的恢复
newStateMachinePersister.restore(newStateMachine, STATE_MACHINE_PREFIX + id);
// 状态机状态的存储
newStateMachinePersister.persist(newStateMachine, STATE_MACHINE_PREFIX + id);
状态改变流程
初始化状态 >> 触发事件 >> 消息通知 >> 状态改变
对应Spring StateMachine的核心步骤为:
定义状态枚举
定义事件枚举
定义状态机配置,设置初始状态,以及状态与事件之间的关系
定义状态监听器,当状态变更时,触发方法
前置态+操作 => 后置态
Spring StateMachine项目模块
Spring StateMachine是Spring官方提供的一个框架,供应用程序开发人员在Spring应用程序中使用状态机。支持状态的嵌套(substate)、状态的并行(parallel,fork,join)、子状态机等等。状态机可以帮助开发者简化状态控制的开发过程,使状态机结构更加层次化。
spring statemachine
需要定义
1、定义状态枚举和事件枚举
如状态枚举
public enum States {
UNPAID, // 待支付
WAITING_FOR_DELIVER, // 待发货
WAITING_FOR_RECEIVE, // 待收货
DONE // 结束
}
事件枚举
public enum Events {
PAY, // 支付
DELIVER, // 发货
RECEIVE // 收货
}
2、简单状态机实现
完成状态机的配置,包括:(1)状态机的初始状态和所有状态;(2)状态之间的转移规则
@Configuration
@EnableStateMachine //启用状态机
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events> {
private Logger logger = LoggerFactory.getLogger(getClass());
//配置初始状态
@Override
public void configure(StateMachineStateConfigurer<States, Events> states)
throws Exception {
states
.withStates()
.initial(States.UNPAID)
.states(EnumSet.allOf(States.class));
}
//配置状态转换的事件关系(多个)
@Override
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
throws Exception {
transitions
.withExternal()
.source(States.UNPAID).target(States.WAITING_FOR_DELIVER).event(Events.PAY)
.and()
.withExternal()
.source(States.WAITING_FOR_DELIVER).target(States.WAITING_FOR_RECEIVE)
.event(Events.DELIVER)
.and()
.withExternal()
.source(States.WAITING_FOR_RECEIVE).target(States.DONE).event(Events.RECEIVE);
}
@Override
public void configure(StateMachineConfigurationConfigurer<States, Events> config)
throws Exception {
config
.withConfiguration()
.listener(listener());
}
@Bean
public StateMachineListener<States, Events> listener() {
return new StateMachineListenerAdapter<States, Events>() {
@Override
public void transition(Transition<States, Events> transition) {
if(transition.getTarget().getId() == States.UNPAID) {
logger.info("订单创建,待支付");
return;
}
if(transition.getSource().getId() == States.UNPAID
&& transition.getTarget().getId() == States.WAITING_FOR_DELIVER) {
logger.info("用户完成支付,待发货");
return;
}
if(transition.getSource().getId() == States.WAITING_FOR_DELIVER
&& transition.getTarget().getId() == States.WAITING_FOR_RECEIVE) {
logger.info("订单已发货,待收货");
return;
}
if(transition.getSource().getId() == States.WAITING_FOR_RECEIVE
&& transition.getTarget().getId() == States.DONE) {
logger.info("用户已收货,订单完成");
return;
}
}
};
}
}
测试类
使用CommandLineRunner接口,在测试类的run方法中启动状态机、发送不同的事件,通过日志验证状态机的流转过程。
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.statemachine.StateMachine;
import javax.annotation.Resource;
@SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... args) throws Exception {
createStateMachine();
}
@Resource
StateMachine<OrderStates, OrderEvents> stateMachine;
public void createStateMachine(){
stateMachine.start();
stateMachine.sendEvent(OrderEvents.PAY);
stateMachine.sendEvent(OrderEvents.DELIVER);
stateMachine.sendEvent(OrderEvents.RECEIVE);
}
}
注解监听器
对于状态监听器,Spring StateMachine还提供了优雅的注解配置实现方式,所有在StateMachineListener接口中定义的事件都能通过注解的方式来进行配置实现。因此,在下面的示例中,我们将监听器放到独立的类中定义,并且使用到了@OnTransition注解配置。它省去了原来事件监听器方法中各种if的判断,从而使代码显得更为简洁,具有更好的可读性。
如下图:
@Component
@WithStateMachine //绑定待监听的状态机
public class OrderEventConfig {
private Logger logger = LoggerFactory.getLogger(getClass());
@OnTransition(target = "UNPAID")
public void create(){
logger.info("订单创建,待支付!");
}
@OnTransition(source = "UNPAID",target = "WAITING_FOR_DELIVER")
public void pay(){
logger.info("用户完成支付,待发货!");
}
@OnTransition(source = "WAITING_FOR_DELIVER",target = "WAITING_FOR_RECEIVE")
public void deliver(){
logger.info("订单已发货,待收货!");
}
@OnTransition(source = "WAITING_FOR_RECEIVE",target = "DONE")
public void receive(){
logger.info("用户已收货,订单完成!");
}
}
多个状态机共存
在实际项目中一般都会有多个状态机并发执行,比如订单,同一时刻会有不止一个订单在运行,而每个订单都有自己的订单状态机流程。但是在上面的例子中,当执行到某一个状态时,再次刷新页面,不会有任何日志出现。也就是说,当一个状态流程执行到某个状态,再次执行这个状态,是不会有任何输出的,因为状态机的机制是只有在状态切换的时候才会触发事件(event)。因此如果想要实现多个状态机的并行执行,就需要用到builder。
@Component
public class OrderStateMachineBuilder {
private final static String MACHINEID = "orderStateMachine";
public StateMachine<OrderStates, OrderEvents> build(BeanFactory beanFactory) throws Exception {
StateMachineBuilder.Builder<OrderStates, OrderEvents> builder = StateMachineBuilder.builder();
Logger logger = LoggerFactory.getLogger(getClass());
logger.info("构建订单状态机");
builder.configureConfiguration()
.withConfiguration()
.machineId(MACHINEID)
.beanFactory(beanFactory);
builder.configureStates()
.withStates()
.initial(OrderStates.UNPAID)
.states(EnumSet.allOf(OrderStates.class));
builder.configureTransitions()
.withExternal()
.source(OrderStates.UNPAID).target(OrderStates.WAITING_FOR_DELIVER)
.event(OrderEvents.PAY)
.and()
.withExternal()
.source(OrderStates.WAITING_FOR_DELIVER).target(OrderStates.WAITING_FOR_RECEIVE)
.event(OrderEvents.DELIVER)
.and()
.withExternal()
.source(OrderStates.WAITING_FOR_RECEIVE).target(OrderStates.DONE)
.event(OrderEvents.RECEIVE);
return builder.build();
}
}
其中MACHINEID指向EventConfig,是状态机的配置类和事件实现类的关联。为了能调用到EventConfig,需要在EventConfig中注明状态机的ID。这个id对应的就是OrderStateMachineBuilder 里面的MACHINEID,被builder写到.machineId(MACHINEID)里面。
@Component
@WithStateMachine(id = "orderStateMachine") //绑定待监听的状态机
public class OrderEventConfig {
private Logger logger = LoggerFactory.getLogger(getClass());
@OnTransition(target = "UNPAID")
public void create(){
logger.info("订单创建,待支付!");
}
...
}
调用状态机。在调用状态机时候,现在每一次调用都会新建一个状态机并发运行。
@Autowired
private OrderStateMachineBuilder orderStateMachineBuilder;
StateMachine<States,Events> stateMachine = orderStateMachineBuilder.build(beanFactory);
stateMachine.start();
stateMachine.sendEvent(Events.PAY);
stateMachine.sendEvent(Events.DELIVER);
stateMachine.sendEvent(Events.RECEIVE);
多种状态机并存
在实际需求中,服务不同的需求所以往往需要多个状态机,因此一个程序可能有不同种类的状态机。在实际操作中,我们只需用MACHINEID来标识不同的状态机流程就可以在一个程序内创建并使用不同的状态机了。
示例
步骤1:创建项目
创建项目
步骤2:配置pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.spring.boot2</groupId>
<artifactId>springboot2-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>springboot2-stateMachine</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Springboot2-stateMachine</name>
<description>Springboot-stateMachine</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-redis</artifactId>
<version>1.2.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-bom</artifactId>
<version>2.3.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-starter</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.14</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
步骤3:配置application.yaml
server:
port: 9000
servlet:
context-path: /
spring:
datasource:
druid:
url: jdbc:mysql://172.17.4.136:3603/springboot2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: P@ssw0rd
initial-size: 2
max-active: 2
min-idle: 1
max-wait: 1000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
max-open-prepared-statements: 20
validation-query: select 1
validation-query-timeout: 2000
test-on-borrow: false
test-on-return: false
test-while-idle: true
time-between-eviction-runs-millis: 2000
min-evictable-idle-time-millis: 600000
max-evictable-idle-time-millis: 900000
filters: stat,wall
filter:
stat:
enabled: true
db-type: mysql
log-slow-sql: false
slow-sql-millis: 2000
wall:
enabled: true
db-type: mysql
config:
delete-allow: false
drop-table-allow: false
# aop-patterns: com.alibaba.druid.spring.boot.demo.cn.spring.boot2.dubbo.demo.api.service.*
web-stat-filter:
# enabled: true
# url-pattern:
# exclusions:
# session-stat-enable:
# session-stat-max-count:
# principal-session-name:
# principal-cookie-name:
# profile-enable:
enabled: true
url-pattern: "/*"
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
stat-view-servlet:
url-pattern: "/druid/*"
allow: 127.0.0.1
deny: 192.0.0.1
reset-enable: false
login-username: admin
login-password: 123456
enabled: true
redis:
database: 10
# Redis IP
host: 172.17.4.136
port: 6382
password: test.cn
client-type: jedis
jedis:
pool:
max-active: 50
max-wait: 5
max-idle: 20
min-idle: 5
timeout: 5000
mybatis:
mapper-locations: classpath:mybatis/mapper/*.xml
#type-aliases-package: cn.spring.boot2.mybatis.model
configuration:
map-underscore-to-camel-case: true
default-fetch-size: 100
default-statement-timeout: 30
步骤4:mapper.xml
orderDetailMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.spring.boot2.statemachine.mapper.OrderDetailMapper">
<!-- 根据订单号获取订单信息 -->
<select id="queryByDetail" resultType="cn.spring.boot2.statemachine.pojo.TradeOrderDetail">
select * from trade_order_Detail where order_sn = #{orderSn}
</select>
<insert id="createTradeOrderDetail" parameterType="cn.spring.boot2.statemachine.pojo.TradeOrderDetail">
insert into trade_order_detail(ORDER_SN,DETAIL_ID,QUANTITY) values(#{orderSn},#{detailId},#{quantity})
</insert>
<insert id="batchCreateTradeOrderDetail">
insert into trade_order_detail(ORDER_SN,DETAIL_ID,QUANTITY) values
<foreach collection="list" item="item" index="index" separator="," >
(#{item.orderSn},#{item.detailId},#{item.quantity})
</foreach>
</insert>
</mapper>
orderMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.spring.boot2.statemachine.mapper.OrderMapper">
<!-- 根据订单号获取订单信息 -->
<select id="queryBySn" resultType="cn.spring.boot2.statemachine.pojo.TradeOrder">
select * from trade_order where order_sn = #{orderSn}
</select>
<insert id="createTradeOrder" parameterType="cn.spring.boot2.statemachine.pojo.TradeOrder">
insert into trade_order(ORDER_SN,ORDER_STATE,ORDER_NAME,CRT_TIME,UPD_TIME,ORDER_STATE_NAME) values(#{orderSn},#{orderState},#{orderName},#{crtTime},#{updTime},#{orderStateName})
</insert>
<update id="updateTradeOrderState" parameterType="cn.spring.boot2.statemachine.pojo.TradeOrder">
update trade_order set ORDER_STATE=#{orderState},ORDER_STATE_NAME =#{orderStateName},UPD_TIME=#{updTime} where order_sn=#{orderSn}
</update>
</mapper>
步骤5:源代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableTransactionManagement
public class SpringbootStateMachineApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootStateMachineApplication.class, args);
}
}
状态描述
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @Descripton:交易订单的状态
* @Version:
**/
public enum TradeOrderStateMachineEnum {
WAIT_FOR_PAY(10, "待付款"),
WAIT_FOR_AUDIT(20, "待评审"),
WAIT_FOR_DELIVER(30, "待发货"),
DELIVER(40, "已发货"),
WAIT_FOR_EVALUATE(70, "待评价"),
COMPLETED(80, "完成"),
CANCLE(90, "取消"),
APPLYREFUND(2001, "申请退款"),
REFUNDED(2999, "已退款"),
AUDIT_CHOICE(1000, "评审选择态"),
SIGN_CHOICE(1001, "签收选择态");
private final Integer value;
private final String desc;
private static final Map<Integer, TradeOrderStateMachineEnum> valueMap = Arrays.stream(values()).collect(Collectors.toMap(TradeOrderStateMachineEnum::getValue, Function.identity()));
private TradeOrderStateMachineEnum(Integer value, String desc) {
this.value = value;
this.desc = desc;
}
public Integer getValue() {
return this.value;
}
public String getDesc() {
return this.desc;
}
public static TradeOrderStateMachineEnum fromValue(Integer value) {
return (TradeOrderStateMachineEnum) Optional.ofNullable(valueMap.get(value)).orElseThrow(() -> {
return new RuntimeException("can not find the enum for this value: " + value);
});
}
}
import java.io.Serializable;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @Descripton:交易订单的操作事件)通过事件操作状态
* @Version:
**/
public enum TradeOrderEvent implements Serializable {
// PAY("PAY","付款"),//付款
// CLOSE("CLOSE","关闭订单"),//关闭订单
// CANCEL("CANCEL","取消数量"),//取消数量
// AUDIT("AUDIT","评审"),//评审
// DELIVER("DELIVER","发货"),//发货
// SIGN("SIGN","签收"),//签收
// EVALUATE("EVALUATE","评价");//评价
PAY("PAY","付款"),//付款
AUDIT("AUDIT","评审"),//评审
// CLOSE("CLOSE","关闭订单"),
CANCEL("CANCEL","取消订单"),
// REFUND("CANCEL","取消订单"),
CLOSE("CLOSE","关闭订单"),
DELIVER("DELIVER","发货"),//发货
SIGN("SIGN","签收"),
EVALUATE("EVALUATE","评价");
private static final Map<String, TradeOrderEvent> valueMap = Arrays.stream(values()).collect(Collectors.toMap(TradeOrderEvent::getEventName, Function.identity()));
TradeOrderEvent(String eventName,String desc){
this.eventName=eventName;
this.desc=desc;
}
public String eventName;
private String desc;
public String getEventName() {
return eventName;
}
public void setEventName(String eventName) {
this.eventName = eventName;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public static TradeOrderEvent fromValue(String eventName) {
return (TradeOrderEvent) Optional.ofNullable(valueMap.get(eventName)).orElseThrow(() -> {
return new RuntimeException("can not find the enum for this value: " + eventName);
});
}
}
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;
import org.springframework.util.StringUtils;
/**
* @Descripton:
* @Version:
**/
public class TradeOrderGuardFactory {
private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;
public class TradeOrderAuditChoiceGuard implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
// if(isAllSign(tradeOrder) && !StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
if(isAllSign(tradeOrder)){
return true;
}
return false;
}
}
public class TradeOrderAuditChoiceGuard2 implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
// if(isAllSign(tradeOrder) && StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
if(isAllSign(tradeOrder)){
return true;
}
return false;
}
}
public class TradeOrderAuditChoiceGuard3 implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
if(isAllDeliver(tradeOrder)){
return true;
}
return false;
}
}
public class TradeOrderAuditChoiceGuard4 implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
MessageData<TradeOrder> messageData=context.getMessage().getHeaders().get(TRADE_ORDER, MessageData.class);
//// TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
TradeOrder tradeOrder = messageData.getData();
// if(isAllAudit(tradeOrder) ){
// return true;
// }
if("pass".equalsIgnoreCase(messageData.getParam())) {
messageData.setCode("200");
messageData.setMsg("审核通过");
return true;
}else{
messageData.setCode("400");
messageData.setMsg("审核不通过");
return false;
}
}
}
public class TradeOrderAuditChoiceRefundGuard implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
System.out.println("====退款");
// MessageData<TradeOrder> messageData=context.getMessage().getHeaders().get(TRADE_ORDER, MessageData.class);
//// TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
// TradeOrder tradeOrder = messageData.getData();
// if(isAllAudit(tradeOrder) ){
// return true;
// }
// messageData.setCode("400");
// messageData.setMsg("订单明细验证不合法");
// return false;
return true;
}
}
public class TradeOrderSignChoiceGuard implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
// if(isAllSign(tradeOrder) && StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
if(isAllSign(tradeOrder)){
return true;
}
return false;
}
}
public class TradeOrderSignChoiceGuard2 implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
// if(isAllSign(tradeOrder) && !StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
if(isAllSign(tradeOrder)){
return true;
}
return false;
}
}
public class TradeOrderGuard implements Guard<TradeOrderStateMachineEnum, TradeOrderEvent> {
@Override
public boolean evaluate(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
boolean result=false;
System.out.println(context.getSource().getId());
System.out.println(context.getTarget().getId());
switch (context.getTarget().getId()) {
case WAIT_FOR_AUDIT:
return WAIT_FOR_AUDIT(context);
case AUDIT_CHOICE:
return AUDIT_CHOICE(context);
case WAIT_FOR_DELIVER:
return WAIT_FOR_DELIVER(context);
case DELIVER:
return DELIVER(context);
case REFUNDED:
return WAIT_FOR_REFUND(context);
case SIGN_CHOICE:
return SIGN_CHOICE(context);
case WAIT_FOR_EVALUATE:
return WAIT_FOR_EVALUATE(context);
case COMPLETED:
return COMPLETED(context);
default:
break;
}
return result;
}
private boolean AUDIT_CHOICE(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
MessageData messageData = context.getMessage().getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
return true;
}
private boolean DELIVER(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
return true;
}
private boolean WAIT_FOR_AUDIT(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
MessageData messageData = context.getMessage().getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
// if(isWaitPay(tradeOrder) && isQuantity(tradeOrder)){
// return true;
// }
return true;
}
private boolean WAIT_FOR_REFUND(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
MessageData messageData = context.getMessage().getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
if(tradeOrder.getOrderState()==20){
return true;
}
return false;
}
private boolean isQuantity(TradeOrder tradeOrder){
if(tradeOrder.getTradeOrderDetailList()!=null&&tradeOrder.getTradeOrderDetailList().size()>0){
return true;
}
return false;
}
private boolean WAIT_FOR_DELIVER(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
MessageData messageData = context.getMessage().getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
// TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
/* if(isAllAudit(tradeOrder)){
return true;
}*/
/*if(isWaitPay(tradeOrder)){
return true;
}*/
return true;
}
private boolean WAIT_FOR_SIGN(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
MessageData messageData = context.getMessage().getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
if(isAllDeliver(tradeOrder)){
return true;
}
return false;
}
private boolean SIGN_CHOICE(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
// TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
MessageData messageData = context.getMessage().getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
if(isAllSign(tradeOrder)){
return true;
}
return false;
}
private boolean WAIT_FOR_EVALUATE(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
// TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
MessageData messageData = context.getMessage().getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
if(isAllSign(tradeOrder)&& StringUtils.isEmpty(tradeOrder.getBuyer())){
return true;
}
return false;
}
private boolean COMPLETED(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context){
// TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
MessageData messageData = context.getMessage().getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
if(isAllSign(tradeOrder)&& !StringUtils.isEmpty(tradeOrder.getBuyer())){
return true;
}
return false;
}
}
private boolean isAllAudit(TradeOrder tradeOrder) {
if(tradeOrder.getTradeOrderDetailList()==null&& tradeOrder.getTradeOrderDetailList().size()<=0){
return false;
}
for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
if(tradeOrderDetail.getQuantity()<=0){
return false;
}
}
return true;
}
private boolean isAllDeliver(TradeOrder tradeOrder) {
/* for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
tradeOrderDetail.setCancelQty(0);
}
if(ObjectUtils.isEmpty(tradeOrderDetail.getDeliverQty())){
tradeOrderDetail.setDeliverQty(0);
}
//待评审的数量
if(tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getDeliverQty()!=0){
return false;
}
}*/
return true;
}
private boolean isAllSign(TradeOrder tradeOrder) {
/* for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
tradeOrderDetail.setCancelQty(0);
}
if(ObjectUtils.isEmpty(tradeOrderDetail.getCustSignQty())){
tradeOrderDetail.setCustSignQty(0);
}
//代签收的数量
if((tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getCustSignQty()!=0)){
return false;
}
}*/
return true;
}
public boolean isWaitPay(TradeOrder tradeOrder){
if(TradeOrderStateMachineEnum.WAIT_FOR_PAY.getValue()==tradeOrder.getOrderState()){
return true;
}
return false;
}
}
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @Descripton:
* @Version:
**/
@Mapper
public interface OrderDetailMapper {
List<TradeOrderDetail> queryByDetail(String orderSn);
boolean createTradeOrderDetail(TradeOrderDetail tradeOrderDetail);
boolean batchCreateTradeOrderDetail(List<TradeOrderDetail> list);
}
import org.apache.ibatis.annotations.Mapper;
/**
* @Descripton:
* @Version:
**/
@Mapper
public interface OrderMapper {
public TradeOrder queryBySn(String orderSn);
public int createTradeOrder(TradeOrder tradeOrder);
public int updateTradeOrderState(TradeOrder tradeOrder);
}
import lombok.Data;
import java.io.Serializable;
/**
* @Descripton:
* @Version:
**/
@Data
public class MessageData<T> implements Serializable {
private String code;
private String msg;
private T data;
private String param;
}
import lombok.Data;
import java.util.List;
/**
* @Descripton:
* @Version:
**/
@Data
public class TradeOrder {
private String orderSn;
private Integer orderState;
private String orderStateName;
private String orderName;
private String crtTime;
private String updTime;
private String buyer;
private List<TradeOrderDetail> tradeOrderDetailList;
}
import lombok.Data;
/**
* @Descripton:
* @Version:
**/
@Data
public class TradeOrderDetail {
private String orderSn;
private String detailId;
private int quantity;
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @Descripton:
* @Version:
**/
@Slf4j
@Repository
public class OrderDao {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderDetailMapper orderDetailMapper;
public boolean createOrder(TradeOrder tradeOrder) throws Exception {
try {
int i=orderMapper.createTradeOrder(tradeOrder);
return i>=1;
}catch(Exception ex){
log.error("创建数据订单错误:{}",ex);
throw new Exception("创建数据订单错误:"+ex.getMessage());
}
}
public boolean updateOrder(TradeOrder tradeOrder) throws Exception {
try{
int i=orderMapper.updateTradeOrderState(tradeOrder);
return i>=1;
}catch(Exception ex){
log.error("更新数据订单错误:{}",ex);
throw new Exception("更新数据订单错误:"+ex.getMessage());
}
}
public TradeOrder queryTradeOrderBySn(String orderSn) throws Exception {
try{
return orderMapper.queryBySn(orderSn);
}catch(Exception ex){
log.error("查询数据订单错误:{}",ex);
throw new Exception("查询数据订单错误:"+ex.getMessage());
}
}
public List<TradeOrderDetail> queryByDetail(String orderSn) throws Exception{
try{
return orderDetailMapper.queryByDetail(orderSn);
}catch(Exception ex){
log.error("查询数据订单明细错误:{}",ex);
throw new Exception("查询数据订单明细错误:"+ex.getMessage());
}
}
public boolean batchCreateOrderDetail(List<TradeOrderDetail> list) throws Exception{
try{
return orderDetailMapper.batchCreateTradeOrderDetail(list);
}catch(Exception ex){
log.error("创建数据订单明细错误:{}",ex);
throw new Exception("创建数据订单明细错误:"+ex.getMessage());
}
}
}
/**
* @Descripton:
* @Version:
**/
public class StateMachineHeaderNameConstants {
//交易订单
public static final String TRADE_ORDER = "tradeOrder";
}
创建有限状态机
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.StateMachineBuilder;
import org.springframework.statemachine.persist.RepositoryStateMachinePersist;
import org.springframework.statemachine.redis.RedisStateMachineContextRepository;
import org.springframework.statemachine.redis.RedisStateMachinePersister;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.EnumSet;
/**
* @Descripton:创建交易订单状态机Bean
* @Version:
**/
@Component
//状态机构建 状态和事件等成对的
@Slf4j
@EnableStateMachine(name= TradeOrderStateMachineBuilder.MACHINEID_TO)
public class TradeOrderStateMachineBuilder {
public final static String MACHINEID_TO = "MACHINEID_TO";//TO状态机
private static final TradeOrderGuardFactory tradeOrderGuardFactory= new TradeOrderGuardFactory();
@Autowired
private BeanFactory beanFactory;
@Resource
private RedisConnectionFactory redisConnectionFactory;
public StateMachine<TradeOrderStateMachineEnum, TradeOrderEvent> build(TradeOrderStateMachineEnum tradeOrderStateMachineEnum) throws Exception {
StateMachine<TradeOrderStateMachineEnum, TradeOrderEvent> stateMachine = build(beanFactory,tradeOrderStateMachineEnum);
log.debug("状态机ID:"+stateMachine.getId());
stateMachine.start();
return stateMachine;
}
/**
* 构建状态机
* -构建TO单状态机
* @param beanFactory
* @return
* @throws Exception
*/
public StateMachine<TradeOrderStateMachineEnum, TradeOrderEvent> build(BeanFactory beanFactory,TradeOrderStateMachineEnum tradeOrderStateMachineEnum) throws Exception {
StateMachineBuilder.Builder<TradeOrderStateMachineEnum, TradeOrderEvent> builder = StateMachineBuilder.builder();
builder.configureConfiguration()
.withConfiguration()
.machineId(MACHINEID_TO)
.beanFactory(beanFactory);
builder.configureStates()
.withStates()
//.initial(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
.initial(tradeOrderStateMachineEnum) //需要根据订单的当前状态设置初始状态
.choice(TradeOrderStateMachineEnum.AUDIT_CHOICE)
// .choice(TradeOrderStateMachineEnum.SIGN_CHOICE)
.states(EnumSet.allOf(TradeOrderStateMachineEnum.class));
builder.configureTransitions()
//支付后,从待付款到待审核
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
.target(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
.event(TradeOrderEvent.PAY)
.guard(tradeOrderGuardFactory.new TradeOrderGuard()) //验证
.and()
// //取消订单,从待付款到关闭
// .withExternal()
// .source(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
// .target(TradeOrderStateMachineEnum.CANCLE)
// .event(TradeOrderEvent.CLOSE).and()
// 从审核选择态 审核通过->待发货,审核不通过申请退款
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
.target(TradeOrderStateMachineEnum.AUDIT_CHOICE)
.event(TradeOrderEvent.AUDIT)
.guard(tradeOrderGuardFactory.new TradeOrderGuard()) //验证
.and()
/*.action(new TradeOrderChoiceAction())
.and()*/
.withChoice()
.source(TradeOrderStateMachineEnum.AUDIT_CHOICE)
.first(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard4(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.APPLYREFUND, tradeOrderGuardFactory.new TradeOrderAuditChoiceRefundGuard(),new TradeOrderRefundChoiceAction())
.last(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT).and()
.withExternal()
.source(TradeOrderStateMachineEnum.APPLYREFUND)
.target(TradeOrderStateMachineEnum.CANCLE)
.event(TradeOrderEvent.CLOSE).and()
//审核后,从待发货->已发货
.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER)
.target(TradeOrderStateMachineEnum.DELIVER)
.event(TradeOrderEvent.DELIVER)
.guard(tradeOrderGuardFactory.new TradeOrderGuard()) //验证状态
.and()
//发货后,从已发货->已签收
.withExternal()
.source(TradeOrderStateMachineEnum.DELIVER)
.target(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE)
.event(TradeOrderEvent.SIGN).guard(tradeOrderGuardFactory.new TradeOrderGuard());
// .and()
// //签收后,从已发货到待签收选择态
// .withExternal()
// .source(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE)
// .target(TradeOrderStateMachineEnum.COMPLETED)
// .event(TradeOrderEvent.EVALUATE);
//签收后,从待签收选择态到待评价或者到已完成
// .withChoice()
// .source(TradeOrderStateMachineEnum.SIGN_CHOICE)
// .first(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE, tradeOrderGuardFactory.new TradeOrderSignChoiceGuard(),new TradeOrderChoiceAction())
// .then(TradeOrderStateMachineEnum.COMPLETED, tradeOrderGuardFactory.new TradeOrderSignChoiceGuard2(),new TradeOrderChoiceAction())
// .last(TradeOrderStateMachineEnum.WAIT_FOR_SIGN).and()
//评价后,从待评价到已完成
// .withExternal()
// .source(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE)
// .target(TradeOrderStateMachineEnum.COMPLETED)
// .event(TradeOrderEvent.EVALUATE);
return builder.build();
}
// @Bean(name = "orderRedisPersister")
/**
* 事务状态机存储(采用redis)
*/
@Bean(name="tradeOrderStateMachinePersister")
public RedisStateMachinePersister<TradeOrderStateMachineEnum, TradeOrderEvent> redisPersister() {
return new RedisStateMachinePersister<TradeOrderStateMachineEnum,TradeOrderEvent>(redisPersist());
}
public StateMachinePersist<TradeOrderStateMachineEnum, TradeOrderEvent,String> redisPersist() {
RedisStateMachineContextRepository<TradeOrderStateMachineEnum, TradeOrderEvent> repository =
new RedisStateMachineContextRepository<>(redisConnectionFactory);
return new RepositoryStateMachinePersist<>(repository);
}
/**
* @TODO 采用
**/
//// @Bean(name = "tradeOrderStateMachinePersister")
// public StateMachinePersist<TradeOrderStateMachineEnum, TradeOrderEvent, String> getOrderPersister() {
// return new DefaultStateMachinePersister<TradeOrderStateMachineEnum, TradeOrderEvent, String>(new StateMachinePersist<TradeOrderStateMachineEnum, TradeOrderEvent, String>() {
// @Override
// public void write(StateMachineContext<TradeOrderStateMachineEnum, TradeOrderEvent> context, String contextObj) {
// }
// @Override
// public StateMachineContext<TradeOrderStateMachineEnum, TradeOrderEvent> read(String contextObj) {
// StateMachineContext<TradeOrderStateMachineEnum, TradeOrderEvent> result = new DefaultStateMachineContext(TradeOrderStateMachineEnum.fromValue(contextObj.getOrderState()),
// null, null, null, null, MACHINEID_TO);
// return result;
// }
// });
// }
}
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;
/**
* @Descripton: 交易类状态机事件类监听器,可以查看各状态变化
* @Version:
**/
@Slf4j
@WithStateMachine(name= TradeOrderStateMachineBuilder.MACHINEID_TO)
public class TradeOrderAction {
private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;
/**
* 整体流程时间如下
* 1、下单-》支付--》审核--》发货--》签收--》评论
* 2、下单-》取消
* 3、下单--> 支付 --》退款 --》取消
**/
@OnTransition(source = "WAIT_FOR_PAY", target = "WAIT_FOR_AUDIT")
public void WAIT_FOR_PAY_TO_WAIT_FOR_AUDIT(Message<TradeOrderEvent> message) {
log.debug("等候支付->支付后的审核 开始");
MessageData messageData = message.getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT.getValue());
tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT.getDesc());
log.debug("message:tradeOrder:{}", JSON.toJSONString(tradeOrder));
log.debug("等候支付->支付后的审核 结束");
}
@OnTransition(source = "WAIT_FOR_PAY", target = "CLOSED")
public void WAIT_FOR_PAY_TO_CLOSE(Message<TradeOrderEvent> message) {
log.debug("等候支付->取消订单 开始");
MessageData messageData = message.getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
tradeOrder.setOrderState(TradeOrderStateMachineEnum.CANCLE.getValue());
tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.CANCLE.getDesc());
log.debug("message:tradeOrder:{}", JSON.toJSONString(tradeOrder));
log.debug("等候支付->取消订单 结束");
}
@OnTransition(source = "WAIT_FOR_AUDIT", target = "AUDIT_CHOICE")
public void WAIT_FOR_AUDIT_TO_AUDIT_CHOICE(Message<TradeOrderEvent> message) {
log.debug("等候审核->选择审核流程 开始");
// TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
MessageData messageData = message.getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
tradeOrder.setOrderState(TradeOrderStateMachineEnum.AUDIT_CHOICE.getValue());
tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.AUDIT_CHOICE.getDesc());
/* if("pass".equalsIgnoreCase(messageData.getParam())){
tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getValue());
tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getDesc());
}else{
tradeOrder.setOrderState(TradeOrderStateMachineEnum.APPLYREFUND.getValue());
tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.APPLYREFUND.getDesc());
}*/
//
log.debug("message:tradeOrder:{}", JSON.toJSONString(tradeOrder));
log.debug("等候审核->选择审核流程 结束");
}
// @OnTransition(source = "AUDIT_CHOICE", target = "WAIT_FOR_DELIVER")
// public void AUDIT_CHOICE_TO_WAIT_FOR_DELIVER(Message<TradeOrderEvent> message) {
// log.debug("审核通过->待发货流程 开始");
//// TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
// MessageData messageData = message.getHeaders().get(TRADE_ORDER, MessageData.class);
// TradeOrder tradeOrder= (TradeOrder) messageData.getData();
// tradeOrder.setOrderState(TradeOrderStateMachineEnum.AUDIT_CHOICE.getValue());
// tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.AUDIT_CHOICE.getDesc());
// /* if("pass".equalsIgnoreCase(messageData.getParam())){
// tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getValue());
// tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getDesc());
// }else{
// tradeOrder.setOrderState(TradeOrderStateMachineEnum.APPLYREFUND.getValue());
// tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.APPLYREFUND.getDesc());
// }*/
////
// tradeOrder.setOrderState(TradeOrderStateMachineEnum.APPLYREFUND.getValue());
// tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.APPLYREFUND.getDesc());
// log.debug("message:tradeOrder:{}", JSON.toJSONString(tradeOrder));
// log.debug("审核通过->待发货流程 结束");
// }
// @OnTransition(source = "AUDIT_CHOICE", target = "APPLYREFUND")
// public void AUDIT_CHOICE_TO_APPLYREFUND(Message<TradeOrderEvent> message) {
// log.debug("审核不通过->选择退款流程 开始");
//// TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
// MessageData messageData = message.getHeaders().get(TRADE_ORDER, MessageData.class);
// TradeOrder tradeOrder= (TradeOrder) messageData.getData();
// tradeOrder.setOrderState(TradeOrderStateMachineEnum.AUDIT_CHOICE.getValue());
// tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.AUDIT_CHOICE.getDesc());
// /* if("pass".equalsIgnoreCase(messageData.getParam())){
// tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getValue());
// tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getDesc());
// }else{
// tradeOrder.setOrderState(TradeOrderStateMachineEnum.APPLYREFUND.getValue());
// tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.APPLYREFUND.getDesc());
// }*/
////
// tradeOrder.setOrderState(TradeOrderStateMachineEnum.APPLYREFUND.getValue());
// tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.APPLYREFUND.getDesc());
// log.debug("message:tradeOrder:{}", JSON.toJSONString(tradeOrder));
// log.debug("审核不通过->选择退款流程 结算");
// }
@OnTransition(source = "APPLYREFUND", target = "REFUND")
public void APPLYREFUND_TO_REFUND(Message<TradeOrderEvent> message) {
log.debug("申请退款->退款 开始");
// TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
MessageData messageData = message.getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
log.debug("message:tradeOrder:{}", JSON.toJSONString(tradeOrder));
log.debug("申请退款->退款 开始");
}
@OnTransition(source = "REFUND", target = "CLOSED")
public void REFUND_TO_CLOSE(Message<TradeOrderEvent> message) {
log.debug("申请退款->退款 开始");
// TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
MessageData messageData = message.getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
log.debug("message:tradeOrder:{}", JSON.toJSONString(tradeOrder));
log.debug("申请退款->退款 结束");
}
@OnTransition(source = "WAIT_FOR_AUDIT", target = "WAIT_FOR_DELIVER")
public void WAIT_FOR_AUDIT_TO_WAIT_FOR_DELIVER(Message<TradeOrderEvent> message) {
log.debug("审核完成->待发货 开始");
// TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
MessageData messageData = message.getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
log.debug("message:tradeOrder:{}", JSON.toJSONString(tradeOrder));
log.debug("审核完成->待发货 结束");
}
@OnTransition(source = "WAIT_FOR_DELIVER", target = "DELIVER")
public void WAIT_FOR_DELIVER_TO_DELIVER(Message<TradeOrderEvent> message) {
log.debug("待发货->已发货 开始");
// TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
MessageData messageData = message.getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
tradeOrder.setOrderState(TradeOrderStateMachineEnum.DELIVER.getValue());
tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.DELIVER.getDesc());
log.debug("message:tradeOrder:{}", JSON.toJSONString(tradeOrder));
log.debug("待发货->已发货 结束");
}
@OnTransition(source = "DELIVER", target = "WAIT_FOR_EVALUATE")
public void DELIVER_TO_WAIT_FOR_EVALUATE(Message<TradeOrderEvent> message) {
// TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
log.debug("已发货->待评论 开始");
MessageData messageData =message.getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
log.debug("message:tradeOrder:{}", JSON.toJSONString(tradeOrder));
log.debug("已发货->待评论 结束");
}
@OnTransition(source = "WAIT_FOR_EVALUATE", target = "COMPLETED")
public void CUSTOMER_EVALUATE(Message<TradeOrderEvent> message) {
log.debug("待评论->评论并订单结束关闭 开始");
// TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
MessageData messageData =message.getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
log.debug("message:tradeOrder:{}", JSON.toJSONString(tradeOrder));
log.debug("待评论->评论并订单结束关闭 结束");
}
}
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.util.StringUtils;
/**
* @Descripton:交易类状态机事件类(选择类)
* @Version:
**/
@Slf4j
public class TradeOrderChoiceAction implements Action<TradeOrderStateMachineEnum, TradeOrderEvent> {
private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;
@Override
public void execute(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
MessageData messageData = context.getMessage().getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
log.debug("messageData:{}", JSON.toJSONString(messageData));
log.debug("==待审后退款请求 开始");
tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getDesc());
tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getValue());
}
// private void SIGN_CHOICE(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
// // TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
// MessageData messageData = context.getMessage().getHeaders().get(TRADE_ORDER, MessageData.class);
// TradeOrder tradeOrder= (TradeOrder) messageData.getData();
// log.info("签收事件之前,订单的状态为:{}"+tradeOrder.getOrderState());
// if(isAllSign(tradeOrder)){
// //全部签收,并且是2C,则为待评价状态
// if(StringUtils.isEmpty(tradeOrder.getBuyer())){
// tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE.getValue());
// }else{
// //全部签收,并且是2B,则为完成状态
// tradeOrder.setOrderState(TradeOrderStateMachineEnum.COMPLETED.getValue());
// }
// }
// log.info("签收事件之后,订单的状态为:{}"+tradeOrder.getOrderState());
// }
private void AUDIT_CHOICE(StateContext<TradeOrderStateMachineEnum, TradeOrderEvent> context) {
// TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
MessageData messageData = context.getMessage().getHeaders().get(TRADE_ORDER, MessageData.class);
TradeOrder tradeOrder= (TradeOrder) messageData.getData();
//如果全部签收,则可能是待评价状态或者是完成状态
if(StringUtils.isEmpty(tradeOrder.getBuyer())){
tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getValue());
tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getDesc());
}else{
//2B,则为完成状态
tradeOrder.setOrderState(TradeOrderStateMachineEnum.APPLYREFUND.getValue());
tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.APPLYREFUND.getDesc());
}
log.info("取消数量事件之后,订单的状态为:{}"+tradeOrder.getOrderState());
}
// private boolean isAllAudit(TradeOrder tradeOrder) {
// for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
// if(tradeOrderDetail.getQuantity()<=0){
// return false;
// }
// }
// return true;
// }
//
// private boolean isAllDeliver(TradeOrder tradeOrder) {
// for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
// if(tradeOrderDetail.getQuantity()<=0){
// return false;
// }
// }
// return true;
// }
//
// private boolean isAllSign(TradeOrder tradeOrder) {
// for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
// if(tradeOrderDetail.getQuantity()<=0){
// return false;
// }
// }
// return true;
// }
}
import lombok.Data;
import java.util.List;
/**
* @Descripton:
* @Version:
**/
@Data
public class TradeOrder {
private String orderSn;
private Integer orderState;
private String orderStateName;
private String orderName;
private String crtTime;
private String updTime;
private String buyer;
private List<TradeOrderDetail> tradeOrderDetailList;
}
import lombok.extern.slf4j.Slf4j;
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.statemachine.redis.RedisStateMachineContextRepository;
import org.springframework.statemachine.redis.RedisStateMachinePersister;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @Descripton:
* @Version:
**/
@Slf4j
@Component
public class StateMachineUtils {
private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;
@Autowired
private TradeOrderStateMachineBuilder tradeOrderStateMachineBuilder;
@Resource(name = "tradeOrderStateMachinePersister")
private RedisStateMachinePersister tradeOrderStateMachinePersister;
public MessageData<TradeOrder> execute( MessageData<TradeOrder> messageData, TradeOrderStateMachineEnum currentStateEnum,TradeOrderEvent event) throws Exception{
log.debug("调用状态机前的订单状态为>>>>>>>>>>{}"+ messageData.getData().getOrderState());
log.debug("调用状态机的事件:"+event.getEventName());
//获取TO状态机
// StateMachine<TradeOrderStateMachineEnum, TradeOrderEvent> stateMachine;
//stateMachine=tradeOrderStateMachinePersister.restore( stateMachine=tradeOrderStateMachineBuilder.build(),messageData.getData().getOrderSn());
StateMachine<TradeOrderStateMachineEnum, TradeOrderEvent> stateMachine= stateMachine=tradeOrderStateMachineBuilder.build(currentStateEnum);
Message message = MessageBuilder.withPayload(event).setHeader(TRADE_ORDER,messageData ).build();
//初始化状态机
// tradeOrderStateMachinePersister.restore(stateMachine,messageData.getData().getOrderSn());
stateMachine.sendEvent(message);
// tradeOrderStateMachinePersister.persist(stateMachine,messageData.getData().getOrderSn());
log.debug("调用状态机后的订单状态为>>>>>>>>>>{}"+messageData.getData().getOrderState());
stateMachine.stop();
return messageData;
}
}
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
/**
* @Descripton:
* @Version:
**/
@Slf4j
@Service
public class OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private StateMachineUtils stateMachineUtils;
@Transactional(rollbackFor = {Exception.class})
public String createOrder(String orderStr){
try{
TradeOrder tradeOrder= JSON.parseObject(orderStr,TradeOrder.class);
tradeOrder.setCrtTime(DateUtil.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_PAY.getValue());
tradeOrder.setOrderStateName(TradeOrderStateMachineEnum.WAIT_FOR_PAY.getDesc());
if(tradeOrder.getTradeOrderDetailList()==null || tradeOrder.getTradeOrderDetailList().size()<=0){
log.debug("订单明细错误");
return "fail";
}
orderDao.createOrder(tradeOrder);
orderDao.batchCreateOrderDetail(tradeOrder.getTradeOrderDetailList());
return "success";
}catch(Exception ex){
log.error("ex:{}",ex);
}
return "fail";
}
public TradeOrder procOrder(String orderSn ,String eventName,String audit){
try{
TradeOrder tradeOrder = getOrder(orderSn);
List<TradeOrderDetail> list = getOrderDetail(orderSn);
tradeOrder.setTradeOrderDetailList(list);
TradeOrderEvent tradeOrderEvent = TradeOrderEvent.fromValue(eventName);
log.debug("tradeOrderEvent:{}",tradeOrderEvent.getEventName());
MessageData<TradeOrder> data=new MessageData();
data.setData(tradeOrder);
data.setParam(audit);
TradeOrderStateMachineEnum tradeOrderStateMachineEnum = TradeOrderStateMachineEnum.fromValue(tradeOrder.getOrderState());
MessageData<TradeOrder> messageData = stateMachineUtils.execute(data,tradeOrderStateMachineEnum, tradeOrderEvent);
log.debug("messageData:{}",JSON.toJSONString(messageData));
TradeOrder executeTradeOrder = messageData.getData();
log.error("executeTradeOrder:{}",JSON.toJSONString(executeTradeOrder));
executeTradeOrder.setUpdTime(DateUtil.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
orderDao.updateOrder(executeTradeOrder);
return executeTradeOrder;
}catch(Exception ex){
log.error("ex:{}",ex);
}
return null;
}
public TradeOrder getOrder(String orderSn){
try{
return orderDao.queryTradeOrderBySn(orderSn);
}catch(Exception ex){
log.error("ex:{}",ex);
}
return null;
}
public List<TradeOrderDetail> getOrderDetail(String orderSn){
try{
return orderDao.queryByDetail(orderSn);
}catch(Exception ex){
log.error("ex:{}",ex);
}
return null;
}
}