cola-statemachine实践
背景
在上一家公司,订单的状态有很多,都是在需求日益增加的条件下,衍生出来的,待支付,待发货,已发货,用户取消,后台取消,批处理取消,上级审核取消等
这些业务其实是破坏了订单状态的初始的设计的,并且在日后的维护中,很难达到一个统一的状态,不知道在哪里就触碰了业务的红线,所以,本着简单就是高效
的方式,我这收拢公司这些业务的时候,用到了状态机模式,这里就是标题所示的,cola-statemachine 的实践
梳理业务
首先把公司的订单业务梳理了一下,关于状态变更的几个特殊的点

关闭订单的简单流程图

关闭订单简单的状态流转图

上代码
添加依赖
<!-- https://mvnrepository.com/artifact/com.alibaba.cola/cola-component-statemachine -->
<dependency>
<groupId>com.alibaba.cola</groupId>
<artifactId>cola-component-statemachine</artifactId>
<version>4.3.1</version>
</dependency>定义一个订单的实体类
package com.study.studycolastatemachine.entity;
import com.study.studycolastatemachine.enums.OrderStatusEnum;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class Order {
public OrderStatusEnum orderStatusEnum;
public Integer orderId;
public String orderName;
}
定义订单的状态的枚举值
package com.study.studycolastatemachine.enums;
public enum OrderStatusEnum {
INIT("0", "待付款"),
WAITING_FOR_DELIVERY("1", "待发货"),
HAVE_BEEN_DELIVERY("2", "已发货"),
CLOSE("3", "已取消");
private final String code;
private final String info;
OrderStatusEnum(String code, String info)
{
this.code = code;
this.info = info;
}
public String getCode()
{
return code;
}
public String getInfo()
{
return info;
}
}
定义订单事件的枚举值
package com.study.studycolastatemachine.enums;
/**
* 订单事件
*
* @author admin
* @date 2023/03/14
*/
public enum OrderEvent {
/**
* 用户关闭
*/
USER_CLOSE("0", "用户取消"),
/**
* 管理员关闭
*/
ADMIN_CLOSE("1", "后台取消"),
/**
* 超时关闭
*/
OVERTIME_CLOSE("2", "超时取消"),
/**
* 检查错误关闭
*/
CHECK_ERROR_CLOSE("3", "上级审核取消"),
/**
* 用户付费
*/
USER_PAY("4", "用户支付");
/**
* 密码
*/
private final String code;
/**
* 信息
*/
private final String info;
/**
* 订单事件
*
* @param code 密码
* @param info 信息
*/
OrderEvent(String code, String info) {
this.code = code;
this.info = info;
}
/**
* 获取代码
*
* @return {@link String}
*/
public String getCode() {
return code;
}
/**
* 获取信息
*
* @return {@link String}
*/
public String getInfo() {
return info;
}
}
在容器启动的时候注册一个订单状态变更的工厂
package com.study.studycolastatemachine.config;
import com.alibaba.cola.statemachine.Action;
import com.alibaba.cola.statemachine.Condition;
import com.alibaba.cola.statemachine.StateMachine;
import com.alibaba.cola.statemachine.builder.StateMachineBuilder;
import com.alibaba.cola.statemachine.builder.StateMachineBuilderFactory;
import com.study.studycolastatemachine.action.UserCloseAction;
import com.study.studycolastatemachine.enums.OrderEvent;
import com.study.studycolastatemachine.enums.OrderStatusEnum;
import com.study.studycolastatemachine.entity.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class StateMachineBuilderConfig {
@Autowired
UserCloseAction userCloseAction;
@Bean("orderOperaMachine")
public StateMachine orderOperaMachine() {
String ORDER_OPERA = "order_opera";
StateMachineBuilder<OrderStatusEnum, OrderEvent, Order> builder = StateMachineBuilderFactory.create();
//订单从初始化状态-待发货-状态-转到-关闭订单状态--用户关闭
builder.externalTransitions()
.fromAmong(OrderStatusEnum.INIT, OrderStatusEnum.WAITING_FOR_DELIVERY)
.to(OrderStatusEnum.CLOSE)
.on(OrderEvent.USER_CLOSE)
.when(checkCondition())
.perform(userCloseAction);
//订单从-初始化状态-已发货-待发货--转到-关闭订单状态--后台操作人员关闭
builder.externalTransitions()
.fromAmong(OrderStatusEnum.INIT, OrderStatusEnum.HAVE_BEEN_DELIVERY, OrderStatusEnum.WAITING_FOR_DELIVERY)
.to(OrderStatusEnum.CLOSE)
.on(OrderEvent.ADMIN_CLOSE)
.when(checkCondition())
.perform(doAction());
//订单从等待发货状态-转为-订单关闭状态-超时关闭
builder.externalTransition()
.from(OrderStatusEnum.WAITING_FOR_DELIVERY)
.to(OrderStatusEnum.CLOSE)
.on(OrderEvent.OVERTIME_CLOSE)
.when(checkCondition())
.perform(doAction());
//订单从待发货状态--转为-订单关闭状态-上级审批不通过关闭
builder.externalTransition()
.from(OrderStatusEnum.WAITING_FOR_DELIVERY)
.to(OrderStatusEnum.CLOSE)
.on(OrderEvent.CHECK_ERROR_CLOSE)
.when(checkCondition())
.perform(doAction());
//订单从初始化状态--转为待发货状态--用户支付完毕动
builder.externalTransition()
.from(OrderStatusEnum.INIT)
.to(OrderStatusEnum.WAITING_FOR_DELIVERY)
.on(OrderEvent.USER_PAY)
.when(checkCondition())
.perform(doAction());
StateMachine orderOperaMachine = builder.build(ORDER_OPERA);
//打印uml图
String plantUML = orderOperaMachine.generatePlantUML();
System.out.println(plantUML);
return orderOperaMachine;
}
private Condition<Order> checkCondition() {
return (ctx) -> {
return true;
};
}
private Action<OrderStatusEnum, OrderEvent, Order> doAction() {
return (from, to, event, ctx) -> {
System.out.println(ctx.getOrderName() + " is operating " + ctx.getOrderId() + " from:" + from + " to:" + to + " on:" + event);
};
}
}
在定义一个特殊的,只是举个例子,可以通过集成的方式集成实现一个用户关单的具体操作
package com.study.studycolastatemachine.action;
import com.alibaba.cola.statemachine.Action;
import com.study.studycolastatemachine.enums.OrderEvent;
import com.study.studycolastatemachine.enums.OrderStatusEnum;
import com.study.studycolastatemachine.entity.Order;
import org.springframework.stereotype.Component;
@Component
public class UserCloseAction implements Action<OrderStatusEnum, OrderEvent, Order> {
@Override
public void execute(OrderStatusEnum from, OrderStatusEnum to, OrderEvent event, Order context) {
System.out.println("用户关闭流程开始走了");
System.out.println("从这个状态-【" + from.getInfo() + "】-转为+【" + to.getInfo() + "】 的状态");
System.out.println("上下文信息:" + context.toString());
System.out.println("中间执行的一些操作.......");
System.out.println("用户关闭流程完毕了");
}
}
定义一个 controller 的操作接口
package com.study.studycolastatemachine.controller;
import com.alibaba.cola.statemachine.StateMachine;
import com.study.studycolastatemachine.enums.OrderEvent;
import com.study.studycolastatemachine.enums.OrderStatusEnum;
import com.study.studycolastatemachine.entity.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 订单操作集合
*
* @author admin
* @date 2023/03/14
*/
@RestController
public class OrderOperaController {
@Autowired
@Qualifier("orderOperaMachine")
StateMachine<OrderStatusEnum, OrderEvent, Order> orderOperaMachine;
/**
* 场景1-用户关闭订单
*
* @return {@link Boolean}
*/
@RequestMapping("userclose")
public Boolean userCloseOrder() {
//把订单状态改为关闭
String machineId = orderOperaMachine.getMachineId();
System.out.println(machineId);
Order order = Order.builder().orderId(1).orderName("用户").orderStatusEnum(OrderStatusEnum.INIT).build();
OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.INIT,OrderEvent.USER_CLOSE, order);
System.out.println(orderStatusEnum.toString());
return true;
}
/**
* 场景2-管理员关闭订单
*
* @return {@link Boolean}
*/
@RequestMapping("adminClose")
public Boolean adminCloseOrder() {
//把订单状态改为关闭
Order order = Order.builder().orderId(1).orderName("后台操作人员").orderStatusEnum(OrderStatusEnum.HAVE_BEEN_DELIVERY).build();
OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.CLOSE, OrderEvent.ADMIN_CLOSE, order);
System.out.println(orderStatusEnum.toString());
return true;
}
/**
* 场景3-超时关闭订单
*
* @return {@link Boolean}
*/
@RequestMapping("overTimeclose")
public Boolean overTimeCloseOrder() {
//把订单状态改为关闭
Order order = Order.builder().orderId(1).orderName("超时了关闭订单")
.orderStatusEnum(OrderStatusEnum.WAITING_FOR_DELIVERY).build();
OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.CLOSE, OrderEvent.OVERTIME_CLOSE, order);
System.out.println(orderStatusEnum.toString());
return true;
}
/**
* 场景4-检查错误关闭订单
*
* @return {@link Boolean}
*/
@RequestMapping("checkErrorClose")
public Boolean checkErrorCloseOrder() {
//把订单状态改为关闭
Order order = Order.builder().orderId(1).orderName("上级检查错误").orderStatusEnum(OrderStatusEnum.WAITING_FOR_DELIVERY).build();
OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.CLOSE, OrderEvent.CHECK_ERROR_CLOSE, order);
System.out.println(orderStatusEnum.toString());
return true;
}
}
最后,启动程序

安装 uml 的插件

创建文件

把刚刚的代码粘贴进去

其实可以进行替换

最后通过接口测试跑一下

符合预期,后面的的几个业务线也是可以跑起来的
参考代码study-cola-statemachine.zip
cola-statemachine实践
http://example.com/2023/03/14/hdqzkbp1ywg1gt1w/