.Net Core 环境下构建强大且易用的规则引擎

在业务的早期时代,也许使用硬编码或者逻辑判断就可以满足要求。但随着业务的发展,越来越多的问题会暴露出来:

逻辑复杂度带来的编码挑战,需求变更时改变逻辑可能会引起灾难

重复性的需求必须可重用,否则必须重复性编码

运行期间无法即时修改规则,但重新部署可能会带来其他问题

上线前的测试变得繁琐且不可控,必须花大量的人力和时间去测试

这些困境在『 小明历险记:规则引擎 drools 教程一』 一文中可以体会一番,一开始只是简单的根据购物金额来发放积分,运行期间又要更改为更多的规则层次,如果不及时引入对应的规范化处理机制,开发人员将慢慢坠入无止尽的业务深渊。对此,聪明的做法是在系统中引入规则引擎对业务操作员要提供尽量简单的操作页面来配置规则,规则引擎和配置尽量不要耦合到一块。

1.2 .Net Core 环境下的选择 -- Nrules

目前最流行的规则引擎应该是Drools, 用 Java 语言编写的开放源码规则引擎,使用 Rete 算法对所编写的规则求值,其操作流程如下:

Drools 操作流程

对于 .Net 应用来说,可以通过 Kie 组件提供的 Rest 接口调用规则引擎运算。然而其过于庞大,仅仅只是需要规则引擎计算核心的部分。对此,查找了 .Net 中开源的规则引擎,发现只有同样实现 Rete 算法的 Nrules 满足要求(支持 .Net Core,运行时加载规则引擎)。

注:本文参考借鉴了美团技术团队 从 0 到 1:构建强大且易用的规则引擎 一文的设计思路,对 Drools 从入门到放弃。

2. Nrules 实战 -- 电商促销活动规则引擎设计 2.1 了解 Nrules

NRules 是基于 Rete 匹配算法的.NET 生产规则引擎,基于.NET Standard ,支持 4.5+ 的应用,提供 流式声明规则、运行时构建规则、专门的规则语言(开发中,不推荐使用到生产,基于.Net 4.5 而不是 .NETStandard )。
其计算机制也与其他规则引擎大同小异:

计算机制

2.2 设计规则配置

前文提到 对业务操作员要提供尽量简单的操作页面来配置规则 ,所以我们定义促销活动的规则配置就要尽量简单。

业务操作员眼中的规则

在设计模型时,我们必须先参考现实生活中遇到的电商促销活动,大致可以想到有这么几种活动类型:满减促销、单品促销、套装促销、赠品促销、满赠促销、多买优惠促销、定金促销等。
在这里,我选择对多买优惠促销做分析,多买促销优惠即所谓的阶梯打折,如买一件9折,买两件8折,其模型大致如下:

public class LadderDiscountPromotion { public List<LadderDiscountRuleItem> Rules { get; set; } public string Name { get; set; } public DateTime StarTime { get; set; } public DateTime EndTime { get; set; } public PromotionState State { get; set; } public List<string> ProductIdRanges { get; set; } public bool IsSingle { get; set; } public string Id { get; set; } } public class LadderDiscountRuleItem { /// <summary> /// 数量 /// </summary> public Int32 Quantity { get; set; } /// <summary> /// 打折的百分比 /// </summary> public Decimal DiscountOff { get; set; } }

这里为了简化设计,设计的模型并不会去约束平台、活动范围、会员等级等,仅仅约束了使用的产品 id 范围。为了匹配现实中可能出现的组合优惠(类似满减活动后还可以使用优惠券等)现象和相反的独斥现象(如该商品参与xx活动后不支持X券),设置了一个字段来判断是否可以组合优惠,也可以理解为所有活动都为组合优惠,只是有些组合优惠只有一个促销活动。

注:想了解更多关于电商促销系统设计可参考脑图

2.3 规则配置转换

为了实现 规则引擎和配置尽量不要耦合到一块,必须有中间层对规则配置进行转换为 Nrules 能够接受的规则描述。联系前文的计算机制,我们可以得到这样一个描述模型:

public class RuleDefinition { /// <summary> /// 规则的名称 /// </summary> public String Name { get; set; } /// <summary> /// 约束条件 /// </summary> public List<LambdaExpression> Conditions { get; set; } /// <summary> /// 执行行动 /// </summary> public List<LambdaExpression> Actions { get; set; } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxppx.html