一、我发现信贷业务的复杂度,本质上就是"规则"在膨胀
我一直在做的这类信贷平台,核心链路无非是进件、授信、提现、还款几步,看起来就是几个接口,但我越往深挖越发现,每个接口背后都是多资方 × 多渠道 × 多风控策略的组合爆炸:
- 一个用户进件,要在几十家资方里按额度、风险等级、时间窗口、当日放款/拒绝配额做多维路由;
- 同一笔提现,不同资方的绑卡方式、验证码逻辑、放款回调状态机完全不一样;
- 同一次还款,十几家资方的还款计划拉取、试算、回调对接也是各玩各的。
我后来想明白了,这根本不是"业务逻辑复杂",而是"规则的数量和组合方式一直在长"。团队人手有限、排期又紧的时候,大家都会选眼下最省事的写法,所以同一个系统里,我看到三条链路演化出了三种完全不同的应对手段:
| 编排点 | 现在用的方式 | 说白了是在解决什么 |
|---|---|---|
| 授信路由 | 一个递归方法,八九个过滤条件挨个判断 | "在一堆资方里选出一个满足所有约束的" |
| 提现放款 | 状态机式的轮询,十几个状态分支 | "把资方那边异步回来的结果收敛成几个确定状态" |
| 还款 | 策略模式,按资方标识查表分发到不同实现 | "同一个动作,每家资方走不同实现" |
这三套机制单独看都没毛病,但共同点是------都在用自己的方式把"规则"硬编码死了:授信路由那几个过滤维度的顺序写死在方法里;还款策略每接一家新资方要新建类、注册、改配置表,三个地方联动;提现状态机的每个分支里又嵌套着各资方的差异化处理。这些代码本身写得都还行,问题出在------规则一变,就得发版。而信贷这个行业,规则变化的频率说实话比大多数系统都高:
- 风控今天说要临时调某家资方的日拒量上限;
- 运营说某个渠道要优先导到费率更低的那家;
- 资方突然通知要维护,得在进件时间窗里临时加一段禁止时段;
- 新接一家资方,授信、提现、还款三条链路都要各插一段差异化逻辑。
我算了下,这些变化其实都是"规则/参数/顺序"层面的调整,却被迫走一遍"改代码→测试→发布"的重流程,怎么算都不划算。这正是我想聊 LiteFlow 这类流程编排/规则引擎的原因。
二、LiteFlow 到底是个什么东西
我理解下来,LiteFlow 是一款很轻量、几乎无侵入的规则编排引擎 ,核心思路挺朴素:把"业务动作"拆成一个个独立的组件(Component) ,再用一段 EL 表达式描述这些组件该怎么编排。规则和组件是分开的------组件还是 Java 代码,规则是一段纯文本,可以扔进数据库或者配置中心,支持热更新。
2.1 组件:一个最小的、可复用的业务单元
java
@LiteflowComponent("checkFundStatus")
public class CheckFundStatusCmp extends NodeBooleanComponent {
@Override
public boolean processBoolean() {
FundConfig fundConfig = this.getContextBean(RouteContext.class).getFundConfig();
// 判断资方是否开启,对应原来那个大方法里的第一层判断
return fundConfig.isEnabled();
}
}
我挺喜欢这种设计的一点是,一个组件只做一件事,职责单一,脱离整体流程也能单独写单测------这比维护一个几百行的递归方法舒服太多了。
2.2 EL 表达式:把"怎么编排"从代码里挪出来
我常用到的编排语义大概这几种:
THEN(a, b, c)------ 串行,依次执行WHEN(a, b, c)------ 并行,几个互不依赖的校验同时跑,默认全过才算过(ignoreError默认为false),也可以配maxWaitSeconds()、percentage()之类的参数放宽成"部分完成/超时也算过"------比如提现确认这种高并发路径上,某个资方查询偶尔超时不该拖垮整体校验SWITCH(x).to(a, b, c)------ 条件分支,按标识路由,我觉得可以直接替代那种Map<String, Bean>查找的写法;还可以配一个.DEFAULT(d)兜底分支,比如"没有资方能匹配时走人工审核"这种场景正好用得上IF(cond, a, b)------ 条件执行FOR/WHILE/ITERATOR------ 循环,比如遍历资方候选序列逐个尝试CATCH(a).DO(c)------ 异常捕获、兜底
拿授信路由那堆过滤条件举例,我会把它写成这样:
xml
<chain name="creditRoute">
THEN(
checkFundStatus,
checkTimeLimit,
checkChannelDailyLimit,
checkFundRejectLimit,
checkFullLoanLimit,
checkSpecialSource,
checkNewOldCustomerLimit,
checkUserLevelMatch
);
</chain>
这段表达式不是代码,是数据------存数据库或者配置中心都行,风控或者运营在后台想调整"某个渠道要不要检查某一项""要不要换个顺序""针对某家资方要不要跳过某一层",改的就是这段文本,不用碰一行 Java 代码,改完立刻生效,不用走发布。
三、我拿三个场景推演了一下,改造之后会是什么样
3.1 授信路由:递归判断 → 责任链 + 可配置规则
现在的问题是,那个递归方法把八九个过滤条件的顺序写死了,每次跳过一个就记一条原因。逻辑挺严谨,但只要想调顺序、加减某个渠道的校验项,就得改这个方法本身。
我的想法是:把每个过滤维度拆成独立组件,"记录跳过原因"这种重复逻辑做成组件基类的一个统一钩子,不用每个组件都写一遍。规则链按渠道+资方维度可以配不同的 EL 表达式------比如某些新渠道不需要查"新老客户进件量上限",直接在规则里把这个节点删掉就行,不用在代码里加一堆 if (channel == xxx) 跳过 的特判。
而且 LiteFlow 的责任链本身就支持"哪个节点没过就终止并返回原因"这种短路语义,跟现在"跳过记原因、继续下一个"的逻辑完全不冲突,只是把硬编码的顺序变成了可配置的顺序。
3.2 还款策略模式:Map 分发 → LiteFlow SWITCH
现在这块已经是策略模式了,按资方标识做 O(1) 查找分发,"新增资方不用改路由框架代码"这点其实做得不错。我觉得 LiteFlow 在这里的增量价值,主要在策略内部的分支逻辑上:
现在每个策略实现里都有一段类似的绑卡分支:
css
[有验证码] 校验签约 → 走还款
[无验证码] 查签约状态 → 分几种情况处理
我发现这段 if-else 在十几个资方的策略类里长得都差不多、又不完全一样------这其实是策略模式的一个软肋:骨架相同,但每个实现类都得重写一遍骨架。如果用 LiteFlow 编排,我可以把"绑卡分支判断"抽成公共组件,各资方只需要提供自己的"验签实现""签约申请实现"这些差异点,用 EL 表达式拼一下:
xml
<chain name="repayFundA">
IF(hasCaptcha,
THEN(confirmSignFundA, doRepay),
THEN(getSignStatusFundA, IF(signed, doRepay, sendCaptchaFundA))
);
</chain>
这样一来,"接一家新资方的还款对接"就从"新建一个几十个方法的策略类"降级成"实现几个差异点组件 + 拼一段规则",开发量和测试量都能降不少。
3.3 提现状态机:LiteFlow 不是万能药
这里我得说句实话:那种跨多次异步回调、需要持久化当前状态的场景,本质上是个状态机,LiteFlow 擅长的是"一次调用内的同步编排",不能直接替代状态机框架,也替代不了状态字段驱动的轮询逻辑。
但我发现状态机里每个状态对应的处理动作 ------比如"资方拒绝了,尝试路由下一家,全部失败就冻结额度"这段逻辑,本身是可以用 LiteFlow 编排的一段子流程。所以我给自己的定位是:状态机负责"我现在在哪个状态、该往哪流转",LiteFlow 负责"到了这个状态该按什么规则做哪些事",两者是互补,不是替代。
四、我觉得能拿到的收益
- 规则配置化,业务方不用等发版:规则和参数存数据库或配置中心,风控、运营在后台自己调,实时生效,我们从"改代码"退成"维护组件库"。
- 接新资方的成本变得可预期:只要组件粒度设计得当,接一家新资方就是"实现几个差异点组件 + 写一段规则",不用再复制一个策略类改几十个方法。
- 可观测性能顺手接上现有监控:LiteFlow 自带责任链执行日志、节点耗时统计,直接接进已有的链路追踪和监控体系就行,不用额外造轮子。
- 组件天然好测:每个组件是个最小单元,脱离整体流程也能单独写单测,比测一个两百行的递归方法轻松多了。
- 可以慢慢来,不用推倒重来:我建议先挑一个变化最频繁、当下最痛的点试点(比如授信路由的过滤维度,或者接新资方的还款对接),验证有效果了再往其他链路铺开。
五、几个我觉得必须正视的坑
- "能配置"不等于"该随便配"。信贷业务对规则变更的审计要求很高------谁改的、什么时候改的、为什么改、有没有人审批。如果只是把 if-else 搬进 EL 表达式扔进数据库,却没配套的审批、灰度、回滚机制,规则变更反而从"有 code review 的一次 PR"退化成"没人审的一次数据库更新",这在信贷这种资损敏感的业务里,风险是变大了,不是变小了。
- 组件粒度不好把握。拆太细,规则链会膨胀到几十个节点,可读性反而更差;拆太粗,又失去了灵活编排的意义。我自己的判断标准是看"是不是一个独立的业务判断点",而不是看代码行数多少。
- 动态表达式是有性能代价的。EL 解析本身,加上如果引入脚本引擎做动态条件判断,都有额外开销。如果链路本身在一个高并发的核心路径上(比如提现确认),我觉得得提前压测一下,评估要不要对规则链做缓存或者预编译。
- 幂等、分布式锁这些底层保障,LiteFlow 管不了。现有的防重锁、状态流转的原子性,这些还是得留在组件内部或者组件的上下文里维护,编排引擎只管"执行顺序",不管"执行安全"。
- 团队要有个适应期。EL 语法、组件生命周期、上下文怎么传,都是新概念,得建立组件命名规范、上下文契约规范,不然组件之间靠隐式的上下文字段互相耦合,很容易又变成一坨新的"意大利面"。
六、写在最后
我想清楚了一件事:授信路由的递归判断、提现的状态机轮询、还款的策略模式,本质上是同一个问题的三种不同答案------规则老在变的时候,怎么把"变化"和"稳定的执行框架"分开。LiteFlow 这类编排引擎给我的价值,不是把复杂度消灭掉,而是把原来散落在代码各处的 if-else 和策略类,收敛成一套统一的组件库加一份可配置的规则,让规则变化的响应速度从"发版周期"缩短到"配置生效的那一刻"。
但我也提醒自己,它解决的只是"编排"问题,解决不了"审计""性能""幂等"这些问题------这些还是得靠配套的规则治理机制,和现有的分布式锁、状态机能力来兜底。对信贷这种强监管、强审计的业务,我觉得落地 LiteFlow 的心态不该是"让业务方随便改规则",而应该是"用一套统一的编排语言,把原本分散、隐性的规则,变得显式、可视化、可追溯"。