news 2026/9/12 7:05:15

基于SpringBoot的规则编排可视化系统设计与实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于SpringBoot的规则编排可视化系统设计与实践

这几天在公司把一套基于 SpringBoot 的规则编排可视化系统从零搭到了线上,运营同事总算不用每次改活动规则都来找我排期了。趁着热乎劲儿,把整个设计思路、技术选型和踩坑过程整理出来,给同样被“业务逻辑变更频繁”折磨的朋友一个参考。

这套东西说白了就干一件事:让不懂代码的业务人员,在界面上拖拖拽拽、点点选选,就能配置出“满100减20且仅限新用户”或者“金额大于5000且命中风控名单则人工审核”这类复杂业务规则,配置完直接生效,不用发版、不用重启。文章里我会把规则模型怎么设计、表达式引擎怎么选、可视化界面怎么和 SpringBoot 后端打通,以及上线后遇到的坑,全部摊开来讲。

1. 规则编排可视化的核心价值与应用场景

1.1 业务背景:规则变更需求的常态化和开发瓶颈

几乎所有业务系统都会面临同一个问题:业务规则比代码变得快。运营今天要做一个“新人专享五折券”的活动,明天要调整成“满三件打八折,且只限指定类目”,后天又要在风控流程里加一条“同一IP下超过5个账号下单需要人工审核”。

这些规则本身不复杂,但架不住组合多、变更快。如果用传统方式,每次变更都要走一遍“提需求 -> 排期 -> 改代码 -> 测试 -> 发版”的流程。哪怕改一个数字,最快也得半天,遇到紧急活动或者大促前的临时调整,开发就成了整个业务的瓶颈。

我当时接手这个需求的时候,业务方已经攒了一堆规则调整的单子。最夸张的一次,运营为了一个促销活动,一周内提了三次变更,每次都是上线当晚发现规则有漏洞,又急着往回改。这种循环不仅消耗开发资源,更致命的是,规则在代码里都是一堆 if-else,时间一长,谁都不敢动那块代码,因为根本不知道改了这一处会影响哪条线上的哪个流程。

规则编排可视化的思路,就是把这些散落在代码里的 if-else 提取出来,转成结构化的配置数据,让业务人员在一个可视化界面里直接维护。开发只需要做两件事:一是把规则执行引擎做稳,二是把可视化配置界面做好用。

1.2 典型应用场景:营销优惠、风控拦截、审批流转

落地之前,先得圈定哪些场景适合用这套东西。我梳理下来,主要有三类场景是刚需。

第一类是营销优惠计算。这是最典型也最容易出成果的场景。优惠规则天然就是“条件 + 动作”的组合:满足什么条件(用户等级、订单金额、商品类目、是否首单),执行什么动作(打折、减钱、送券、包邮)。而且规则变化极频繁,每个活动一套规则,活动之间还可能叠加互斥。

第二类是风控拦截和策略配置。风控规则的特点是条件多、更新快、对时效要求高。比如“注册时间小于7天的账号下单金额超过2000需要人工审核”“同一收货手机号当天关联订单超过3笔自动拦截”。这些规则如果靠开发写死在代码里,风控人员根本没法快速响应突发的刷单行为。

第三类是审批流程的节点条件。现在的审批流引擎(比如Flowable、Activiti)一般支持条件表达式,但表达式都是写在线路里的,非技术人员看着一脸懵。把审批条件和打分卡做成可视化规则,业务人员就能自己配置“金额大于5万且部门为销售部则走总监审批,否则走经理审批”这类逻辑。

当然,不是所有场景都适合可视化编排。如果规则极其固定、一年都不变一次,或者逻辑极其复杂、涉及多表关联的多轮循环计算,那还是老老实实写代码更合适。可视化编排最适合的是“规则变化频繁、组合有一定复杂度但可控、需要业务人员自助维护”的中间地带。

1.3 方案目标:千人千面、在线生效、业务自助

说到底,做这套系统就为了三个目标。

