背景
在分析Seata的saga模式实现时,实在是被其复杂的 json 状态语言定义文件劝退,我是有点没想明白为啥要用这么来实现状态机;盲猜可能是基于可视化的状态机设计器来定制化流程,更方便快捷且上手快吧,毕竟可以通过UI直接操作,设计状态流转图,但我暂时不太能get到。对于Saga模式的实现,之前的博文中已经阐述了基于状态机模式实现Saga,是比较常见且合适的做法,因此了解了下Java中的状态机实现方案,以后有相关的业务场景也可以直接上手使用状态机。
Cola-StateMachine
Cola-StateMachine组件是一种轻量级的、无状态的、基于注解的状态机实现,可以方便地管理订单等业务对象的状态转换。COLA框架的状态机使用了连贯接口(Fluent Interfaces)来定义状态和事件,以及对应的动作和检查。COLA框架的状态机是COLA 4.0应用架构的一部分,旨在控制复杂度,提高开发效率。开发背景可见
实现一个状态机引擎,教你看清DSL的本质。
基础模型
在Cola-StateMachine组件中有如下的抽象概念模型:
1.State:状态
2.Event:事件,状态由事件触发,引起变化
3.Transition:流转,表示从一个状态到另一个状态
4.External Transition:外部流转,两个不同状态之间的流转
5.Internal Transition:内部流转,同一个状态之间的流转
6.Condition:条件,表示是否允许到达某个状态
7.Action:动作,到达某个状态之后,可以做什么
8.StateMachine:状态机
Cola-StateMachine链路图
业务应用示例
基于订单业务的场景,做一个简单的demo。
关闭订单的简单流程图
![971683-20230607104224840-139959165](http://admin.liuhaihua.cn/wp-content/uploads/2024/01/971683-20230607104224840-139959165.png)
关闭订单简单的状态流转图
![971683-20230607104251671-1755578301](http://admin.liuhaihua.cn/wp-content/uploads/2024/01/971683-20230607104251671-1755578301.png)
添加依赖
<dependency>
<groupId>com.alibaba.cola</groupId>
<artifactId>cola-component-statemachine</artifactId>
<version>4.3.1</version>
</dependency>
定义一个订单的实体类、订单状态的枚举值、订单事件的枚举值
@Data
@Builder
public class Order {
public OrderStatusEnum orderStatusEnum;
public Integer orderId;
public String orderName;
}
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;
}
}
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;
OrderEvent(String code, String info) {
this.code = code;
this.info = info;
}
public String getCode() {
return code;
}
public String getInfo() {
return info;
}
}
在容器启动的时候注册一个订单状态变更的工厂
@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);
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() + " 正在操作 " + ctx.getOrderId() + " from:" + from + " to:" + to + " on:" + event);
};
}
}
在定义一个特殊的,只是举个例子,可以通过集成的方式集成实现一个用户关单的具体操作
@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 的操作接口
@RestController
public class OrderOperaController {
@Autowired
@Qualifier("orderOperaMachine")
StateMachine<OrderStatusEnum, OrderEvent, Order> orderOperaMachine;
@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;
}
@RequestMapping("adminClose")
public Boolean adminCloseOrder() {
Order order = Order.builder().orderId(1).orderName("后台操作人员").orderStatusEnum(OrderStatusEnum.HAVE_BEEN_DELIVERY).build();
OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.HAVE_BEEN_DELIVERY, OrderEvent.ADMIN_CLOSE, order);
System.out.println(orderStatusEnum.toString());
return true;
}
@RequestMapping("overTimeclose")
public Boolean overTimeCloseOrder() {
Order order = Order.builder().orderId(1).orderName("超时了关闭订单")
.orderStatusEnum(OrderStatusEnum.WAITING_FOR_DELIVERY).build();
OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.WAITING_FOR_DELIVERY, OrderEvent.OVERTIME_CLOSE, order);
System.out.println(orderStatusEnum.toString());
return true;
}
@RequestMapping("checkErrorClose")
public Boolean checkErrorCloseOrder() {
Order order = Order.builder().orderId(1).orderName("上级检查错误").orderStatusEnum(OrderStatusEnum.WAITING_FOR_DELIVERY).build();
OrderStatusEnum orderStatusEnum = orderOperaMachine.fireEvent(OrderStatusEnum.WAITING_FOR_DELIVERY, OrderEvent.CHECK_ERROR_CLOSE, order);
System.out.println(orderStatusEnum.toString());
return true;
}
}
启动程序
![971683-20230607104712113-873185374](http://admin.liuhaihua.cn/wp-content/uploads/2024/01/971683-20230607104712113-873185374.png)
安装UML
![971683-20230607104839023-2044143687](http://admin.liuhaihua.cn/wp-content/uploads/2024/01/971683-20230607104839023-2044143687.png)
随便新建一个uml文件,然后将启动程序的控制台输出内容复制到uml中
![971683-20230607105018665-1260635964](http://admin.liuhaihua.cn/wp-content/uploads/2024/01/971683-20230607105018665-1260635964.png)
最后运行下