系统设计

项目背景:

简介

风控系统 —— c端风控,c端进入风控系统后,根据c端资质有相应的风控流程;对其进行一系列风控处理;


- 系统基本面


信息获取
	1. 调用外部系统接口,通过mq异步返回数据; 如征信系统、第三方平台风控系统等 
	2. 调用当前应用缓存数据;如redis or db;
节点数据判别
	1.需对某些节点返回的数据进行判别,跳转节点
	2.每个节点需对c端做功能处理产生的数据和流程中的节点状态扭转日志进行落地(人行征信拒绝、等待担保人提交问卷等)


- 系统需求:


1. 流程可配置化 ——— 应对业务快速发展
2. 每个功能点进行模块化
优势:1.功能单一,开发人员需求清晰、责任分明;
     2.容易对某个功能进行复用、扩展和测试

- 系统设计


流程工厂 — 根据不同资质的用户有相应的风控流程

<!-- 流程工厂 -->
<bean id="flowFactory" class="com.souche.risk.admittance.executor.FlowFactory">
    <property name="flowMap">
        <map>
            <entry key="xxxFlow" value-ref="xxxFlow"/>
			...
        </map>
    </property>
</bean>
功能模块定义

    <!-- 征信查询 -->
    <bean id="aaa"
          class="com.souche.risk.admittance.executor.handle.AaaHandler"/>

    <!-- 额度查询 -->
    <bean id="bbb"
          class="com.souche.risk.admittance.executor.handle.BbbHander"/>
	...
流程 - handler组装
 <bean id="guaranteeFlow" class="com.souche.risk.admittance.executor.GuaranteeFlow" init-method="init">
        <property name="handlerMap">
            <map>
                <entry key="0" value-ref="aaa"  />
                <entry key="1" value-ref="bbb" />
				...
            </map>

        </property>
    </bean>

截止目前为止,已经满足了上面所提到的需求;这种有点偏类似于工作流的模式;

操作步骤
1.当请求到该系统时,系统根据入参选择相应的流程;
2.循环调用该流程中的每个handler进行处理;

通过handler 接口的演化,可以类似于责任链的模式;以往工作经历中有使用过类似的设计方式;当时戏称为工作流+责任链模式;

------------

but .... 系统远没有如此简单;上面可以简称为 toy code;

根据业务发展,我们发现每个handler有自己的特性,分为以下几点:
1. 普通handler;按照指定的顺序,执行完功能方法就可以执行下一个了;
1. 判别handler;根据当前handler处理的情况;该flow的执行流程会发生如下改变:
- - 终结态,当前线程的链路执行结束; egg: a -> b -> c -> d  ===>> a -> b
- - 节点的跳转;egg:a -> b -> c -> d  ===>> a -> b -> d
- - 如普通handler一样,顺序执行下一个handler;

而且每个handler可能会对流程中产生的数据进行入库操作和日志记录等操作; handler接口定义:

public interface ProcessHandler<T> {
    /**
     * handler处理主方法
     *
     * @param reqParam
     * @return
     */
    T process(ReqParam reqParam);

    /**
     * 该handler的下一个handler处理的下标
     *
     * @param reqParam
     * @param flow
     * @return
     */
    default int nextHanderIndex(ReqParam reqParam, Flow flow) {
        return flow.getNextHandlerIndex(getBeanName());
    }

    /**
     * 判断该handler是否需要将数据更新库中
     *
     * @return
     */
    default boolean updateDb() {
        return true;
    }

    /**
     * 获取handler类名
     */
    String getBeanName();

}
  • process method —— 执行该handler的功能体
  • nextHanderIndex —— 返回该handler处理完后的下一个handler下标
  • updateDb —— 判断该hander是否有数据进行落库
  • getBeanName —— 获取该handler的bean id

这里有一个小的技巧;虽然flow包含一个handler集合;但flow和handler之间仍然是低耦合的;当判断下一个handler下标的时候,并非交给该handler去决定;而是交回给执行该handler的flow去处理;防止handler和某个flow强耦合;

某个handler实现片段代码:

@Slf4j
public class AaaQueryHandler implements ProcessHandler<Void> {

    private static final String AAA_REJECT = "aaa_reject";

    private static final String BEAN_NAME = "aaaQuery";

    @Resource
    private Service riskService;


    /**
     * @param reqParam 参数列表
     * 
     */
    @Override
    public Void process(ReqParam reqParam) {
        RiskAdmittanceQueryParams param = reqParam.getParams();
		...
        reqParam.putEle(ReqParam.UPDATE_TYPE, UpdateDataType.UPDATE_BUSINESS);
        reqParam.putEle(AAA_REJECT, true);

        return null;
    }

    @Override
    public int nextHanderIndex(ReqParam reqParam, Flow flow) {
        boolean aaaReject = reqParam.getEle(AAA_REJECT);
        return flow.judgeAaa(aaaReject, BEAN_NAME);
    }

    @Override
    public String getBeanName() {
        return BEAN_NAME;
    }
}

flow 抽象类执行片段:

@Data
public abstract class BaseFlow implements Flow {

    Map<Integer, ProcessHandler> handlerMap;
    private int length;
    /**
     * 当前flow中的handler bean name 对应的执行number
     */
    public Map<String, Integer> numberOfHandlerMap;

    public void init() {
        length = handlerMap.size();

        numberOfHandlerMap = handlerMap.entrySet()
                .stream()
                .collect(Collectors.toMap(entry -> entry.getValue().getBeanName(), Map.Entry::getKey));
    }
    /**
     * 从该flow中指定的index开始执行相应的handler处理器
     *
     * @param index
     * @param reqParam
     */

    @Override
    public void execute(int index, ReqParam reqParam) {

        while (index < length) {
            ProcessHandler handler = handlerMap.get(index);
            handler.process(reqParam);

            // 是否对数据进行落库操作
            boolean flag = handler.updateDb();
            if (flag) {
                getDataUtil().updateData(reqParam);
            }

            // 判断该handler处理完是否终结
            boolean endFlag = reqParam.isEnd();
            if (endFlag) {
                return;
            }

            index = handler.nextHanderIndex(reqParam, this);
        }
    }

    abstract DataUtil getDataUtil();
		...
}

执行顺序:

  • 获取从第几个handler开始执行,开始时,从第0个handler开始处理;但当调用外部接口时,通过mq异步返回,则需指定从第几个handler开始执行;
  • 判断执行的handler是否需要存储数据库或缓存
  • 判断当前handler是否为终结状态;该链接完全结束
  • 获取下一个需要执行的handler下标;

该系统设计较简单;但适合即是最优的,在一定程度上对系统后续扩展起到比较好的兼容性;如系统复杂性不断加强时,可能会作出相应的架构上的优化;后续会在此篇博客上进行续写;


如大家对此类系统有着较先进和丰富的设计理念,并能给出该系统后续的发展方向;欢迎留言指正!

转载于:https://my.oschina.net/u/257801/blog/3080141

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值