第一个目标是在线生效。规则配置完成后,不需要发版、不需要重启服务,配置同步到执行引擎后,下一次请求立即生效。这就把规则变更的响应时间从“半天起步”压缩到“分钟级”。我当时的方案是配置保存后写库,通过 Redis 发布订阅通知各实例刷新本地缓存,这样即使多实例部署也能保证规则准实时生效。

第二个目标是业务自助。业务人员自己进系统、自己配规则、自己测试验证、自己发布上线。开发彻底从这些琐碎的规则调整中解放出来,只处理引擎报错和新增的规则类型。

第三个目标是千人千面。有了规则配置平台之后,不同用户、不同场景看到的活动价格、营销权益可以完全不同。这就是“千人千面”的底层支撑,不是每个用户写一套代码,而是通过不同规则的组合计算出不同结果。

2. 技术选型:自己造轮子还是用框架

2.1 常见方案对比:Drools、EasyRules、Flowable、表达式引擎

规则引擎这块,Java 生态里其实有好几个现成的选择,当时我都调研了一遍。

Drools 是老牌规则引擎,基于 Rete 算法,规则用 DRL 语法编写,能处理非常复杂的推理场景。但问题也明显:DRL 语法学习成本高,业务人员根本不可能直接写 DRL;Drools 框架本身偏重,引入后内存和类加载器方面多多少少会有些坑;而且规则量一大,调试和维护都要专门的知识储备。

EasyRules 是个轻量级规则引擎,底层还是 Java 代码定义规则,支持用注解或者规则描述文件(比如 JSON、YAML)声明规则。它比 Drools 简单很多,适合做“多条规则顺序执行,命中即停或继续”的场景。但对复杂条件组合的支持不够灵活,本质上还是扁平规则列表,不好表达嵌套的 AND/OR 逻辑。

Flowable / Activiti 这类工作流引擎是用来做流程编排的,适合审批流、任务流,但它是节点流转的思维,不是“条件判断 + 动作执行”的思维。虽然也能实现复杂的条件网关,但配置界面重,学习成本也不低,而且为了算一个优惠价去引入一个工作流引擎,有点杀鸡用牛刀了。

SQL 引擎和表达式引擎是另一个方向。本质上规则就是一个 boolean 表达式,表达式引擎负责把字符串表达式解析成可执行逻辑。比如 Aviator、QLExpress、SpEL 这些都是成熟方案。用表达式引擎的好处是执行效率高、依赖轻,坏处是表达式本身有语法,业务人员直接写表达式仍然有门槛,需要可视化界面帮忙生成表达式。

综合比较之后,我选择了“自研规则模型 + 表达式引擎执行”的路线:规则的结构用一套自定义的 JSON 模型表达,可视化界面负责把用户的拖拽操作转成 JSON,执行引擎解析 JSON 并调用表达式引擎完成最终判断。

2.2 为什么选择表达式引擎加自研规则模型

这里详细说说选型理由,方便你理解我为什么没有直接用现成规则引擎。

第一个理由是控制力。Drools 这样的重型引擎,封装层次太高,出了问题很难定位。自研规则模型意味着数据结构完全可控,规则存储、转换、校验、测试每一个环节都掌握在自己手里,出了问题可以快速定位是模型问题还是表达式问题。

第二个理由是学习成本和维护成本。Drools 在国内的使用率其实没有想象中那么高,团队里真正玩过 DRL 的人很少。而表达式引擎(比如 Aviator)语法简单,团队同学看着文档就能上手维护。可视化配置界面直接从 JSON 模型渲染,不用去处理 DRL 的编译和加载机制。

第三个理由是灵活性。自研规则模型可以非常贴合业务。比如我可以给条件节点定义“匹配方式”“值类型”“自定义函数”,这些在通用规则引擎里要么不支持,要么需要额外扩展。而我们是自己想怎么设计就怎么设计。

当然,自研也有代价。规则引擎的很多细节坑得自己踩,比如表达式编译性能、函数注册、上下文变量传递、沙箱安全这些都得自己处理。但站在现在往回看,这个选择是值得的。后面我会把每个坑怎么填都讲清楚。

2.3 可视化方案选型:AntV X6、LogicFlow、vue-flow

