最近帮朋友调试一个双十一抽奖系统时,发现他们设置的「华为Mate60手机」奖品被同个用户抽中三次——这要真上线了,估计财务部得连夜追杀技术部。今天就和大家聊聊在ThinkPHP框架里,怎么像超市大妈管控特价鸡蛋那样牢牢管住奖品数量。

频道:游戏攻略 日期: 浏览:1

一、奖品池的数据库设计

就像在菜市场买菜得先准备零钱,咱们得先在数据库里搭建好奖品池。建议在奖品表里增加这几个关键字段:

  • total_num:奖品总库存
  • day_num:每日限量(适合长期活动)
  • user_day_limit:用户每日限抽次数
  • probability:中奖概率(建议用万分制)
// 迁移文件示例
Schema::create('prizes', function (Blueprint $table) {
$table->integer('total_num')->comment('总库存');
$table->integer('day_num')->default(0)->comment('日库存');
$table->integer('user_day_limit')->default(1)->comment('用户日限抽');
});

1.1 库存更新的原子操作

记得用「库存卫士」守护你的奖品数量,就像超市收银员扫码那样严谨:

Db::name('prize')
->where('id', $prizeId)
->where('total_num', '>', 0)
->dec('total_num')
->update;

二、多维度限制策略

根据《电商促销系统设计规范》,完整的限制策略应该像洋葱有多层防护:

限制维度 适用场景 实现方式 数据来源
全局总量 稀缺奖品(如手机) 数据库原子操作 《ThinkPHP数据库操作手册》
用户日次数 防羊毛党 Redis计数器 《Redis实战》第3章
活动时段 限时抢购 Crontab定时任务 Linux系统管理指南

2.1 中间件拦截器

就像小区门禁系统,在请求进入核心逻辑前先做过滤:

class LotteryLimit
public function handle($request, \\Closure $next)
$userId = Session::get('user_id');
if(Redis::get("user:{$userId}:today_count") >= 3){
return json(['code'=>400,'msg'=>'今日机会已用完']);
return $next($request);

三、概率算法的艺术

根据《游戏数值设计入门》里的建议,别直接用rand函数,试试这个经典算法:

使用ThinkPHP抽奖活动源码时如何设置奖品数量限制

function getPrize($prizes){
$proArr = array_column($prizes, 'probability');
$result = null;
// 概率数组循环
$proSum = array_sum($proArr);
$randNum = mt_rand(1, $proSum);
foreach ($prozes as $key => $proVal) {
if ($randNum <= $proVal) {
$result = $key;
break;
} else {
$randNum -= $proVal;
return $result;

调试的时候发现个有趣现象——把谢谢惠顾的概率设为9990,实际测试时前100次抽奖竟然中了两次大奖。后来改用分段累进算法才解决这个问题,具体可参考《概率论在游戏中的应用》。

四、应急熔断机制

就像电热水壶的自动断电保护,我们在控制器里加了这么个钩子:

// 当某奖品库存低于5%时触发预警
if($prize['total_num'] < ($prize['init_num']0.05)){
Mail::send('[email protected]','库存预警邮件',"奖品{$prize['name']}即将售罄");

上周做压力测试时,这个机制成功拦截了某Bug导致的「无限抽奖」事故。当时模拟500并发用户,库存1000的奖品在3秒内就被抢光,但系统及时锁死了该奖品的抽取通道。

4.1 数据统计看板

用ECharts做了个实时监控面板,老板最喜欢看那个动态更新的「已抽中/剩余」柱状图。核心SQL大概是这样的:

SELECT
DATE_FORMAT(create_time,'%Y-%m-%d') as day,
prize_id,
count as total
FROM
lottery_records
GROUP BY
day,prize_id
ORDER BY
day DESC

调试抽奖系统就像在游乐场维护旋转木马——既要保证运转顺畅,又要防止有人从马上摔下来。上周五下班前,运营小妹跑来说想给VIP用户增加抽奖次数,我顺手在Redis里加了条白名单规则:

if(in_array($userId, VIPUsers::getIds)){
$maxTimes = 5; // VIP每日5次
} else {
$maxTimes = 3;

现在系统平稳运行了两个促销周期,财务部再也没举着计算器来找过我们。倒是市场部同事总来打听,能不能把某个奖品的概率「稍微」调高那么一点点...

网友留言(0)

评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。