Spring StateMachine订单业务实战(持久化,事物,异常)

想做一个有实战借鉴的项目(本文只有几个固定的订单状态,没考虑到失败情况)

贴上项目源代码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.作为共通工具,比如说时间工具比对工具

statemachine的重点 就在

项目的sql在

项目基本时序先发一下(注:原本想做成一个接口,所以画了一个时序,现在拆分两个,但是意思还是相同的

 

这里开始代码解读:

   状态机两个重点:状态,事件

首先是订单的状态:我定义了这几种,没考虑退货等等,但是后期加很容易

    

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<GdOrder> 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<OrderEvent> event, OrderStates state,String stateMachineId) throws Exception {
        //even 存在几个参数
        //当前 state 状态,触发event 事件,选择的状态值:true or false
        stateMachine.stop();
        List<StateMachineAccess<OrderStates, OrderEvent>> withAllRegions = stateMachine.getStateMachineAccessor().withAllRegions();
        for (StateMachineAccess<OrderStates, OrderEvent> 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<OrderStates, OrderEvent> initMachine(BeanFactory beanFactory) throws Exception {
        Builder<OrderStates, OrderEvent> 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<String, Object> headers,
                             ExtendedState extendedState,
                             StateMachine<String, String> stateMachine,
                             Message<String> 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<String, Object> headers,
                             ExtendedState extendedState,
                             StateMachine<String, String> stateMachine,
                             Message<String> 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<OrderStates, OrderEvent> state, Message<OrderEvent> message, Transition<OrderStates, OrderEvent> transition, StateMachine<OrderStates, OrderEvent> 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("自定义异常");
            }
        }

 

  • 5
    点赞
  • 20
    收藏
  • 打赏
    打赏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论 9

打赏作者

狗蛋儿_312

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值