The project finally uses the Spring state machine, which is very elegant!

1. What is a state machine

1.1 What is a state

Let's first explain what is "state" (State). Real things have different states. For example, an automatic door has two states: open and closed. What we usually call a state machine is a finite state machine, that is, the number of states of the described thing is limited. For example, the state of an automatic door is two open and closed.

State machine, that is, State Machine, does not refer to an actual machine, but refers to a mathematical model. To put it bluntly, it generally refers to a state transition diagram. For example, according to the operating rules of automatic doors, we can abstract the following graph.

The automatic door has two states, open and closed. In the closed state, if the door open signal is read, the state will switch to open. If the door close signal is read in the open state, the state will switch to closed.

The full name of state machine is finite state automata, and the word automatic also contains important meanings. Given a state machine, given its current state and input, the output state can be calculated explicitly. For example, for an automatic door, given the initial state closed and given the input "open the door", then the next state can be calculated.

In this way, we have introduced the basic definition of the state machine. Repeat: state machine is the abbreviation of finite state automata, and it is a mathematical model abstracted from the operation rules of real things.

1.2 Four concepts

The four concepts of the state machine are given below.

  • The first one is State, state. A state machine must contain at least two states. For example, in the example of the automatic door above, there are two states: open and closed.

  • The second is Event, event. An event is a trigger condition or password to perform an operation. For automatic doors, "press the door open button" is an event.

  • The third is Action, action. Actions to be performed after an event occurs. For example, the event is "press the button to open the door", and the action is "open the door". When programming, an Action generally corresponds to a function.

  • The fourth is Transition, transformation. That is, changing from one state to another. For example, "door opening process" is a transformation.

1.3 State machine

A finite state machine (Finite-state machine, FSM), also known as a finite state automata, or a state machine for short, is a mathematical model that represents a finite number of states and behaviors such as transitions and actions between these states.

FSM is an algorithmic idea. Simply put, a finite state machine consists of a set of states, an initial state, an input, and a transition function that transitions to the next state based on the input and the existing state.

Its function is mainly to describe the state sequence experienced by the object in its life cycle, and how to respond to various events from the outside world.

2. State machine diagram

When making requirements, you need to understand the following six elements: start, end, current state, next state (target state), action, condition, and we can complete a state machine diagram:

Take an order as an example: Take the transition from the pending payment status to the pending delivery status as an example

  • ①Current state: refers to the current state. To be paid

  • ②Condition: Also known as "event", when a condition is met, an action will be triggered, or a state transition will be performed. payment event

  • ③ Action: The action to be executed after the condition is met. After the action is executed, it can migrate to a new state, or it can remain in the original state. Actions are not required. When the conditions are met, you can directly migrate to the new state without performing any actions. Status transitioned to Pending Shipment

  • ④ Second state: the new state to be moved to after the condition is met. The "secondary state" is relative to the "present state". Once the "secondary state" is activated, it will transform into a new "present state". Precautions for delivery

1. Avoid treating a certain "program action" as a "state". So how to distinguish between "action" and "state"? "Action" is unstable. Even if there is no conditional trigger, the "action" will end once it is executed; while "state" is relatively stable. If there is no external condition trigger, a state will continue forever.

2. Some states are missed during state division, resulting in incomplete jump logic. Therefore, when designing a state machine, we need to repeatedly check the designed state diagram or state table, and finally achieve an unbreakable design scheme.

3、spring statemachine

3.1 Overview of state machine spring statemachine

Spring Statemachine is a framework for application developers to use state machine concepts in Spring applications

Spring Statemachine is designed to provide the following capabilities:

  1. Easy to use flat single stage state machine for simple use cases.

  2. Hierarchical state machine structure to simplify complex state configuration.

  3. The state machine area provides more complex state configuration.

  4. Use triggers, transitions, guards and actions.

  5. Type the security configuration adapter.

  6. Builder pattern for simple instantiation of common use-case recipes for use outside of a Spring Application context

  7. Distributed state machine based on Zookeeper

  8. State machine event listener.

  9. UML Eclipse Papyrus modeling.

  10. Store computer configurations in persistent storage.

  11. Spring IOC integration associates beans with state machines.

