热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于电商中复杂促销手段的一个解决思路规则表达式

2019独角兽企业重金招聘Python工程师标准一般来说,只要是面向零售,不管是B2C,C2C还是B2B2C模式,商城

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

一般来说,只要是面向零售,不管是B2C,C2C还是B2B2C模式,商城的促销模式都是差不多的,比如满就送,满x减x,满x赠x,满x折扣x,组合销售,优惠券等等。所有促销活动都有明确的起止周期,超期后活动自动关闭。

个人理解电商行业的促销行为有如下几个特点

手段灵活多变,而且多种多样。  
促销的主体是运营商和商家。  
经常根据营销策略动态调整  
促销必须定义准确,不能存在歧义  
促销可以组合应用,甚至多种促销手段存在依存关系或互斥关系

因此,最好用一种灵活可变的方式来设计促销机制。

我考虑的是用表达式引擎实现,非常灵活,最重要是很简单。

首先设计之初要理清促销的两大要素,一个是条件,另一个是资源,条件是促销规则成立的基础,资源是促销规则所要分配的内容,比如红包,金额,包邮之类。

条件主要包括

地域,组织,会员生日月份,会员生日,会员积分,会员等级,下单时间,整点,金额,件数,指定商品,下单日期/时间,定时(用做商品秒杀),限定数量,限定金额,商品组合等

资源主要包括

赠送现金
扣减费用
免单 
免邮费 
成为会员 
送积分xx
送红包
送商品

表达式中可以插入java对象/方法,如某店搞活动,活动规则为

1.满100返10块,最高上限200元 
2.晚上10点之后半价 
两个规则不能同时成立

规则计算的返回值一定是一组结果(比如金额,红包,优惠券,减免费用额度等)。同时多个规则之间也存在着互斥的因素,比如以上的两个规则就存在互斥。

促销规则的思路基本上是检查条件并生成结果,如果有多个规则,按照定义先后顺次执行,最终运算所得结果做为最终促销规则应用之后的费用计算和买家返利的依据。

目前考虑把整个规则分解成两类对象

条件:一旦条件达成就执行规则,比如整点秒杀,超过200元等。  
资源:资源类似结果,比如减免10元钱,包邮等。

规则体系需要具有如下特点

易于维护:以类似Excel公式的形式编写的一组公式,确保让一些不会编程但熟悉excel公式的人可以马上上手使用,即使不会excel公式也不要紧,因为非常简单。  
规则可以累加:可以存在多条规则,规则会按定义顺序依次执行,如果符合规则1又符合规则2,那么就同时享有两个规则所定义的资源。  
规则可以互斥:可以设定规则的互斥特性,一个规则生效后,另一个规则就无法执行,确保不能同时享有多个规则所定义的资源。

通过以上规则体系可以较好的解决目前商城中促销行为需要硬编码或者以数据库方式定义规则又不够灵活的问题。

下面举例说明,假设某店庆活动搞了一次促销,从本月1日到15日有效

满100赠10块,并且上限500封顶,会员享受半价并且会员不享受满赠

要实现这个促销规则就要先定义两个促销规则,预设规则如下

预设规则定义 说明
public booelan f_满赠(boolean condiation, Resource res1, Resource res2);满xxx元就送xxxx,送的东西可以是钱/积分/商品/运费/红包等
public f_会员优惠(boolean condiation, Resource res1, Resource res2);针对会员优惠的规则

另外还要设置一些内置函数,实现获取数据,变更金额和资源等功能,如下所示

预设函数定义 说明
public void f_满赠(boolean condiation, Resource res1, Resource res2) throws Exception;
满xxx元就送xxxx,送的东西可以是钱/积分/商品/运费/红包等。
public Resoure 当前结算金额()当前订单的结算金额,这个结算金额会根据规则的执行而变化,比如应付款是100,规则1是满100减1元,执行了规则1之后,当前结算金额就变成了90
public Resource 结算总金额();订单的结算总金额,这个金额不会随着规则的执行而变化
public Resource 减价(double money);对结算金额进行扣减,扣减后当前结算金额会有变化
public Resource 无优惠();不会对当前结算金额有影响,也不会对当前的任何资源有影响
public void f_会员优惠(boolean condiation, Resource res1, Resource res2) throws Exception;针对会员优惠的规则
public boolean 是否会员(User current_user)判断是否是会员,传入的参数是当前用户
public User 当前用户();返回订单指向的购买者
public boolean 上一规则条件()返回上一条规则是否执行成功,如果当前正在执行的就是第一条规则,那么就返回true。这个函数主要是用来处理和判断规则互斥用的
public List 资源集合();执行完成之后的资源列表,其中的资源主要是指非现金的资源,比如红包,积分,礼品等.此方法是只读的。

下面是最终的规则表达式

