场景

  • 线上抽奖
  • 活动方设定了奖品数量,不允许超发

项目变数

  • 参与人数未知
  • 活动持续时间未知(活动方可能根据活动热度增减时间,以月为单位)
  • 可预见的『老板需求』:活动人数太多了,临时增加1个大奖 & 无数个小奖。或者大奖太少,临时增加出大奖概率刺激活动效果。

举例:奖池设定

  • 一等奖: 2个
  • 二等奖:30个
  • 三等奖:500个
  • 四等奖:20000个

实现方案讨论

  • rand 程序随机? 可能会超发,据说有策划团队已经赔过钱了
  • 提前把奖品数量转化为数据库的记录。用户抽奖时,利用数据库的随机查询 select * from 奖池表 where status = '未被抽中' order by rand() limit 0,1 。问题:记录数太多,调整(增减)奖品数量不灵活,方案不优雅

最终方案

  • 每次开奖结果:rand(1, 当时的奖品总数) ,具体看代码
  • 优势:随时增减奖池数量,没有关联其它表(灵活、解耦),不会超发

代码

<?php
// 设置的奖品信息
$priceInfo = [
    '102'  => 3, // 奖品id,奖品数量
    '203'  => 10,
    '3092' => 100,
];

// 抽奖函数
function getPrice(&$priceInfo)
{
    if (!array_sum($priceInfo)) {
        return 0; //奖池已空
    }
    $randId = rand(1, array_sum($priceInfo)); // 170

    $i = 0;
    foreach ($priceInfo as $id => $num) {
        $i += $num;
        if ($i >= $randId) {
            $priceInfo[$id]--;
            return $id;
        }
    }
}
// 模拟100次抽奖
for ($i = 0; $i < 130; $i++) {
    echo "\n 中奖ID: " . getPrice($priceInfo);
}

//剩余的奖品
echo "\n 奖池状态:\n";
print_r($priceInfo);