规则模型定下来之后,还有一个关键选型:前端可视化用什么画。

如果规则表达是树形结构(其实就是一棵条件树),那方案就多了,普通表单就能实现,用树形控件嵌套条件组也能做。但如果想要“连线式”的规则编排体验,让用户像画流程图一样把条件、动作节点串起来,就需要用图编辑引擎。

我当时调研了三个主流方案。

AntV X6 是国内蚂蚁集团开源的关系图编辑引擎,生态比较成熟,文档齐全,内置了很多交互能力,比如节点拖拽、连线、撤销重做、小地图等。如果是做复杂的连线编排,X6 是很稳的选择。

LogicFlow 是滴滴开源的前端流程图编辑框架,主打逻辑编排场景,API 设计得很清晰,而且自带了类似“节点面板 - 画布 - 属性面板”的布局方案,做规则编排很对味。

vue-flow 是 React Flow 的 Vue 移植版,交互流畅,但在国内社区和中文文档上比前两者弱一些。

我做了一个权衡:连线式的编排虽然炫酷,但对业务人员来说上手门槛反而更高,拖拽连线乍一看直观,但复杂规则一多,连线在画布上交错,根本看不清。最终我采用了“表单嵌套 + 树形展示”的方案,左侧选择字段和操作符,右侧实时生成可读的条件语句,中间用缩进和图标表现层级关系。这样业务人员第二天就能上手,不需要理解“节点”和“连线”的概念。

这个取舍很重要。可视化不等于花哨,关键是效率。如果界面看起来高大上但业务人员学不会,反而成了负担。后面在界面设计部分我会具体展示这种方案的效果。

3. 规则模型设计:从逻辑到数据结构的转换

3.1 原子节点设计:条件节点、动作节点、组合节点

规则模型是整个系统的地基,这一步设计不好,后面全盘皆输。我设计的核心思路是:把任何复杂的业务规则拆成有限的原子节点,再用树形结构组合起来。

一个规则由两部分组成:条件和动作。

条件部分是一个条件树,树的每个节点有两类:

  • 条件节点(Condition):表达一个最小的判断单元。由三要素组成:左操作数、操作符、右操作数。比如“订单金额 >= 1000”,左操作数是订单金额,操作符是 >=,右操作数是 1000。
  • 组合节点(Group):表达一组条件的组合逻辑,只有两种:AND(所有子条件同时成立)和 OR(子条件任意一个成立)。组合节点可以嵌套,这样就天然支持了“(A AND B) OR (C AND D)”这种复杂逻辑。

动作部分是一个动作列表,每个动作由一个动作类型和一组参数构成。比如动作类型是“APPLY_DISCOUNT”,参数里指定折扣力度;动作类型是“SET_STATUS”,参数里指定状态值。将来有新的动作类型,只要后端注册一个处理函数就行,前端配置界面也会自动多出一个可选动作。

把规则转成 JSON 之后,大概长这样:

{ "ruleName": "新用户满减活动", "conditionGroup": { "logic": "AND", "children": [ { "type": "condition", "left": "user.isNewUser", "operator": "==", "right": true }, { "type": "group", "logic": "OR", "children": [ { "type": "condition", "left": "order.amount", "operator": ">=", "right": 100 }, { "type": "condition", "left": "cart.itemCount", "operator": ">=", "right": 3 } ] } ] }, "actions": [ { "type": "APPLY_DISCOUNT", "params": { "discountRate": 0.8 } } ] }

这个 JSON 既可以直接存储到数据库,也可以作为前后端交互的协议,还能拿来渲染可视化界面和执行。

3.2 规则执行引擎:表达式解析与上下文传递

规则模型定好了,接下来就是执行引擎。执行引擎要做的事情非常简单:接收规则 JSON 和业务上下文 Map,解析条件树,逐个计算条件节点,最终返回是否命中以及应该执行哪些动作。

但实现的时候有几个关键点要处理好。

第一个是条件节点的计算。条件节点里的左操作数通常不是固定值,而是一个变量路径,比如order.amount,它代表从上下文中取订单金额。执行的时候需要从业务上下文里把这个值解析出来。