f_满赠(金额()>&#61;100 and 金额()<500,减价(金额()/100*10),无优惠())&#xff1b;  
f_会员优惠((not 上一规则条件()) and 是否会员(当前用户()), 减价(金额()/2),无优惠())

从上表中可以看出分别定义了两个规则&#xff08;用分号隔开&#xff09;&#xff0c;当f_满赠成立后&#xff0c;f_会员优惠就不执行了&#xff0c;两个规则是互斥的。

至于规则的生效时间&#xff0c;可以加入到表达式里面&#xff0c;也可以在规则执行前后检查&#xff0c;个人认为时间条件是通用需求&#xff0c;所有表达式规则都有时间条件&#xff0c;完全可以在规则执行之前检查&#xff0c;而且这样做性能也略好一点。

这组规则表达式执行完成之后&#xff0c;当前结算金额()&#xff1a;也就是买方的应付金额。 资源集合():就是订单的赠品

这个思路应该问题不大&#xff0c;难点在于找个什么表达式引擎能够进行表达式计算&#xff0c;并且支持绑定java实例和方法&#xff0c;但又要阻止在脚本里调用其它的java类&#xff0c;只能调用自己预置的函数。

表达式引擎可以使用jdk自带Javascript引擎&#xff0c;不过它的缺点是可以调用java类&#xff0c;只是不太好屏蔽掉对java类的反射&#xff0c;无法阻止在表达式里面创建java类和对象&#xff0c;总觉得不安全也不放心&#xff0c;万一有人利用这一点&#xff0c;用你的表达式脚本搞点破坏就麻烦了。

也可以使用其它的脚本引擎比如beanshell&#xff0c;这东西是开源的&#xff0c;可以修改代码禁用掉对java类的反射。

当然还有别的一堆&#xff0c;可以从github上找找。

下面给出一部分使用ScriptEngine的代码&#xff0c;此段代码仅仅用来阐明思路&#xff0c;因为不能有效阻止js里反射调用java类&#xff0c;因此并不适合做表达式引擎。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;public class Demo1 {public static abstract class Resource{public abstract String getType();}public static class MoneyResource extends Resource{private double money;public MoneyResource(double money){this.money &#61; money;}public double getMoney(){return money;}public String getType(){return "money";}}public static class Rules{private Functions func;private List resources &#61; new ArrayList(200);private double curMoney;//当前结算金额&#xff0c;这个值会随着规则的应用而变化private double totalMoney;//结算总金额public double getCurMoney() {return curMoney;}public void setCurMoney(double curMoney) {this.curMoney &#61; curMoney;}public double getTotalMoney() {return totalMoney;}public void setTotalMoney(double totalMoney) {this.totalMoney &#61; totalMoney;}public List getResources(){return Collections.unmodifiableList(resources);}public void setFunc(Functions func){this.func &#61; func;}public void f_满赠(boolean condiation, Resource res1, Resource res2) throws Exception{//......if (!condiation) return;if ("money".equals(res1.getType())){curMoney &#43;&#61; ((MoneyResource)res1).getMoney();}else{resources.add(res1);}}public void f_会员优惠(boolean condiation, Resource res1, Resource res2) throws Exception{//功能和f_满赠类似就不写了}}public static class Functions{private Rules rules;public Functions(Rules rules){this.rules &#61; rules;}public Resource 当前结算金额(){}public Resource 结算总金额(){}public Resource 减价(double money){}public Resource 无优惠(){}public boolean 是否会员(User current_user){}public User 当前用户(){}public boolean 上一规则条件(){}public List 资源集合(){}}public static void main(String[] args) throws ScriptException {ScriptEngineManager engineManager &#61; new ScriptEngineManager();ScriptEngine engine &#61; engineManager.getEngineByName("js");Rules rule &#61; new Rules();Functions func &#61; new Functions(rule);rule.setCurMoney(200);rule.setTotalMoney(200);engine.put("r", rule);engine.put("func", func);engine.eval("r.f_满赠(func.金额()>&#61;100 && func.金额()<500,func.减价(func.金额()/100*10),func.无优惠())&#xff1b;  "&#43;"r.f_会员优惠((not func.上一规则条件()) && func.是会员(func.当前用户()), func.减价(func.金额()/2),func.无优惠())");System.out.println(rule.getCurMoney());System.out.println(rule.getResources());}

上面这段代码只是为了说明过程代码并不能直接运行&#xff0c;而且表达式和先前定义略有出入&#xff08;主要是为了适应js引擎做的调整&#xff0c;思路并没有什么变化&#xff09;。

由于编写规则表达式的时候相对容易出错&#xff0c;特别是经验不足的人更是如此&#xff0c;因此建议添加表达式校验&#xff0c;代入数据试算功能来验证公式是否正确。


转:https://my.oschina.net/jim19770812/blog/300556



推荐阅读
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 安卓select模态框样式改变_微软Office风格的多端(Web、安卓、iOS)组件库——Fabric UI...
    介绍FabricUI是微软开源的一套Office风格的多端组件库,共有三套针对性的组件,分别适用于web、android以及iOS,Fab ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文总结了Java中日期格式化的常用方法,并给出了示例代码。通过使用SimpleDateFormat类和jstl fmt标签库,可以实现日期的格式化和显示。在页面中添加相应的标签库引用后,可以使用不同的日期格式化样式来显示当前年份和月份。该文提供了详细的代码示例和说明。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
author-avatar
乱七八糟的孤岛_217
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有