• 状态机+策略在工单流转里的使用


    昨天刚写了 可编排策略在交易系统的应用,今天小伙伴就给我提了个需求。

    背景

    最近由于新业务的需要,需要对原有工单的推单逻辑进行变动。我是想着尽可能不对原有逻辑进行改动,毕竟业务还一直在跑,就开启了一个小模块进行代码的组合。

    在设计评审中,我一直强调要设计的灵活些。

    校验规则一定要原子化,然后可编排在任何一处场景。校验的的入参和出参必须抽取,并标准化。

    直接把我昨天写的文章扔过去了。

    然后就差一个状态机了,得我就把框架搭起来。

    设计

    工单的流转完全可以状态驱动,如果发现两个状态都指向了一个处理逻辑,那就说明,你需要加一个状态。本着这个原则开始设计。

    执行策略的实现

    首先定义工单状态策略

    /**
     * 工单状态流转策略
     * @Author: yxkong
     * @Date: 2022/8/23 11:28 AM
     * @version: 1.0
     */
    public interface OrderStateStrategy {
    
        /**
         * 状态流转前置校验
         * @param context
         * @return
         */
        OrderStateResult check(OrderStateContext context);
    
        /**
         * 状态流转统一入口
         *   执行的模板
         * @param context
         * @return
         */
        OrderStateResult execute(OrderStateContext context);
    
        /**
         * 状态流转处理器,由各个策略实现
         *   如果需要重试,在code = -1的时候,retry = true
         * @param context
         * @return
         */
        OrderStateResult handler(OrderStateContext context);
    
        /**
         * 状态流转后置处理
         * @param context
         * @param rst
         * @return
         */
        boolean after(OrderStateContext context, OrderStateResult rst);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    进行模板方法封装,将整个公共部分抽取

    /**
     * 公共处理
     * @Author: yxkong
     * @Date: 2022/8/23 11:47 AM
     * @version: 1.0
     */
    public abstract class AbstractOrderStateStrategy implements OrderStateStrategy {
    
        @Override
        public OrderStateResult check(OrderStateContext context) {
            /**
             * 此处公共校验
             * 根据工单id查询工单的状态
             */
            OrderStateEnum dbStatus = OrderStateEnum.init;
            if (context.getCurrentState() != dbStatus){
                return new OrderStateResult(null,false,CodeStateEnum.fail,"当前状态不匹配");
            }
            return new OrderStateResult(CodeStateEnum.succ,"校验通过!");
        }
    
        @Override
        public OrderStateResult execute(OrderStateContext context) {
            OrderStateResult check = check(context);
            if (!check.isSucc()){
                /**
                 * 这块埋点,因为什么拦截的
                 */
                return check ;
            }
            OrderStateResult handler = handler(context);
            //执行后的统一处理
            boolean rst = this.after(context, handler);
            if (!rst){
                /**
                 * 要保证最终一致性,这里业务已经处理完了
                 * 记录日志或者,推入消息队列做补偿
                 */
            }
            return handler;
        }
    
        @Override
        public boolean after(OrderStateContext context, OrderStateResult rst) {
            /**
             *  统一发送领域事件
             *  统一记录日志,失败也需要记录日志
             *  处理成功,返回true,处理失败,返回false,需要补偿
             */
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    具体的策略实现

    /**
     * 关单策略实现
     * @Author: yxkong
     * @Date: 2022/8/23 12:16 PM
     * @version: 1.0
     */
    @Service("closeOrderStrategy")
    public class CloseOrderStrategyImpl extends AbstractOrderStateStrategy {
        @Override
        public OrderStateResult handler(OrderStateContext context) {
            return null;
        }
    }
    
    /**
     * 推单策略实现
     * @Author: yxkong
     * @Date: 2022/8/23 11:59 AM
     * @version: 1.0
     */
    @Service("pushOrderStrategy")
    public class PushOrderStrategyImpl extends AbstractOrderStateStrategy {
        @Override
        public OrderStateResult check(OrderStateContext context) {
            /**
             * 定制化拦截
             * 比如,查询推单开关是否开启,重试的次数
             */
            String closeSwitch = "open";
            if ("close".equals(closeSwitch)){
                return  new OrderStateResult(CodeStateEnum.fail,"推单开关关闭,不允许推单");
            }
            return super.check(context);
        }
    
        @Override
        public OrderStateResult handler(OrderStateContext context) {
            /**
             * 推单的处理逻辑
             */
            return null;
        }
    }
    
    /**
     * 重新路由策略实现
     * @Author: yxkong
     * @Date: 2022/8/23 12:10 PM
     * @version: 1.0
     */
    @Service("reRouteStrategy")
    public class ReRouteStrategyImpl extends AbstractOrderStateStrategy {
        @Override
        public OrderStateResult handler(OrderStateContext context) {
            /**
             * 路由到具体的机构后,将数据库工单改成推单状态
             */
            return new OrderStateResult(OrderStateEnum.pass, CodeStateEnum.succ,"路由成功");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    最后类结构如下:

    image-20220823181005766

    状态机的实现

    状态机接口定义

    /**
     * 状态机服务
     * @Author: yxkong
     * @Date: 2022/8/23 5:40 PM
     * @version: 1.0
     */
    public interface OrderStateMachineService {
        /**
         * 状态机执行
         * @param context
         * @return
         */
        OrderStateResult execute(OrderStateContext context);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    状态机实现

    /**
     * 工单状态机
     * @Author: yxkong
     * @Date: 2022/8/23 12:32 PM
     * @version: 1.0
     */
    @Service
    public class OrderStateMachineServiceImpl implements OrderStateMachineService {
        @Autowired
        private Map<String, OrderStateStrategy> map;
    
        /**
         * 状态机执行
         * @param context
         * @return
         */
        @Override
        public OrderStateResult execute(OrderStateContext context){
            OrderStateEnum currentState = context.getCurrentState();
            if ( OrderStateEnum.isFinalState(currentState)){
                //终态不处理,直接返回
                return null;
            }
            OrderStateMappingEnum mapping = OrderStateMappingEnum.of(currentState.getState());
            if (mapping == null){
                throw new RuntimeException("暂不支持该状态的处理"+ currentState);
            }
            //获取对应状态的处理策略
            OrderStateStrategy stateStrategy = map.get(mapping.getHandlerBean());
            //执行具体的策略
            OrderStateResult execute = stateStrategy.execute(context);
            //是否需要递归调用
            if (execute.isExecute()){
                /**
                 * 一个状态执行完,重新赋值状态
                 */
                context.setCurrentState(execute.getOrderStatus());
                context.setRetry(execute.isRetry());
                return this.execute(context);
            }
            return execute;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    在这里利用spring的DI机制,将同类型的注入到map里,方便使用

    同时利用枚举来封装状态与处理器的关系。

    枚举类

    /**
     * 状态与处理映射
     * @author yxkong
     */
    public enum OrderStateMappingEnum {
    	closeOrder(5,"closeOrderStrategy","关单"),
    	reroute(1,"reRouteStrategy","重新路由"),
    	retry(2,"retryStrategy", "重试"),
    	blockOrder(3,"blockOrderStrategy", "卡单");
    	private int state;
    	private String handlerBean;
    	private String desc;
    
    	OrderStateMappingEnum(int state, String handlerBean, String desc) {
    		this.state = state;
    		this.handlerBean = handlerBean;
    		this.desc = desc;
    	}
    
    	public int getState() {
    		return state;
    	}
    
    	public String getHandlerBean() {
    		return handlerBean;
    	}
    
    
    	public String getDesc() {
    		return desc;
    	}
    
    	/**
    	 * 获取枚举
    	 *
    	 * @param state 枚举值
    	 * @return OrderStateMappingEnum
    	 */
    	public static OrderStateMappingEnum of(final int state) {
    		for (final OrderStateMappingEnum s : OrderStateMappingEnum.values()) {
    			if (Objects.equals(s.state, state)) {
    				return s;
    			}
    		}
    		return null;
    	}
    	
    }
    
    
    /**
     * 状态枚举
     * @Author: yxkong
     * @Date: 2022/8/23 11:41 AM
     * @version: 1.0
     */
    public enum OrderStateEnum {
        init(0,"初始化工单"),
        waitingClose(8,"等待关闭"),
        waitingRoute(2,"等待路由"),
        refuse(6,"工单拒绝"),
        pass(5,"工单通过");
    
    
        private int state;
        private String desc;
    
        private OrderStateEnum(int state, String desc) {
            this.state = state;
            this.desc = desc;
        }
        public static OrderStateEnum of(final int state) {
            for (final OrderStateEnum s : OrderStateEnum.values()) {
                if (Objects.equals(s.state, state)) {
                    return s;
                }
            }
            return null;
        }
    
        /**
         * 是否终态
         * @param state
         * @return
         */
        public static boolean isFinalState(OrderStateEnum state){
            return OrderStateEnum.refuse.equals(state) || OrderStateEnum.pass.equals(state);
        }
    
        public int getState() {
            return state;
        }
    
        public String getDesc() {
            return desc;
        }
    }
    
    /**
     * 返回code枚举
     * @Author: yxkong
     * @Date: 2022/8/23 5:56 PM
     * @version: 1.0
     */
    public enum CodeStateEnum {
        succ(1,"成功"),
        fail(0,"失败"),
        error(-1,"异常");
        private int code;
        private String desc;
    
        private CodeStateEnum(int code, String desc) {
            this.code = code;
            this.desc = desc;
        }
        public static CodeStateEnum of(final int code) {
            for (final CodeStateEnum s : CodeStateEnum.values()) {
                if (Objects.equals(s.code, code)) {
                    return s;
                }
            }
            return null;
        }
    
        public int getCode() {
            return code;
        }
    
        public String getDesc() {
            return desc;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132

    上下文与出参

    /**
     * 工单状态上下文
     * @Author: yxkong
     * @Date: 2022/8/23 11:28 AM
     * @version: 1.0
     */
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class OrderStateContext {
        /**工单id*/
        private String appId;
        /**当前状态*/
        private OrderStateEnum currentState;
        /**重试*/
        private boolean retry;
    }
    
    
    /**
     * 状态流转返回结果
     *
     * @Author: yxkong
     * @Date: 2022/8/23 11:30 AM
     * @version: 1.0
     */
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class OrderStateResult {
        /**执行后的工单状态*/
        private OrderStateEnum orderStatus;
        /**是否重试*/
        private boolean retry ;
        /**当前执行状态码  1成功,0失败,-1 异常*/
        private CodeStateEnum code;
        /**当前执行结果描述*/
        private String message;
    
        public OrderStateResult(CodeStateEnum code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public OrderStateResult(OrderStateEnum orderStatus, CodeStateEnum code, String message) {
            this.orderStatus = orderStatus;
            this.code = code;
            this.message = message;
        }
    
        /**
         * 是否成功
         * @return
         */
        public boolean isSucc(){
            return this.code == CodeStateEnum.succ;
        }
        /**
         * 是否继续执行
         * @return
         */
        public boolean isExecute(){
            if (CodeStateEnum.succ == this.code  || (CodeStateEnum.error == this.code && retry)){
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    整个代码结构如下:

    image-20220823182045689

    这么一来,我们只需要关心具体的业务逻辑就行了,状态不够就加状态,入口统一,关系映射在一处,也方便排查问题。

    这里不像上一篇里的规则,定义以后可以用在多个地方,每个地方,我都可以编排。

    这里就是一对一的映射。

  • 相关阅读:
    ubuntu2204部署hbase2.3.7
    JavaScript 异步操作里的嵌套回调函数
    【开发技术】springboot自动化测试 【单元测试、集成测试】
    springboot绿色食品商城毕业设计-附源码061109
    【RocketMQ】主从同步实现原理
    修改静态IP和配置主机名
    建造者模式
    Python selenium基础用法详解
    麒麟OS V10 设置开机自启动
    Vue2--11种组件通信、Vue2处理响应式数据
  • 原文地址:https://blog.csdn.net/f80407515/article/details/126498018