我用了一个变量解析器,支持点号路径,比如user.level会先取 context 里的 user 对象,再取它的 level 属性。这个解析器支持 Map 和 Java Bean 两种类型,毕竟有的业务上下文是 Map,有的直接塞了个 Object。

第二个是操作符的扩展。除了常见的 ==、!=、>、<、>=、<=,还需要支持 in、not in、contains、startWith、matches(正则)等操作符。每个操作符就是一个策略类,接收左值和右值,返回 boolean 结果。

第三个是上下文传递。执行引擎不能只判断“命中/未命中”,很多时候动作计算需要中间结果。我在引擎里设计了一个可变的执行上下文,前面动作的计算结果可以放入上下文,供后续动作或者条件使用。这就实现了简单的链式计算。

执行引擎用了一个很经典的设计:规则编译和规则执行分离。规则 JSON 从库里读出来后,会先编译成一颗“可执行节点树”,节点对象持有预处理后的操作符策略和值对象,这样执行的时候不用反复做字符串解析和类型转换,性能会好很多。

3.3 整体架构:配置中心加规则存储加执行引擎

把整个系统的架构串起来看,大概是这样的:

  • 前端配置平台(Vue + 自研表单组件):负责把规则 JSON 渲染成可视化表单,用户保存时再把表单转回 JSON。
  • 后端管理服务(SpringBoot):提供规则的增删改查、版本管理、发布、测试接口。
  • 规则存储(MySQL + Redis缓存):规则 JSON 存 MySQL,发布后加载到 Redis,本地实例再做内存缓存,保证高频读取场景的性能。
  • 执行引擎(SpringBoot业务系统内嵌):业务代码里调用执行引擎,传入场景编码和业务上下文,引擎自动定位规则并执行。

你可能注意到,这里没有把执行引擎做成独立的微服务,而是作为依赖包嵌入到各个业务系统。原因很简单:规则执行是高频低延迟调用,多一次远程调用就多一份网络开销和故障风险。嵌入式的方案性能最好,部署最简单,规则更新通过 Redis 广播通知刷新本地缓存即可。

4. 核心代码实现:一个可运行的规则执行引擎

4.1 规则实体与条件节点定义

Java 这边,我定义了几个核心实体类。Rule 是整个规则的顶层对象,包含规则编号、名称、场景编码、条件树、动作列表、状态等字段。

@Data public class Rule { private String ruleCode; private String ruleName; private String sceneCode; private ConditionGroup conditionGroup; private List<RuleAction> actions; private Integer status; private Integer version; }

ConditionGroup 对应组合节点,Condition 对应条件节点。一个 Group 里既有子 Group 又有 Condition,所以用 List在序列化时会比较麻烦。我选择了在子节点列表中用一个 type 字段区分类型,这样既清楚又好扩展。

