最近好多业务都是流程状态的传递,借此机会写词文章,记录一下,什么样的场景设计需要我们的状态机:
目前市场流行的状态机:
1.Spring Statemachine
2.阿里COLA4.4
状态机框架
- Spring Statemachine (重量级选手)1.2k+ star.
- squirrel-foundation(松鼠)1.8k+ star.
- cola-statemachine (可乐)6.6k+ star.
springboot合cola-statemachine 4.4.0
cola-statemachine(阿里出品)
相比Spring statemachine状态机等的复杂,功能多;我们实际业务员需要常用的功能,简单使用,所以这类就显得不简洁;再看cola-statemachine相比就是小巧、无状态、简单、轻量、性能极高的状态机DSL实现,解决业务中的状态流转问题。
github:
https://github.com/alibaba/COLA/tree/master/cola-components/cola-component-statemachine
学习这玩意什么用:
状态机优势
1、状态机建立的控制中心是跟外界低耦合的,通过event通信;
2、控制中心所有的状态都是预设好的,不会超预料;
3、状态的跳转都是有设定控制条件的,会按照预设的转移路径运动;
4、状态机还非常容易的扩展和变更,支持因业务的发展而变更或扩展复杂业务流程。
Spring Boot StateMachine实现
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>1.2.0.RELEASE</version>
</dependency>
Spring Boot cola实现
<dependency>
<groupId>com.alibaba.cola</groupId>
<artifactId>cola-component-statemachine</artifactId>
<version>4.4.0-SNAPSHOT</version>
</dependency>
状态机实现步骤:
第一步生成一个状态机
第二步:设置一个外部状态转移类型的builder
第三步:设置状态机的id和ready
第四步:触发状态机
概念:
State:状态
Event:事件,状态由事件触发,引起变化
Transition:流转,表示从一个状态到另一个状态
External Transition:外部流转,两个不同状态之间的流转
Internal Transition:内部流转,同一个状态之间的流转
Condition:条件,表示是否允许到达某个状态
Action:动作,到达某个状态之后,可以做什么
StateMachine:状态机
完成状态机配置:状态机的初始状态和所有状态机的转移规则
整个状态的调度逻辑主要依靠配置方式的定义,而所有的业务逻辑操作都被定义在了状态监听器中,其实状态监听器可以实现的功能远不止上面我们所述的内容,它还有更多的事件捕获
都是技术人员介绍那么多都是废话,直接上代码它不香
好多代码都是demo根本没有实际试用起来。今天给大家上点干活。
无脑选择spring自带的状态机:
场景一:电商的订单,和发货,退货的状态
企业无商城,你玩什么。因此我们的实际场景就是我们经常看到的订单状态的流转
状态机定义三件事:
1.定义枚举状态
2.定义枚举事件
3.定义事件流转的触发条件
有人就说了,为什么定义枚举呢?
答:你想定义啥,定义啥,你有本事,可以直接定义字典,定义接口,定义静态字段。
1.先给我们的业务订单定义状态
public enum OrderState implements IEnum<String> {
CREATED(0, ""),//新创建订单
PENDING(10, "Pending"),//支付待确认
WAIT_SHIP(20, "Preparing"),//待发货
REFUND_CHOICE(30, ""),//订单退款逻辑选择器,伪状态,数据库中不存在
DELIVERING(40, "Shipped"),//已发货[运输中],所有的子单都发货,订单变更为已发货状态
DELIVERED(50, "Shipped"),//已妥投[运输中],所有的子单都妥投,订单变更为妥投状态
RECEIVED(60, "Received"),//已签收,所有的子单都签收(刨除逆向订单),订单变更为已签收
COMPLETED(70, "Completed"),//完成,所有的子单都完成(刨除逆向订单),订单变更为已完成
CANCELED(80, "Canceled"),//已取消,未支付订单、取消订单
REFUND_AUDIT(90, "Reviewing"),//退货审核,所有子单都处于该状态
REFUND_AUDIT_CHOICE(95, ""),//订单退款审核逻辑选择器,伪状态,数据库中不存在
REFUNDING(100, "Refunding"),//退款中,所有子单都处于该状态
REFUNDED(110, "Refunded");//已退款,所有子单都处于该状态
/**
* 此值为了解决父订单的状态取值而设,越大说明业务流程越深,时间离现在越近,订单的状态取此值最小的子单的状态
*/
@Getter
private final int logicDepth;
@Getter
@Setter
private String customerDesc;
OrderState(int depth, String desc) {
this.logicDepth = depth;
this.customerDesc = desc;
}
@Override
public String getValue() {
return name();
}
}
2.定义事件
public enum OrderEvent implements IEnum<String> {
COD_CHECKOUT,//创建订单后选择COD支付
COD_CONFIRM,//支付确认
COD_CANCEL,//支付取消
COD_TIMEOUT,//支付确认超时
PAY_SUCCESS,//PPD支付成功
PAY_TIMEOUT,//支付超时
PAY_CANCEL,//取消支付
USER_CANCEL,//支付后/支付确认后用户取消
PLATFORM_CANCEL,//支付后平台取消
TIMEOUT_CANCEL,//订单匹配超时取消
DO_SHIP,//发货
ORDER_ARRIVE,//妥投
CONFIRM_RECEIVE,//用户签收
ORDER_REVIEW,//订单评价
REJECT_ORDER,//用户拒签
APPLY_RETURN,//申请退货退款
RETURN_AGGREE,//退货退款审核通过
RETURN_REJECT,//退货退款审核拒绝
GOODS_RETURNED,//平台收到退回货物
DO_REFUND,//收到货后,平台操作发起退款
PPD_REFUND_SUCCESS,;//退款成功回调
COD_REFUND_SUCCESS,//退款成功回调
@Override
public String getValue() {
return name();
}
}
3.定义状态机配置
@Configuration
public class OrderStateMachineConfig {
final static String MACHINEID = "orderItemStateMachine";
@Bean
public StateMachinePersister<OrderState, OrderEvent, OrderStateAware> orderItemPersister(
StateMachinePersist<OrderState, OrderEvent, OrderStateAware> orderItemStateMachinePersist) {
return new DefaultStateMachinePersister<>(orderItemStateMachinePersist);
}
@Bean
public StateMachine<OrderState, OrderEvent> orderItemStateMachine() throws Exception {
// @formatter:off
StateMachineBuilder.Builder<OrderState, OrderEvent> builder = StateMachineBuilder.builder();
builder.configureConfiguration()
.withConfiguration()
.machineId(MACHINEID)
.beanFactory(beanFactory);
builder.configureStates()
.withStates()
.initial(OrderState.CREATED)
.choice(OrderState.REFUND_CHOICE)
.choice(OrderState.REFUND_AUDIT_CHOICE)
.end(OrderState.COMPLETED)
.end(OrderState.CANCELED)
.end(OrderState.REFUNDED)
.states(EnumSet.allOf(OrderState.class));
builder.configureTransitions()
//CREATED 状态流转
.withExternal()
.source(OrderState.CREATED).target(OrderState.WAIT_SHIP).event(OrderEvent.PAY_SUCCESS)
.and().withExternal()
.source(OrderState.CREATED).target(OrderState.CANCELED).event(OrderEvent.PAY_CANCEL)
.and().withExternal()
.source(OrderState.CREATED).target(OrderState.CANCELED).event(OrderEvent.PAY_TIMEOUT)
.and().withExternal()
.source(OrderState.CREATED).target(OrderState.PENDING).event(OrderEvent.COD_CHECKOUT)
//PENDING 状态流转
.and().withExternal()
.source(OrderState.PENDING).target(OrderState.WAIT_SHIP).event(OrderEvent.COD_CONFIRM)
.and().withExternal()
.source(OrderState.PENDING).target(OrderState.CANCELED).event(OrderEvent.COD_CANCEL)
.and().withExternal()
.source(OrderState.PENDING).target(OrderState.CANCELED).event(OrderEvent.COD_TIMEOUT)
//WAIT_SHIP 状态流转
.and().withExternal()
.source(OrderState.WAIT_SHIP).target(OrderState.DELIVERING).event(OrderEvent.DO_SHIP)
.and().withExternal()
.source(OrderState.WAIT_SHIP).target(OrderState.REFUND_CHOICE).event(OrderEvent.USER_CANCEL)
.and().withExternal()
.source(OrderState.WAIT_SHIP).target(OrderState.REFUND_CHOICE).event(OrderEvent.PLATFORM_CANCEL)
.and().withExternal()
.source(OrderState.WAIT_SHIP).target(OrderState.REFUND_CHOICE).event(OrderEvent.TIMEOUT_CANCEL)
//退货退款/仅退款/cancel choice流转
.and().withChoice()
.source(OrderState.REFUND_CHOICE)
//如果为COD支付[wait_ship,delivered],状态变为Canceled,事物提交后返还库存、退还限购,发送异步消息通知A端
//COD received状态 不允许退款
.first(OrderState.CANCELED, codRefundChoiceGuard,codRefundChoiceAction)
//否则 !cod & wait_ship & 实付=0,状态变为Refunded,事物提交后返还库存、退还限购,退还cash,发送异步消息通知A端
.then(OrderState.REFUNDED,cashRefundChoiceGuard,cashRefundChoiceAction)
//否则 !cod & wait_ship & 实付>0, 状态变为Refunding,事物提交后返还库存、退还限购,退还cash,调用三方支付退款api,发送异步消息通知A端
.then(OrderState.REFUNDING, ppdRefundOnlyChoiceGuard,ppdRefundOnlyChoiceAction)
//否则 (!cod & received)(可能包含cash支付,此时也需要审核)
.then(OrderState.REFUND_AUDIT, ppdRefundChoiceGuard,ppdRefundChoiceAction)
//DELIVERING 状态流转
.and().withExternal()
.source(OrderState.DELIVERING).target(OrderState.DELIVERED).event(OrderEvent.ORDER_ARRIVE)
//DELEVERED 状态流转
.and().withExternal()
.source(OrderState.DELIVERED).target(OrderState.RECEIVED).event(OrderEvent.CONFIRM_RECEIVE)
.and().withExternal()
.source(OrderState.DELIVERED).target(OrderState.REFUND_CHOICE).event(OrderEvent.REJECT_ORDER)
//RECEIVED 状态流转
.and().withExternal()
.source(OrderState.RECEIVED).target(OrderState.COMPLETED).event(OrderEvent.ORDER_REVIEW)
.and().withExternal()
.source(OrderState.RECEIVED).target(OrderState.REFUND_CHOICE).event(OrderEvent.APPLY_RETURN)
//RETURN_AUDIT 状态流转
.and().withExternal()
.source(OrderState.REFUND_AUDIT).target(OrderState.COMPLETED).event(OrderEvent.RETURN_REJECT)
.and().withExternal()
.source(OrderState.REFUND_AUDIT).target(OrderState.REFUND_AUDIT_CHOICE).event(OrderEvent.RETURN_AGGREE)
//审核通过后
.and().withChoice()
.source(OrderState.REFUND_AUDIT_CHOICE)
//实付=0
.first(OrderState.REFUNDED,refundAuditChoiceGuard)
//实付>0
.last(OrderState.REFUNDING)
//REFUNDING 状态流转
.and().withExternal()
.source(OrderState.REFUNDING).target(OrderState.REFUNDED).event(OrderEvent.PPD_REFUND_SUCCESS)
// .and().withExternal()
// .source(OrderState.REFUNDING).target(OrderState.REFUNDED).event(OrderEvent.COD_REFUND_SUCCESS)
;
// @formatter:on
return builder.build();
}
@Bean
public StateMachinePersist<OrderState, OrderEvent, OrderStateAware> orderItemStateMachinePersist() {
return new StateMachinePersist<OrderState, OrderEvent, OrderStateAware>() {
@Override
public void write(StateMachineContext<OrderState, OrderEvent> context, OrderStateAware contextObj) {
}
@Override
public StateMachineContext<OrderState, OrderEvent> read(OrderStateAware contextObj) {
return new DefaultStateMachineContext<>(
contextObj.getState(),
null, null, null, null, MACHINEID);
}
};
}
}
4.定义状态机
public interface OrderEventFirer extends OrderStateAware {
default void fireEvent(StateMachineRestorer restorer, OrderEvent event) {
StateMachine<OrderState, OrderEvent> stateMachine = restorer.restore(this);
stateMachine.sendEvent(event);
OrderState newState = stateMachine.getState().getId();
changeState(newState,event);
}
default void fireEvent(StateMachineRestorer restorer, OrderEvent event, Map<String,Object> headers) {
StateMachine<OrderState, OrderEvent> stateMachine = restorer.restore(this);
MessageBuilder<OrderEvent> builder = MessageBuilder.withPayload(event);
for (Map.Entry<String, Object> entry : headers.entrySet()) {
builder.setHeader(entry.getKey(), entry.getValue());
}
Message<OrderEvent> message = builder.build();
stateMachine.sendEvent(message);
OrderState newState = stateMachine.getState().getId();
changeState(newState,event);
}
default void changeState(OrderState newState, OrderEvent event){
OrderState currentState = this.getState();
Assert.state(currentState != newState,"Undefined state transition.state=" + currentState + ",event=" + event);
this.setBackState(currentState);
this.setState(newState);
this.setLatestAction(event);
}
void setState(OrderState state);
void setBackState(OrderState state);
void setLatestAction(OrderEvent event);
}
5.业务代码使用
@Override
public void doDelivered(String orderItemUuid, TrackDetailsDto tracking) {
OrderItem orderItem = baseMapper.findByUuid(orderItemUuid);
orderItem.setDeliverAt(tracking.getDeliverDate());
orderItem.fireEvent(stateMachineRestorer, OrderEvent.ORDER_ARRIVE);
if(null == orderItem.getPaymentAt()) {
orderItem.setPaymentAt(orderItem.getDeliverAt());
}
baseMapper.updateById(orderItem);
Order order = orderDao.findByUuid(orderItem.getOrderUuid());
if (null == order.getDeliveredCount()) {
order.setDeliveredCount(0);
}
order.setDeliveredCount(order.getDeliveredCount() + 1 );
List<OrderState> subStates = baseMapper.findByOrderUuid(order.getUuid()).stream().map(OrderItem::getState).collect(Collectors.toList());
if (order.stateModified(subStates)) {
smsSender.sendSms(
SmsBizType.ORDER_SINGED,
order.getLanguage(),
order.getShippingAddressMobile());
}
orderDao.updateById(order);
}
写完收工:
好多网友看了,欲哭无泪,看了这么久不能运行。白扯半天。😄 里面缺几个实现类,缺数据库表设计。这是实际代码,能贴出来嘛。根据思路去实现才是王道,比一些demo强太多了。