想做一个有实战借鉴的项目(本文只有几个固定的订单状态,没考虑到失败情况)
贴上项目源代码github:https://github.com/shenyang312/goudaner-platform
想要分布式的请异步我的另一个spring cloud项目github:https://blog.csdn.net/weixin_37352094/article/details/83178594
谈一下对状态机的理解:
个人理解状态机的好处,就是完美的代码解耦,把逻辑拆分成:状态,事件
不然就需要自己去判断switch case 这种方式,或者if else
首先是作为一个可是实战spring boot项目做了几点:
1.基础架构:Spring boot+ mybatis + tkmybatis + fastjson + logback + 本文的重点statemachine
2.项目基础base以来第三方等自己的基础类
3.作为共通工具,比如说时间工具比对工具
项目基本时序先发一下(注:原本想做成一个接口,所以画了一个时序,现在拆分两个,但是意思还是相同的)
这里开始代码解读:
状态机两个重点:状态,事件
首先是订单的状态:我定义了这几种,没考虑退货等等,但是后期加很容易
public enum OrderStates {
UNPAID(1,"UNPAID","待支付"), // 待支付
WAITING_DELIVERY(2,"WAITING_DELIVERY","待发货"), // 待支付
WAITING_FOR_RECEIVE(3,"WAITING_FOR_RECEIVE","待收货"), // 待收货
RECEIVE(4,"RECEIVE","签收"), // 结束
NOT_RECEIVE(5,"NOT_RECEIVE","拒收"), // 结束
CHOICE(501,"CHOICE","选择");
private Integer code;
private String msg;
private String value;
OrderStates(Integer code, String msg,String value) {
this.code = code;
this.msg = msg;
this.value = value;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public static String getDesc(Integer value) {
OrderStates[] orderStates = values();
for (OrderStates orderStatesEnum : orderStates) {
if (orderStatesEnum.getCode().equals(value)) {
return orderStatesEnum.getMsg();
}
}
return null;
}
}
出发状态改变的事件
public enum OrderEvent {
CREATE(0,"CREATE","创建订单"), // 支付
PAY(1,"PAY","支付"), // 支付
DELIVERY(2,"DELIVERY","发货"),
RECEIVE(3,"RECEIVE","收货"),
NOT_RECEIVE(4,"NOT_RECEIVE","拒收货");
private Integer code;
private String msg;
private String value;
OrderEvent(Integer code, String msg,String value) {
this.code = code;
this.msg = msg;
this.value = value;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public static String getDesc(Integer value) {
OrderEvent[] orderEvents = values();
for (OrderEvent orderEventsEnum : orderEvents) {
if (orderEventsEnum.getCode().equals(value)) {
return orderEventsEnum.getMsg();
}
}
return null;
}
}
核心逻辑都在 状态修改上,所以新增订单就不解读了,新增订单就是把订单持久化到db中,插入初始化状态,修改订单状态的时候需要去取
这个地方解读下:传入得是状态code,是想避免事件明文传递
public String orderEvent(GdOrderDto gdOrderDto) throws Exception {
Example example = new Example(GdOrder.class);
Example.Criteria criteria = example.createCriteria().andEqualTo("orderId", gdOrderDto.getOrderId());
List gdOrderList = mapper.selectByExample(example);
if(SyUtil.isEmpty(gdOrderList)|| gdOrderList.size()>1 )return "没有这个订单";
GdOrder gdOrder = gdOrderList.get(0);
Boolean retFlag = handler.handleEventWithState(MessageBuilder.withPayload(OrderEvent.valueOf(OrderEvent.getDesc(gdOrderDto.getEventCode())))
.setHeader("gdOrderDto", gdOrderDto).build(), OrderStates.valueOf(OrderStates.getDesc(gdOrder.getOrderState())),"orderStateMachine");
//如果内部异常外部回滚,但是现在的情况只有查询,不用回滚
if(retFlag) TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return retFlag?"出错了,就让你知道知道":gdOrder.getOrderId();
}
之后进入Handler方法,状态机最关键的一个对象stateMachine
/**
* Handle event with entity.
*
* @param event the event
* @param state the state
* @return true if event was accepted
*/
public boolean handleEventWithState(Message event, OrderStates state,String stateMachineId) throws Exception {
//even 存在几个参数
//当前 state 状态,触发event 事件,选择的状态值:true or false
stateMachine.stop();
List> withAllRegions = stateMachine.getStateMachineAccessor().withAllRegions();
for (StateMachineAccess a : withAllRegions) {
a.resetStateMachine(new DefaultStateMachineContext<>(state, null, null, null, null, stateMachineId));
}
stateMachine.start();
try {
stateMachine.sendEvent(event);
System.out.println("过了一会");
Boolean errorFlag = stateMachine.hasStateMachineError();
//判断状态机内部是否发生异常,如果发生为true
if(errorFlag){
stateMachine.setStateMachineError(null);
//充值当前beanFactory上下文中的orderStateMachine 的StateMachine
// this.stateMachine = fSMBuilder.initMachine(beanFactory);
}
return errorFlag;
}catch (Exception e){
e.printStackTrace();
this.stateMachine = fSMBuilder.initMachine(beanFactory);
return true;
}
}
@Configuration
public class FSMBuilder {
public StateMachine initMachine(BeanFactory beanFactory) throws Exception {
Builder builder = StateMachineBuilder.builder();
builder.configureConfiguration().withConfiguration().machineId("orderStateMachine").beanFactory(beanFactory);
builder.configureStates().withStates().initial(OrderStates.UNPAID).states(EnumSet.allOf(OrderStates.class));
builder.configureTransitions()
.withExternal()
.source(OrderStates.UNPAID)
.target(OrderStates.WAITING_DELIVERY)
.event(OrderEvent.PAY)
.and()
.withExternal()
.source(OrderStates.WAITING_DELIVERY)
.target(OrderStates.WAITING_FOR_RECEIVE)
.event(OrderEvent.DELIVERY)
.and()
.withExternal()
.source(OrderStates.WAITING_FOR_RECEIVE)
.target(OrderStates.RECEIVE)
.event(OrderEvent.RECEIVE)
.and()
.withExternal()
.source(OrderStates.WAITING_FOR_RECEIVE)
.target(OrderStates.NOT_RECEIVE)
.event(OrderEvent.NOT_RECEIVE);
return builder.build();
}
}
之后到了我的状态机的监听器
@OnTransition(target = "UNPAID")
public void init() {
System.out.println("创建订单");
}
@OnTransition(source = "UNPAID", target = "WAITING_DELIVERY")
public void tansFrom1To2(@EventHeaders Map headers,
ExtendedState extendedState,
StateMachine stateMachine,
Message message,
Exception e) {
try {
logger.info("支付成功,待发货,主订单");
if (message != null && message.getHeaders().containsKey("gdOrderDto")) {
GdOrderDto group = message.getHeaders().get("gdOrderDto", GdOrderDto.class);
// throw new Exception("自定义异常");
gdOrderService.modifyGdOrder(GdOrder.builder().orderId(group.getOrderId()).orderState(OrderStates.WAITING_DELIVERY.getCode()).build(),"orderId");
}
}catch (Exception exception){
stateMachine.setStateMachineError(exception);
exception.printStackTrace();
}
}
@OnTransition(source = "UNPAID", target = "WAITING_DELIVERY")
public void tansFrom1To4(@EventHeaders Map headers,
ExtendedState extendedState,
StateMachine stateMachine,
Message message,
Exception e) {
logger.info("支付成功,待发货,订单详情");
try {
if (message != null && message.getHeaders().containsKey("gdOrderDto")) {
GdOrderDto group = message.getHeaders().get("gdOrderDto", GdOrderDto.class);
gdOrderMerchService.modifyGdOrderMerch(GdOrderMerch.builder().orderId(group.getOrderId()).gdsState(OrderStates.WAITING_DELIVERY.getCode()).build(),"orderId");
}
}catch (Exception exception){
stateMachine.setStateMachineError(exception);
exception.printStackTrace();
}
}
监听器执行完毕,执行自定义共通Listener
public void onPersist(State state, Message message, Transition transition, StateMachine stateMachine) throws Exception {
if (message != null && message.getHeaders().containsKey("gdOrderDto")) {
GdOrderDto group = message.getHeaders().get("gdOrderDto", GdOrderDto.class);
System.out.println("end-----orderId"+group.getOrderId());
logger.info("之后做一个mq发送给消息模块");
// throw new Exception("自定义异常");
}
}