版权声明:本文为博主原创文章,未经博主容许不得转载。关注公众号 技术汇(ID: jishuhui_2015) 可联系到做者。数据库
优惠券系统的核心在于各类券种的管理,发放和使用。编程
一般的设计角度是从终端用户出发,所谓“所见即所得”,终端用户所见到的形形色色的优惠券,正是开发整个系统的挑战所在。app
能够想象,为了配合不一样形式的线上、线下活动,优惠券系统势必有较大的改动,如何最大限度的下降改动的成本,成为了最核心的挑战。框架
就上述问题而言,解决的方法就是:规则与执行相隔离。编程语言
规则层,便是各种优惠券的使用限制,以及能达到的效果。ui
执行层,能够理解为根据规则计算最终成交价格。设计
对于规则层,笔者并不想将其上升到规则引擎的高度,只作适当的抽象。何况市面上的规则引擎开发框架未必能知足优惠券这一复杂的应用场景。cdn
值得注意的是,优惠券不等同于任何形式的优惠促销活动,但倒是整个营销系统的一部分。相较于优惠券,促销活动系统的实现还要更困难。本文旨在阐述怎么开发一个优惠券系统,促销活动的设计与实现不在本文的范畴了。对象
在生成优惠券以前,咱们先来了解一下『优惠券模板』的概念。blog
顾名思义,按照模板去生成优惠券。
就比如工业生产流水线上的模具,只需注入原材料进行加工,便可制成成品。
所以,模板上携带了大量优惠券相关的信息,包括但不限于名称、时效性、各种优惠规则,以及优惠券面额等,模板自己也有标题,状态,建立人等基本信息。
当须要生成优惠券的时候,指定是哪套模板,而后填写准备要生成的券总数便可,亦可在生成券的环节,指定接收对象,将券的派发操做一并完成。
可能读者不由会发问了:这样设计彷佛有些多余,券模板和券实体重合的属性不少,何不直接跳过券模板的环节,直接生成优惠券呢?
这样作的缘由有两个:
其一,不想重复输入相同的优惠券信息,只要输入一遍模板信息,便可实现批量生成券,能提升运营人员的工做效率;
其二,建立券模板和发送券实体是有两类不一样权限的人完成的。可能更高权限的运营人员能掌握着建立模板的权限,普通的运营人员则只须要按模板发优惠券便可。
以下图,可反映出券模板和券实体的关系:
上图涉及的内容基本上都能一目了然,接下来咱们将重点放在优惠规则的设计上。
优惠规则无疑是优惠券系统的核心部分,能不能最大限度的适应市场运营需求,就看优惠规则实现得怎么样了。
显然,用穷尽法去实现市面上的优惠规则是不明智。
正所谓“世上惟一不变的就是变”,咱们必须提炼出优惠规则的本质,才能“以不变应万变”。
笔者对市面上常见的优惠规则进行了调研,并结合自身业务需求,能够将优惠规则大体分红两大类:
一、计算型规则。形如“无门槛直降20元”,“满xx减xx”等,这些规则都暴露给终端用户,是显而易见的,让用户知晓这个优惠券是如何参与计算。
二、限制型规则。相较于计算型规则,限制型规则大多数状况下是隐性的。好比:限制某些用户领取,限制某套券模板最多能发放多少金额的优惠券,限制优惠券的渠道等。这类规则能够很好的支撑平常的运营工做。
“万物相生相克”,众多的优惠规则,也并非都能共存的,这里就引入了规则『互斥性』的概念。当一个优惠规则与另外一个优惠规则不能同时存在于一套模板里的时候,咱们就认为这两个规则是互斥的,这在设计规则的时候也须要有所考虑。
其次,优惠规则也会讲究前后顺序,因此必然就带来了一个优惠规则的『优先级』属性,咱们约定,数字越小,表示越优先,也就是按从小到大的顺序。
如下给出一张完整的规则表,信息量较大,笔者将作必要的解释。
规则表中,渠道限制、对象限制、金额限制和数量限制四者皆属于限制型规则,优先级排在了较前面。同时,也给出了参数说明,规则描述,相对于模板的关系以及互斥规则,这些都是一目了然的。
接下来的扣减规则和封顶规则同属于计算型规则,也算是优惠券的重中之重了。
笔者着重解释一下满减规则中的“阶梯满减”。咱们日常会看到有这类说法:每满100元减10元,言下之意即是:满100元减10元,满200减20,以此类推,笔者将其称之为“阶梯满减”规则。
规则表中有涉及好多数字,笔者设计了一套生成规则。
规则编号是int型,Java 编程语言中,int全长 32位,以下图所示:
一、第一位是符号位,固定为0,且不容许出现 32位全是0的状况,即为正整数;
二、高8位是规则组别编号,理论上容许的数值范围是0~255,可是实际的业务规则是假设最多有15组优惠规则,每组优惠规则编号取10的倍数,范围即为 10~150;
三、第10位和11位做为备用,暂无实际用处,固定为00;
四、中间15位,存放规则组下的细则编号,容许的范围0~32767,可是实际业务规则是要达到两两互斥的目的,取值以下(以四位二进制为例):
0001
0010
0100
1000
结论:排除全为0的状况,那么有N位,就有N组两两互斥。若是组内组外互斥都考虑,那么可取值就更少了;
五、末6位存放规则组的优先级,容许的值范围是0~63,实际取值从1开始,考虑到以后会插入其余的规则组,会在每两个规则组别直接预留两个级别,初始的优先级设置为1,4,7,10,13,16…;
六、按照上述规则,根据既定的组别和优先级能够生成上表中的细则编号。
渠道限制 83888577
0 00001010 00 000000000100111 000001
用户类型限制 167775300
0 00010100 00 000000000110001 000100
指定用户限制 167776900
0 00010100 00 000000001001010 000100
总金额限制 251660487
0 00011110 00 000000000100011 000111
单位金额限制 251662535
0 00011110 00 000000001000011 000111
总量限制 335545290
0 00101000 00 000000000001111 001010
我的所获限制 335544778
0 00101000 00 000000000000111 001010
无门槛直减 419430989
0 00110010 00 000000000001001 001101
满减 419431565
0 00110010 00 000000000010010 001101
打折 419436813
0 00110010 00 000000001100100 001101
封顶规则 503323024
0 00111100 00 000000001100110 010000
在作程序设计以前,咱们必须把握住关于优惠券的三个主要动做:
一、管理。指的是对券模板的建立,规则设置,关闭等操做。固然,在给模板设置规则参数的时候,须要校验规则参数的合理性。
二、派发/领取。笔者将这两个操做进行了合并,二者的区别无非就是有没有绑定终端用户,在接口层面是能够合并的。一般,限制型规则会在此发挥做用,当触发了这两个操做的其中一个,在生成优惠券以前都将会先用限制型规则进行校验。
三、使用。就是将优惠券花出去,计算型规则会在此发挥做用,主要是判断满不知足券的使用条件,计算减免金额等。
综上,程序设计就靠这三个动做进行延展。
咱们先来设计一下规则层。
根据规则表中定义的一系列规则,反映在程序中能够有两种形式:
一种是使用配置文件(一般是XML),而后程序去解析;
另外一种则是直接使用枚举类(或者其余形式的类)。
第一种开发难度稍大,想作得比较强大的话,是须要花费很多功夫的。
对于优惠规则的定义和实现都是事先内置在程序中的,并非非专业人员改变一下配置文件就能达到效果的。
因此基于此考虑,使用了第二种实现方式。以下所示即为规则定义:
Rulers含有五种重要的属性:规则编号(ruleNo),规则名称(ruleName),排序(sort),对应的Model Class对象,以及实现特定接口的serviceName。
接下来的三个方法也较为重要:经过规则编号直接找到对应的枚举,判断是不是计算型规则,规则是否有多参数,和券模板是1~*的关系就表示是多参数。
固然,也少不了持久化到数据库的各种Beans,每一个Bean都会继承一个叫做SuperRule的抽象类,子类的属性都是规则表中说起到的。Rulers中的Model Class就是来自于此。因为规则较多,下面只展现SuperRule:
由于将规则分红了两大类,天然而然的就能派生出两个Service,分别对应计算型规则和限制型规则。
最后一个是对规则参数的校验,使用了泛型R,继承自SuperRule。
至此,接口的设计就囊括了上述所说的三种优惠券相关的操做,也就意味着,若是出现了一个新的规则,至多实现上述的三个Service(由于可能有既是限制型规则,又是计算型规则),不过大多数状况只要实现两个Service,而后在Rulers配置好此新规则便可。至于规则基本的CRUD操做,只要继承AbstractCouponRuleService,不须要额外花精力去实现。
这么多规则,天然是须要一个『工厂类』进行调度的,好比生成一个规则Bean实例,生成一个Service实例。
本文从业务设计,到程序设计,对优惠券系统作了一个比较全面清晰的阐述,若是读者正好也须要研发一个优惠券系统,这篇文章将会是很好的参考。