美文网首页spring statemachine
Spring StateMachine状态机引擎在项目中的应用(

Spring StateMachine状态机引擎在项目中的应用(

作者: AlanKim | 来源:发表于2019-09-24 17:48 被阅读0次

    背景

    这期说下状态以及对应事件的相关设计,这部分内容是后续状态机相关的配置的基础,其中有些设计在实现的时候来回修改了几版,还是挺考验细节设计的。

    状态变迁

    状态机是为了解决订单的状态变迁问题,为了方便理解,就需要有一个具体的状态变化图,下面是之前处理过的一个案例,先上其对应的状态变迁图:

    复杂订单状态机.png

    说明:

    1. 这个状态变化比较复杂,也比较奇葩,将部分不应该由订单来维护的状态都统一放到了订单中,比如待实名认证、待业务审核、待用户资料不全、待上传影像这种,其实都是开户过程中应该做的,但是业务方抽象能力比较弱,又比较难沟通,且如果希望可以尽快落地,只能由订单系统吞掉这部分逻辑,所以整个流程就变成了上图所示,一个很复杂的状态节点变更流程。
    2. 用户在创建订单时,创建只是一个瞬时状态,只会有待实名及待借款两种情况,所以在这里,create状态设计成了一个choice;统一的初始入口比较容易处理、理解。
    3. 其实还有一些终结状态,比如各种审核失败、风控失败等等,这里为了简化处理,统一设置为close状态。
    状态枚举

    根据上图所示的状态,设计其对应的状态枚举如下:

    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import org.apache.commons.lang3.StringUtils;
    
    import java.util.Arrays;
    import java.util.Optional;
    
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    public enum BizOrderStatusEnum {
    
        CREATE("1", "创建"),
    
        WYD_INITIAL_JUMP("2", "初始创建时可能为待实名校验,也可能为待发起借款,需要用guard判断"),
    
        WAIT_REAL_NAME_AUTH("3", "待实名认证"),
    
        WAIT_BORROW("4", "待发起借款"),
    
        CANCEL("999", "用户取消"),
    
        CLOSE("996", "订单关闭"),
    
        SUCCESS("888", "成功,指已销账,订单完全终结"),
    
        /**
         * 审核相关状态定义-start
         */
        AUDITING("100", "审核中"), 
    
        WAIT_BIZ_AUDIT("101", "待业务审核"),
    
        BIZ_APPROVED("102", "业务审核通过"),
    
        WAIT_COMPLEMENT("103", "待补全审核资料"), 
    
        CHECK_COMPLEMENT("104", "补全资料检查"),// --流程内部使用
    
        WAIT_UPLOAD_IMG("105", "待上传影像资料"), 
    
        CHECK_UPLOAD("106", "上传影像检查"),//  --- 流程内部使用
    
        WAIT_BEF_DEAL_RISK_AUDIT("111", "待贷前额度评估"),
    
        IN_DEAL_RISK_AUDITING("121", "贷中风控审核"),
    
        WAIT_AF_DEAL_RISK_AUDIT("131", "待贷后审核"),  
    
        AF_DEAL_RISK_APPROVED("132", "贷后审核通过"), 
    
        APPROVED("198", "审核通过"),
    
        /**
         * 审核相关状态定义--end
         */
        WAIT_SIGN("200", "待签约"),
    
        SIGNED("288", "签约完成"),
    
        LOANING("300", "放款中"),
    
        LOANED("388", "放款完成"),
    
        REFUNDING("401", "退款中"), // 对于消费贷,存在退款情况
    
        REFUNDED("488", "退款完成"), // 对于消费贷,存在退款情况
    
        BILL_GEN("501", "生成账单"),
    
        REPAYING("600", "还款中"),  // 内部使用的中间瞬时状态,外部传入时不要使用,部分还款采用PART_REPAID类型
    
        PART_REPAID("601", "部分还款"),
    
        REPAID("688", "已还清"),
    
        OVERDUE("700", "已逾期"),;
    
    
        private String status;
    
        private String desc;
    
        /**
         * status是否合法
         *
         * @param status
         * @return
         */
        public static boolean isIn(String status) {
            return Arrays.asList(BizOrderStatusEnum.values()).parallelStream().
                    anyMatch(value -> StringUtils.equals(value.getStatus(), status));
    
        }
    
        /**
         * 判断status是否相等
         *
         * @param status
         * @param statusEnum
         * @return
         */
        public static boolean equals(String status, BizOrderStatusEnum statusEnum) {
            return StringUtils.equalsIgnoreCase(status, statusEnum.getStatus());
    
        }
    
        /**
         * status-->statusEnum
         *
         * @param status
         * @return
         */
        public static BizOrderStatusEnum getByStatus(String status) {
            Optional<BizOrderStatusEnum> statusEnumOptional = Arrays.asList(BizOrderStatusEnum.values()).parallelStream()
                    .filter(statusEnum -> StringUtils.equalsIgnoreCase(status, statusEnum.getStatus())).findAny();
    
            if (statusEnumOptional.isPresent()) {
                return statusEnumOptional.get();
            }
    
            return null;
    
        }
    
        /**
         * 判断status是否合法
         *
         * @param status
         * @param statusEnums
         * @return
         */
        public static boolean isIn(String status, BizOrderStatusEnum... statusEnums) {
            return Arrays.asList(statusEnums).parallelStream().
                    anyMatch(value -> StringUtils.equals(value.getStatus(), status));
    
        }
    
        /**
         * 判断是否订单已终结,取消、关闭、成功、拒绝都属于终结状态
         *
         * @param status
         * @return
         */
        public static boolean isFinish(String status) {
            return isIn(status, SUCCESS, CANCEL, CLOSE);
        }
    
        /**
         * 判断订单是否是初始创建状态
         * 对于: WAIT_REAL_NAME_AUTH, WAIT_BORROW 都可能是初始状态
         * 对于其他:暂时为CREATE状态
         *
         * @param status
         * @return
         */
        public static boolean isInitialStatus(String status) {
            return isIn(status, CREATE, WAIT_REAL_NAME_AUTH, WAIT_BORROW);
        }
    
    }
    

    需要注意的是,这里面有些状态在一些业务场景是用不到的,比如退款,在当前业务场景并没有用,这里的状态是全集。

    事件枚举

    事件会导致订单的状态发生变化(当然,不是绝对,有些事件是内部事件,并不会导致状态变化,这时就需要用withInternal来串联,详见下节配置),下面是本次的事件枚举

    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import org.apache.commons.lang3.StringUtils;
    
    import java.util.Arrays;
    import java.util.Optional;
    
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    public enum BizOrderStatusChangeEventEnum {
    
        EVT_CREATE("evt_create"),
    
        EVT_CANCEL("evt_cancel"), // 取消
    
        EVT_NAME_AUTH("evt_name_auth"), // 实名
    
        EVT_SYS_CLOSE("evt_sys_close"), // 系统关单,如超时或风控关单等
    
        EVT_AUDIT("evt_audit"), // 审核
    
        EVT_COMPLEMENT("evt_complement"), // 补全材料
    
        EVT_UPLOAD_IMG("evt_upload_img"), // 上传影像
    
        EVT_APPROVED("evt_approved"), // 批准
    
        EVT_REFUSE("evt_refuse"), // 拒绝
    
        EVT_SIGN("evt_sign"), // 签约
    
        EVT_LOAN("evt_loan"), // 放款
    
        EVT_LOAN_FAILED("evt_loan_failed"),
    
        EVT_REFUND("evt_refund"), // 退款
    
        EVT_REPAY("evt_repay"), // 还款
    
        EVT_GEN_BILL("evt_gen_bill"), // 生成账单
    
        EVT_TOSUCCESS("evt_tosuccess"), // 销账
    
        EVT_RETRY("evt_retry"), // 重试
    
        EVT_OVERDUE("evt_overdue")  // 逾期,用户无动作,由系统定时任务发起
        ,
        EVT_LOAN_SUCC("evt_loan_succ"),
    
        EVT_NEED_NAME_AUTH("evt_need_name_auth");
    
        String event;
    
        /**
         * 判断
         * @param eventName
         * @return
         */
        public static BizOrderStatusChangeEventEnum getEvent(String eventName) {
            if (StringUtils.isBlank(eventName)) {
                return null;
            }
    
            Optional<BizOrderStatusChangeEventEnum> resultOptional = Arrays.asList(BizOrderStatusChangeEventEnum.values()).parallelStream().filter(eventEnum ->
                    StringUtils.equals(eventName, eventEnum.getEvent())).findAny();
    
            if (resultOptional.isPresent()) {
                return resultOptional.get();
            }
            return null;
        }
    }
    

    这里的状态就是StateMachine<S,E>中的S,事件就是对应的E。

    相关文章

      网友评论

        本文标题:Spring StateMachine状态机引擎在项目中的应用(

        本文链接:https://www.haomeiwen.com/subject/nfdbuctx.html