State machines are powerful because the behavior is always guaranteed to be consistent, making debugging relatively easy. This is because the operating rules are written when the machine starts up. The idea is that your application may exist in a finite number of states, and certain predefined triggers can move your application from one state to another. Such triggers can be event or timer based.

It's much easier to define high-level logic outside of the application and then rely on a state machine to manage the state. You can interact with the state machine by sending events, listening for changes, or just requesting the current state.

Official website: spring.io/projects/sp…

3.2 Quick start

Take the example of order status reversal as an example:

The table structure is designed as follows:

CREATE TABLE `tb_order` (
      `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
      `order_code` varchar(128) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '订单编码',
      `status` smallint(3) DEFAULT NULL COMMENT '订单状态',
      `name` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '订单名称',
      `price` decimal(12,2) DEFAULT NULL COMMENT '价格',
      `delete_flag` tinyint(2) NOT NULL DEFAULT '0' COMMENT '删除标记,0未删除  1已删除',
      `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
      `update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '更新时间',
      `create_user_code` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '创建人',
      `update_user_code` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '更新人',
      `version` int(11) NOT NULL DEFAULT '0' COMMENT '版本号',
      `remark` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';
    
    /*Data for the table `tb_order` */
    
    insert  into `tb_order`(`id`,`order_code`,`status`,`name`,`price`,`delete_flag`,`create_time`,`update_time`,`create_user_code`,`update_user_code`,`version`,`remark`) values 
    (2,'A111',1,'A','22.00',0,'2022-10-15 16:14:11','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL),
    (3,'A111',1,'订单A','22.00',0,'2022-10-02 21:53:13','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL),
    (4,'A111',1,'订单A','22.00',0,'2022-10-02 21:53:13','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL),
    (5,'A111',1,'订单A','22.00',0,'2022-10-03 09:08:30','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL);

1) Introduce dependencies

 <!-- redis持久化状态机 -->
    <dependency>
        <groupId>org.springframework.statemachine</groupId>
        <artifactId>spring-statemachine-redis</artifactId>
        <version>1.2.9.RELEASE</version>
    </dependency>
    <!--状态机-->
    <dependency>
        <groupId>org.springframework.statemachine</groupId>
        <artifactId>spring-statemachine-starter</artifactId>
        <version>2.0.1.RELEASE</version>
    </dependency>

2) Define state machine states and events

State enumeration:

/**
* author:公众号:码猿技术专栏
*/
public enum OrderStatus {
        // 待支付,待发货,待收货,已完成
        WAIT_PAYMENT(1, "待支付"),
        WAIT_DELIVER(2, "待发货"),
        WAIT_RECEIVE(3, "待收货"),
        FINISH(4, "已完成");
        private Integer key;
        private String desc;
        OrderStatus(Integer key, String desc) {
            this.key = key;
            this.desc = desc;
        }
        public Integer getKey() {
            return key;
        }
        public String getDesc() {
            return desc;
        }
        public static OrderStatus getByKey(Integer key) {
            for (OrderStatus e : values()) {
                if (e.getKey().equals(key)) {
                    return e;
                }
            }
            throw new RuntimeException("enum not exists.");
        }
    }

event:

/**
* author:公众号:码猿技术专栏
*/
public enum OrderStatusChangeEvent {
        // 支付,发货,确认收货
        PAYED, DELIVERY, RECEIVED;
}

3) Define state machine rules and configure state machine

 @Configuration
    @EnableStateMachine(name = "orderStateMachine")
    public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> {
        /**
         * 配置状态
         *
         * @param states
         * @throws Exception
         */
        public void configure(StateMachineStateConfigurer<OrderStatus, OrderStatusChangeEvent> states) throws Exception {
            states
                    .withStates()
                    .initial(OrderStatus.WAIT_PAYMENT)
                    .states(EnumSet.allOf(OrderStatus.class));
        }
        /**
         * 配置状态转换事件关系
         *
         * @param transitions
         * @throws Exception
         */
        public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderStatusChangeEvent> transitions) throws Exception {
            transitions
                    //支付事件:待支付-》待发货
                    .withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED)
                    .and()
                    //发货事件:待发货-》待收货
                    .withExternal().source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE).event(OrderStatusChangeEvent.DELIVERY)
                    .and()
                    //收货事件:待收货-》已完成
                    .withExternal().source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH).event(OrderStatusChangeEvent.RECEIVED);
        }
    }

Configuration persistence:

 /**
 * author:公众号:码猿技术专栏
 */ 
    @Configuration
    @Slf4j
    public class Persist<E, S> {
        /**
         * 持久化到内存map中
         *
         * @return
         */
        @Bean(name = "stateMachineMemPersister")
        public static StateMachinePersister getPersister() {
            return new DefaultStateMachinePersister(new StateMachinePersist() {
                @Override
                public void write(StateMachineContext context, Object contextObj) throws Exception {
                    log.info("持久化状态机,context:{},contextObj:{}", JSON.toJSONString(context), JSON.toJSONString(contextObj));
                    map.put(contextObj, context);
                }
                @Override
                public StateMachineContext read(Object contextObj) throws Exception {
                    log.info("获取状态机,contextObj:{}", JSON.toJSONString(contextObj));
                    StateMachineContext stateMachineContext = (StateMachineContext) map.get(contextObj);
                    log.info("获取状态机结果,stateMachineContext:{}", JSON.toJSONString(stateMachineContext));
                    return stateMachineContext;
                }
                private Map map = new HashMap();
            });
        }
    
        @Resource
        private RedisConnectionFactory redisConnectionFactory;
        /**
         * 持久化到redis中,在分布式系统中使用
         *
         * @return
         */
        @Bean(name = "stateMachineRedisPersister")
        public RedisStateMachinePersister<E, S> getRedisPersister() {
            RedisStateMachineContextRepository<E, S> repository = new RedisStateMachineContextRepository<>(redisConnectionFactory);
            RepositoryStateMachinePersist p = new RepositoryStateMachinePersist<>(repository);
            return new RedisStateMachinePersister<>(p);
        }
    }

4) Business system

controller:

 /**
 * author:公众号:码猿技术专栏
 */ 
    @RestController
    @RequestMapping("/order")
    public class OrderController {
        @Resource
        private OrderService orderService;
        /**
         * 根据id查询订单
         *
         * @return
         */
        @RequestMapping("/getById")
        public Order getById(@RequestParam("id") Long id) {
            //根据id查询订单
            Order order = orderService.getById(id);
            return order;
        }
        /**
         * 创建订单
         *
         * @return
         */
        @RequestMapping("/create")
        public String create(@RequestBody Order order) {
            //创建订单
            orderService.create(order);
            return "sucess";
        }
        /**
         * 对订单进行支付
         *
         * @param id
         * @return
         */
        @RequestMapping("/pay")
        public String pay(@RequestParam("id") Long id) {
            //对订单进行支付
            orderService.pay(id);
            return "success";
        }
    
        /**
         * 对订单进行发货
         *
         * @param id
         * @return
         */
        @RequestMapping("/deliver")
        public String deliver(@RequestParam("id") Long id) {
            //对订单进行确认收货
            orderService.deliver(id);
            return "success";
        }
        /**
         * 对订单进行确认收货
         *
         * @param id
         * @return
         */
        @RequestMapping("/receive")
        public String receive(@RequestParam("id") Long id) {
            //对订单进行确认收货
            orderService.receive(id);
            return "success";
        }
    }

served:

 /**
 * author:公众号:码猿技术专栏
 */ 
    @Service("orderService")
    @Slf4j
    public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
        @Resource
        private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
        @Resource
        private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;
        @Resource
        private OrderMapper orderMapper;
        /**
         * 创建订单
         *
         * @param order
         * @return
         */
        public Order create(Order order) {
            order.setStatus(OrderStatus.WAIT_PAYMENT.getKey());
            orderMapper.insert(order);
            return order;
        }
        /**
         * 对订单进行支付
         *
         * @param id
         * @return
         */
        public Order pay(Long id) {
            Order order = orderMapper.selectById(id);
            log.info("线程名称:{},尝试支付,订单号:{}" ,Thread.currentThread().getName() , id);
            if (!sendEvent(OrderStatusChangeEvent.PAYED, order)) {
                log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);
                throw new RuntimeException("支付失败, 订单状态异常");
            }
            return order;
        }
        /**
         * 对订单进行发货
         *
         * @param id
         * @return
         */
        public Order deliver(Long id) {
            Order order = orderMapper.selectById(id);
            log.info("线程名称:{},尝试发货,订单号:{}" ,Thread.currentThread().getName() , id);
            if (!sendEvent(OrderStatusChangeEvent.DELIVERY, order)) {
                log.error("线程名称:{},发货失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);
                throw new RuntimeException("发货失败, 订单状态异常");
            }
            return order;
        }
        /**
         * 对订单进行确认收货
         *
         * @param id
         * @return
         */
        public Order receive(Long id) {
            Order order = orderMapper.selectById(id);
            log.info("线程名称:{},尝试收货,订单号:{}" ,Thread.currentThread().getName() , id);
            if (!sendEvent(OrderStatusChangeEvent.RECEIVED, order)) {
                log.error("线程名称:{},收货失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);
                throw new RuntimeException("收货失败, 订单状态异常");
            }
            return order;
        }
        /**
         * 发送订单状态转换事件
         * synchronized修饰保证这个方法是线程安全的
         *
         * @param changeEvent
         * @param order
         * @return
         */
        private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
            boolean result = false;
            try {
                //启动状态机
                orderStateMachine.start();
                //尝试恢复状态机状态
                stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
                Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
                result = orderStateMachine.sendEvent(message);
                //持久化状态机状态
                stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
            } catch (Exception e) {
                log.error("订单操作失败:{}", e);
            } finally {
                orderStateMachine.stop();
            }
            return result;
        }
    }

Listen for state changes:

 /**
 * author:公众号:码猿技术专栏
 */     
    @Component("orderStateListener")
    @WithStateMachine(name = "orderStateMachine")
    @Slf4j
    public class OrderStateListenerImpl {
        @Resource
        private OrderMapper orderMapper;
        
        @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
        public void payTransition(Message<OrderStatusChangeEvent> message) {
            Order order = (Order) message.getHeaders().get("order");
            log.info("支付,状态机反馈信息:{}",  message.getHeaders().toString());
            //更新订单
            order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
        }
        @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
        public void deliverTransition(Message<OrderStatusChangeEvent> message) {
            Order order = (Order) message.getHeaders().get("order");
            log.info("发货,状态机反馈信息:{}",  message.getHeaders().toString());
            //更新订单
            order.setStatus(OrderStatus.WAIT_RECEIVE.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
        }
        @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
        public void receiveTransition(Message<OrderStatusChangeEvent> message) {
            Order order = (Order) message.getHeaders().get("order");
            log.info("确认收货,状态机反馈信息:{}",  message.getHeaders().toString());
            //更新订单
            order.setStatus(OrderStatus.FINISH.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
        }
    }

3.3 Test verification

1) Verify business

  • add an order

    http://localhost:8084/order/create

  • Pay for the order

    http://localhost:8084/order/pay?id=2

  • Ship the order

    http://localhost:8084/order/deliver?id=2

  • Confirm receipt of order

    http://localhost:8084/order/receive?id=2

The normal process ends. If you pay for an order and pay again, an error will be reported:http://localhost:8084/order/pay?id=2

The error is as follows:

2) Verify persistence

Memory

Use the memory persistence class to persist:

 /**
 * author:公众号:码猿技术专栏
 */ 
 @Resource
    private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;
    
    /**
     * 发送订单状态转换事件
     * synchronized修饰保证这个方法是线程安全的
     *
     * @param changeEvent
     * @param order
     * @return
     */
    private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
        boolean result = false;
        try {
            //启动状态机
            orderStateMachine.start();
            //尝试恢复状态机状态
            stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
            Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
            result = orderStateMachine.sendEvent(message);
            //持久化状态机状态
            stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
        } catch (Exception e) {
            log.error("订单操作失败:{}", e);
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }

redis persistence

Import dependencies:

<!-- redis持久化状态机 -->
<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-redis</artifactId>
    <version>1.2.9.RELEASE</version>
</dependency>

Configure yaml:

spring:
  redis:
    database: 0
    host: localhost
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        max-wait: ''
        min-idle: 0
    password: ''
    port: 6379
    timeout: 0

Persist using the redis persistence class:

 /**
 * author:公众号:码猿技术专栏
 */ 
 @Resource
    private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineRedisPersister;
    
    /**
     * 发送订单状态转换事件
     * synchronized修饰保证这个方法是线程安全的
     *
     * @param changeEvent
     * @param order
     * @return
     */
    private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
        boolean result = false;
        try {
            //启动状态机
            orderStateMachine.start();
            //尝试恢复状态机状态
            stateMachineRedisPersister.restore(orderStateMachine, String.valueOf(order.getId()));
            Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
            result = orderStateMachine.sendEvent(message);
            //持久化状态机状态
            stateMachineRedisPersister.persist(orderStateMachine, String.valueOf(order.getId()));
        } catch (Exception e) {
            log.error("订单操作失败:{}", e);
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }

3.4 Problems with the state machine

1) stateMachine cannot throw an exception, and the exception will be digested by the state machine

problem phenomenon

The result obtained from orderStateMachine.sendEvent(message); cannot be perceived. Returns true whether the execution was normal or an exception was thrown.

 @Resource
    private OrderMapper orderMapper;
    
    @Resource
    private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
    
    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
    @Transactional(rollbackFor = Exception.class)
    public void payTransition(Message<OrderStatusChangeEvent> message) {
        Order order = (Order) message.getHeaders().get("order");
        log.info("支付,状态机反馈信息:{}",  message.getHeaders().toString());
        try {
            //更新订单
            order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
            //模拟异常
            if(Objects.equals(order.getName(),"A")){
                throw new RuntimeException("执行业务异常");
            }
        } catch (Exception e) {
            //如果出现异常,记录异常信息,抛出异常信息进行回滚
            log.error("payTransition 出现异常:{}",e);
            throw e;
        }
    }

The listening event throws an exception, which cannot be sensed in the sending event:

 private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
        boolean result = false;
        try {
            //启动状态机
            orderStateMachine.start();
            //尝试恢复状态机状态
            stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
            Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
             //事件执行异常了,依然返回true,无法感知异常
            result = orderStateMachine.sendEvent(message);
            if(result){
                //持久化状态机状态,如果根据true持久化,则会出现问题
                stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
            }
        } catch (Exception e) {
            log.error("订单操作失败:{}", e);
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }

Debugging found that sending events and listening to events is a thread, and the result of sending events is returned after the listening operation is completed.

Listening thread:

Solution: save the exception to the database or memory by yourself, and make a judgment

You can also pass the interface: org.springframework.statemachine.StateMachine##getExtendedState

method puts the execution state into this variable

public interface ExtendedState {
        Map<Object, Object> getVariables();
        <T> T get(Object var1, Class<T> var2);
        void setExtendedStateChangeListener(ExtendedState.ExtendedStateChangeListener var1);
        public interface ExtendedStateChangeListener {
            void changed(Object var1, Object var2);
        }
    }

org.springframework.statemachine.support.DefaultExtendedState##getVariables

private final Map<Object, Object> variables;
    
    public DefaultExtendedState() {
        this.variables = new ObservableMap(new ConcurrentHashMap(), new DefaultExtendedState.LocalMapChangeListener());
    }
    
    public Map<Object, Object> getVariables() {
        return this.variables;
    }

Modify the monitoring state: save the execution result of the business, 1 succeeds, 0 fails

    @Resource
    private OrderMapper orderMapper;
    @Resource
    private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
    
    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
    @Transactional(rollbackFor = Exception.class)
    public void payTransition(Message<OrderStatusChangeEvent> message) {
        Order order = (Order) message.getHeaders().get("order");
        log.info("支付,状态机反馈信息:{}",  message.getHeaders().toString());
        try {
            //更新订单
            order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
            //模拟异常
            if(Objects.equals(order.getName(),"A")){
                throw new RuntimeException("执行业务异常");
            }
            //成功 则为1
            orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);
        } catch (Exception e) {
            //如果出现异常,则进行回滚
            log.error("payTransition 出现异常:{}",e);
            //将异常信息变量信息中,失败则为0
            orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(), 0);
            throw e;
        }
    }

Retrofit of sending events: If a business execution exception is obtained, it will return failure, and the state machine will not be persisted com.zengqingfa.springboot.state.demo.service.impl.OrderServiceImpl##sendEvent

 @Resource
    private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
    @Resource
    private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;
    
    /**
     * 发送订单状态转换事件
     * synchronized修饰保证这个方法是线程安全的
     *
     * @param changeEvent
     * @param order
     * @return
     */
    private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order){
        boolean result = false;
        try {
            //启动状态机
            orderStateMachine.start();
            //尝试恢复状态机状态
            stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
            Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
            result = orderStateMachine.sendEvent(message);
            if(!result){
                return false;
            }
            //获取到监听的结果信息
            Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition + order.getId());
            //操作完成之后,删除本次对应的key信息
            orderStateMachine.getExtendedState().getVariables().remove(CommonConstants.payTransition+order.getId());
            //如果事务执行成功,则持久化状态机
            if(Objects.equals(1,Integer.valueOf(o))){
                //持久化状态机状态
                stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
            }else {
                //订单执行业务异常
                return false;
            }
        } catch (Exception e) {
            log.error("订单操作失败:{}", e);
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }

Code optimization

  • The sending event is only for payment, what if it is a non-payment event?

//获取到监听的结果信息
Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition + order.getId());
  • The code that monitors the setting status has duplicate code and needs to be optimized. AOP can be used

try {
        //TODO 其他业务
        //成功 则为1
        orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);
    } catch (Exception e) {
        //如果出现异常,则进行回滚
        log.error("payTransition 出现异常:{}",e);
        //将异常信息变量信息中,失败则为0
        orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(), 0);
        throw e;
    }

Constant class:

public interface CommonConstants {
        String orderHeader="order";
        String payTransition="payTransition";
        String deliverTransition="deliverTransition";
        String receiveTransition="receiveTransition";
    }

Payment sending event: com.zengqingfa.springboot.state.demo.service.impl.OrderServiceImpl##pay

 @Resource
    private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
    @Resource
    private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;
    @Resource
    private OrderMapper orderMapper;
    
    /**
     * 对订单进行支付
     *
     * @param id
     * @return
     */
    public Order pay(Long id) {
        Order order = orderMapper.selectById(id);
        log.info("线程名称:{},尝试支付,订单号:{}" ,Thread.currentThread().getName() , id);
        if (!sendEvent(OrderStatusChangeEvent.PAYED, order,CommonConstants.payTransition)) {
            log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);
            throw new RuntimeException("支付失败, 订单状态异常");
        }
        return order;
    }
    
    /**
     * 发送订单状态转换事件
     * synchronized修饰保证这个方法是线程安全的
     *
     * @param changeEvent
     * @param order
     * @return
     */
    private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order,String key){
        boolean result = false;
        try {
            //启动状态机
            orderStateMachine.start();
            //尝试恢复状态机状态
            stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
            Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
            result = orderStateMachine.sendEvent(message);
            if(!result){
                return false;
            }
            //获取到监听的结果信息
            Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(key + order.getId());
            //操作完成之后,删除本次对应的key信息
            orderStateMachine.getExtendedState().getVariables().remove(key+order.getId());
            //如果事务执行成功,则持久化状态机
            if(Objects.equals(1,Integer.valueOf(o))){
                //持久化状态机状态
                stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
            }else {
                //订单执行业务异常
                return false;
            }
        } catch (Exception e) {
            log.error("订单操作失败:{}", e);
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }

Use aop to listen to the event aspect, and encapsulate the business execution results into the variables of the state machine. Note:

 @Retention(RetentionPolicy.RUNTIME)
    public @interface LogResult {
        /**
         *执行的业务key
         *
         * @return String
         */
        String key();
    }

section:

 @Component
    @Aspect
    @Slf4j
    public class LogResultAspect {
    
        //拦截 LogHistory注解
        @Pointcut("@annotation(com.zengqingfa.springboot.state.demo.aop.annotation.LogResult)")
        private void logResultPointCut() {
            //logResultPointCut 日志注解切点
        }
        @Resource
        private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
        
        @Around("logResultPointCut()")
        public Object logResultAround(ProceedingJoinPoint pjp) throws Throwable {
            //获取参数
            Object[] args = pjp.getArgs();
            log.info("参数args:{}", args);
            Message message = (Message) args[0];
            Order order = (Order) message.getHeaders().get("order");
            //获取方法
            Method method = ((MethodSignature) pjp.getSignature()).getMethod();
            // 获取LogHistory注解
            LogResult logResult = method.getAnnotation(LogResult.class);
            String key = logResult.key();
            Object returnVal = null;
            try {
                //执行方法
                returnVal = pjp.proceed();
                //如果业务执行正常,则保存信息
                //成功 则为1
                orderStateMachine.getExtendedState().getVariables().put(key + order.getId(), 1);
            } catch (Throwable e) {
                log.error("e:{}", e.getMessage());
                //如果业务执行异常,则保存信息
                //将异常信息变量信息中,失败则为0
                orderStateMachine.getExtendedState().getVariables().put(key + order.getId(), 0);
                throw e;
            }
            return returnVal;
        }
    }

Listener classes use annotations:

 @Component("orderStateListener")
    @WithStateMachine(name = "orderStateMachine")
    @Slf4j
    public class OrderStateListenerImpl {
        @Resource
        private OrderMapper orderMapper;
    
        @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
        @Transactional(rollbackFor = Exception.class)
        @LogResult(key = CommonConstants.payTransition)
        public void payTransition(Message<OrderStatusChangeEvent> message) {
            Order order = (Order) message.getHeaders().get("order");
            log.info("支付,状态机反馈信息:{}", message.getHeaders().toString());
            //更新订单
            order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
            //模拟异常
            if (Objects.equals(order.getName(), "A")) {
                throw new RuntimeException("执行业务异常");
            }
        }
        @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
        @LogResult(key = CommonConstants.deliverTransition)
        public void deliverTransition(Message<OrderStatusChangeEvent> message) {
            Order order = (Order) message.getHeaders().get("order");
            log.info("发货,状态机反馈信息:{}", message.getHeaders().toString());
            //更新订单
            order.setStatus(OrderStatus.WAIT_RECEIVE.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
        }
        @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
        @LogResult(key = CommonConstants.receiveTransition)
        public void receiveTransition(Message<OrderStatusChangeEvent> message) {
            Order order = (Order) message.getHeaders().get("order");
            log.info("确认收货,状态机反馈信息:{}", message.getHeaders().toString());
            //更新订单
            order.setStatus(OrderStatus.FINISH.getKey());
            orderMapper.updateById(order);
            //TODO 其他业务
        }
    }

Guess you like

Origin blog.csdn.net/m0_71777195/article/details/130700027