@Data public class ConditionGroup { private String logic; // AND / OR private List<ConditionNode> children; } @Data public class ConditionNode { private String type; // "condition" or "group" private String left; private String operator; private Object right; private ConditionGroup conditionGroup; // 当 type=group 时有值 }

动作实体很简单,就是一个动作编码加一个参数 Map。不同的动作类型,处理时读取参数里的字段做不同逻辑。

@Data public class RuleAction { private String type; // 如 APPLY_DISCOUNT、SET_STATUS private Map<String, Object> params; }

4.2 表达式引擎集成:Aviator 的用法

条件节点最终是要计算的。我在左边变量和右边常量之间做判断,最开始自己手写操作符解析,后来踩了几个类型转换的坑,索性换成了 Aviator 表达式引擎来兜底。

Aviator 是一个高性能的轻量级 Java 表达式求值引擎,语法和数学表达式、布尔表达式很接近。比如order.amount >= 100 && user.level == 2这种表达式,Aviator 可以直接求值。

我的做法是:把整个条件树在编译期转成一个 Aviator 表达式字符串,条件节点用&&||连接,执行的时候直接调用 Aviator 求值。

public class AviatorEvaluator { public static boolean evaluate(String expression, Map<String, Object> env) { Object result = AviatorEvaluatorInstance.execute(expression, env); return Boolean.TRUE.equals(result); } }

比如前面的 JSON 规则,编译后的表达式是:

(user.isNewUser == true) && ((order.amount >= 100) || (cart.itemCount >= 3))

Aviator 对变量名和点号路径的原生支持已经很到位,user.isNewUser这种写法可以直接从 env 里嵌套取值。不过有一个坑要记住:Aviator 默认把变量名大小写敏感,且当变量在 env 中不存在时会抛异常。我在编译期会把规则里引用到的字段做一次白名单校验,避免运行业务上下文里缺字段导致整个规则崩溃。

4.3 完整执行流程:从 JSON 配置到规则结果

执行引擎对外暴露的接口很简单,核心就是一个 execute 方法。

public RuleResult execute(String sceneCode, Map<String, Object> context) { Rule rule = ruleLoader.getActiveRule(sceneCode); if (rule == null) { return RuleResult.notHit(rule); } boolean matched = ConditionEvaluator.evaluate(rule.getConditionGroup(), context); if (!matched) { return RuleResult.notHit(rule); } List<ActionResult> actionResults = new ArrayList<>(); for (RuleAction action : rule.getActions()) { ActionResult result = actionExecutor.execute(action.getType(), action.getParams(), context); actionResults.add(result); } return RuleResult.hit(rule, actionResults); }

这个流程看起来简单,但真正要跑起来还要解决几个问题。

第一是规则加载。ruleLoader 先从本地缓存拿规则,拿不到就查 Redis,Redis 也没有就查数据库。数据库加载完写回 Redis 和本地缓存。每次规则发布时清空本地缓存并推 Redis 消息,各实例收到消息后自动重载。

第二是类型转换。条件节点里的 right 值在 JSON 里是字符串,但比较的时候可能是数字、布尔、日期。我在编译期根据 left 字段的类型元数据自动做类型转换。比如 left 是order.amount,元数据里标记了是 BigDecimal 类型,那 right 值就转成 BigDecimal 再比较。

第三是安全兜底。条件树编译成 Aviator 表达式后,如果某个字段值非法(比如 null 参与比较),Aviator 的行为有时候会让人懵。我统一封装了空值处理策略:凡是条件节点里左值为 null,一律视为条件不成立,只有配置了“允许空值”的场景才特殊处理。这样就避免了规则因为个别用户数据缺失而大面积失效的问题。

4.4 可视化配置界面:表单加树形展示

前端部分,我没有用流程图的方案,而是做了一个“表单驱动”的配置交互。

整个配置界面分成三块。左侧是字段面板,列出了所有业务场景下可用的字段。中部是条件配置区,展示了当前条件树的所有节点。右侧是属性编辑区,选中条件节点后,在这里配置操作符、比较值、参数等。

条件树在界面上的表现是缩进列表,每个组合节点有一条竖线,子节点带连接线图标。AND 组合和 OR 组合之间的切换,直接点击逻辑按钮就行。

新增条件时,用户从左侧拖拽字段到目标条件组下,或者点击组节点上的“新增条件”按钮,在弹窗里选择字段、操作符和值。每一步操作,界面都会实时生成一条自然语言的描述,比如“(订单金额 >= 100) 且 (用户新用户标记 == 是)”。这样业务人员不用理解数据结构,看描述就知道自己配置的逻辑对不对。

配置完成,界面把树形结构转成规则 JSON,点击“测试”可以先填一组测试数据验证规则命中结果,没问题再点“发布”。整个流程没有接触任何代码,也没有任何语法学习成本。

5. 落地过程中的常见问题与避坑指南

5.1 表达式安全问题:沙箱、函数白名单

自研规则引擎最容易被忽略的就是安全。规则里可以写 Aviator 表达式,如果表达式引擎允许调用任意 Java 方法,那有配置权限的人理论上就能通过表达式执行任意代码,这等于给系统留了一个后门。

我当时做安全加固主要是三层。

第一层是操作符白名单。可视化界面生成表达式时,只允许使用固定的操作符集合(如 ==、!=、>、<、in、contains 等),凡是白名单之外的操作符一律不允许。这样 Aviator 表达式字符串就只能在有限的语法空间内变化。

第二层是函数白名单。如果规则里需要自定义函数(比如根据地址文本解析省市区),我会在引擎里先注册好函数,表达式里只允许调用已注册的函数。Aviator 提供了addFunction方法,没注册的函数默认不允许调用。

第三层是语法校验和编译缓存。规则保存和发布前,都会经过一次表达式编译校验,编译不通过的规则不允许发布。编译通过后的表达式会缓存起来,避免每次执行都重新解析。

如果你们团队用的是 QLExpress 或者 SpEL,做法类似,先禁掉默认的反射调用能力,再开白名单。这一点安全必须做在配置阶段,而不是执行阶段,因为执行阶段拦截已经晚了。

5.2 性能问题:规则数量膨胀时的优化

规则可视化之后,业务人员配置规则的积极性很高,规则数量很快就会膨胀。从几十条到几百条,再到几千条,执行引擎的性能压力就上来了。

一开始我的执行逻辑很简单:拿到场景编码,遍历这个场景下所有规则,逐条执行,第一条命中的生效。规则少的时候没问题,规则多了以后,一次请求可能要执行几十上百个条件判断,CPU 消耗明显飙升。

优化分了三步。

第一步是规则索引。把所有规则按场景编码存档,场景编码做索引,同一个场景下的规则按优先级排序,执行时只取该场景下的规则子集,绝不去遍历全量规则。

第二步是条件预编译。规则加载的时候,条件树直接编译成可执行的对象树,不再运行时做 JSON 解析和字符串拼接。Aviator 的表达式字符串也缓存好,避免重复编译。

第三步是规则缓存加上限。本地缓存做 LRU 淘汰,控制每个场景缓存的规则数量不超过阈值。支持一个场景下配置多条规则,按优先级返回命中结果。

性能优化之后,单次规则执行的耗时从上万条规则场景下的毫秒级降到了几百微秒级别,加上本地缓存命中,整体几乎无感。

5.3 版本管理与灰度发布

规则配置是业务人员自己发布的,如果没有版本管理,出问题想回滚就没地方退了。我在规则表里设计了版本号字段,每次发布不直接覆盖当前版本,而是新增一个版本记录,只有“已发布”状态的版本会生效,历史版本全部留档。

这样一来,如果业务人员发现新规则有误,可以一键回滚到上一个发布版本。后端实现也很简单,回滚就是把指定历史版本的状态改为新发布,原发布版本状态改为历史。

灰度也做了。规则表里增加了一个灰度配置字段,可以配置该规则对哪些用户生效,比如按用户ID取模、按城市白名单、按用户类型名单等。执行引擎判断命中之前,先做灰度过滤,只有灰度范围内的请求才允许命中新规则。

灰度配置看起来很基础,但非常重要。大促期间改优惠规则,直接全量上线万一算错了价,损失不可预估。灰度发布让业务人员可以小范围验证,跑了半小时确认没问题再全量放开。

5.4 权限控制:谁能编辑谁只能看

还有一个经常被忽视的问题是权限。规则配置直接影响线上业务逻辑,必须严格区分谁能编辑、谁能发布、谁能只看。

我是这样安排的:业务人员默认只有“编辑草稿”权限,可以保存草稿但没法发布上线。发布权限单独给到团队里的一个资深运营或者业务负责人。管理员可以有全部权限,包括新增字段、修改字段元数据、注册新动作类型等。

前端界面根据当前用户的角色动态展示按钮,后端接口也统一做了权限校验,不能只靠前端隐藏按钮。权限控制这一层如果做漏了,业务人员误操作发布了一条错误规则,造成的影响是直接的线上损失,这个责任谁都担不起。

5.5 规则测试机制:预置样本与运行预览

规则发布前的测试环节,是保证规则正确性的最后一道防线。可视化配置界面上我加了一个“测试运行”功能,用户可以填一组模拟的业务数据,点击执行后,界面直接展示这条规则是否命中、命中的动作结果是什么。

如果规则里有 and/or 组合,界面还会把每个条件节点的命中情况逐条列出来,方便用户判断是哪一层条件没有满足。

测试的数据怎么来?我提供了两个来源。一个是用户手工输入,在界面上点选字段填值;另一个是从线上请求抓样本数据。每个场景下都配置了请求日志抽样,把最近命中的请求上下文脱敏后存起来,测试时直接选一条样本数据回放,看规则执行结果。

这个机制上线后效果非常好,业务人员自己配置完规则,随手选一个线上样本跑一下,如果结果不对当场就能调整,不用再来回找开发排查。

6. 一些值得再说的实操心得

最后分享几个我在这个项目里学到的经验。

第一个是规则语义要尽量贴近业务人员,而不是贴近开发。比如“订单金额 >= 100”在代码里可能是一个数字比较,但业务人员更习惯看到“订单金额【不低于】100元”。我在界面里把所有操作符都做了中文别名,匹配方式用中文展示:等于、不等于、大于、大于等于、小于、小于等于、包含、不包含、在列表中、不在列表中。这样即便是不熟悉技术的人,理解起来也完全没障碍。

第二个是规则的变更要可追溯。虽然我们做了版本管理,但时间久了之后,业务人员之间容易因为“谁改的、为什么改”产生分歧。后来我在规则发布接口里加了一个变更记录表,记录每次发布的规则内容、操作人、操作时间和备注说明。规则汇总展示的时候,一眼就能看到最新版本是谁改的、改了什么。

第三个是避免过度设计。一开始我想做一个特别灵活通用的规则模型,支持任意的函数嵌套、任意的变量组合,甚至支持脚本。后来做了一半发现,这种灵活性带来的是配置界面的极大复杂化和表达式的不可控。最终我限制住规则模型的表达能力,只支持我们业务真正用到的那些条件类型和动作类型,界面清爽了,维护成本也低了。

第四个是做好兜底。规则执行引擎万一崩了,不能影响主业务流程。我在客户端调用引擎的外层做了一个 try-catch,如果引擎执行抛异常,走默认的业务逻辑(比如默认不命中、走人工审核),同时打日志报警。这个兜底看着简单,关键时刻能救命,因为规则配置平台的故障不能拖垮主站的交易主链路。

这套系统上线到现在,规则量已经突破了三千条,业务人员每天自己配置和调整规则,开发这边基本不再为规则变更占用时间。从技术角度看,它不是一个多复杂的系统,但确实解决了实际的问题。如果你也在为业务规则频繁变化发愁,不妨按这个思路试一试,先跑通一个场景,再逐步推广。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 7:04:22

编辑器生态全解析:从通用工具到专用场景,如何选对提升效率

我是一个挺喜欢折腾工具的人。这些年换过的编辑器少说也有几十个&#xff0c;从系统自带的记事本&#xff0c;到重量级的 IDE&#xff0c;再到各种偏门到可能只有几百个人在用的专用文件编辑器&#xff0c;我都试过。所以当有人抛出“editor”这个词的时候&#xff0c;我第一反…

作者头像 李华
网站建设 2026/9/12 7:03:41

AI自动把课程视频变成讲义:完整流程与实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 7:03:38

30分钟本地部署Duix-Avatar,生成第一条AI数字人口播视频

30分钟本地部署Duix-Avatar&#xff0c;生成第一条AI数字人口播视频 【免费下载链接】Duix-Avatar &#x1f680; Truly open-source AI avatar(digital human) toolkit for offline video generation and digital human cloning. 项目地址: https://gitcode.com/GitHub_Tren…

作者头像 李华
网站建设 2026/9/12 7:03:12

Java中VarHandle与Unsafe性能对比及使用场景分析

1. 项目概述在Java 9发布后&#xff0c;VarHandle作为Unsafe的替代方案被引入&#xff0c;这引发了关于两者性能差异的热烈讨论。作为一名经历过多次Java技术面试的开发者&#xff0c;我发现途虎养车等一线互联网企业在面试中特别喜欢考察候选人对底层API的理解程度。VarHandle…

作者头像 李华
网站建设 2026/9/12 7:02:32

Espresso自动化测试底层原理与最佳实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华