ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

抽赏小程序技术复盘:特殊赏玩法、并发库存与合规落地全解析

抽赏小程序技术复盘:特殊赏玩法、并发库存与合规落地全解析 先说结论抽赏小程序看着是个“转盘抽奖”的壳子实际拆开看它至少横跨了前端交互、后端调度、库存并发、支付对账、风控防刷、合规审计六块硬骨头。尤其是特殊赏比如隐藏款、保底玩法、收集兑换、概率递增这类非标准抽奖逻辑普通的“点击-随机-发放”思路根本撑不住必须从一开始就把玩法模型和架构边界定清楚否则后面每一个需求都是灾难。这篇文章不是概念科普是我把一个抽赏小程序从零到一做完之后沉淀下来的完整复盘特殊赏是怎么拆成技术方案的、架构上哪些组件是必须的、并发扣库存怎么才不出错、概率公示和未成年人保护在工程上怎么落地、以及上线后我踩过的一堆坑。适合正在做或者准备做抽赏、盲盒、扭蛋类小程序的朋友尤其是需要自己负责技术方案的人。1. 玩法定义与需求拆解先把“特殊赏”翻译成代码逻辑1.1 特殊赏到底特殊在哪里普通抽赏就是“一份钱抽一次池子里N个奖品抽中哪个给哪个”逻辑上就是一个加权随机。但特殊赏玩法的“特殊”主要体现在几类规则上隐藏款/稀有款概率极低可能要抽几百次才出一个但不能永远不出需要保底或者保底券机制。阶梯概率随着抽取次数增加中奖概率动态变化比如前50抽概率维持0.5%第51抽开始逐步提升。全套收集/兑换抽到的碎片或普通款可以累加集齐指定组合可兑换大奖这里涉及“组合判定”和“兑换库存”。指定款直购/置换抽出来的物品可以加价换购其他款式需要打通抽赏结果与订单系统的状态流转。限时活动奖池特殊赏还会叠加活动周期比如“本期卡池”与“下期卡池”切换池子里的奖品配置完全不同。这些规则单独看都不难难的是组合在一起。比如一个用户参与了“阶梯概率隐藏保底收集兑换”后端至少需要一条贯穿全局的用户抽赏状态机而不是简单地在抽奖接口里写几行随机数。1.2 用户动线设计与状态机我给这个项目画的核心用户动线是进入首页 → 浏览活动专题 → 查看奖池明细与概率公示 → 选择抽赏次数 → 确认支付 → 后端执行抽奖与库存扣减 → 返回中奖结果 → 查看我的赏品 → 触发兑换/发货/售后这条动线里至少有四个关键状态节点需要后端管理活动状态未开始、进行中、已售罄、已结束、已下架。奖池状态已配置、已发布、已暂停、已关闭。用户抽赏状态未参与、已参与、进行中保底进度、已完成。订单状态待支付、已支付、抽赏中、已开奖、待发货、已发货、已签收、已兑换、已售后。我把这四个状态机拆到了不同的服务模块里没有揉在一起。一开始图省事把活动状态和奖池状态放一张表结果运营改活动配置时经常出现“活动改了但池子没同步”的别扭情况后来全部拆开各管各的状态变更通过事件去联动。1.3 为什么一定要做小程序而不是纯H5这个项目选小程序除了流量入口的考虑工程上也有实际原因小程序支付的用户流程最顺拉起微信支付不需要跳浏览器。小程序订阅消息可以触达抽赏结果通知H5在这方面受限。分享给好友/朋友圈的能力天然适合抽赏这种“拉人一起玩”的场景。合规角度小程序平台有明确的虚拟支付、抽奖规范做了反而相当于有一层平台侧的风控和审核兜底。当然小程序的痛点也明显包体限制、渲染性能不如原生 App、不能随便跳外链。我们把页面保持轻量奖品图片全部走 CDN 懒加载核心逻辑放在后端前端只做表现层这是抽赏这种重交互场景比较稳妥的做法。2. 架构支撑从单体到模块拆分的实战取舍2.1 整体架构分层项目的最终架构是经典的“前端小程序 后端服务 管理后台 定时任务”四层小程序端微信小程序 后端服务用户、活动、抽奖、订单、支付、库存、消息 管理后台运营配置、奖池管理、订单处理、数据报表 基础组件Redis、MySQL、定时任务、消息队列、对象存储后端服务我没有一上来就上微服务而是用了模块化单体。抽赏业务阶段早期微服务的成本远大于收益分布式事务、服务治理、链路追踪这些都要时间。我用的是 Go 写的后端按业务模块拆目录模块间调用走内部接口这样既能保持单体的部署简单又为将来拆服务留了切口。Redis 是整个架构里最不能省的一环。它的用途有三个抽奖活动配置的缓存、分布式锁的载体、用户抽赏进度的缓存。MySQL 存订单、库存流水、用户资产这些强一致数据。2.2 库存模型与并发控制抽赏的核心资源是“奖池库存”。这里的库存跟普通商品库存不太一样它有几个特点奖池总库存是固定的比如一个卡池设定 500 抽抽完结束。每个奖品有单独的库存但所有奖品的库存总和要等于总抽数。隐藏款、普通款、谢谢参与这些要配置不同概率但概率之和必须为 1。库存扣减和概率抽取必须在一个原子操作里完成不能先抽完再扣库存。我用的方案是预分配库存 Redis Lua 脚本扣减。活动上线前后端把每个奖池的奖品列表、库存量、概率配置加载到 Redis抽奖的时候调 Lua 脚本完成“概率计算 库存消耗 进度记录”这一整个原子动作。Lua 脚本在 Redis 里是单线程执行的天然避免并发超卖。2.3 抽奖概率算法与保底机制这块是整个项目的技术核心我单独讲。普通加权随机的实现很简单但叠加“隐藏款保底”和“阶梯概率”就没那么简单了。我当时抽了整整一周时间跟产品对齐规则最后抽象出一套可配置的概率引擎。规则配置是放在数据库里的每期活动都可以调整。这里的难点就在保底逻辑与随机算法的结合不能破坏随机性又要保证用户的抽赏体验不能太差。保底机制我拆成了“硬保底”和“软保底”。硬保底指的是抽到一定次数必出稀有比如 300 抽必出隐藏软保底是抽到一定次数后概率开始逐步抬高。硬保底的关键是进度字段必须持久化而且只在真正触发保底的那一次抽奖中才消耗保底状态软保底则是动态概率每次抽取都重新算。这个环节很容易踩坑因为硬保底如果记录在 Redis 而不落库一旦 Redis 丢失用户已经积累的抽取次数就会报废甚至引发客诉。所以保底进度必须落库Redis 只做加速读取。概率配置也不能单靠开发人员写死进代码里必须做成运营配置项否则每期活动都要发一次版本这在小程序审核周期下完全不可行。我实现了一张“概率配置表”字段直接存关键信息让运营在后台上做调整每次修改留快照和修改日志。这是合规审计的基础——未来出了概率争议起码能查出“当时配置是多少谁改的”。2.4 为什么需要消息队列项目早期没上消息队列抽赏成功之后后端直接同步去触发一系列动作比如发微信订阅消息、生成对账流水、更新数据看板。流量一上来同步链路里的任何一个下游抖动都会拖慢抽奖主接口的响应。后来引入了轻量消息队列把抽中的结果放到队列里异步去处理订阅消息推送、库存快照落库、运营数据统计。抽奖接口的响应时间从接近一秒钟降到了百毫秒级。消息队列选择了轻量模式部署也不复杂还能顺手把消息推送失败重试的问题一起解决。对于抽赏这种“高频支付 低频抽中”的业务形态异步化是一个性价比很高的动作。2.5 管理后台与运营配置体系抽赏小程序对运营后台的要求比一般电商高得多因为运营天天在改池子奖品图片、概率、库存、活动时间、保底参数。我一开始准备了一套后台功能结果被运营吐槽“改个概率要找开发”后来咬咬牙把后台补全了核心模块是这几个活动管理、奖池配置、订单管理、用户管理、数据报表再加上一个单独的“操作审计日志”模块所有后台的配置变更都会记录下来这个模块在合规复盘中价值很大。后台的技术选型也值得说一下没有用重型框架就是一个服务端渲染的管理端所有接口与小程序端共用同一套后端服务。这样节省了一套权限体系权限上只做了超管、运营、客服三角色。3. 核心功能实现细节概率、并发、支付与风控3.1 抽奖主流程的完整实现抽奖主流程的代码层面核心就是那个 Lua 脚本。我把它写成了一段完整的脚本包含从 Redis 获取卡池、校验活动状态、读取概率配置、执行随机抽取、扣减库存、更新用户抽赏进度。脚本执行完毕后返回中奖记录号主服务再根据返回结果写 MySQL 订单数据。这段脚本必须保证原子性用户同时请求时脚本排队执行从根源上避免了并发冲突。主服务拿到脚本结果后还有一步需要特别注意中奖结果不能直接返回给前端就完事。因为抽赏结果牵扯支付、库存、保底状态多方面的数据一致性我会先把数据写到本地日志再异步刷新到主库。如果后续对账发现问题还能拿这个日志做原始凭证。3.2 微信登录与手机号授权链路的坑抽赏小程序核心业务绕不开微信登录和获取手机号这块我踩了不少坑值得单独拿出来说。首先是登录问题现在微信已经调整了多年没变的登录方式wx.login 拿到的 code 换 openid 的逻辑依然是最稳定的但这只是登录态的基础。真正的关键点在于抽赏业务直接涉及资产和交易登录态不能只依赖前端缓存每次进入核心页面时都必须向后端校验 session 是否有效。如果不强校验用户登录过期后还能继续发起抽赏请求后端拿不到用户真实身份很容易被刷单脚本利用。然后是手机号我当时被“小程序获取手机号”这个能力坑过一次。旧的手机号快速验证组件是前端弹窗授权用户点允许后前端拿到加密的手机号数据再传回后端换取真实号码。新版的能力变成必须要使用指定组件并且要在用户真实点击后才能触发。这个“用户真实点击”是个硬性要求如果前端在页面加载时自动触发授权根本拉不起来授权弹窗。更要注意的是手机号的解密必须在后端完成绝不能在前端进行前端只能用插件拿到的临时凭证后端再用会话密钥去解密否则会有极高的数据泄露风险。我象征性地把这个逻辑在后端做了兜底前端拿不到解密后的完整手机号后端才是唯一能拿到明文的地方。3.3 支付回调与对账体系支付是抽赏项目里绝对不能出错的环节。微信支付的回调通知我是用“独立接口 幂等表”来处理的。回调接口只管校验签名、更新支付单状态然后往本地消息表里塞一条带唯一键的支付成功事件。之后再做幂等处理。重要的事说三遍回调处理必须幂等必须幂等必须幂等。用户一次支付微信可能推送多次回调不同接口之间也可能有延迟如果幂等没做好就会导致用户支付一次却抽了多次。此外我单独做了一个“每日对账任务”每天凌晨拉取微信支付的对账单跟本地订单表做比对。比对结果分成三个状态本地有但微信没有、微信有但本地没有、金额不一致。这三种情况分别进入不同的处理流程客服系统里也会有“待对账异常单”的可视化入口。这笔功让我在版本上线第二周就发现了一个隐藏bug——某条订单状态在回调时漏更新了用户其实已经支付了但系统给他发了“支付失败”还好对账任务提前发现了不然要赔一堆隐藏款。3.4 风控与防刷设计抽赏业务天然要被薅羊毛尤其是新用户优惠、分享助力、低价尝鲜这类入口。风控我是分了几个层次去堵一是设备维度。微信小程序拿不到完整的设备指纹但能拿到一些基础标识。我的做法是结合登录态、IP、行为频率三件事做交叉验证。比如一个手机号在短时间内绑定多个微信号或者一个 IP 在短时间高频调用抽奖接口直接拉黑。二是账号维度。新注册用户如果立刻参与高价值活动会进入“观察模式”——抽中的大额奖品延迟发货人工审核之后才出库。这个方法很土但非常有效能拦截相当一部分批量注册的刷单群体。三是业务维度。某种抽法在短时间内出现异常高并发时触发限流策略比如每用户每日抽赏次数上限、单活动单人参与次数上限这些在上线前就配置好。活动一旦被刷爆补损失的成本远比提前防刷高。4. 合规落地抽赏玩法的红线与工程应对4.1 概率公示与信息透明抽赏本质上是一种带有随机性、射幸性的营销活动头部平台近几年对这类玩法越来越严格。合规落地的第一原则就是公示。我这边做了三层公示小程序活动页底部固定展示本期奖池所有奖品、款式、数量、抽取概率。概率明细里要区分“普通款”和“隐藏款”隐藏款必须注明获得方式例如“抽满300次必得”或“以实际公示概率为准”但必须有明确的保底规则说明。概率配置一经发布后台不能静默修改任何修改都要重新发布并留下记录。光有公示还不够技术上要能校验“公示概率与实际中奖率一致”。我们做了一个简单的统计模块每天对比每个奖品的“理论中奖次数”和“实际中奖次数”跑一个偏差率。如果偏差率超过预设阈值就自动告警。这个不是为了证明我们一定没问题而是为了出问题时能第一时间拿出数据支撑。抽赏业务最大的公关风险就是“用户质疑概率造假”一款能自动化校验概率的统计工具是必须在这个项目里超前建设的。4.2 未成年人保护合规里另一条红线是未成年人保护。抽赏涉及付费充值必须做实名限制和未成年人保护措施。微信小程序登录时通常会拿到用户的实名状态但要拿到“是否成年人”的真实数据并不容易微信没有直接开放年龄段接口给普通开发者。我的做法是把风控判断前置实名认证接口里增加年龄段字段如果识别到风险用户或者用户在支付环节触发异常就要求进行额外的认证。前端的体验上也需要配合涉及购买、抽赏操作时必须弹出“理性消费、适度参与”的提示未通过年龄校验的用户要限制支付。这一条不是为了应付审核而是合规审查中项目能正常活下去的底线。如果上线后被查到未成年人可以进行大额抽赏轻则下架整改重则承担法律责任。所以我在支付前强烈建议做一次风险校验宁可牺牲一点点转化率。4.3 售后与虚拟商品交付抽赏小程序里中奖分为实物和虚拟两种。实物商品走普通的物流发货流程虚拟商品如兑换码、电子卡券也要走一套“卡密生成-加密存储-用户查看-核销记录”的流程卡密不能直接明文存数据库至少要做加密。售后方面用户抽完不想要了能不能退这不能简单一刀切。实物未发货前可以支持“申请退款”但已开奖的抽赏记录要留痕虚拟卡密一旦查看过默认不支持退款这个规则要在用户协议和活动规则页面有明确说明不能藏在后台。每一笔退款也走后台审核流不能是用户一键退否则很容易被恶意用户钻空子。4.4 数据安全与用户隐私抽赏小程序涉及手机号、微信昵称、头像、收货地址、支付记录等个人敏感信息。这些信息的采集、存储、使用都必须符合数据安全的通行要求。工程实现上我做了几件事手机号解密后立刻脱敏展示完整明文的查询权限只对客服角色开放日志系统对敏感字段做自动化掩码支付相关的数据单独存储并且与普通业务数据隔离。数据安全不是加分项而是这个项目能不能走得远的基础。5. 踩坑实录与排查技巧真实事故复盘5.1 并发抽奖导致的库存超卖第一版上线时抽奖接口用的是“先查库存再扣库存”的普通事务方式。上线第一天小范围测试没问题等第二天我找了几个朋友模拟并发一上来就复现了库存超卖总共库存 500 份并发打进来 200 个请求实际只应该卖掉 200 份却成功扣了 215 份。根因是老生常谈的并发场景下查出来的库存数据相互覆盖扣减时把同一份库存扣了多次。后来全部改为 Redis Lua 原子扣减才彻底解决。这也说明了抽赏这种每一次抽中都要实时扣资源的业务必须从一开始就用原子操作不能靠“乐观锁那点小聪明”抽赏的粒度太细、频次太高乐观锁重试的成本会指数级上升。5.2 回调丢失与订单状态不一致微信支付回调偶尔会延迟甚至漏推单靠回调驱动订单状态更新迟早会遇到“用户已支付但系统没收到通知”。我在流程里补了一个“主动查询状态”的兜底任务每隔一段时间扫描“已支付但未开奖”的订单主动调用微信查询订单接口如果查到用户已付款就补触发开奖流程。这一个兜底动作至少把订单状态不一致的概率降到了极低。5.3 概率配置更新后缓存不一致运营在后台改完概率前端看到的还是旧概率。原因是配置是缓存在 Redis 里的后台更新时只改了 MySQL没有主动失效 Redis 缓存。后来改成了“先更新 MySQL再删除 Redis 缓存最后发布事件让所有节点重新加载”一步都不能少否则一定会有灰度中的脏数据。5.4 分享助力功能被脚本刷抽赏活动一般都有“分享给好友助力”的功能这个入口是刷单重灾区。我第一版只做了简单的次数限制结果被脚本用批量微信号刷了几千次助力兑换了一批虚拟奖品发现的时候已经造成实际损失。后面修正为三个措施的叠加助力必须绑定真实支付过至少一单的用户、单日助力上限压低、触发异常后进入人工审核队列。所以说任何形式的免费奖励都必须绑定某种成本要么是身份成本要么是行为成本成本为零的奖励一定被薅穿。6. 写在最后的一点私货坦白讲抽赏小程序做下来最大的体会是“技术不难难的是把业务规则翻译成准确无歧义的技术约束”。特殊赏玩法的核心从来不是那个随机算法而是它底下连着的整条链路配置怎么发、库存怎么管、支付怎么对、概率怎么证、合规怎么守。你把这些链路都想清楚了玩法本身反而是最简单的部分。最后分享一个我个人的小建议如果你是从零开始做这类项目先把“概率配置 Redis Lua 幂等回调 对账任务 操作审计日志”这五块骨架搭好再去填充各种花哨的特殊赏玩法。骨架稳了玩法加多少都不怕骨架松了任何一个隐藏款都可能成为压垮项目的最后一根稻草。
返回列表