阿里开源规则引擎QLExpress-入门实战
介紹
規則引擎,顧名思義是針對我們業務系統中普世的規則進行統一管理,通過該引擎進行調度計算,可以動態調整規則的表達式內容,而不影響業務系統代碼,常見的業務典型場景有電商中促銷活動,單品折扣、整場活動滿減或滿折
規則引擎
常用的規則引擎目前主要有Drools、Aviator、Easyrule、QLExpress,如下表格是對這些規則引擎的分析對比:
結合本人實際項目,我們的項目業務屬性是電商,對性能要求比較高,因此發現阿里開源的QLExpress更適合現實的訴求,下面的章節將以電商促銷活動的例子介紹QLExpress如何落地實操到業務代碼中的
電商促銷活動示例
比如我們的購物車現在有兩個商品,對應的商品SKU分別為11120781和11120782,商品11120781參與的單品促銷優惠活動,11120782參與的是單品促銷打折活動,假設這兩個商品來自同一個店家,店家給的運費優惠是滿100減50關于兩個活動規則的介紹如下:
單品促銷優惠
該活動是在商品價格基礎上優惠xxx元,比如商品原價為150,促銷優惠50元,則優惠后的金額為150-50=100
單品促銷打折
該活動是在商品價格基礎上進行折扣,比如商品原價為150,促銷打折5折,則優惠后的金額為150*0.5=75
第一步 引入依賴包
如下圖所示,引入QLExpress的包
<dependency><groupId>com.alibaba</groupId><artifactId>QLExpress</artifactId><version>3.2.0</version></dependency>編寫針對某一類活動規則的業務接口
此接口主要處理同一類型規則的邏輯,以上述的單品促銷為例,代碼示例如下:
/*** 查詢商品促銷活動匹配單品促銷規則計算后的優惠金額** @param promotionList 參與的促銷活動* @param productPrice 商品價格* @return*/private List<Double> querySinglePromotionPrice(List<Promotion> promotionList, double productPrice) {List<Double> nicePrices = Lists.newArrayList();DefaultContext<String, Object> context = new DefaultContext<>();ExpressRunner expressRunner = new ExpressRunner();promotionList = promotionList.stream().filter(r -> "singlePromotion".equals(r.getPromotionType())).collect(Collectors.toList());for (Promotion promotion : promotionList) {//查詢促銷活動對應的規則模版配置信息RuleTemplate template = queryRuleTemplate(promotion.getPromotionType());String express = template.getExpress();context.put("price", productPrice);context.put("promotionType", promotion.getPromotionWay());context.put("promotionValue", Double.parseDouble(promotion.getPromotionValue()));System.out.println("參與促銷活動【" + promotion.getPromotionName() + "】優惠前的商品金額:" + productPrice);Object execute = null;try {execute = expressRunner.execute(express, context, null, true, true);} catch (Exception e) {System.out.println("計算規則[" + template.getTemplateName() + "]時發生異常,原因:" + e.getMessage());}System.out.println("優惠后的金額:" + execute);nicePrices.add((Double) execute);}return nicePrices;}涉及到的運費計算,代碼示例如下:
private double queryMinShippingFree(List<Promotion> promotionList,double productPrice,double freeShipping ){DefaultContext<String, Object> context = new DefaultContext<>();double minShipping = 0.0;List<Double> niceShipping = Lists.newArrayList();ExpressRunner expressRunner = new ExpressRunner();promotionList = promotionList.stream().filter(r -> "freeShipping".equals(r.getPromotionType())).collect(Collectors.toList());for (Promotion promotion : promotionList) {RuleTemplate template = queryRuleTemplate(promotion.getPromotionType());String express = template.getExpress();String condition = promotion.getConditionConfig();context.put("price", productPrice);context.put("amount", Double.parseDouble(JSONObject.parseObject(condition).getString("amountCondition")));context.put("shippingPrice",freeShipping);context.put("promotionValue", Double.parseDouble(JSONObject.parseObject(condition).getString("promotionAmount")));System.out.println("參與促銷活動【" + promotion.getPromotionName() + "】優惠前的運費:" + freeShipping);Object execute = null;try {execute = expressRunner.execute(express, context, null, true, true);} catch (Exception e) {System.out.println("計算規則[" + template.getTemplateName() + "]時發生異常,原因:" + e.getMessage());}niceShipping.add((Double) execute);}if(niceShipping.isEmpty()){return minShipping;}minShipping = Collections.min(niceShipping);System.out.println("優惠后的運費:" + minShipping);return minShipping;}封裝以上兩個方法,輸出購物車中的最終優惠金額,代碼示例如下:
/*** 計算最優促銷活動價格** @param cart 購物車信息* @param shippingFee 運費* @return 計算出最優價格* @throws Exception*/@Overridepublic double queryMinPromotionPrice(String cart,double shippingFee) {JSONArray cartArray = JSONObject.parseArray(cart);double sum = 0.0,sumNicePrice = 0.0,sumNiceShipping = 0.0;for(int i=0;i<cartArray.size();i++){double minNicePrice = 0.0d;String productCode = cartArray.getJSONObject(i).getString("productCode");double productPrice = cartArray.getJSONObject(i).getDoubleValue("price");//查詢商品參與的促銷活動List<Promotion> promotionList = queryPromotion(productCode);sum += productPrice;//根據促銷活動匹配的規則計算出優惠價格List<Double> nicePrices = querySinglePromotionPrice(promotionList, productPrice);//計算出最便宜的優惠價格minNicePrice = Collections.min(nicePrices);sumNicePrice +=minNicePrice;//運費double niceShipping = queryMinShippingFree(promotionList,productPrice,shippingFee);sumNiceShipping+=niceShipping;}sum = sum+shippingFee;System.out.println("參與促銷之前購物車結算價格為:"+sum);sumNicePrice = sumNicePrice+sumNiceShipping;System.out.println("參與促銷之后購物車結算價格金額:" + sumNicePrice);return sumNicePrice;}暴露給外部的http接口,代碼示例如下:
@PostMapping("/queryMinPromotionPrice")public double queryMinPromotionPrice(String cart,double shippingFee) {System.out.println("開始獲取商品優惠金額");long startTime = System.currentTimeMillis();double minPrice = expressService.queryMinPromotionPrice(cart,shippingFee);System.out.println("獲取商品優惠金額結束,耗時:"+(System.currentTimeMillis()-startTime)+"毫秒");return minPrice;}以上的這行代碼 RuleTemplate template = queryRuleTemplate(promotion.getPromotionType());是代表規則表達式的模版,可以配置在數據看中,如下圖所示:
以上代碼,通過postman或者insomnia工具,執行結果如下圖所示:
執行過程打印log信息如下圖所示:
總結
以上是生活随笔為你收集整理的阿里开源规则引擎QLExpress-入门实战的全部內容,希望文章能夠幫你解決所遇到的問題。