我们先来分析下规律。
设定总金额为10元,有N个人随机领取:
N=1 第一个
则红包金额=X元;
N=2 第二个
为保证第二个红包可以正常发出,第一个红包金额=0.01至9.99之间的某个随机数。
第二个红包=10-第一个红包金额;
N=3 第三个
红包1=0.01至9.99之间的某个随机数
红包2=0.01至(10-红包1-0.01)的某个随机数
红包3=10-红包1-红包2
……
于是我们得到一个规律,在分配当前红包金额时,先预留剩余红白所需最少金额,然后在0.01至总金额-预留金额间取随机数,得到的随机数就是当前红包分配的金额。
实际应用中,程序先将红包金额分配好,即发红包时,红包个数以及每个红包的金额都分配好了,那么用户来抢红包时,我们随机给用户返回一个红包即可。
红包分配代码:
$total=20;//红包总金额
$num=10;// 分成10个红包,支持10人随机领取
$min=0.01;//每个人最少能收到0.01元
for ($i=1;$i<$num;$i++)
{
$safe_total=($total-($num-$i)*$min)/($num-$i);//随机安全上限
$money=mt_rand($min*100,$safe_total*100)/100;
$total=$total-$money;
echo '第'.$i.'个红包:'.$money.' 元,余额:'.$total.' 元
';
}
echo '第'.$num.'个红包:'.$total.' 元,余额:0 元';
运行以上代码,会输出以下结果:
第1个红包,金额2.08元,余额17.92元
第2个红包,金额1.81元,余额16.11元
第3个红包,金额0.15元,余额15.96元
第4个红包,金额1.61元,余额14.35元
第5个红包,金额1.11元,余额13.24元
第6个红包,金额1.51元,余额11.73元
第7个红包,金额1.21元,余额10.52元
第8个红包,金额2.58元,余额7.94元
第9个红包,金额5.4元,余额2.54元
第10个红包,金额2.54元,余额0元
/**
* 抢红包.
*
* @param int $userId 用户ID
* @param int $shareId 大红包ID
*
* @return array 包含状态码,状态,消息
*/
public function grabHongBao(
string $mobile,
int $userId,
int $shareId,
int $orderId,
int $orderUserId,
$noRegUserId = 0,
$openId = '',
$avatar = '',
$nickname = '')
{
// 大红包信息
$hbsInfo = HBShareModel::multiWhere([
'id' => $shareId,
'order_id' => $orderId,
'user_id' => $orderUserId,
])->first();
if ( ! $hbsInfo) {
return [
'code' => -1,
'status' => 'failure',
'message' => '出错了',
];
}
// 检查是否领取过
$hbobj = new HongBao();
if ($hbobj->checkExistsByOrder($userId, $noRegUserId, $hbsInfo->order_id)) {
return [
'code' => -5,
'status' => 'failure',
'message' => '已抢过',
];
}
// 过期时间
if ($hbsInfo->expire < CURRENT_TIME) {
return [
'code' => -2,
'status' => 'failure',
'message' => '来晚了',
];
}
// 还剩下多少个红包
$shareNum = intval($hbsInfo->total) - intval($hbsInfo->shared);
if ($shareNum == 0) {
return [
'code' => -3,
'status' => 'failure',
'message' => '已抢光',
];
}
// 格式化
$hbsInfo->surplus = floatval($hbsInfo->surplus);
// 已经抢光
if ($hbsInfo->surplus <= 0.00) {
return [
'code' => -3,
'status' => 'failure',
'message' => '已抢光',
];
}
// 红包设置
$setting = Setting::get('hb_hongbao');
// 红包最大数额
$max = abs(floatval($setting[$hbsInfo->type]['amount_max']));
$min = abs(floatval($setting[$hbsInfo->type]['amount_min']));
if ($hbsInfo->surplus <= $min) {
$amount = $hbsInfo->surplus;
}
// 最后一个红包
elseif ($shareNum == 1) {
$amount = min($hbsInfo->surplus, $max);
}
// 最小额
elseif ($shareNum * $min == $hbsInfo->surplus) {
$amount = $min;
}
// 非最后一个红包
else {
$maxAmount = $hbsInfo->surplus - ($shareNum - 1) * $min;
$maxAmount = min($maxAmount, $max);
$amount = rand($maxAmount * 100, $min * 100) / 100;
}
$amount = number_format($amount, 2, '.', '');
$amount = floatval($amount);
if ($amount <= 0.00) {
return [
'code' => -3,
'status' => 'failure',
'message' => '已抢光',
];
}
if ($amount > 0.10) {
$amount = number_format($amount, 1, '.', '');
$amount = number_format($amount, 2, '.', '');
$amount = floatval($amount);
}
// 满多少可用
$threshold = $setting[$hbsInfo->type]['threshold'] ? ceil($amount / $setting[$hbsInfo->type]['threshold']) : 0;
// 过期时间
$expire = strtotime('today') + $setting[$hbsInfo->type]['expire'] * 86400;
$message = '';
$avg = ($max - $min) / 10;
for ($i = 0; $i <= 9; $i++) {
$formatMin = floatval(number_format(($max - $avg), 2, '.', ''));
if ($amount > $formatMin and $amount <= $max) {
$message = HBConst::$shareMessage[$i];
break;
}
$max -= $avg;
$max = floatval(number_format($max, 2, '.', ''));
}
if ( ! $message) {
$message = end(HBConst::$shareMessage);
}
// 生成红包
$result = $hbobj->create([
'user_id' => $userId,
'amount' => $amount,
'type' => $hbsInfo->type,
'threshold' => $threshold,
'expire' => $expire,
'from' => 'order',
'link_id' => $hbsInfo->order_id,
'share_id' => $shareId,
'noreg_id' => $noRegUserId,
'open_id' => $openId,
'avatar' => $avatar,
'nickname' => $nickname,
'message' => $message,
]);
if ( ! $result) {
return [
'code' => -4,
'status' => 'failure',
'message' => '出错了',
];
}
// 保存到大红包
$hbsInfo->surplus -= $amount;
$hbsInfo->shared ++;
$hbsInfo->save();
if ($userId) {
MessageModel::insert([
'to_user_id' => $userId,
'title' => '您收到了一个新红包',
'content' => '您收到了一个'. $amount.'元的红包',
'create_time' => CURRENT_TIME,
'type' => 'hongbao'
]);
}
if ($shareNum == 1) {
$this->_doubleEggAward($hbsInfo, $hbobj);
}
unset($hbobj);
return [
'code' => 1,
'status' => 'success',
'message' => '',
'amount' => $amount,
'type' => $hbsInfo->type,
];
}
浙公网安备 33010602011771号