Skip to content

openquartz/easy-indicator-rule

Repository files navigation

Easy Indicator Rule

一个面向 Spring Boot 业务系统的指标规则组件:用代码定义指标元数据,用页面管理规则,用运行时执行真实业务对象上的规则。

🌐 中文 | English

Easy Indicator Rule 适合这类场景:

  • 业务系统里已经出现越来越多的"审核规则、拦截规则、路由规则、异常识别规则"
  • 规则变更频繁,但又不想每次都改代码、发版
  • 希望把"业务字段/计算结果"抽象成指标,让运营或后台人员可配置规则
  • 希望规则执行仍然紧贴真实领域对象,而不是重新设计一套脱离业务模型的 DSL

它提供了一套相对完整的模式:

  1. 业务代码定义 场景 + 触发节点 + 指标
  2. 管理后台维护规则
  3. 运行时按真实业务对象执行规则并返回命中结果

项目价值

很多业务系统发展到一定阶段后,都会堆出大量类似逻辑:

  • 订单审核规则
  • 履约异常识别规则
  • 风控拦截规则
  • 客服分单规则
  • 营销人群匹配规则

这些逻辑如果一直写死在代码里,通常会带来几个问题:

  • 规则修改需要研发介入和重新发布
  • 业务和研发围绕规则沟通成本高
  • 相同模式在多个系统里重复实现
  • 缺少统一的规则模型和治理方式

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
Loading

你可以把它理解成三层:

  • 元数据层:业务代码定义有哪些场景、触发节点、指标、单位和值范围
  • 管理层:规则和指标同步到数据库,通过内置 API 或前端进行管理
  • 执行层:规则被编译成表达式,运行时对业务对象执行并返回结果

管理台界面预览

下面的截图来自仓库内示例前端 easy-indicator-rule-example-ui,用于快速展示这套组件在业务系统里的实际使用形态。

1. 规则管理页

规则管理页用于按规则编码、规则名称、状态筛选规则,并直接完成编辑、查看历史版本、启停和删除。

规则管理页

2. 规则编辑页

规则编辑页展示了"场景 + 触发节点 + 逻辑组 + 条件"的完整编排方式。业务系统定义指标元数据后,运营或后台人员就可以在这里组合出可执行规则。

规则编辑页

3. 规则历史版本页

规则历史版本页用于审计规则变更,支持按版本查看表达式快照,适合对接审计、回溯和发布核对流程。

规则历史版本页

4. 指标库页

指标库页统一展示当前场景下可用于规则编排的指标,包括指标编码、名称、类型和状态,并支持与后端元数据同步。

指标库页

5. 操作日志详情

操作日志除了展示规则创建、修改等动作外,还保留了前后快照、表达式预览和变更字段,方便排查"规则为什么变成这样"。

操作日志详情

6. 执行测试页

执行测试页可以直接输入上下文 JSON,验证某个场景和触发节点下的规则执行结果。这对于接入联调、规则验收和日常排查都很实用。

执行测试页

快速开始

1. 本地安装 Starter

如果组件还没有发布到公共制品库,可以先在本地安装:

cd easy-indicator-rule-spring-boot-starter
mvn clean install

2. 启动示例后端

示例后端默认使用 MySQL:

cd easy-indicator-rule-example/easy-indicator-rule-example-server
mvn spring-boot:run

默认配置文件:

默认使用这些环境变量:

  • MYSQL_HOST
  • MYSQL_PORT
  • MYSQL_DB
  • MYSQL_USER
  • MYSQL_PASS

服务默认端口:

  • http://localhost:9988

3. 启动示例前端

cd easy-indicator-rule-example/easy-indicator-rule-example-ui
npm install
npm run dev

示例前端包含:

  • 规则列表页
  • 规则编辑页
  • 指标库页
  • 执行测试页

4. 使用 Docker 快速启动示例后端和示例前端

示例目录提供了一个 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)

5. 同步指标

示例后端启动时会自动同步指标元数据,也可以手动调用:

POST /api/indicators/sync

示例场景

仓库中的示例后端演示的是"履约审核"场景:

  • Scenariocontract_order_audit
  • TriggerNodeaudit_ordercreate_ordership_order
  • 业务对象:FulfillmentOrder

示例指标包括:

  • deliveryWarehouse
  • countryCode
  • auditLevel
  • logisticsLine
  • orderAmount
  • buyerVip
  • sales_obj
  • orderCreateDate(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
  }
}

如何接入你的业务系统

1. 引入依赖

<dependency>
    <groupId>com.openquartz</groupId>
    <artifactId>easy-indicator-rule-spring-boot-starter</artifactId>
    <version>1.0.1</version>
</dependency>

2. 初始化表结构

默认表名前缀为 ir,可直接使用:

默认表:

  • ir_indicator
  • ir_rule_group
  • ir_rule
  • ir_condition_group
  • ir_condition
  • ir_operation_log
  • ir_rule_version

ir_rule_group 用于聚合同一 scenarioCode + triggerNode 下的规则。当前实现中,规则每次创建或更新都会先按该维度 touch 对应的规则组:

  • 不存在则创建
  • 存在则基于 version 做乐观锁更新,并把 rule_group_id 回写到规则

这层聚合为后续做"按场景 + 触发节点限制规则数量"等能力提供了稳定落点。

