JVS-Rules规则引擎自定义函数扩展实战:从源码结构到热加载全流程解析
摘要
规则引擎的内置函数库无法覆盖所有业务场景——金融场景的LPR利率计算、电商场景的复杂促销叠加、政企场景的特定数据脱敏规则,都需要开发者自行扩展函数能力。本文以支持全量源码交付的JVS-Rules规则引擎为例,从源码结构、函数注册机制、Groovy动态加载原理到完整实战案例进行全链路解析,手把手演示如何扩展一个自定义规则函数并实现热加载。
1. 为什么需要自定义规则函数
JVS-Rules内置了八大类函数——脱敏函数、对象函数、数学函数、时间函数、转换函数、集合函数、文字函数、逻辑函数——覆盖了90%的通用场景。但企业级应用总有特殊需求:
-
行业特有计算:如金融LPR浮动利率计算、制造业工时费计算
-
复杂业务逻辑:如电商“满减+会员折扣+品类优惠”多条件叠加计算
-
外部系统集成:如调用第三方风控API、查询实时汇率
-
定制数据脱敏:如特定格式的证件号、银行卡号脱敏规则

当内置函数不够用时,具备源码交付能力的规则引擎允许开发者自行扩展。JVS-Rules采用 Spring Cloud + VUE 技术架构,支持全量源码二次开发。
2. 源码结构解析
JVS-Rules的函数管理相关核心模块如下:
text
jvs-rules/
├── jvs-rules-core/ # 核心引擎
│ ├── src/main/java/
│ │ ├── com.jvs.rules.engine/ # 规则引擎执行器
│ │ ├── com.jvs.rules.function/ # 函数管理
│ │ │ ├── FunctionRepository # 函数仓库
│ │ │ ├── FunctionRegistry # 函数注册器
│ │ │ └── builtin/ # 内置函数实现
│ │ │ ├── MathFunctions # 数学函数
│ │ │ ├── DateFunctions # 时间函数
│ │ │ └── ...
│ │ └── com.jvs.rules.parser/ # 规则解析器
├── jvs-rules-web/ # Web管理端
│ └── src/main/
│ ├── java/ # 后端API
│ └── resources/ # 前端资源
└── jvs-rules-api/ # API定义
关键类说明:
-
FunctionRepository:函数仓库,管理所有已注册函数的生命周期。 -
FunctionRegistry:函数注册器,支持动态注册和卸载自定义函数。 -
builtin/:内置函数实现,每个函数对应一个Java方法,通过注解标记。
3. 自定义函数的实现机制
JVS-Rules的函数扩展基于Groovy动态语言。函数体可以使用Groovy原生语法,也可以调用Java工具类。

所有函数存储在数据库表 jvs_function_base 中:
sql
CREATE TABLE `jvs_function_base` (
`name` varchar(64) NOT NULL COMMENT '函数名称',
`type` varchar(32) NOT NULL COMMENT '函数分类',
`info` text COMMENT '函数描述',
`param_count` varchar(10) COMMENT '参数数量',
`dynamic_param` tinyint DEFAULT 0 COMMENT '是否动态参数',
`enable_cache` tinyint DEFAULT 1 COMMENT '是否启用缓存',
`jvs_param_type` varchar(32) DEFAULT 'any' COMMENT '参数类型',
`body` text COMMENT '函数体(Groovy代码)'
);
函数注册流程:
-
在Web管理端点击“新增函数”,填写函数名称、分类、描述
-
指定参数个数和参数类型
-
编写函数体(Groovy代码)
-
点击“测试”验证函数正确性
-
保存后,在“运维设置”中点击“更新”重新加载缓存

4. 实战:扩展一个自定义函数
以金融风控场景的“计算LPR浮动利率”为例,演示完整的自定义函数扩展过程。
场景描述:风控规则中需要根据贷款金额和期限计算基于LPR(贷款市场报价利率)的浮动利率。LPR值需从外部API实时获取。
方案一:纯Groovy脚本扩展(适合简单逻辑)
在Web管理端的函数新增界面中填写:
-
函数名称:
calcLprRate -
分类:
数学函数 -
参数个数:
2(贷款金额、贷款期限) -
函数体:
groovy
// 使用Groovy语法实现LPR利率计算
import java.time.LocalDate
// 模拟LPR查询(实际项目可调用外部API)
def getCurrentLpr() {
// 简化示例:1年期LPR固定值3.45%
return 3.45
}
def baseLpr = getCurrentLpr()
def amount = params[0] as double
def term = params[1] as int
// 金额加成:超过100万加0.2%
def amountPremium = amount > 1000000 ? 0.2 : 0
// 期限加成:超过5年加0.3%
def termPremium = term > 5 ? 0.3 : 0
return baseLpr + amountPremium + termPremium
方案二:调用Java工具类扩展(适合复杂逻辑)
先在Java源码中编写工具类:
java
package com.jvs.rules.custom;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class LprCalculator {
private static final HttpClient client = HttpClient.newHttpClient();
public static double getLprRate(double amount, int term) {
double baseLpr = fetchLprFromApi();
double amountPremium = amount > 1000000 ? 0.2 : 0;
double termPremium = term > 5 ? 0.3 : 0;
return baseLpr + amountPremium + termPremium;
}
private static double fetchLprFromApi() {
// HTTP调用央行LPR查询接口
// 返回当前LPR值
}
}
然后在函数体中导入并调用:
groovy
import com.jvs.rules.custom.LprCalculator
def amount = params[0] as double
def term = params[1] as int
return LprCalculator.getLprRate(amount, term)
方案三:动态参数函数
如果需要支持任意数量的参数(如求和、求平均),配置动态参数即可:
groovy
// 动态参数函数:支持任意数量的数字求和
// param_count: [0], dynamic_param: 1
import cn.hutool.core.util.NumberUtil
def sum = 0
for (int i = 0; i < params.size(); i++) {
def val = params[i]
if (val instanceof String) {
val = NumberUtil.parseNumber(val as String)
}
sum = NumberUtil.add((Number) val, (Number) sum)
}
return sum
5. 热加载机制解析
JVS-Rules的自定义函数支持热加载——修改后无需重启应用即可生效:
-
函数体存储在数据库中,修改后点击保存
-
系统检测到变更,触发GroovyClassLoader重新编译
-
编译后的函数实例替换内存中的旧实例
-
后续规则调用自动使用新函数,全程对业务无感知
6. 扩展建议与最佳实践
1. 善用工具库:JVS-Rules默认集成了Hutool工具类,可直接调用 cn.hutool.core.util.* 下的工具方法。
2. 注意参数类型校验:函数参数可能来自规则配置界面的不同输入,建议在函数体中进行类型转换和空值判断。
3. 启用缓存提升性能:对于计算开销大的函数(如外部API调用),开启 enable_cache 可显著提升性能。
4. 编写清晰的函数描述:使用HTML标签格式化 info 字段描述,方便其他开发者理解和使用。
7. 总结
JVS-Rules的自定义函数扩展机制基于 Groovy动态语言 + 数据库存储 + 热加载,开发者无需修改核心引擎代码即可扩展新的规则函数。核心价值在于灵活扩展、一次编写多规则共享、按需定制高性能算法。对于金融风控、电商促销、行业规则计算等场景,这一能力大幅降低了规则引擎的定制门槛。
本文为技术实践分享,基于JVS-Rules开源版本编写。
更多推荐




所有评论(0)