一个面向 Spring Boot 业务系统的指标规则组件:用代码定义指标元数据,用页面管理规则,用运行时执行真实业务对象上的规则。
🌐 中文 | English
Easy Indicator Rule 适合这类场景:
- 业务系统里已经出现越来越多的"审核规则、拦截规则、路由规则、异常识别规则"
- 规则变更频繁,但又不想每次都改代码、发版
- 希望把"业务字段/计算结果"抽象成指标,让运营或后台人员可配置规则
- 希望规则执行仍然紧贴真实领域对象,而不是重新设计一套脱离业务模型的 DSL
它提供了一套相对完整的模式:
- 业务代码定义
场景 + 触发节点 + 指标 - 管理后台维护规则
- 运行时按真实业务对象执行规则并返回命中结果
很多业务系统发展到一定阶段后,都会堆出大量类似逻辑:
- 订单审核规则
- 履约异常识别规则
- 风控拦截规则
- 客服分单规则
- 营销人群匹配规则
这些逻辑如果一直写死在代码里,通常会带来几个问题:
- 规则修改需要研发介入和重新发布
- 业务和研发围绕规则沟通成本高
- 相同模式在多个系统里重复实现
- 缺少统一的规则模型和治理方式
Easy Indicator Rule 的目标,就是把这部分能力沉淀成一个可以直接接入业务系统的基础组件。
- 基于 Spring Boot Starter 的自动装配能力
- 支持 指标元数据建模:字符串、数字、布尔、日期、时间、日期列表、字符串列表、数字列表型值
- 支持 场景 + 触发节点 的业务建模方式
- 内置 规则管理 REST API,支持分页查询、条件筛选
- 基于 QLExpress4 的规则编译和执行
- 支持 指标与常量比较,以及部分场景下的 指标与指标比较
- 支持 指标与常量比较、指标与指标比较,以及列表指标的"全部属于"、"任意属于"语义
- 支持 自由数组 Chip 输入 和 中文表达式数组展示优化
- 支持 日期、时间和列表类型指标:DATE、TIME、STRING_LIST、NUMBER_LIST、DATE_LIST
- 支持 动态值范围,适合远程下拉选项场景
- 单次执行内支持 指标函数缓存 单次执行同一指标计算一次
- 支持 表达式启动预热,减少冷启动延迟
- 支持 编译缓存监控与自动重置
- 支持 规则复制,快速克隆已有规则
- 支持 规则历史版本,记录每次创建的表达式快照
- 支持 操作日志审计,保留前后快照、表达式预览和变更字段
- 支持 规则事务钩子,在规则创建/更新/启停/删除/复制的事务内执行业务回调
- 统一的 异常与错误码体系:
IndicatorRuleException + IndicatorRuleErrorCode - 支持 条件组 AND/OR 组合 逻辑
- 提供 示例后端 和 示例前端管理台
- 仓储、执行器、校验器、事务适配器等关键能力支持替换
- 支持 审计信息与编码生成 扩展点
.
├── easy-indicator-rule-spring-boot-starter
├── easy-indicator-rule-example
│ ├── easy-indicator-rule-example-server
│ └── easy-indicator-rule-example-ui
├── docs
└── .codex/skills
说明:
easy-indicator-rule-spring-boot-starter组件核心,包含自动配置、规则管理、规则执行、JDBC 持久化、规则版本、操作日志、内置 View API、表达式预热、编译缓存管理easy-indicator-rule-example-server示例后端,演示如何定义场景、触发节点、指标和执行接口easy-indicator-rule-example-ui示例前端,演示规则管理、指标库、操作日志、规则历史版本和执行测试页面docs补充的架构设计、接入指南和 SQL 脚本
flowchart LR
A["业务系统"] --> B["Scenario / TriggerNode / IndicatorMetadata"]
B --> C["元数据注册"]
C --> D["指标目录同步"]
D --> E["ir_indicator / ir_rule_group / ir_rule / ir_condition_group / ir_condition"]
A --> F["RuleExecuteService"]
E --> F
F --> G["RuleEngine"]
G --> H["规则编译 + QLExpress 执行器"]
H --> I["基于 businessData 的指标函数"]
A --> J["内置规则管理 API"]
J --> E
你可以把它理解成三层:
- 元数据层:业务代码定义有哪些场景、触发节点、指标、单位和值范围
- 管理层:规则和指标同步到数据库,通过内置 API 或前端进行管理
- 执行层:规则被编译成表达式,运行时对业务对象执行并返回结果
下面的截图来自仓库内示例前端 easy-indicator-rule-example-ui,用于快速展示这套组件在业务系统里的实际使用形态。
规则管理页用于按规则编码、规则名称、状态筛选规则,并直接完成编辑、查看历史版本、启停和删除。
规则编辑页展示了"场景 + 触发节点 + 逻辑组 + 条件"的完整编排方式。业务系统定义指标元数据后,运营或后台人员就可以在这里组合出可执行规则。
规则历史版本页用于审计规则变更,支持按版本查看表达式快照,适合对接审计、回溯和发布核对流程。
指标库页统一展示当前场景下可用于规则编排的指标,包括指标编码、名称、类型和状态,并支持与后端元数据同步。
操作日志除了展示规则创建、修改等动作外,还保留了前后快照、表达式预览和变更字段,方便排查"规则为什么变成这样"。
执行测试页可以直接输入上下文 JSON,验证某个场景和触发节点下的规则执行结果。这对于接入联调、规则验收和日常排查都很实用。
如果组件还没有发布到公共制品库,可以先在本地安装:
cd easy-indicator-rule-spring-boot-starter
mvn clean install示例后端默认使用 MySQL:
cd easy-indicator-rule-example/easy-indicator-rule-example-server
mvn spring-boot:run默认配置文件:
默认使用这些环境变量:
MYSQL_HOSTMYSQL_PORTMYSQL_DBMYSQL_USERMYSQL_PASS
服务默认端口:
http://localhost:9988
cd easy-indicator-rule-example/easy-indicator-rule-example-ui
npm install
npm run dev示例前端包含:
- 规则列表页
- 规则编辑页
- 指标库页
- 执行测试页
示例目录提供了一个 run.sh 脚本,可以一键构建镜像并拉起 MySQL、示例后端和示例前端:
cd easy-indicator-rule-example
./run.sh脚本支持以下子命令:
| 命令 | 说明 |
|---|---|
./run.sh |
构建镜像并启动所有容器(默认) |
./run.sh build |
仅重新构建镜像 |
./run.sh up |
不重建,直接启动容器 |
./run.sh down |
停止并移除容器 |
./run.sh restart |
重启容器 |
./run.sh clean |
移除容器 + 镜像 + 数据卷 |
./run.sh logs |
实时查看日志 |
./run.sh status |
查看容器状态和镜像大小 |
启动后默认访问地址:
- 示例前端管理台:
http://localhost:8080 - 示例后端接口:
http://localhost:9988 - MySQL:
localhost:3307(宿主机 3306 端口已被占用时自动映射到 3307)
示例后端启动时会自动同步指标元数据,也可以手动调用:
POST /api/indicators/sync仓库中的示例后端演示的是"履约审核"场景:
Scenario:contract_order_auditTriggerNode:audit_order、create_order、ship_order- 业务对象:
FulfillmentOrder
示例指标包括:
deliveryWarehousecountryCodeauditLevellogisticsLineorderAmountbuyerVipsales_objorderCreateDate(DATE 类型)paymentMethod(STRING 类型)skuTags(STRING_LIST 类型)itemDiscountRates(NUMBER_LIST 类型)packageScanTimes(DATE_LIST 类型)
示例执行请求:
POST /api/rule-executions/evaluate
Content-Type: application/json
{
"scenarioCode": "contract_order_audit",
"triggerNode": "audit_order",
"context": {
"deliveryWarehouse": "广州仓",
"countryCode": "United States",
"auditLevel": "mandatoryReviewOrder",
"logisticsLine": "DHL Express",
"orderAmount": 1500,
"buyerVip": true
}
}<dependency>
<groupId>com.openquartz</groupId>
<artifactId>easy-indicator-rule-spring-boot-starter</artifactId>
<version>1.0.1</version>
</dependency>默认表名前缀为 ir,可直接使用:
默认表:
ir_indicatorir_rule_groupir_ruleir_condition_groupir_conditionir_operation_logir_rule_version
ir_rule_group 用于聚合同一 scenarioCode + triggerNode 下的规则。当前实现中,规则每次创建或更新都会先按该维度 touch 对应的规则组:
- 不存在则创建
- 存在则基于
version做乐观锁更新,并把rule_group_id回写到规则
这层聚合为后续做"按场景 + 触发节点限制规则数量"等能力提供了稳定落点。
easy:
indicator:
rule:
enabled: true
table-prefix: ir
scan-packages: com.example.rule
max-rule-limit: 50
view:
enabled: true
path-prefix: /api
compile-cache:
enabled: true
timeout-millis: 5000
precise: false
expression-warmup:
enabled: true
async: true说明:
scan-packages用来扫描枚举形式的场景、触发节点、单位、指标元数据- 如果指标以 Spring Bean 形式声明,也会通过 BeanPostProcessor 自动注册
view.enabled=false时可以关闭内置管理 API,只保留核心执行能力compile-cache.precise=true启用精确计算模式(BigDecimal),适用于金融场景expression-warmup.enabled=true开启启动预热,减少冷启动延迟
完整配置说明请参阅:组件介绍与接入指南
一个最小的指标定义示例:
@Component
public class AmountIndicator extends DoubleIndicatorMetadata<OrderContext> {
@Override
public String getCode() {
return "amount";
}
@Override
public String getDesc() {
return "订单金额";
}
@Override
public List<Scenario<? extends OrderContext>> supportScenario() {
return List.of(OrderScenario.ORDER_AUDIT);
}
@Override
public Function<OrderContext, Double> resolve() {
return OrderContext::getAmount;
}
}接入时你需要定义:
Scenario<E>TriggerNodeUnitIndicatorMetadata<K, V>- 如需日期/时间/列表指标,可使用
DateIndicatorMetadata、TimeIndicatorMetadata、StringListIndicatorMetadata等类型(或自定义IndicatorMetadata)
可以在启动时同步:
@Bean
public ApplicationRunner syncIndicators(IndicatorCatalogService indicatorCatalogService) {
return args -> indicatorCatalogService.syncFromMetadata();
}EvaluationResult result = ruleExecuteService.execute(
OrderScenario.ORDER_AUDIT,
OrderTriggerNode.CREATE_ORDER,
orderContext
);你可以从执行结果里读取:
- 是否命中:
result.isRuleValue() - 命中的规则:
result.getMatchRule() - 执行说明:
result.getMessage()或result.getExecutionResult().getMessage() - 执行结果对象:
result.getExecutionResult(),包含ruleValue、message、error - 本次已计算指标:
result.getIndicatorMap()
详细的接入步骤和代码示例请参阅:组件介绍与接入指南
规则管理操作(创建、更新、启停、删除、复制)支持在同一事务内执行业务回调,通过 RuleTransactionRequest 传递业务参数和结果持有者:
// 创建规则并在事务内执行业务回调
ruleManageService.createRule(
command,
RuleTransactionRequest.of(
bizArg,
resultHolder,
(ruleId, arg, holder) -> {
// 在事务内执行业务逻辑
}
)
);
// 更新规则并在事务内执行业务回调
ruleManageService.updateRule(
ruleId,
command,
RuleTransactionRequest.of(bizArg, resultHolder, (ruleId, arg, holder) -> {
// 在事务内执行业务逻辑
})
);
// 启用/禁用/删除/复制规则同样支持事务钩子
ruleManageService.enableRule(ruleId, transactionRequest);
ruleManageService.disableRule(ruleId, transactionRequest);
ruleManageService.deleteRule(ruleId, transactionRequest);
ruleManageService.copyRule(ruleId, newName, transactionRequest);业务系统可以通过替换 TxSupport Bean 来自定义事务传播策略。
starter 内部业务异常统一抛出 IndicatorRuleException,并携带 IndicatorRuleErrorCode。
- 错误码接口:
IndicatorRuleErrorCode - 通用错误码枚举:
CommonIndicatorRuleErrorCode - 统一异常类型:
IndicatorRuleException
常见错误码示例:
IndicatorRule-01:参数非法IndicatorRule-02:规则不存在IndicatorRule-03:规则数量超过上限IndicatorRule-07:规则组乐观锁更新失败
业务系统若需要统一 API 错误响应,建议在全局异常处理器中优先捕获 IndicatorRuleException,按 errorCode + message 输出。
默认前缀:/api
GET /api/rules— 分页查询规则列表GET /api/rules/{ruleId}— 获取规则详情(含条件组和条件树)POST /api/rules— 创建规则PUT /api/rules/{ruleId}— 更新规则POST /api/rules/compile— 编译预览规则表达式,不写入数据库PUT /api/rules/{ruleId}/enable— 启用规则PUT /api/rules/{ruleId}/disable— 禁用规则DELETE /api/rules/{ruleId}— 删除规则
POST /api/indicators/sync— 全量同步指标元数据到数据库GET /api/indicators— 查询指定场景下的已同步指标GET /api/indicators/unsynced— 查询未同步的指标元数据GET /api/indicators/comparable— 查询可参与指标比较的指标列表PUT /api/indicators/{indicatorId}/enable— 启用指标PUT /api/indicators/{indicatorId}/disable— 禁用指标
GET /api/options/scenarios— 获取场景列表GET /api/options/scenarios/{scenarioCode}/trigger-nodes— 获取场景下的触发节点GET /api/options/indicator-types/{indicatorType}/operators— 获取指标类型对应的操作符GET /api/options/condition-value-sources— 获取条件值来源选项GET /api/options/enums— 获取枚举选项
GET /api/compile-cache/status— 查询编译缓存状态POST /api/compile-cache/warmup— 手动触发表达式预热POST /api/compile-cache/reset— 重置编译缓存
GET /api/operation-logs— 分页查询操作日志GET /api/operation-logs/{logId}— 查询操作日志详情(含前后快照)
GET /api/rules/{ruleId}/versions— 查询规则的历史版本列表GET /api/rules/versions/{versionId}— 查询指定版本快照
对后端开发来说:
- 不需要从零设计一套规则 DSL
- 执行入口直接面向业务领域对象
- 指标元数据强类型,和业务代码天然靠近
- 规则管理能力已经具备基础可用性
对技术负责人和架构师来说:
场景 + 节点 + 指标 + 规则的边界是明确的- 规则治理可以从零散 if/else 逻辑中抽离出来
- 关键扩展点可替换,便于逐步集成到现有系统
- 管理层和执行层拆分清晰,适合后续演进
对 GitHub 访客来说:
- 不只是一个 starter,还有完整的前后端示例
- 可以很快看清楚这个项目解决什么问题
- 可以直接看到接入路径,而不是只看到一堆底层代码
你可以替换或扩展这些能力:
SequenceSupport— 规则/指标编码生成LoginSupport— 当前登录用户信息TxSupport— 事务执行适配器IndicatorRepositoryRuleRepositoryRuleValidationServiceRuleCompileServiceRuleExecutorRuleEngineIndicatorCatalogServiceRuleVersionServiceOperationLogServiceEntityAuditSupport— 实体审计信息(创建/修改人、时间)EntityCodeSupport— 实体编码生成RemoteValueRange.register(...)— 远程动态值范围CompileCacheProbe— 编译缓存监控探针
- Java 11+
- Spring Boot 2.7.x
- Spring JDBC 持久化
- QLExpress4 规则执行
- Repository 查询 SQL 显式列字段,不使用
SELECT * - 单次执行内有指标函数缓存
- 规则按
scenario + trigger node查询 - 支持表达式启动预热(同步/异步)
- 支持编译缓存监控与自动重置
- 支持条件组 AND/OR 组合逻辑
- 支持规则复制、历史版本快照和操作日志审计
- 规则管理操作支持事务钩子回调
当前默认执行语义是:
- 加载当前场景和触发节点下的启用规则
- 按优先级降序排序
- 命中第一条规则即返回
- 架构与设计文档 — 详细的架构设计、领域模型、执行流程、扩展点说明
- 组件介绍与接入指南 — 完整的后端接入步骤、前端接入指南、生产接入清单、常见问题
- 版本历史 — 版本发布记录
- Starter SQL 脚本
仓库里还补充了两个项目级 Codex skill,用于后续在业务系统里快速接入:
它们的目标不是给最终用户看,而是帮助后续 agent 基于当前项目结构快速完成集成。
- 发布到公共 Maven 仓库
- 增加更适合 GitHub 首页的截图、GIF 或演示视频
- 提供
docker compose一键启动 Demo - 增强多规则命中策略,而不只是一条命中即返回
- 提供更适合生产环境的 ID 生成默认方案
- 补充更多真实业务场景的集成测试
如果你要扩展这个项目,建议先理解这几个原则:
- 元数据在代码里定义
- 规则在存储里维护
- 执行发生在真实业务对象上
- SQL 显式列字段,避免
SELECT *
在改动规则模型、持久化结构或执行语义之前,建议先阅读架构文档。
本项目基于 MIT License 开源。





