作者:Tom哥
公众号:微观技术
博客:https://offercome.cn
人生理念:知道的越多,不知道的越多,努力去学
很多网站都有优惠券业务,但是剖析优惠券架构的技术文章却很少,优惠券看似简单,但内部涉及用户、商品、平台、店铺等综合维度下各种营销玩法,复杂度一下会提高很多。所以写了这篇文章来看看优惠券是如何技术架构的。
IT 界有句名言,”talk is cheap, show me the code“。废话不都说,开始进入正题
我们先简单了解下优惠券都包含哪些重要信息,做了个优惠券信息的思维导图
优惠券创建完成后,接下来就是透出、使用。透出涉及的位置很多,比如活动会场、开机屏,但流量最大的还是在商品详情页。
而使用主要体现在下面几个环节:优惠券领取、下单时优惠券查询、核销等。另外还有一些周边的附加操作,比如优惠券的过期处理、数据统计、用户券包展示等。接下来对于一些核心操作,我们重点剖析如何技术设计。
商品详情、产品介绍是几乎每个产品运营、营销推广人员都会面对的工作。作为产品对外的传播物料,直接影响到了产品的转化,若是电商渠道更是直接影响产品销售额。
商品详情除了商品图片、标题、SKU规格、库存、价格、成交记录、评价、商品描述等信息外,还有一块非常重要的信息,那就是优惠券。如下图所示,会提示商品可用的优惠券,并引导用户领取,并促成下单交易。
优惠券设置除了基本信息外,还有适用的商品列表,但单独存储在优惠券的关联商品表中。ER模型如下图所示
商品详情页查找优惠券,只需要按item_id反查商品表即可,就可以查到所有配置到该商品的优惠券。由于是通用页面,所以无视用户登录属性,所有人看到的页面都是一样。具体的校验规则(比如有些券只能新人领取)会放在用户”点击领取“时做逻辑控制校验。
逻辑看似简单,但对于一个电商网站而言,商品详情页的访问量通常是比较大的,如何支持高并发访问,我们一般会借助缓存,有专门的”优惠券同步中心“,将运营发布的优惠券按商品维度维护到Cache中,采用空间换时间的方式,提前预热好数据,这样当商详页访问时只需要查缓存即可,可以支持比较高的QPS。
有人可能会继续问,优惠券有自己的生命周期,如果券过期了怎么维护缓存的数据一致性。因为商品详情需要查优惠券的详细信息,所以接口内部会引入stream流的 filter机制,会对优惠券的可用时间做校验计算,将失效的券过滤掉。另外,我们会发一个优惠券过期的消息,由”优惠券同步中心“接受MQ消息对缓存中已经失效的优惠券做清理。
优惠券并不是每个人都可以领取的,有很多限制条件,比如”有些优惠券只有新人可以领“,”有些优惠券一天只能领一张“,”有些券需要完成任务或达到一定门槛才可以领“,等等。看似复杂,但”物以类聚人以群分“,我们可以采用相似聚合、分层来处理,一切都简单很多。
我们简单看下优惠券领取的流程图:
优惠券的领取,逻辑相对简单些,需要关注券模板、领取记录,用户标签等几个维度校验。如果满足条件就可以给用户发放优惠券。
券模板校验:
优惠券不是随便乱发的,毕竟要计入公司的支出成本,一旦涉及到钱的问题便是个严肃的问题。定位好用户群体,什么样的优惠券发给什么类型的用户才能获得最大收益。所以我们在创建优惠券时,除了设置适用的商品范围,还要限制优惠券的用户类型。人尽其用,物尽其才。
适用商品范围一般在页面展示或下单时才用到。而用户类型则相反,需要前置控制,也就是说在用户领取时校验,一旦用户领取成功就属于用户个人财产,只要没有过期都可以使用。
用户领取记录校验:
用户标签校验:
技术挑战:
另外优惠券计算接口,也会返回不可用的优惠券,并标明不可用原因,引导用户调整下单商品。
用户对勾选的商品(也可能是购物车批量商品下单)创建订单时,会计算有哪些优惠券可以使用。
如上图所示,页面输出信息很简单,只显示可以当前可用且按优惠力度排序的优惠券,但如何计算出满足当前订单的优惠券,后台涉及哪些复杂业务逻辑,又会遇到哪些技术难点挑战,下面来我们来分析下。
整体的架构思想还是采用过滤机制,首先查出用户下所有的优惠券,然后根据优惠的各种标记位做相应的策略处理。
渠道过滤:
上面的图是多点APP的券包截图,该券限制使用条件必须是”多点配送(到家可用)“,如果是”门店自提“则订单无法使用该优惠券。所以我们可以清楚判断,优惠券请求接口势必要传入配送方式参数。
下单时计算可用的优惠券,首要一条就是判断订单中的商品是否在优惠券的商品范围里。常见的商品作用范围有哪些:
上图优惠券就是商家券,除了”商家券“的标记外,还会设置商家的id,也就是下单的商品中属于这家的商品才会累计校验金额门槛。
优惠券一般都有适用的商品范围,可作为一个独立页面对外呈现。下图是商家券的详情页,同时提供了”推荐“、”热销“、”价格“,三个维度的排序。
一般这种玩法都是借助搜索来技术实现的,创建优惠券模板后,会以商品的维度将数据同步ES中,索引结构:
{
"properties" : {
"couponId" : {
"type" : "long"
},
"itemId" : {
"type" : "long"
},
"sort" : {
"type" : "long"
},
"salesCount" : {
"type" : "long"
},
"salePrice" : {
"type" : "long"
},
"gmtCreated" : {
"type" : "date",
"format" : "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"gmtModified" : {
"type" : "date",
"format" : "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
至于搜索数据的维护,尤其是商品销量定时写入,这些都是些常规的业务实现,这里就不一一累述了。
用户的卡券包实现很简单,只需分页查询用户的券表即可,加上过滤条件”用户券的状态要是未使用“。如果是社区电商,一般有区域限制,券列表展示会根据当前区域做判断,如果当前区域不可用会置灰。