名著阅读 > 微信公众平台开发:从零基础到ThinkPHP5高性能框架实践 > 24.5.6 中奖算法实现 >

24.5.6 中奖算法实现

中奖算法实现是程序中的一个核心功能,它决定着整个抽奖是否能正常运作。下面将这一过程分解后进行讲解。

1)身份合法性判断。

首先获取用户微信的OpenID,如果没有传输过来,或者传输了但值为空,则返回空。其实现代码如下。


include_once('mysql.class.php');
$db = new class_mysql;

if (!isset($_GET['openid']) || ($_GET['openid'] == "")){
    echo "非法调用";
    return ;
}

$openid = $_GET['openid'];

// 1.判断该用户是否已注册,未注册不返回
$mysql_state = "SELECT * FROM 'wx_user' WHERE 'openid' = '".$openid."' ";
$userInfo = $db->query_array_one($mysql_state);
// var_dump($userInfo);
if (count($userInfo) == 0){
    echo "未注册或非法用户";
    return;
}
  

2)抽奖资格判断。

首先获取系统配置表中每个用户每天最大的抽奖次数,然后查询当前用户当天已抽奖的次数。如果用户当前的抽奖次数已达到每天最大次数,则返回“max_times”的错误类型。


// 2.判断该用户是否还有抽奖资格
$mysql_state = "SELECT * FROM 'wx_config' WHERE 'id' = 1 LIMIT 0 , 1";
$config = $db->query_array_one($mysql_state);
// var_dump($config);
$mysql_state = "SELECT * FROM 'wx_winner' WHERE 'openid' =  '".$openid."' AND 'getdate' = '".date("Y-m-d",time)."'";
$qualification = $db->query_array($mysql_state);
// var_dump($qualification);
if (count($qualification) >= $config['maxtimes']){
    // echo "已抽奖";
    echo '{
        "error": "max_times",
        "message": "",
        "prizelevel": "", 
        "success": ""
    }';
    return;
}
  

3)奖品余留判断。

即使用户还有抽奖资格,也要对当前奖品剩余量进行判断。如果奖品已经都抽完了,那么所有用户都不应该再抽到奖品。


// 3. 判断奖项是否仍有剩余
$mysql_state = "SELECT * FROM 'wx_award' WHERE LENGTH('name') > 0";
$award = $db->query_array($mysql_state);
if (count($award) == 0){
    echo "没有配置奖项???";
    return;
}
  

4)生成中奖概率。

用户能不能中奖,是以所有概率相加之后为基数,以当前的随机数为分子来进行匹配的如果能匹配到某个奖品等级的区间,则认为他中了该等级的奖。其代码如下。


// 4.生成中奖概率
// 统计总的中奖概率
$allprob = 0;
for ($i = 0; $i < count($award); $i++) {
    $allprob += $award[$i]['prob'];
}

// 总和不足100,补齐100
if ($allprob <= 100){
    $allprob =100;
}
// var_dump('allprob:'.$allprob);

// 4. 随机生成本次中奖概率
// 使用区间分布方法,保证各概率上的倍率一致
$random = rand(1, $allprob); // 21/100 = 20% 最终中奖概率
$min = 0;
$max = 0;
$level = -1;
// var_dump('random:'.$random);
for ($i = 0; $i < count($award); $i++) {
    $min += ($i < 1)?0:$award[$i-1]['prob'];
    $max += $award[$i]['prob'];
    // var_dump('min:'.$min);
    // var_dump('max:'.$max);
    if ($random >= $min  && $random < $max ){
        $level = $i + 1;
        break;
    }
}
  

5)奖品分配及返回。

如果在上一步计算出用户应该中奖,那么在中奖奖品仍有剩余的情况下,给他分配当前奖品,并且将中奖记录写入数据库,同时返回中奖信息。如果没有中奖,则消耗一次中奖机会。其代码如下。


// 24.分配奖品
$lucky = false;
$finally = "";
for ($i = 0; $i < count($award); $i++) {
    
    if ($award[$i]['id'] ==  $level && $award[$i]['total'] >= 1){
        // 奖项减1
        $mysql_state1 = "UPDATE 'wx_award' SET 'total' = 'total' -1 WHERE 'id' = ".$level;
        $result = $db->execute($mysql_state1);
        // 写入抽奖并且中奖
        $mysql_state1 = "INSERT INTO 'wx_winner' ('id' ,'openid' ,'award' ,'getdate' ,
        'status') VALUES (NULL ,  '".$openid."', '".$award[$i]['name']."',  '".date
        ("Y-m-d",time)."',  '0');";
        $result = $db->execute($mysql_state1);
        // 要返回的结果
        $finally = '{
            "error": "ok",
            "message": "'.$award[$i]['name'].'",
            "prizelevel": "'.$award[$i]['id'].'", 
            "success": "y"
        }';
        $lucky = true;
        break;
    }
}
if (!$lucky){
    // 写入已抽奖,但不中奖
    $mysql_state1 = "INSERT INTO 'wx_winner' ('id' ,'openid' ,'award' ,'getdate' ,
    'status') VALUES (NULL ,  '".$openid."', '',  '".date("Y-m-d",time)."',  '0');";
    // var_dump($mysql_state1);
    $result = $db->execute($mysql_state1);
    $finally = '{
        "error": "noprize",
        "message": "",
        "prizelevel": "", 
        "success": ""
    }';
}
echo $finally;
  

经过上述5个烦琐的步骤,中奖的实现过程就实现了。