目录
盲盒是什么
盲盒(Blind Box):就是装着不同的手办或玩偶的一个严密盒子,消费者购买前无法通过外观分辨盒子里面的东西,只有打开才会知道自己买到了什么。盲盒一般分普通款与隐藏款,抽到隐藏款血赚。盲盒没打开之前,不知道里面到底是什么,因为结果的不确定性,使整个过程充满神秘感,这种猎奇心理,让人兴奋,满怀期待,容易上瘾,不断的买买买。
为了避免用户抽盲盒上瘾,尤其在有很多低龄用户的情况下,有必要按一定维度对用户做下单额度的限购。
防沉迷维度
用户身份
盲盒订单来自多个渠道,包括不限于官网、微信小程序、QQ小程序、推广商,甚至公司旗下有很多个子App,各个渠道的账号体系不一定是互通的,同一个玩家可能拥有多个账号。如果账号是互通的,并且拥有用户身份证情况下可以通过身份证去汇总单个用户下单金额,但身份证是用户敏感信息,不一定能获取得到。需要有一种通用的方式对同一个用户进行额度限制。对订单系统来说确定单个用户身份有以下几种方案。
唯一标志 | 优点 | 缺点 |
用户ID | 实现简单,一个用户一个ID | 一个用户可以注册多个账号,不同平台用户ID不一样 |
手机号 | 当前平台一个手机号只允许注册一个账号 | 一个用户可能拥有多个手机号 |
身份证 | 能够确认用户身份唯一 | 用户敏感信息,不一定有,订单系统不一定有权限获取到 |
支付Open ID | 由于支付渠道来自公司旗下SDK,不管是微信、支付宝,用户拥有一个支付Open Id | 用户拥有多个支付App:微信、支付宝,不同支付App分配的Open Id不一样,还是无法确定用户唯一 |
最终选取了通过支付Open ID来作为用户身份唯一判定标准。因为经过调研,不同渠道用户基本只用一种支付方式,要么支付宝、要么微信,两种支付方式交替使用的是少数。由于支付SDK是公司支付团队做的,能够保证无论用户来自微信小程序、公司子App,用户支付Open ID基本是唯一的。而且防沉迷最核心的需求是限制单一用户的支付额度,使用支付Open ID能够最大限度防止同一个用户无脑买买买。
时间维度
按分钟,每天(截止到午夜)、每周(截止周末),每月(截止月底,不管从几号开始统计,也就是自然月)的维度去统计用户下单额度。
时间 | 汇总 | 过期时间 |
分钟 | N分钟内最多支付N元 | 超过N分钟额度恢复 |
每天 | 从凌晨0点截止到午夜23:59:59秒的支付额度 | 超过当天自动过期,额度恢复 |
每周 | 从周一到周末23:59:59的支付额度 | 超过本周自动过期,额度恢复 |
每月 | 从1号截止到月底23:59:59的支付额度 | 超过当月自动过期,额度恢复 |
基于Redis实现时间额度限制。
根据当前时间、用户支付Open ID生成Key
例如当月的key= trade_2010_10_user001,超过当月就读取新的月份的key。
基于Redis Setex原子命令设置Key Value跟超时时间,基于Redis INCR实现Value原子自增。
每次计算距离午夜,周末、月底的剩余时间,剩余时间随机加几分钟防止缓存雪崩,然后用剩余时间作为Redis Key Value的过期时间。
频率
同一个用户ID针对同一个商品,每30秒最多下一单,避免用户疯狂抽盲盒。
把用户ID跟商品ID组合起来作为唯一键,用分布式缓存Redis创建一个分布式锁,锁超时时间设置为30秒,在30秒内把同用户对同一商品的重复下单拦截掉。分布式锁的实现没有基于Redis的Redlock,我们的目的就是怎么简单怎么来,本身Redis就很稳定了,即便出现极端情况锁丢失了也无所谓。
订单统计
订单额度统计
订单额度统计要求实时性要高,并且要同业务逻辑解耦。
最终通过订阅订单binlog来实现。中间件把订单binlog推送到支持顺序消费的kafka,调用方创建一个专门监听有效订单的消费者,来汇总订单额度。这种方式优点是实时性不错,缺点是binlog消息有丢失的可能。由于额度汇总不需要达到支付交易那种严格的水平,只需要一个大致的结果就行,99.9%的消息是不会丢失的,能够满足订单额度统计需求。
有效订单统计
部分订单是用现金购买的,这种订单直接统计实际支付金额即可。
还有一部分订单是积分兑换、抽奖所得、金币兑换所得,这种需要特殊处理。
类型 | 额度统计规则 |
现金支付 | 订单额度 = 实际支付金额 |
抽奖、积分兑换等无支付场景 | 无支付金额,不计入下单额度 |
金币兑换 | 根据订单号,反查金币支付金额,例如99个金币花费10元购买,查询到实际支付金额再计入订单金额统计 |
退款 | 虽然退款了,但不撤销订单额度,额度只累加 |
部分特殊商品无支付流水 | 订单额度 = 商品总价-优惠金额 |