3. 添加配置

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 开启启动预热,减少冷启动延迟

完整配置说明请参阅:组件介绍与接入指南

4. 定义场景、触发节点和指标

一个最小的指标定义示例:

@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>
  • TriggerNode
  • Unit
  • IndicatorMetadata<K, V>
  • 如需日期/时间/列表指标,可使用 DateIndicatorMetadataTimeIndicatorMetadataStringListIndicatorMetadata 等类型(或自定义 IndicatorMetadata

5. 同步指标元数据

可以在启动时同步:

@Bean
public ApplicationRunner syncIndicators(IndicatorCatalogService indicatorCatalogService) {
    return args -> indicatorCatalogService.syncFromMetadata();
}

6. 在业务流程里执行规则

EvaluationResult result = ruleExecuteService.execute(
        OrderScenario.ORDER_AUDIT,
        OrderTriggerNode.CREATE_ORDER,
        orderContext
);

你可以从执行结果里读取:

  • 是否命中:result.isRuleValue()
  • 命中的规则:result.getMatchRule()
  • 执行说明:result.getMessage()result.getExecutionResult().getMessage()
  • 执行结果对象:result.getExecutionResult(),包含 ruleValuemessageerror
  • 本次已计算指标: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

默认前缀:/api

规则 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} — 删除规则

指标 API

  • 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 — 禁用指标

选项 API

  • 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 — 获取枚举选项

编译缓存 API

  • GET /api/compile-cache/status — 查询编译缓存状态
  • POST /api/compile-cache/warmup — 手动触发表达式预热
  • POST /api/compile-cache/reset — 重置编译缓存

操作日志 API

  • GET /api/operation-logs — 分页查询操作日志
  • GET /api/operation-logs/{logId} — 查询操作日志详情(含前后快照)

规则版本 API

  • GET /api/rules/{ruleId}/versions — 查询规则的历史版本列表
  • GET /api/rules/versions/{versionId} — 查询指定版本快照

为什么它适合真实业务系统

对后端开发来说:

  • 不需要从零设计一套规则 DSL
  • 执行入口直接面向业务领域对象
  • 指标元数据强类型,和业务代码天然靠近
  • 规则管理能力已经具备基础可用性

对技术负责人和架构师来说:

  • 场景 + 节点 + 指标 + 规则 的边界是明确的
  • 规则治理可以从零散 if/else 逻辑中抽离出来
  • 关键扩展点可替换,便于逐步集成到现有系统
  • 管理层和执行层拆分清晰,适合后续演进

对 GitHub 访客来说:

  • 不只是一个 starter,还有完整的前后端示例
  • 可以很快看清楚这个项目解决什么问题
  • 可以直接看到接入路径,而不是只看到一堆底层代码

关键扩展点

你可以替换或扩展这些能力:

  • SequenceSupport — 规则/指标编码生成
  • LoginSupport — 当前登录用户信息
  • TxSupport — 事务执行适配器
  • IndicatorRepository
  • RuleRepository
  • RuleValidationService
  • RuleCompileService
  • RuleExecutor
  • RuleEngine
  • IndicatorCatalogService
  • RuleVersionService
  • OperationLogService
  • EntityAuditSupport — 实体审计信息(创建/修改人、时间)
  • EntityCodeSupport — 实体编码生成
  • RemoteValueRange.register(...) — 远程动态值范围
  • CompileCacheProbe — 编译缓存监控探针

当前实现特点

  • Java 11+
  • Spring Boot 2.7.x
  • Spring JDBC 持久化
  • QLExpress4 规则执行
  • Repository 查询 SQL 显式列字段,不使用 SELECT *
  • 单次执行内有指标函数缓存
  • 规则按 scenario + trigger node 查询
  • 支持表达式启动预热(同步/异步)
  • 支持编译缓存监控与自动重置
  • 支持条件组 AND/OR 组合逻辑
  • 支持规则复制、历史版本快照和操作日志审计
  • 规则管理操作支持事务钩子回调

当前默认执行语义是:

  1. 加载当前场景和触发节点下的启用规则
  2. 按优先级降序排序
  3. 命中第一条规则即返回

文档

项目级 Skill

仓库里还补充了两个项目级 Codex skill,用于后续在业务系统里快速接入:

它们的目标不是给最终用户看,而是帮助后续 agent 基于当前项目结构快速完成集成。

后续可以继续增强的方向

  • 发布到公共 Maven 仓库
  • 增加更适合 GitHub 首页的截图、GIF 或演示视频
  • 提供 docker compose 一键启动 Demo
  • 增强多规则命中策略,而不只是一条命中即返回
  • 提供更适合生产环境的 ID 生成默认方案
  • 补充更多真实业务场景的集成测试

Contributing

如果你要扩展这个项目,建议先理解这几个原则:

  • 元数据在代码里定义
  • 规则在存储里维护
  • 执行发生在真实业务对象上
  • SQL 显式列字段,避免 SELECT *

在改动规则模型、持久化结构或执行语义之前,建议先阅读架构文档。

License

本项目基于 MIT License 开源。

About

一个面向 Spring Boot 业务系统的指标规则组件:用代码定义指标元数据,用页面管理规则,用运行时执行真实业务对象上的规则。内置规则管理API、QLExpress4 执行引擎、历史版本快照和操作日志审计

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors