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

由于 Nrules 支持流式声明,所以约束条件和产生的结果都可以用 LambdaExpression 表达式实现。现在我们需要把阶梯打折的配置转换成规则描述,那我们需要先分析一下。假设满一件9折,满两件8折,满三件7折,那我们可以将其分解为:

大于等于三件打 7 折

大于等于两件且小于三件打 8 折

大于等于一件且小于两件 9 折

基于此分析,我们可以看出,只有第一个最多的数量规则是不一样的,其他规则都是比前一个规则的数量小且大于等于当前规则的数量,那么我们可以这样转换我们的规则配置:

List<RuleDefinition> BuildLadderDiscountDefinition(LadderDiscountPromotion promotion) { var ruleDefinitions = new List<RuleDefinition>(); //按影响的数量倒叙 var ruleLimits = promotion.Rules.OrderByDescending(r => r.Quantity).ToList(); var currentIndex = 0; var previousLimit = ruleLimits.FirstOrDefault(); foreach (var current in ruleLimits) { //约束表达式 var conditions = new List<LambdaExpression>(); var actions = new List<LambdaExpression>(); if (currentIndex == 0) { Expression<Func<Order, bool>> conditionPart = o => o.GetRangesTotalCount(promotion.ProductIdRanges) >= current.Quantity; conditions.Add(conditionPart); } else { var limit = previousLimit; Expression<Func<Order, bool>> conditionPart = o => o.GetRangesTotalCount(promotion.ProductIdRanges) >= current.Quantity && o.GetRangesTotalCount(promotion.ProductIdRanges) < limit.Quantity; conditions.Add(conditionPart); } currentIndex = currentIndex + 1; //触发的行为表达式 Expression<Action<Order>> actionPart = o => o.DiscountOrderItems(promotion.ProductIdRanges, current.DiscountOff, promotion.Name, promotion.Id); actions.Add(actionPart); // 增加描述 ruleDefinitions.Add(new RuleDefinition { Actions = actions, Conditions = conditions, Name = promotion.Name }); previousLimit = current; } return ruleDefinitions; } 2.4 生成规则集合

在 Nrules 的 wiki 中,为了实现运行时加载规则引擎,我们需要引入实现 IRuleRepository ,所以我们需要将描述模型转换成 Nrules 中的 RuleSet:

public class ExecuterRepository : IRuleRepository, IExecuterRepository { private readonly IRuleSet _ruleSet; public ExecuterRepository() { _ruleSet = new RuleSet("default"); } public IEnumerable<IRuleSet> GetRuleSets() { //合并 var sets = new List<IRuleSet>(); sets.Add(_ruleSet); return sets; } public void AddRule(RuleDefinition definition) { var builder = new RuleBuilder(); builder.Name(definition.Name); foreach (var condition in definition.Conditions) { ParsePattern(builder, condition); } foreach (var action in definition.Actions) { var param = action.Parameters.FirstOrDefault(); var obj = GetObject(param.Type); builder.RightHandSide().Action(ParseAction(obj, action, param.Name)); } _ruleSet.Add(new[] { builder.Build() }); } PatternBuilder ParsePattern(RuleBuilder builder, LambdaExpression condition) { var parameter = condition.Parameters.FirstOrDefault(); var type = parameter.Type; var customerPattern = builder.LeftHandSide().Pattern(type, parameter.Name); customerPattern.Condition(condition); return customerPattern; } LambdaExpression ParseAction<TEntity>(TEntity entity, LambdaExpression action, String param) where TEntity : class, new() { return NRulesHelper.AddContext(action as Expression<Action<TEntity>>); } } 2.5 执行规则引擎

做了转换处理仅仅是第一步,我们还必须创建一个规则引擎的处理会话,并把相关的事实对象(fact)传递到会话,执行触发的代码,相关对象发生了变化,其简单代码如下:

var repository = new ExecuterRepository(); //加载规则 repository.AddRule(new RuleDefinition()); repository.LoadRules(); // 生成规则 ISessionFactory factory = repository.Compile(); // 创建会话 ISession session = factory.CreateSession(); // 加载事实对象 session.Insert(new Order()); // 执行 session.Fire(); 2.6 应用场景示例

我们假设有这么一个应用入口:传入一个购物车(这里等价于订单)id,获取其可以参加的促销活动,返回对应活动优惠后的结果,并按总价的最低依次升序,那么可以这么写:

public IEnumerable<AllPromotionForOrderOutput> AllPromotionForOrder([FromQuery]String id) { var result = new List<AllPromotionForOrderOutput>(); var order = _orderService.Get(id) ?? throw new ArgumentNullException("_orderService.Get(id)"); var promotionGroup = _promotionService.GetActiveGroup(); var orderjson = JsonConvert.SerializeObject(order); foreach (var promotions in promotionGroup) { var tempOrder = JsonConvert.DeserializeObject<Order>(orderjson); var ruleEngineService = HttpContext.RequestServices.GetService(typeof(RuleEngineService)) as RuleEngineService; ruleEngineService.AddAssembly(typeof(OrderRemarkRule).Assembly); ruleEngineService.ExecutePromotion(promotions, new List<object> { tempOrder }); result.Add(new AllPromotionForOrderOutput(tempOrder)); } return result.OrderBy(i => i.Order.GetTotalPrice()); }

假设这么一个购物车id,买一件时最优惠是参加 A 活动,买两件时最优惠是参加 B 和 C 活动,那么其效果图可能如下:

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

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