2025-10-12 10:09:07 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
namespace app\api\model;
|
|
|
|
|
|
use app\common\controller\Push;
|
|
|
|
|
|
use think\Cache;
|
|
|
|
|
|
use think\Log;
|
|
|
|
|
|
use think\Model;
|
|
|
|
|
|
use think\Db;
|
|
|
|
|
|
use think\Session;
|
|
|
|
|
|
use Redis;
|
|
|
|
|
|
/*
|
|
|
|
|
|
* 盲盒转盘优化后方法
|
|
|
|
|
|
*
|
|
|
|
|
|
*/
|
|
|
|
|
|
class BlindBoxTurntableGiftDrawWorld extends Model
|
|
|
|
|
|
{
|
|
|
|
|
|
private $redis;
|
|
|
|
|
|
|
|
|
|
|
|
public function __construct()
|
|
|
|
|
|
{
|
|
|
|
|
|
parent::__construct();
|
|
|
|
|
|
try {
|
|
|
|
|
|
$this->redis = new Redis();
|
|
|
|
|
|
// 连接到Redis服务器
|
|
|
|
|
|
$this->redis->connect(config('redis.host'), config('redis.port')); // 根据实际配置调整主机和端口
|
|
|
|
|
|
// 选择数据库1
|
|
|
|
|
|
$this->redis->select(1);
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
Log::record('Redis连接失败: ' . $e->getMessage(), 'error');
|
|
|
|
|
|
$this->redis = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 重构后的抽奖方法 - 优化响应速度
|
|
|
|
|
|
*/
|
2025-10-12 15:47:40 +08:00
|
|
|
|
public function draw_gift($gift_bag_id, $user_id, $gift_user_ids, $num = 1, $room_id = 0, $heart_id = 0,$auction_id = 0)
|
2025-10-12 10:09:07 +08:00
|
|
|
|
{
|
2025-10-14 15:23:36 +08:00
|
|
|
|
// 最大重试次数
|
|
|
|
|
|
$maxRetries = 3;
|
|
|
|
|
|
for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 1. 验证参数并提前处理错误
|
|
|
|
|
|
$validationResult = $this->validateDrawParameters($gift_bag_id, $user_id, $gift_user_ids);
|
|
|
|
|
|
if ($validationResult !== true) {
|
|
|
|
|
|
return $validationResult;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 2. 预加载必要数据
|
|
|
|
|
|
$loadResult = $this->loadDrawData($gift_bag_id, $user_id, $room_id, $num, $gift_user_ids);
|
|
|
|
|
|
if ($loadResult['code'] !== 1) {
|
|
|
|
|
|
return $loadResult;
|
|
|
|
|
|
}
|
2025-10-15 14:36:13 +08:00
|
|
|
|
// 添加以下检查以防止 null 错误
|
|
|
|
|
|
if (!isset($loadResult['data']) || !is_array($loadResult['data'])) {
|
|
|
|
|
|
return ['code' => 0, 'msg' => '数据加载失败', 'data' => null];
|
|
|
|
|
|
}
|
2025-10-14 15:23:36 +08:00
|
|
|
|
['bag_data' => $bag_data, 'room' => $room, 'xlh_ext' => $xlh_ext] = $loadResult['data'];
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 预计算抽奖结果
|
|
|
|
|
|
$precomputeResult = $this->precomputeDrawResults(
|
|
|
|
|
|
$bag_data,
|
|
|
|
|
|
$gift_user_ids,
|
|
|
|
|
|
$num
|
|
|
|
|
|
);
|
|
|
|
|
|
if ($precomputeResult['code'] !== 1) {
|
|
|
|
|
|
return $precomputeResult;
|
|
|
|
|
|
}
|
|
|
|
|
|
$precomputedResults = $precomputeResult['data']['results'];
|
|
|
|
|
|
$availableGiftss = $precomputeResult['data']['availableGifts'];
|
|
|
|
|
|
$currentXlhPeriodsNum = $precomputeResult['data']['current_xlh_periods_num'];
|
2025-10-14 16:54:22 +08:00
|
|
|
|
$addcurrentXlhPeriodsNum = $precomputeResult['data']['addcurrentXlhPeriodsNum'];
|
2025-10-14 15:23:36 +08:00
|
|
|
|
$expectedCount = count(explode(',', $gift_user_ids)) * $num;
|
|
|
|
|
|
if (count($precomputedResults) != $expectedCount) {
|
|
|
|
|
|
// 记录错误到Redis
|
|
|
|
|
|
$this->recordDrawErrorToRedis($expectedCount, count($precomputedResults), $room_id, $user_id, $gift_bag_id, $num, $gift_user_ids, $precomputedResults);
|
|
|
|
|
|
return ['code' => 0, 'msg' => '网络加载失败,请重试!', 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
// 4. 执行抽奖事务(核心操作)
|
|
|
|
|
|
$transactionResult = $this->executeDrawTransaction(
|
|
|
|
|
|
$bag_data,
|
|
|
|
|
|
$user_id,
|
|
|
|
|
|
$room_id,
|
|
|
|
|
|
$num,
|
|
|
|
|
|
$precomputedResults,
|
|
|
|
|
|
$availableGiftss,
|
|
|
|
|
|
$gift_user_ids,
|
|
|
|
|
|
$heart_id,
|
|
|
|
|
|
$auction_id
|
|
|
|
|
|
);
|
|
|
|
|
|
if ($transactionResult['code'] !== 1) {
|
|
|
|
|
|
return $transactionResult;
|
|
|
|
|
|
}
|
|
|
|
|
|
$boxTurntableLog = $transactionResult['data']['log_id'];
|
|
|
|
|
|
$giftCounts = $transactionResult['data']['gift_counts'];
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 处理后续操作(非事务性操作)
|
|
|
|
|
|
$this->handlePostDrawOperations(
|
|
|
|
|
|
$precomputedResults,
|
|
|
|
|
|
$boxTurntableLog,
|
|
|
|
|
|
$room_id,
|
|
|
|
|
|
$xlh_ext,
|
|
|
|
|
|
$currentXlhPeriodsNum,
|
2025-10-14 16:54:22 +08:00
|
|
|
|
$addcurrentXlhPeriodsNum,
|
2025-10-14 15:23:36 +08:00
|
|
|
|
$room
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 构建并返回结果
|
|
|
|
|
|
return $this->buildDrawResult($boxTurntableLog, $giftCounts);
|
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
$key = 'blind_box_draw_errors_' . date('Y-m-d-H-i-s');
|
|
|
|
|
|
$errorData = [
|
|
|
|
|
|
'gift_bag_id' => $gift_bag_id,
|
|
|
|
|
|
'user_id' => $user_id,
|
|
|
|
|
|
'gift_user_ids' => $gift_user_ids,
|
|
|
|
|
|
'num' => $num,
|
|
|
|
|
|
'room_id' => $room_id,
|
|
|
|
|
|
'heart_id' => $heart_id,
|
|
|
|
|
|
'auction_id' => $auction_id,
|
|
|
|
|
|
];
|
|
|
|
|
|
if ($this->redis) {
|
|
|
|
|
|
$this->redis->setex($key, 86400 * 7, $e->getMessage() . ' ' . json_encode($errorData));
|
|
|
|
|
|
}
|
|
|
|
|
|
// 如果是死锁且还有重试机会
|
|
|
|
|
|
if (strpos($e->getMessage(), 'Deadlock') !== false && $attempt < $maxRetries - 1) {
|
|
|
|
|
|
// 随机延迟后重试
|
|
|
|
|
|
usleep(rand(50000, 200000)); // 50-200ms
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
return ['code' => 0, 'msg' => "网络加载失败,请重试!", 'data' => null];
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 验证抽奖参数
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function validateDrawParameters($gift_bag_id, $user_id, $gift_user_ids)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 提前验证收礼人
|
|
|
|
|
|
$toarray = explode(',', $gift_user_ids);
|
|
|
|
|
|
if (in_array($user_id, $toarray)) {
|
|
|
|
|
|
return ['code' => 0, 'msg' => "收礼人不能包含自己", 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
// 验证用户ID
|
|
|
|
|
|
if (empty($user_id)) {
|
|
|
|
|
|
return ['code' => 0, 'msg' => '用户ID不能为空', 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
// 验证盲盒ID
|
|
|
|
|
|
if (empty($gift_bag_id)) {
|
|
|
|
|
|
return ['code' => 0, 'msg' => '盲盒ID不能为空', 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 预加载必要数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function loadDrawData($gift_bag_id, $user_id, $room_id,$num,$gift_user_ids)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1. 合并查询盲盒配置和礼物信息
|
|
|
|
|
|
$bag_data = db::name("vs_gift_bag")
|
|
|
|
|
|
->alias('bag')
|
|
|
|
|
|
->join('vs_gift gift', 'gift.gid = JSON_UNQUOTE(JSON_EXTRACT(bag.ext, "$.gift_id"))', 'LEFT')
|
|
|
|
|
|
->field('bag.id,bag.name,bag.ext,gift.gid as gift_id,gift.gift_price')
|
|
|
|
|
|
->where('bag.id', $gift_bag_id)
|
|
|
|
|
|
->find();
|
2025-10-16 10:58:57 +08:00
|
|
|
|
if (!$bag_data || !is_array($bag_data) || !isset($bag_data['gift_price'])) {
|
2025-10-12 10:09:07 +08:00
|
|
|
|
return ['code' => 0, 'msg' => '盲盒配置不存在或盲盒礼物不存在', 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
// 2. 获取房间信息
|
|
|
|
|
|
$room = db::name('vs_room')
|
|
|
|
|
|
->field('id,room_name,xlh_periods,xlh_periods_num,is_open_blind_box_turntable')
|
|
|
|
|
|
->where(['id' => $room_id])
|
|
|
|
|
|
->find();
|
|
|
|
|
|
|
|
|
|
|
|
if (!$room) {
|
|
|
|
|
|
return ['code' => 0, 'msg' => '房间不存在', 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 检查用户金币
|
|
|
|
|
|
$user_waller = db::name('user_wallet')
|
|
|
|
|
|
->where(['user_id' => $user_id])
|
|
|
|
|
|
->find();
|
|
|
|
|
|
|
|
|
|
|
|
if (!$user_waller) {
|
|
|
|
|
|
return ['code' => 0, 'msg' => '用户钱包不存在', 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($user_waller['coin'] < $bag_data['gift_price'] * $num * count(explode(',', $gift_user_ids))) {
|
|
|
|
|
|
return ['code' => 0, 'msg' => '用户金币不足', 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
// 4. 获取巡乐会配置(使用缓存)
|
|
|
|
|
|
$xlh_ext = $this->getCachedXlhConfig();
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
'code' => 1,
|
|
|
|
|
|
'msg' => '数据加载成功',
|
|
|
|
|
|
'data' => [
|
|
|
|
|
|
'bag_data' => $bag_data,
|
|
|
|
|
|
'room' => $room,
|
|
|
|
|
|
'xlh_ext' => $xlh_ext,
|
2025-10-15 14:36:13 +08:00
|
|
|
|
// 'user_waller' => $user_waller
|
2025-10-12 10:09:07 +08:00
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 预计算抽奖结果
|
|
|
|
|
|
*/
|
2025-10-13 11:26:30 +08:00
|
|
|
|
private function precomputeDrawResults($bag_data, $gift_user_ids, $num)
|
2025-10-12 10:09:07 +08:00
|
|
|
|
{
|
|
|
|
|
|
$toarray = explode(',', $gift_user_ids);
|
|
|
|
|
|
// 1. 计算奖池信息
|
|
|
|
|
|
$poolInfo = $this->calculatePoolInfo($bag_data['id']);
|
|
|
|
|
|
if ($poolInfo['code'] !== 1) {
|
|
|
|
|
|
return $poolInfo;
|
|
|
|
|
|
}
|
2025-10-14 11:26:45 +08:00
|
|
|
|
$totalRemaining = $poolInfo['data']['total_remaining'];//奖池剩余数量
|
|
|
|
|
|
$periods = $poolInfo['data']['periods']; //奖池期数
|
|
|
|
|
|
$totalDrawTimes = $poolInfo['data']['total_draw_times']; //总抽奖次数
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
|
|
|
|
|
// 2. 获取可用礼物
|
|
|
|
|
|
$availableGifts = $this->getAvailableGifts($bag_data['id'], $totalDrawTimes);
|
|
|
|
|
|
if (empty($availableGifts)) {
|
2025-10-14 11:26:45 +08:00
|
|
|
|
$availableGifts = $this->resetPoolAndReload($bag_data['id']); //重置奖池并重新加载
|
2025-10-12 10:09:07 +08:00
|
|
|
|
if (empty($availableGifts)) {
|
|
|
|
|
|
throw new \Exception('重置奖池后仍无可用礼物');
|
|
|
|
|
|
}
|
2025-10-14 11:26:45 +08:00
|
|
|
|
$totalDrawTimes = 0;//总抽奖次数重置
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
2025-10-16 10:58:57 +08:00
|
|
|
|
// 在处理奖池重置逻辑之前添加检查
|
|
|
|
|
|
if (!is_array($availableGifts)) {
|
|
|
|
|
|
throw new \Exception('可用礼物数据格式错误');
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
// 3. 预加载礼物信息(减少后续查询)
|
|
|
|
|
|
$giftInfoMap = $this->preloadGiftInfo($availableGifts);
|
|
|
|
|
|
|
2025-10-14 11:26:45 +08:00
|
|
|
|
// 4. 处理奖池重置逻辑 - 优化版本
|
2025-10-14 10:21:22 +08:00
|
|
|
|
$needGiftNum = count($toarray) * $num; //总需要的礼物数
|
2025-10-14 11:26:45 +08:00
|
|
|
|
$remaining_available_gifts = [];
|
|
|
|
|
|
$remainingGiftCount = array_sum(array_column($availableGifts, 'remaining_number'));
|
|
|
|
|
|
|
|
|
|
|
|
if ($remainingGiftCount < $needGiftNum) {
|
|
|
|
|
|
// 如果当前奖池礼物不足以满足需求
|
|
|
|
|
|
// 保存当前剩余礼物作为上期剩余
|
|
|
|
|
|
$remaining_available_gifts = $availableGifts;
|
|
|
|
|
|
|
|
|
|
|
|
// 重置奖池
|
|
|
|
|
|
$availableGifts = $this->resetPoolAndReload($bag_data['id']);
|
2025-10-12 10:09:07 +08:00
|
|
|
|
if (empty($availableGifts)) {
|
|
|
|
|
|
throw new \Exception('重置奖池后仍无可用礼物');
|
|
|
|
|
|
}
|
2025-10-14 11:26:45 +08:00
|
|
|
|
$totalDrawTimes = 0; // 总抽奖次数重置
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
2025-10-14 16:54:22 +08:00
|
|
|
|
$current_xlh_periods_num = $this->getCachedXlhPeriodsNum("get");
|
2025-10-12 10:09:07 +08:00
|
|
|
|
// 5. 使用Alias Method预计算抽奖结果(O(1)复杂度)
|
|
|
|
|
|
$precomputedResults = $this->precomputeResultsWithAliasMethod(
|
|
|
|
|
|
$toarray,
|
|
|
|
|
|
$num,
|
|
|
|
|
|
$availableGifts,
|
|
|
|
|
|
$giftInfoMap,
|
|
|
|
|
|
$totalDrawTimes,
|
|
|
|
|
|
$periods,
|
|
|
|
|
|
$current_xlh_periods_num,
|
|
|
|
|
|
$remaining_available_gifts
|
|
|
|
|
|
);
|
|
|
|
|
|
if (empty($precomputedResults['precomputedResults'])) {
|
|
|
|
|
|
throw new \Exception('预计算抽奖结果失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
'code' => 1,
|
|
|
|
|
|
'msg' => '预计算成功',
|
|
|
|
|
|
'data' => [
|
|
|
|
|
|
'results' => $precomputedResults['precomputedResults'],
|
|
|
|
|
|
'current_xlh_periods_num' => $current_xlh_periods_num,
|
|
|
|
|
|
'availableGifts' => $precomputedResults['precomputedResultss'],
|
2025-10-14 16:54:22 +08:00
|
|
|
|
'addcurrentXlhPeriodsNum' => $precomputedResults['addcurrentXlhPeriodsNum'],
|
2025-10-12 10:09:07 +08:00
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 使用Alias Method预计算抽奖结果(O(1)复杂度)
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function precomputeResultsWithAliasMethod(
|
|
|
|
|
|
$toarray,
|
|
|
|
|
|
$num,
|
|
|
|
|
|
$availableGifts,
|
|
|
|
|
|
$giftInfoMap,
|
|
|
|
|
|
&$totalDrawTimes,
|
|
|
|
|
|
&$periods,
|
|
|
|
|
|
&$currentXlhPeriodsNum,
|
|
|
|
|
|
$remaining_available_gifts
|
|
|
|
|
|
) {
|
2025-10-16 10:58:57 +08:00
|
|
|
|
if (!is_array($toarray) || !is_array($availableGifts) || !is_array($giftInfoMap)) {
|
|
|
|
|
|
throw new \Exception('预计算参数格式错误');
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
$precomputedResults = [];
|
|
|
|
|
|
$precomputedResultss = [];
|
2025-10-14 16:54:22 +08:00
|
|
|
|
$addcurrentXlhPeriodsNum = 0;
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
2025-10-14 11:26:45 +08:00
|
|
|
|
// 计算上期剩余礼物总数
|
|
|
|
|
|
$remainingGiftCount = array_sum(array_column($remaining_available_gifts, 'remaining_number'));
|
|
|
|
|
|
$needGiftNum = count($toarray) * $num;
|
|
|
|
|
|
$newGiftsNeeded = max(0, $needGiftNum - $remainingGiftCount); // 计算还需要多少礼物从新奖池中抽取
|
|
|
|
|
|
|
|
|
|
|
|
// 先从上期剩余礼物中分配
|
|
|
|
|
|
if (!empty($remaining_available_gifts)) {
|
|
|
|
|
|
$aliasTableForRemaining = $this->buildAliasTable($remaining_available_gifts);
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($toarray as $giftUserId) {
|
|
|
|
|
|
// 为每个用户先分配上期剩余礼物
|
|
|
|
|
|
$userRemainingAllocation = floor($remainingGiftCount / count($toarray));
|
|
|
|
|
|
if (count($toarray) > 0) { // 防止除零错误
|
|
|
|
|
|
$extraGifts = $remainingGiftCount % count($toarray);
|
|
|
|
|
|
if (array_search($giftUserId, $toarray) < $extraGifts) {
|
|
|
|
|
|
$userRemainingAllocation++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for ($i = 0; $i < $userRemainingAllocation; $i++) {
|
|
|
|
|
|
$selectedGift = $this->selectGiftWithAliasMethod($aliasTableForRemaining);
|
|
|
|
|
|
if ($selectedGift) {
|
|
|
|
|
|
$gift = $giftInfoMap[$selectedGift['foreign_id']] ?? null;
|
2025-10-12 10:09:07 +08:00
|
|
|
|
$precomputedResults[] = [
|
|
|
|
|
|
'gift_user_id' => $giftUserId,
|
2025-10-14 11:26:45 +08:00
|
|
|
|
'gift_bag_detail' => $selectedGift,
|
|
|
|
|
|
'gift' => $gift,
|
|
|
|
|
|
'draw_times' => $totalDrawTimes,
|
|
|
|
|
|
'periods' => $periods,
|
|
|
|
|
|
];
|
|
|
|
|
|
$precomputedResultss[] = [
|
|
|
|
|
|
'gift_user_id' => $giftUserId,
|
|
|
|
|
|
'gift_bag_detail' => $selectedGift,
|
2025-10-12 10:09:07 +08:00
|
|
|
|
'gift' => $gift,
|
|
|
|
|
|
'draw_times' => $totalDrawTimes,
|
|
|
|
|
|
'periods' => $periods,
|
|
|
|
|
|
];
|
|
|
|
|
|
$totalDrawTimes++;
|
|
|
|
|
|
$currentXlhPeriodsNum++;
|
2025-10-14 16:54:22 +08:00
|
|
|
|
$addcurrentXlhPeriodsNum++;
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
2025-10-14 11:26:45 +08:00
|
|
|
|
// 更新Alias表
|
|
|
|
|
|
$this->updateAliasTable($aliasTableForRemaining, $selectedGift['id']);
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-14 11:26:45 +08:00
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
2025-10-14 11:26:45 +08:00
|
|
|
|
// 再从新奖池中分配剩余所需礼物
|
|
|
|
|
|
if ($newGiftsNeeded > 0 && !empty($availableGifts)) {
|
|
|
|
|
|
$aliasTableForNew = $this->buildAliasTable($availableGifts);
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($toarray as $giftUserId) {
|
|
|
|
|
|
// 计算每个用户需要从新奖池获得的礼物数量
|
|
|
|
|
|
$userNewAllocation = floor($newGiftsNeeded / count($toarray));
|
|
|
|
|
|
if (count($toarray) > 0) {
|
|
|
|
|
|
$extraGifts = $newGiftsNeeded % count($toarray);
|
|
|
|
|
|
if (array_search($giftUserId, $toarray) < $extraGifts) {
|
|
|
|
|
|
$userNewAllocation++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
2025-10-14 11:26:45 +08:00
|
|
|
|
for ($i = 0; $i < $userNewAllocation; $i++) {
|
|
|
|
|
|
$selectedGift = $this->selectGiftWithAliasMethod($aliasTableForNew);
|
|
|
|
|
|
if ($selectedGift) {
|
|
|
|
|
|
$gift = $giftInfoMap[$selectedGift['foreign_id']] ?? null;
|
|
|
|
|
|
$precomputedResults[] = [
|
|
|
|
|
|
'gift_user_id' => $giftUserId,
|
|
|
|
|
|
'gift_bag_detail' => $selectedGift,
|
|
|
|
|
|
'gift' => $gift,
|
|
|
|
|
|
'draw_times' => $totalDrawTimes,
|
|
|
|
|
|
'periods' => $periods,
|
|
|
|
|
|
];
|
|
|
|
|
|
$precomputedResultss[] = [
|
|
|
|
|
|
'gift_user_id' => $giftUserId,
|
|
|
|
|
|
'gift_bag_detail' => $selectedGift,
|
|
|
|
|
|
'gift' => $gift,
|
|
|
|
|
|
'draw_times' => $totalDrawTimes,
|
|
|
|
|
|
'periods' => $periods,
|
|
|
|
|
|
];
|
|
|
|
|
|
$totalDrawTimes++;
|
|
|
|
|
|
$currentXlhPeriodsNum++;
|
2025-10-14 16:54:22 +08:00
|
|
|
|
$addcurrentXlhPeriodsNum++;
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
2025-10-14 11:26:45 +08:00
|
|
|
|
// 更新Alias表
|
|
|
|
|
|
$this->updateAliasTable($aliasTableForNew, $selectedGift['id']);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-14 16:54:22 +08:00
|
|
|
|
return ['precomputedResults' => $precomputedResults, 'precomputedResultss' => $precomputedResultss, 'addcurrentXlhPeriodsNum' => $addcurrentXlhPeriodsNum];
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构建Alias表(O(n)复杂度,只执行一次)
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function buildAliasTable($gifts)
|
|
|
|
|
|
{
|
|
|
|
|
|
$n = count($gifts);
|
|
|
|
|
|
if ($n === 0) return null;
|
|
|
|
|
|
|
|
|
|
|
|
$totalRemaining = array_sum(array_column($gifts, 'remaining_number'));
|
|
|
|
|
|
if ($totalRemaining <= 0) return null;
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化Alias表
|
|
|
|
|
|
$small = [];
|
|
|
|
|
|
$large = [];
|
|
|
|
|
|
$prob = [];
|
|
|
|
|
|
$alias = [];
|
|
|
|
|
|
$indexMap = [];
|
|
|
|
|
|
|
|
|
|
|
|
// 归一化概率并填充索引映射
|
|
|
|
|
|
foreach ($gifts as $i => $gift) {
|
|
|
|
|
|
$indexMap[$i] = $gift;
|
|
|
|
|
|
$prob[$i] = $gift['remaining_number'] * $n / $totalRemaining;
|
|
|
|
|
|
if ($prob[$i] < 1.0) {
|
|
|
|
|
|
$small[] = $i;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$large[] = $i;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建Alias表
|
|
|
|
|
|
while (!empty($small) && !empty($large)) {
|
|
|
|
|
|
$s = array_pop($small);
|
|
|
|
|
|
$l = array_pop($large);
|
|
|
|
|
|
$alias[$s] = $l;
|
|
|
|
|
|
$prob[$l] = ($prob[$l] + $prob[$s]) - 1.0;
|
|
|
|
|
|
|
|
|
|
|
|
if ($prob[$l] < 1.0) {
|
|
|
|
|
|
$small[] = $l;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$large[] = $l;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
'n' => $n,
|
|
|
|
|
|
'prob' => $prob,
|
|
|
|
|
|
'alias' => $alias,
|
|
|
|
|
|
'index_map' => $indexMap,
|
|
|
|
|
|
'gifts' => $gifts
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 使用Alias Method选择礼物(O(1)复杂度)
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function selectGiftWithAliasMethod($aliasTable)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!$aliasTable) return null;
|
|
|
|
|
|
|
|
|
|
|
|
$n = $aliasTable['n'];
|
|
|
|
|
|
$k = mt_rand(0, $n - 1);
|
|
|
|
|
|
|
|
|
|
|
|
// 随机选择
|
|
|
|
|
|
if (mt_rand() / mt_getrandmax() < $aliasTable['prob'][$k]) {
|
|
|
|
|
|
return $aliasTable['index_map'][$k];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return $aliasTable['index_map'][$aliasTable['alias'][$k]] ?? null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新Alias表(模拟库存减少)
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function updateAliasTable(&$aliasTable, $giftId)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 查找礼物在Alias表中的位置
|
|
|
|
|
|
$gifts = &$aliasTable['gifts'];
|
|
|
|
|
|
$indexMap = &$aliasTable['index_map'];
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($gifts as &$gift) {
|
|
|
|
|
|
if ($gift['id'] == $giftId) {
|
|
|
|
|
|
$gift['remaining_number']--;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重新构建Alias表(当剩余数量变化较大时)
|
|
|
|
|
|
// 这里可以根据实际情况调整重建频率
|
|
|
|
|
|
$totalRemaining = array_sum(array_column($gifts, 'remaining_number'));
|
|
|
|
|
|
if ($totalRemaining <= 0) {
|
|
|
|
|
|
$aliasTable = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 执行抽奖事务(核心操作)
|
|
|
|
|
|
*/
|
2025-10-12 15:47:40 +08:00
|
|
|
|
private function executeDrawTransaction($bag_data, $user_id, $room_id, $num, $precomputedResults,$availableGiftss,$gift_user_ids,$heart_id,$auction_id)
|
2025-10-12 10:09:07 +08:00
|
|
|
|
{
|
|
|
|
|
|
$gift_user_num = count(explode(',', $gift_user_ids)); //人数
|
|
|
|
|
|
$bagGiftPrice = $bag_data['gift_price'] * $num * $gift_user_num;
|
|
|
|
|
|
|
2025-10-14 15:23:36 +08:00
|
|
|
|
// 增加重试机制
|
|
|
|
|
|
$maxRetries = 3;
|
|
|
|
|
|
for ($retry = 0; $retry < $maxRetries; $retry++) {
|
2025-10-15 16:03:46 +08:00
|
|
|
|
try {
|
2025-10-14 15:23:36 +08:00
|
|
|
|
db::startTrans();
|
|
|
|
|
|
// 按照固定顺序处理事务步骤
|
|
|
|
|
|
// 1. 扣除用户金币(优先处理)
|
|
|
|
|
|
$this->deductUserCoins($user_id, $bagGiftPrice, $room_id);
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 创建抽奖记录
|
|
|
|
|
|
$boxTurntableLog = db::name('vs_blind_box_turntable_log')->insertGetId([
|
|
|
|
|
|
'user_id' => $user_id,
|
|
|
|
|
|
'gift_bag_id' => $bag_data['id'],
|
|
|
|
|
|
'num' => $num,
|
|
|
|
|
|
'room_id' => $room_id,
|
|
|
|
|
|
'bag_price' => $bag_data['gift_price'],
|
|
|
|
|
|
'createtime' => time()
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
if (!$boxTurntableLog) {
|
|
|
|
|
|
throw new \Exception('添加盲盒转盘记录失败');
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
2025-10-14 15:23:36 +08:00
|
|
|
|
// 3. 批量更新库存(按ID排序避免死锁)
|
|
|
|
|
|
$this->batchUpdateGiftInventory($availableGiftss, $room_id);
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
2025-10-14 15:23:36 +08:00
|
|
|
|
// 4. 批量插入礼包发放记录
|
|
|
|
|
|
$this->batchInsertGiftBagReceiveLog($user_id, $boxTurntableLog, $bag_data, $room_id, $precomputedResults);
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
2025-10-14 15:23:36 +08:00
|
|
|
|
// 5. 发送礼物
|
|
|
|
|
|
$result = $this->sendGiftsToRecipients($precomputedResults, $room_id,$user_id,$heart_id,$auction_id);
|
|
|
|
|
|
if (isset($result['code']) && $result['code'] !== 1) {
|
|
|
|
|
|
throw new \Exception($result['msg']);
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
2025-10-14 15:23:36 +08:00
|
|
|
|
db::commit();
|
|
|
|
|
|
|
|
|
|
|
|
// 统计礼物数量
|
|
|
|
|
|
$giftCounts = $this->countGifts($precomputedResults);
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
'code' => 1,
|
|
|
|
|
|
'msg' => '事务执行成功',
|
|
|
|
|
|
'data' => [
|
|
|
|
|
|
'log_id' => $boxTurntableLog,
|
|
|
|
|
|
'gift_counts' => $giftCounts
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
2025-10-15 16:03:46 +08:00
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
db::rollback();
|
|
|
|
|
|
// 检查是否是死锁错误
|
|
|
|
|
|
if (strpos($e->getMessage(), 'Deadlock') !== false && $retry < $maxRetries - 1) {
|
|
|
|
|
|
// 等待随机时间后重试
|
|
|
|
|
|
usleep(rand(10000, 100000)); // 10-100ms
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
return ['code' => 0, 'msg' => $e->getMessage(), 'data' => null];
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
2025-10-14 15:23:36 +08:00
|
|
|
|
return ['code' => 0, 'msg' => '操作超时,请重试', 'data' => null];
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量更新礼物库存
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function batchUpdateGiftInventory($precomputedResults, $room_id)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 按礼物ID分组统计需要减少的数量
|
|
|
|
|
|
$inventoryUpdates = [];
|
|
|
|
|
|
foreach ($precomputedResults as $result) {
|
2025-10-16 14:14:48 +08:00
|
|
|
|
$giftId = $result['gift_bag_detail']['id']??0;
|
2025-10-12 10:09:07 +08:00
|
|
|
|
$inventoryUpdates[$giftId] = ($inventoryUpdates[$giftId] ?? 0) + 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-14 15:23:36 +08:00
|
|
|
|
// 按ID排序避免死锁
|
|
|
|
|
|
ksort($inventoryUpdates);
|
|
|
|
|
|
|
2025-10-12 10:09:07 +08:00
|
|
|
|
// 批量更新
|
|
|
|
|
|
foreach ($inventoryUpdates as $giftId => $count) {
|
2025-10-14 15:23:36 +08:00
|
|
|
|
$ret = db::name("vs_gift_bag_detail")->where('id',$giftId)
|
|
|
|
|
|
->setDec('remaining_number', $count);
|
2025-10-12 10:09:07 +08:00
|
|
|
|
if (!$ret) {
|
|
|
|
|
|
Log::record('巡乐会更新礼物剩余数量: ' . $room_id."【数据】".var_export($precomputedResults, true),"info");
|
|
|
|
|
|
throw new \Exception('更新礼物剩余数量失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量插入礼包发放记录
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function batchInsertGiftBagReceiveLog($user_id, $boxTurntableLog, $bag_data, $room_id, $precomputedResults)
|
|
|
|
|
|
{
|
|
|
|
|
|
$batchInsertData = [];
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($precomputedResults as $result) {
|
|
|
|
|
|
$batchInsertData[] = [
|
|
|
|
|
|
'user_id' => $user_id,
|
|
|
|
|
|
'gift_user_id' => $result['gift_user_id'],
|
|
|
|
|
|
'parent_id' => $boxTurntableLog,
|
|
|
|
|
|
'gift_bag_id' => $bag_data['id'],
|
|
|
|
|
|
'gift_id' => $result['gift_bag_detail']['foreign_id'],
|
|
|
|
|
|
'periods' => $result['periods'],
|
|
|
|
|
|
'room_id' => $room_id,
|
|
|
|
|
|
'gift_price' => $result['gift']['gift_price'],
|
|
|
|
|
|
'bag_price' => $bag_data['gift_price'],
|
|
|
|
|
|
'createtime' => time()
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!empty($batchInsertData)) {
|
2025-10-13 11:26:30 +08:00
|
|
|
|
$insertResult = db::name("vs_gift_bag_receive_pan_log")->insertAll($batchInsertData);
|
2025-10-12 10:09:07 +08:00
|
|
|
|
if (!$insertResult) {
|
|
|
|
|
|
throw new \Exception('插入礼包发放记录失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 扣除用户金币
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function deductUserCoins($user_id, $bagGiftPrice, $room_id)
|
|
|
|
|
|
{
|
2025-10-14 15:23:36 +08:00
|
|
|
|
// 使用悲观锁查询用户钱包
|
|
|
|
|
|
$userWallet = db::name('user_wallet')
|
|
|
|
|
|
->where(['user_id' => $user_id])
|
|
|
|
|
|
->find();
|
|
|
|
|
|
if (!$userWallet || $userWallet['coin'] < $bagGiftPrice) {
|
|
|
|
|
|
throw new \Exception('用户金币不足');
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
$walletUpdate = model('GiveGift')->change_user_cion_or_earnings_log(
|
|
|
|
|
|
$user_id,
|
|
|
|
|
|
$bagGiftPrice,
|
|
|
|
|
|
$room_id,
|
|
|
|
|
|
1,
|
|
|
|
|
|
10,
|
|
|
|
|
|
'盲盒转盘抽奖消耗'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (!$walletUpdate) {
|
|
|
|
|
|
throw new \Exception('扣除用户金币失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$userLevel = model('Level')->user_level_data_update(
|
|
|
|
|
|
$user_id,
|
|
|
|
|
|
$bagGiftPrice,
|
|
|
|
|
|
1,
|
|
|
|
|
|
$room_id
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (!$userLevel) {
|
|
|
|
|
|
throw new \Exception('用户等级更新失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理抽奖后的后续操作(非事务性)
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function handlePostDrawOperations(
|
|
|
|
|
|
$precomputedResults,
|
|
|
|
|
|
$boxTurntableLog,
|
|
|
|
|
|
$room_id,
|
|
|
|
|
|
$xlh_ext,
|
|
|
|
|
|
$currentXlhPeriodsNum,
|
2025-10-14 16:54:22 +08:00
|
|
|
|
$addcurrentXlhPeriodsNum,
|
2025-10-12 10:09:07 +08:00
|
|
|
|
$room
|
|
|
|
|
|
) {
|
|
|
|
|
|
// 1. 批量插入盲盒转盘结果记录
|
|
|
|
|
|
$this->batchInsertBlindBoxResults($precomputedResults, $boxTurntableLog);
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 处理巡乐会相关操作
|
|
|
|
|
|
if (!empty($xlh_ext) && $xlh_ext['inlet_bag_id'] == $precomputedResults[0]['gift_bag_detail']['gift_bag_id']) {
|
2025-10-14 16:54:22 +08:00
|
|
|
|
$this->handleXlhOperations($room_id, $xlh_ext, $currentXlhPeriodsNum,$addcurrentXlhPeriodsNum,$room);
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量插入盲盒转盘结果记录
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function batchInsertBlindBoxResults($precomputedResults, $boxTurntableLog)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 统计每个用户每个礼物的数量
|
|
|
|
|
|
$giftUserCounts = [];
|
|
|
|
|
|
foreach ($precomputedResults as $result) {
|
|
|
|
|
|
$key = $result['gift_user_id'] . '_' . $result['gift_bag_detail']['foreign_id'];
|
|
|
|
|
|
if (!isset($giftUserCounts[$key])) {
|
|
|
|
|
|
$giftUserCounts[$key] = [
|
|
|
|
|
|
'gift_user_id' => $result['gift_user_id'],
|
|
|
|
|
|
'gift_id' => $result['gift_bag_detail']['foreign_id'],
|
|
|
|
|
|
'count' => 0,
|
|
|
|
|
|
'gift_price' => $result['gift']['gift_price']
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
$giftUserCounts[$key]['count']++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 批量插入
|
|
|
|
|
|
$batchInsertData = [];
|
|
|
|
|
|
foreach ($giftUserCounts as $userGift) {
|
|
|
|
|
|
$batchInsertData[] = [
|
|
|
|
|
|
'tid' => $boxTurntableLog,
|
|
|
|
|
|
'gift_user_id' => $userGift['gift_user_id'],
|
|
|
|
|
|
'gift_id' => $userGift['gift_id'],
|
|
|
|
|
|
'count' => $userGift['count'],
|
|
|
|
|
|
'gift_price' => $userGift['gift_price'],
|
|
|
|
|
|
'all_gift_price' => $userGift['gift_price'] * $userGift['count'],
|
|
|
|
|
|
'createtime' => time(),
|
|
|
|
|
|
'heart_id' => 0
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!empty($batchInsertData)) {
|
|
|
|
|
|
db::name('vs_blind_box_turntable_results_log')->insertAll($batchInsertData);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 发送礼物给接收者
|
|
|
|
|
|
*/
|
2025-10-12 15:47:40 +08:00
|
|
|
|
private function sendGiftsToRecipients($precomputedResults, $room_id,$user_id,$heart_id,$auction_id)
|
2025-10-12 10:09:07 +08:00
|
|
|
|
{
|
|
|
|
|
|
// 统计每个用户每个礼物的数量
|
|
|
|
|
|
$giftUserCounts = [];
|
|
|
|
|
|
foreach ($precomputedResults as $result) {
|
|
|
|
|
|
$key = $result['gift_user_id'] . '_' . $result['gift_bag_detail']['foreign_id'];
|
|
|
|
|
|
if (!isset($giftUserCounts[$key])) {
|
|
|
|
|
|
$giftUserCounts[$key] = [
|
|
|
|
|
|
'gift_user_id' => $result['gift_user_id'],
|
|
|
|
|
|
'gift_id' => $result['gift_bag_detail']['foreign_id'],
|
|
|
|
|
|
'count' => 0,
|
|
|
|
|
|
'gift_price' => $result['gift']['gift_price']
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
$giftUserCounts[$key]['count']++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 批量发送礼物
|
|
|
|
|
|
foreach ($giftUserCounts as $userGift) {
|
|
|
|
|
|
if($userGift['count'] > 9){ //防止礼物超发,礼物超10个则不发送
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
$giveGiftExt = [
|
|
|
|
|
|
'gift_id' => $userGift['gift_id'],
|
|
|
|
|
|
'count' => $userGift['count'],
|
|
|
|
|
|
'gift_price' => $userGift['gift_price'],
|
|
|
|
|
|
'all_gift_price' => $userGift['gift_price'] * $userGift['count'],
|
|
|
|
|
|
'is_draw_gift' => 1
|
|
|
|
|
|
];
|
2025-10-12 15:47:40 +08:00
|
|
|
|
if(!empty($auction_id)){
|
|
|
|
|
|
model('RoomAuction')->room_auction_join($auction_id,$user_id,$userGift['gift_id'],$userGift['count'],2,$giveGiftExt);
|
|
|
|
|
|
}else{
|
|
|
|
|
|
$res = model('Room')->room_gift(
|
|
|
|
|
|
$user_id,
|
|
|
|
|
|
$userGift['gift_user_id'],
|
|
|
|
|
|
$userGift['gift_id'],
|
|
|
|
|
|
$userGift['count'],
|
|
|
|
|
|
1,
|
|
|
|
|
|
$room_id,
|
|
|
|
|
|
0,
|
|
|
|
|
|
$heart_id,
|
|
|
|
|
|
$giveGiftExt
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
if (isset($res) && $res['code'] != 1) {
|
|
|
|
|
|
Log::record('发送礼物失败: ' . $res['msg'] . $userGift['gift_user_id'], "info");
|
|
|
|
|
|
return ['code' => 0, 'msg' => $res['msg'], 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return ['code' => 1, 'msg' => '发送礼物成功', 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理巡乐会相关操作
|
|
|
|
|
|
*/
|
2025-10-14 16:54:22 +08:00
|
|
|
|
private function handleXlhOperations($room_id, $xlh_ext, $currentXlhPeriodsNum,$addcurrentXlhPeriodsNum,$room)
|
2025-10-12 10:09:07 +08:00
|
|
|
|
{
|
2025-10-16 18:27:45 +08:00
|
|
|
|
$xlhIsPiaoPing = 0;
|
|
|
|
|
|
$xlhPeriodsNum = $this->getCachedXlhPeriodsNum("get");
|
|
|
|
|
|
if($xlhPeriodsNum < $xlh_ext['open_condition']['waiting_start_num'] && $currentXlhPeriodsNum >= $xlh_ext['open_condition']['waiting_start_num']){
|
|
|
|
|
|
$xlhIsPiaoPing = 1;
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
2025-10-16 18:27:45 +08:00
|
|
|
|
if($xlhPeriodsNum < $xlh_ext['open_condition']['start_num'] && $currentXlhPeriodsNum >= $xlh_ext['open_condition']['start_num']){
|
|
|
|
|
|
$xlhIsPiaoPing = 2;
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
2025-10-16 18:27:45 +08:00
|
|
|
|
// 更新房间巡乐会次数
|
|
|
|
|
|
$this->getCachedXlhPeriodsNum("set",$addcurrentXlhPeriodsNum);
|
2025-10-16 18:24:08 +08:00
|
|
|
|
|
2025-10-16 18:27:45 +08:00
|
|
|
|
// 处理飘屏
|
|
|
|
|
|
if ($xlhIsPiaoPing == 1 || $xlhIsPiaoPing == 2) {
|
|
|
|
|
|
$this->handleXlhPiaoPing($room_id, $xlh_ext, $xlhIsPiaoPing);
|
|
|
|
|
|
}
|
2025-10-16 19:22:00 +08:00
|
|
|
|
|
|
|
|
|
|
$this->updateAndPushXlhStatus($room_id, $xlh_ext, $currentXlhPeriodsNum);
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
2025-10-16 11:37:50 +08:00
|
|
|
|
private function handleXlhPiaoPing($room_id, $xlh_ext, $xlhIsPiaoPing){
|
2025-10-12 10:09:07 +08:00
|
|
|
|
if($xlhIsPiaoPing == 1){
|
|
|
|
|
|
// 即将开始推送飘屏
|
2025-10-14 10:21:22 +08:00
|
|
|
|
$text = "巡乐会即将开始...";
|
2025-10-12 10:09:07 +08:00
|
|
|
|
// 推送礼物横幅
|
|
|
|
|
|
$push = new Push(UID, $room_id);
|
|
|
|
|
|
$text_list_new = [
|
|
|
|
|
|
'text' => $text,
|
|
|
|
|
|
'room_id' => $room_id,
|
2025-10-15 10:20:50 +08:00
|
|
|
|
'from_type' => 101
|
2025-10-12 10:09:07 +08:00
|
|
|
|
];
|
|
|
|
|
|
$push->xunlehui($text_list_new);
|
|
|
|
|
|
}
|
|
|
|
|
|
if($xlhIsPiaoPing == 2){
|
|
|
|
|
|
// 正式开始推送飘屏
|
2025-10-14 10:21:22 +08:00
|
|
|
|
$text = "巡乐会游戏已正式开启...";
|
2025-10-12 10:09:07 +08:00
|
|
|
|
// 推送礼物横幅
|
|
|
|
|
|
$push = new Push(UID, $room_id);
|
|
|
|
|
|
$text_list_new = [
|
|
|
|
|
|
'text' => $text,
|
|
|
|
|
|
'room_id' => $room_id,
|
2025-10-15 10:20:50 +08:00
|
|
|
|
'from_type' => 102
|
2025-10-12 10:09:07 +08:00
|
|
|
|
];
|
|
|
|
|
|
$push->xunlehui($text_list_new);
|
|
|
|
|
|
// 巡乐会正式开始
|
2025-10-13 18:07:08 +08:00
|
|
|
|
$this_xlh_periods = $this->getCachedXlhPeriods('get');
|
2025-10-12 10:09:07 +08:00
|
|
|
|
$pan_xlh_id = db::name('vs_room_pan_xlh')->insertGetId([
|
|
|
|
|
|
'room_id' => $room_id,
|
|
|
|
|
|
'gift_id' => $xlh_ext['locking_condition']['locking_gift_id'],
|
|
|
|
|
|
'homeowner_gift_id' => $xlh_ext['locking_condition']['give_homeowner_gift_id'],
|
|
|
|
|
|
'periods' => $this_xlh_periods+1,
|
|
|
|
|
|
'num' => 0,
|
|
|
|
|
|
'end_time' => time() + $xlh_ext['locking_time']['end_time'] * 60,
|
|
|
|
|
|
'createtime' => time()
|
|
|
|
|
|
]);
|
|
|
|
|
|
if(!$pan_xlh_id){
|
|
|
|
|
|
return ['code' => 0, 'msg' => '创建巡乐会失败!', 'data' => []];
|
|
|
|
|
|
}
|
2025-10-13 18:07:08 +08:00
|
|
|
|
$this->getCachedXlhPeriods('set', $this_xlh_periods+1);//修改巡乐会期数
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
private function updateAndPushXlhStatus($room_id, $xlh_ext, $currentXlhPeriodsNum){
|
|
|
|
|
|
$xlh['waiting_start_num'] = $xlh_ext['open_condition']['waiting_start_num'];//等待开奖次数
|
|
|
|
|
|
$xlh['start_num'] = $xlh_ext['open_condition']['start_num'];//开始开奖次数
|
|
|
|
|
|
// 当前抽奖次数
|
|
|
|
|
|
$xlh['current_num'] = $currentXlhPeriodsNum;
|
|
|
|
|
|
$xlh['end_time'] = 0;
|
|
|
|
|
|
// 状态
|
|
|
|
|
|
if($xlh['current_num'] >= $xlh_ext['open_condition']['start_num']){
|
|
|
|
|
|
$xlh['status'] = 1;//状态 1:巡乐会开始 2:即将开始开始 0:等待开始
|
|
|
|
|
|
//查询巡乐会信息
|
2025-10-16 11:37:50 +08:00
|
|
|
|
$pan_xlh = db::name('vs_room_pan_xlh')->where('send_time',0)->order('id desc')->find();
|
2025-10-16 15:44:53 +08:00
|
|
|
|
if(empty($pan_xlh)){
|
|
|
|
|
|
$this->handleXlhPiaoPing($room_id, $xlh_ext, 2);
|
|
|
|
|
|
$pan_xlh = db::name('vs_room_pan_xlh')->where('send_time',0)->order('id desc')->find();
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
$xlh['end_time'] = $pan_xlh['end_time'] ?? 0;
|
|
|
|
|
|
} elseif($xlh['current_num'] >= $xlh_ext['open_condition']['waiting_start_num'] && $xlh['current_num'] < $xlh_ext['open_condition']['start_num']){
|
|
|
|
|
|
$xlh['status'] = 2;//状态 1:巡乐会开始 2:即将开始开始 0:等待开始
|
|
|
|
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
|
$xlh['status'] = 0;
|
|
|
|
|
|
}
|
2025-10-14 10:21:22 +08:00
|
|
|
|
// 推送进度条
|
|
|
|
|
|
$push = new Push(UID, $room_id);
|
|
|
|
|
|
$text_list_new = [
|
2025-10-12 10:09:07 +08:00
|
|
|
|
'xlh_data' => $xlh,
|
2025-10-14 10:21:22 +08:00
|
|
|
|
'text' => "",
|
2025-10-15 11:40:41 +08:00
|
|
|
|
'room_id' => $room_id,
|
2025-10-15 10:20:50 +08:00
|
|
|
|
'from_type' => 100
|
2025-10-12 10:09:07 +08:00
|
|
|
|
];
|
2025-10-14 10:21:22 +08:00
|
|
|
|
$push->xunlehui($text_list_new);
|
|
|
|
|
|
|
2025-10-12 10:09:07 +08:00
|
|
|
|
// 聊天室推送系统消息
|
2025-10-14 10:21:22 +08:00
|
|
|
|
// 推送
|
2025-10-15 16:12:22 +08:00
|
|
|
|
// $text = [
|
|
|
|
|
|
// 'xlh_data' => $xlh,
|
|
|
|
|
|
// 'text' => ""
|
|
|
|
|
|
// ];
|
|
|
|
|
|
// model('Chat')->sendMsg(1056,$room_id,$text);
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 统计礼物数量
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function countGifts($precomputedResults)
|
|
|
|
|
|
{
|
|
|
|
|
|
$giftCounts = [];
|
|
|
|
|
|
foreach ($precomputedResults as $result) {
|
|
|
|
|
|
$giftId = $result['gift_bag_detail']['foreign_id'];
|
|
|
|
|
|
if (!isset($giftCounts[$giftId])) {
|
|
|
|
|
|
$giftCounts[$giftId] = [
|
|
|
|
|
|
'gift_id' => $giftId,
|
|
|
|
|
|
'count' => 0,
|
|
|
|
|
|
'gift_price' => $result['gift']['gift_price']
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
$giftCounts[$giftId]['count']++;
|
|
|
|
|
|
}
|
|
|
|
|
|
return $giftCounts;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构建抽奖结果
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function buildDrawResult($boxTurntableLog, $giftCounts)
|
|
|
|
|
|
{
|
|
|
|
|
|
$resultList = [];
|
|
|
|
|
|
foreach ($giftCounts as $gift) {
|
|
|
|
|
|
$resultList[] = [
|
|
|
|
|
|
'gift_id' => $gift['gift_id'],
|
|
|
|
|
|
'count' => $gift['count']
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
'code' => 1,
|
|
|
|
|
|
'msg' => '成功',
|
|
|
|
|
|
'data' => [
|
|
|
|
|
|
'blind_box_turntable_id' => $boxTurntableLog,
|
|
|
|
|
|
'reslut_list' => $resultList
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 计算奖池信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function calculatePoolInfo($gift_bag_id)
|
|
|
|
|
|
{
|
|
|
|
|
|
$roomPanInfo = db::name("vs_gift_bag_detail")
|
|
|
|
|
|
->where(['gift_bag_id' => $gift_bag_id])
|
|
|
|
|
|
->field('SUM(quantity) as total_quantity, SUM(remaining_number) as total_remaining')
|
|
|
|
|
|
->find();
|
|
|
|
|
|
$periods = db::name("vs_gift_bag")
|
|
|
|
|
|
->where(['id' => $gift_bag_id])
|
|
|
|
|
|
->value('periods');
|
|
|
|
|
|
$totalQuantity = $roomPanInfo['total_quantity'] ?: 0;
|
|
|
|
|
|
$totalRemaining = $roomPanInfo['total_remaining'] ?: 0;
|
|
|
|
|
|
$periods = $periods ?: 0;
|
2025-10-14 11:26:45 +08:00
|
|
|
|
$totalDrawTimes = max(0, $totalQuantity - $totalRemaining); //总抽奖次数
|
2025-10-12 10:09:07 +08:00
|
|
|
|
return [
|
|
|
|
|
|
'code' => 1,
|
|
|
|
|
|
'msg' => '计算成功',
|
|
|
|
|
|
'data' => [
|
|
|
|
|
|
'total_quantity' => $totalQuantity,
|
|
|
|
|
|
'total_remaining' => $totalRemaining,
|
|
|
|
|
|
'periods' => $periods,
|
|
|
|
|
|
'total_draw_times' => $totalDrawTimes
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取可用礼物
|
|
|
|
|
|
* @param $gift_bag_id
|
|
|
|
|
|
* @param $total_draw_times 总抽奖次数
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function getAvailableGifts($gift_bag_id, $total_draw_times = 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
$where = [
|
|
|
|
|
|
'gift_bag_id' => $gift_bag_id,
|
|
|
|
|
|
'quantity' => ['>', 0],
|
|
|
|
|
|
'remaining_number' => ['>', 0],
|
|
|
|
|
|
'weight' => ['<=', $total_draw_times],
|
|
|
|
|
|
];
|
|
|
|
|
|
return db::name("vs_gift_bag_detail")
|
|
|
|
|
|
->field('id,quantity,remaining_number,weight,foreign_id,gift_bag_id')
|
|
|
|
|
|
->where($where)
|
|
|
|
|
|
->select();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 预加载礼物信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function preloadGiftInfo($available_gifts)
|
|
|
|
|
|
{
|
|
|
|
|
|
$giftIds = array_unique(array_column($available_gifts, 'foreign_id'));
|
|
|
|
|
|
if (empty($giftIds)) return [];
|
|
|
|
|
|
$gifts = db::name('vs_gift')
|
|
|
|
|
|
->where('gid', 'in', $giftIds)
|
|
|
|
|
|
->select();
|
|
|
|
|
|
return array_column($gifts, null, 'gid');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 重置奖池并重新加载
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function resetPoolAndReload($gift_bag_id)
|
|
|
|
|
|
{
|
|
|
|
|
|
$this->reset_gift_pool($gift_bag_id);
|
|
|
|
|
|
return $this->getAvailableGifts($gift_bag_id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取缓存的巡乐会配置
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function getCachedXlhConfig() {
|
|
|
|
|
|
$cacheKey = "xlh_config_13";
|
|
|
|
|
|
$ext = Cache::get($cacheKey);
|
|
|
|
|
|
if (!$ext) {
|
|
|
|
|
|
$bag_data = db::name("vs_gift_bag")
|
|
|
|
|
|
->field('id,name,ext,periods')
|
|
|
|
|
|
->where('id', 13)
|
|
|
|
|
|
->find();
|
|
|
|
|
|
if (!$bag_data) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
$ext = json_decode($bag_data['ext'], true);
|
|
|
|
|
|
$ext['gift_bag_name'] = $bag_data['name'];
|
|
|
|
|
|
Cache::set($cacheKey, $ext, 3600); // 缓存1小时
|
|
|
|
|
|
}
|
|
|
|
|
|
return $ext;
|
|
|
|
|
|
}
|
2025-10-13 11:26:30 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取缓存的巡乐会开启次数
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function getCachedXlhPeriodsNum($type="get",$num=1) {
|
|
|
|
|
|
$cacheKey = "xlh_periods_num";
|
2025-10-16 18:27:45 +08:00
|
|
|
|
$xlh_periods_num = Cache::get($cacheKey) ?? 0;
|
|
|
|
|
|
if($type=="set"){
|
|
|
|
|
|
$xlh_periods_num = $xlh_periods_num + $num;
|
|
|
|
|
|
Cache::set($cacheKey, $xlh_periods_num, 0);
|
2025-10-13 11:26:30 +08:00
|
|
|
|
}
|
2025-10-16 18:27:45 +08:00
|
|
|
|
return $xlh_periods_num;
|
2025-10-13 11:26:30 +08:00
|
|
|
|
}
|
2025-10-13 18:07:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取缓存的巡乐会期数
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function getCachedXlhPeriods($type="get",$periods=1) {
|
|
|
|
|
|
$cacheKey = "this_xlh_periods";
|
|
|
|
|
|
$xlh_periods_num = Cache::get($cacheKey) ?? 0;
|
|
|
|
|
|
if($type=="set"){
|
|
|
|
|
|
$xlh_periods_num = $periods;
|
|
|
|
|
|
Cache::set($cacheKey, $xlh_periods_num, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
return $xlh_periods_num;
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 重置奖池
|
|
|
|
|
|
* @param int $room_id 房间ID
|
|
|
|
|
|
* @param int $gift_bag_id 礼物包ID
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function reset_gift_pool($gift_bag_id,$remaining_available_gifts=[]) {
|
2025-10-12 12:38:20 +08:00
|
|
|
|
$bag_detail = db::name("vs_gift_bag_detail")->where('gift_bag_id',$gift_bag_id)->select();
|
2025-10-12 10:09:07 +08:00
|
|
|
|
db::name("vs_gift_bag")->where('id',$gift_bag_id)->setInc('periods'); //更新期数
|
|
|
|
|
|
db::name("vs_gift_bag_detail")->where('gift_bag_id',$gift_bag_id)->update(['remaining_number'=>db::raw('quantity')]);//重置奖池
|
|
|
|
|
|
//防止并发,上把如果件数小于0,则加上
|
|
|
|
|
|
foreach ($bag_detail as $pan) {
|
|
|
|
|
|
if($pan['remaining_number']<0){
|
|
|
|
|
|
db::name("vs_gift_bag_detail")->where('id', $pan['id'])->setInc('remaining_number', $pan['remaining_number']);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//补充上把礼物有剩余
|
|
|
|
|
|
if(!empty($remaining_available_gifts)){
|
|
|
|
|
|
foreach ($remaining_available_gifts as $gift) {
|
|
|
|
|
|
db::name("vs_gift_bag_detail")->where('id', $gift['id'])->setInc('remaining_number',$gift['remaining_number']);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* 巡乐会抽奖-----------------------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
/*
|
|
|
|
|
|
* 巡乐会抽奖(优化版)
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function xlh_draw_gift($user_id, $num, $room_id)
|
|
|
|
|
|
{
|
|
|
|
|
|
$gift_bag_id = 13;
|
|
|
|
|
|
// 1. 获取并缓存盲盒配置
|
|
|
|
|
|
$ext = $this->getCachedXlhConfig();
|
|
|
|
|
|
// 2. 检查用户金币和房间状态
|
|
|
|
|
|
$bag_gift_price = $ext['xlh_box_price'] * $num;
|
|
|
|
|
|
// 3. 检查巡乐会状态
|
|
|
|
|
|
$pan_xlh = db::name('vs_room_pan_xlh')
|
2025-10-15 10:20:50 +08:00
|
|
|
|
->where(['send_time' => 0])
|
2025-10-13 18:07:08 +08:00
|
|
|
|
->order('id', 'desc')
|
2025-10-12 10:09:07 +08:00
|
|
|
|
->find();
|
|
|
|
|
|
if (empty($pan_xlh)) {
|
2025-10-15 10:20:50 +08:00
|
|
|
|
return ['code' => 0, 'msg' => '本轮未开始', 'data' => null];
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
if ($pan_xlh['end_time'] <= time()) {
|
|
|
|
|
|
return ['code' => 0, 'msg' => '本轮已结束', 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
// 4. 预加载必要数据
|
|
|
|
|
|
$gift_bag_detail = db::name("vs_gift_bag_detail")
|
|
|
|
|
|
->field('id,quantity,remaining_number,weight,foreign_id,gift_bag_id')
|
|
|
|
|
|
->where(['gift_bag_id'=>$gift_bag_id])
|
2025-10-15 10:20:50 +08:00
|
|
|
|
->where(['remaining_number' => ['>', 0]])
|
2025-10-12 10:09:07 +08:00
|
|
|
|
->select();
|
|
|
|
|
|
|
|
|
|
|
|
// 计算总数量和剩余数量
|
|
|
|
|
|
$total_quantity = db::name("vs_gift_bag_detail")
|
|
|
|
|
|
->where(['gift_bag_id' => $gift_bag_id])
|
|
|
|
|
|
->sum('quantity');
|
|
|
|
|
|
|
|
|
|
|
|
$total_remaining = array_sum(array_column($gift_bag_detail, 'remaining_number'));
|
2025-10-15 10:20:50 +08:00
|
|
|
|
$total_draw_times = $total_quantity - $total_remaining; // 已抽奖次数
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
|
|
|
|
|
if ($total_draw_times < 0) $total_draw_times = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 获取可用礼物(提前过滤无效礼物)
|
|
|
|
|
|
$available_gifts = [];
|
|
|
|
|
|
foreach ($gift_bag_detail as $pan) {
|
|
|
|
|
|
if ($pan['remaining_number'] > 0 && $pan['weight'] <= $total_draw_times) {
|
|
|
|
|
|
$available_gifts[] = $pan;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (empty($available_gifts)) {
|
|
|
|
|
|
// 移除已无剩余数量的礼物
|
|
|
|
|
|
$remaining_available_gifts = array_filter($gift_bag_detail, function($gift) {
|
|
|
|
|
|
return $gift['remaining_number'] > 0;
|
|
|
|
|
|
});
|
|
|
|
|
|
$this->reset_gift_pool($gift_bag_id,$remaining_available_gifts);
|
2025-10-15 10:20:50 +08:00
|
|
|
|
$available_gifts = $this->getAvailableGifts($gift_bag_id);
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 预计算抽奖结果
|
|
|
|
|
|
$drawn_gifts = []; // 用于统计抽中结果
|
|
|
|
|
|
$main_prize_updates = []; // 用于记录主奖品更新
|
|
|
|
|
|
$is_zhong_jiang = 0;
|
|
|
|
|
|
$pan_xlh_num = $pan_xlh['num'];
|
|
|
|
|
|
|
|
|
|
|
|
// 批量处理配置
|
|
|
|
|
|
$batch_size = min(10, $num); // 每批次处理10次
|
|
|
|
|
|
$total_processed = 0;
|
|
|
|
|
|
$all_results = []; // 存储所有抽奖结果
|
2025-10-15 14:58:37 +08:00
|
|
|
|
try {
|
|
|
|
|
|
db::startTrans();
|
|
|
|
|
|
while ($total_processed < $num) {
|
|
|
|
|
|
$current_batch = min($batch_size, $num - $total_processed); // 当前批次处理数量
|
|
|
|
|
|
// 批量扣除金币(只在第一次事务中处理)
|
|
|
|
|
|
if ($total_processed == 0) {
|
|
|
|
|
|
$user_waller = db::name('user_wallet')->where(['user_id' => $user_id])->find();
|
|
|
|
|
|
if (!$user_waller || $user_waller['coin'] < $bag_gift_price) {
|
|
|
|
|
|
return ['code' => 0, 'msg' => '用户金币不足', 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
$wallet_update = model('GiveGift')->change_user_cion_or_earnings_log(
|
|
|
|
|
|
$user_id,
|
|
|
|
|
|
$bag_gift_price,
|
|
|
|
|
|
$room_id,
|
|
|
|
|
|
1,
|
|
|
|
|
|
10,
|
|
|
|
|
|
$ext['gift_bag_name'].'抽奖消耗'
|
|
|
|
|
|
);
|
|
|
|
|
|
if (!$wallet_update) {
|
|
|
|
|
|
throw new \Exception('扣除用户金币失败');
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
2025-10-15 14:58:37 +08:00
|
|
|
|
$user_level = model('Level')->user_level_data_update(
|
|
|
|
|
|
$user_id,
|
|
|
|
|
|
$bag_gift_price,
|
|
|
|
|
|
1,
|
|
|
|
|
|
$room_id
|
|
|
|
|
|
);
|
|
|
|
|
|
if (!$user_level) {
|
|
|
|
|
|
throw new \Exception('用户等级更新失败');
|
2025-10-15 10:20:50 +08:00
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-15 14:58:37 +08:00
|
|
|
|
// 处理当前批次的抽奖
|
|
|
|
|
|
$inventory_updates = []; // 用于记录库存变化
|
|
|
|
|
|
$remaining_available_gifts = $available_gifts;
|
|
|
|
|
|
for ($i = 0; $i < $current_batch; $i++) {
|
|
|
|
|
|
// 从可用礼物中选择
|
|
|
|
|
|
$selected_gift = $this->selectGiftFromAvailable($available_gifts);
|
|
|
|
|
|
if (!$selected_gift) {
|
|
|
|
|
|
$gift_bag_detail = $this->resetPoolAndReload($gift_bag_id);
|
|
|
|
|
|
$selected_gift = $this->selectGiftFromAvailable($gift_bag_detail);
|
|
|
|
|
|
if(!$selected_gift){
|
|
|
|
|
|
throw new \Exception('预计算抽奖失败,重置后无可用礼物');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 记录库存变化
|
|
|
|
|
|
$inventory_updates[$selected_gift['id']] =
|
|
|
|
|
|
($inventory_updates[$selected_gift['id']] ?? 0) + 1;
|
|
|
|
|
|
|
|
|
|
|
|
// 记录抽中结果
|
|
|
|
|
|
$gift_id = $selected_gift['foreign_id'];
|
|
|
|
|
|
$drawn_gifts[$gift_id] = ($drawn_gifts[$gift_id] ?? 0) + 1;
|
|
|
|
|
|
|
|
|
|
|
|
// 处理主奖品
|
|
|
|
|
|
if ($gift_id == $ext['locking_condition']['selected_gift_id']) {
|
|
|
|
|
|
$pan_xlh_num++;
|
|
|
|
|
|
$main_prize_updates[] = [
|
|
|
|
|
|
'num' => $pan_xlh_num,
|
|
|
|
|
|
'user_id' => $user_id,
|
|
|
|
|
|
'gift_id' => $gift_id
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// 计算延长时间
|
|
|
|
|
|
$add_end_time = $this->calculateEndTime($pan_xlh_num, $ext, $room_id);
|
|
|
|
|
|
$end_time = time() + $add_end_time;
|
|
|
|
|
|
|
|
|
|
|
|
// 记录主奖品更新
|
|
|
|
|
|
$main_prize_updates[count($main_prize_updates) - 1]['end_time'] = $end_time;
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
2025-10-15 14:58:37 +08:00
|
|
|
|
// 记录完整结果
|
|
|
|
|
|
$all_results[] = [
|
|
|
|
|
|
'gift_id' => $gift_id,
|
|
|
|
|
|
'gift_detail_id' => $selected_gift['id'],
|
|
|
|
|
|
'gift_bag_detail' => $selected_gift
|
|
|
|
|
|
];
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
2025-10-15 14:58:37 +08:00
|
|
|
|
// 更新可用礼物缓存
|
|
|
|
|
|
foreach ($available_gifts as &$gift) {
|
|
|
|
|
|
if ($gift['id'] == $selected_gift['id']) {
|
|
|
|
|
|
$gift['remaining_number']--;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
2025-10-15 14:58:37 +08:00
|
|
|
|
unset($gift);
|
|
|
|
|
|
|
|
|
|
|
|
// 移除已无剩余数量的礼物
|
|
|
|
|
|
$available_gifts = array_filter($available_gifts, function($gift) {
|
|
|
|
|
|
return $gift['remaining_number'] > 0;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否需要重置奖池
|
|
|
|
|
|
$total_remaining = array_sum(array_column($available_gifts, 'remaining_number'));
|
|
|
|
|
|
if ($total_remaining <= 0 && $total_processed + $i + 1 < $num) {// 剩余数量小于等于0且当前处理数量小于总数量
|
|
|
|
|
|
$available_gifts = $this->resetPoolAndReload($gift_bag_id);
|
|
|
|
|
|
if (empty($available_gifts)) {
|
|
|
|
|
|
throw new \Exception('重置奖池后仍无可用礼物');
|
|
|
|
|
|
}
|
2025-10-15 11:40:41 +08:00
|
|
|
|
}
|
2025-10-15 10:56:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-15 14:58:37 +08:00
|
|
|
|
// 批量更新库存
|
|
|
|
|
|
ksort($inventory_updates); // 按ID排序
|
|
|
|
|
|
foreach ($inventory_updates as $detail_id => $count) {
|
|
|
|
|
|
db::name("vs_gift_bag_detail")->where('id',$detail_id)->setDec('remaining_number', $count);
|
|
|
|
|
|
}
|
2025-10-15 11:40:41 +08:00
|
|
|
|
|
2025-10-15 14:58:37 +08:00
|
|
|
|
// 处理主奖品更新
|
|
|
|
|
|
if (!empty($main_prize_updates)) {
|
|
|
|
|
|
$last_update = end($main_prize_updates);
|
|
|
|
|
|
db::name('vs_room_pan_xlh')->where('id', $pan_xlh['id'])->update([
|
|
|
|
|
|
'user_id' => $last_update['user_id'],
|
|
|
|
|
|
'room_id' => $room_id,
|
|
|
|
|
|
'pay_price' => $ext['xlh_box_price'],
|
|
|
|
|
|
'locking_gift_id' => $last_update['gift_id'],
|
|
|
|
|
|
'num' => $last_update['num'],
|
|
|
|
|
|
'end_time' => $last_update['end_time'],
|
|
|
|
|
|
'updatetime' => time()
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
db::name('vs_room_pan_xlh_log')->insert([
|
|
|
|
|
|
'xlh_id' => $pan_xlh['id'],
|
|
|
|
|
|
'user_id' => $last_update['user_id'],
|
|
|
|
|
|
'room_id' => $room_id,
|
|
|
|
|
|
'num' => $last_update['num'],
|
|
|
|
|
|
'locking_end_time' => $last_update['end_time'],
|
|
|
|
|
|
'createtime' => time()
|
|
|
|
|
|
]);
|
|
|
|
|
|
$is_zhong_jiang = 1;
|
2025-10-16 14:40:33 +08:00
|
|
|
|
}else{
|
|
|
|
|
|
$is_zhong_jiang = 0;
|
2025-10-15 14:58:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
$total_processed += $current_batch;
|
2025-10-15 11:40:41 +08:00
|
|
|
|
}
|
2025-10-15 14:58:37 +08:00
|
|
|
|
// 7. 批量处理结果记录
|
2025-10-15 10:31:51 +08:00
|
|
|
|
if(count($drawn_gifts) > $num){
|
2025-10-15 10:20:50 +08:00
|
|
|
|
$key = 'xlh_draw_gift_errors_' . date('Y-m-d-H-i-s');
|
|
|
|
|
|
$errorData = [
|
|
|
|
|
|
'user_id' => $user_id,
|
|
|
|
|
|
'gift_bag_id' => $gift_bag_id,
|
|
|
|
|
|
'room_id' => $room_id,
|
|
|
|
|
|
'num' => $num,
|
|
|
|
|
|
'drawn_gifts_num' => count($drawn_gifts)
|
|
|
|
|
|
];
|
|
|
|
|
|
$this->redis->setex($key, 86400 * 7, "巡乐会抽奖失败-数量超限". ' ' .json_encode($errorData));
|
2025-10-12 10:09:07 +08:00
|
|
|
|
return ['code' => 0, 'msg' => "抽奖中,请稍等...", 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
// 批量插入礼包发放记录
|
|
|
|
|
|
$gift_records = [];
|
2025-10-15 17:10:43 +08:00
|
|
|
|
$periods = $this->getCachedXlhPeriods('get') ?? 0;
|
2025-10-12 10:09:07 +08:00
|
|
|
|
foreach ($drawn_gifts as $gift_id => $count) {
|
|
|
|
|
|
$gift_records[] = [
|
|
|
|
|
|
'user_id' => $user_id,
|
|
|
|
|
|
'parent_id' => $pan_xlh['id'],
|
|
|
|
|
|
'gift_bag_id' => $gift_bag_id,
|
|
|
|
|
|
'gift_id' => $gift_id,
|
|
|
|
|
|
'periods' => $periods,
|
|
|
|
|
|
'room_id' => $room_id,
|
|
|
|
|
|
'num' => $count,
|
|
|
|
|
|
'gift_price' => $this->getGiftPrice($gift_id),
|
|
|
|
|
|
'bag_price' => $ext['xlh_box_price'],
|
|
|
|
|
|
'createtime' => time()
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!empty($gift_records)) {
|
2025-10-13 11:26:30 +08:00
|
|
|
|
db::name("vs_gift_bag_receive_pan_log")->insertAll($gift_records);
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 批量处理用户礼物包
|
|
|
|
|
|
foreach ($drawn_gifts as $gift_id => $count) {
|
|
|
|
|
|
$res = model('UserGiftPack')->change_user_gift_pack(
|
|
|
|
|
|
$user_id,
|
|
|
|
|
|
$gift_id,
|
|
|
|
|
|
$count,
|
|
|
|
|
|
model('UserGiftPack')::XLH_DRAW_GIFT_GET,
|
|
|
|
|
|
"巡乐会抽奖所得"
|
|
|
|
|
|
);
|
|
|
|
|
|
if ($res['code'] != 1) {
|
|
|
|
|
|
throw new \Exception($res['msg']);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加活动记录
|
|
|
|
|
|
Db::name('vs_activities_receive')->insert([
|
|
|
|
|
|
'user_id' => $user_id,
|
|
|
|
|
|
'activities_id' => 6,
|
|
|
|
|
|
'room_id' => $room_id,
|
|
|
|
|
|
'createtime' => time(),
|
|
|
|
|
|
'updatetime' => time()
|
|
|
|
|
|
]);
|
|
|
|
|
|
db::commit();
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
db::rollback();
|
2025-10-15 11:40:41 +08:00
|
|
|
|
$key = 'xlh_draw_gift_errors_' . date('Y-m-d-H-i-s');
|
|
|
|
|
|
$errorData = [
|
|
|
|
|
|
'user_id' => $user_id,
|
|
|
|
|
|
'gift_bag_id' => $gift_bag_id,
|
|
|
|
|
|
'room_id' => $room_id,
|
|
|
|
|
|
];
|
2025-10-15 14:58:37 +08:00
|
|
|
|
$this->redis->setex($key, 86400 * 7, $e->getMessage(). ' ' .json_encode($errorData));
|
|
|
|
|
|
return ['code' => 0, 'msg' => "抽奖中,请稍等...", 'data' => null];
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
// 8. 处理推送消息
|
|
|
|
|
|
if ($is_zhong_jiang == 1) {
|
2025-10-14 10:21:22 +08:00
|
|
|
|
$this->handlePrizeNotification($user_id,$gift_id, $room_id, $pan_xlh_num, $end_time,$ext['locking_condition']['locking_gift_id']);
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 9. 构建返回结果
|
|
|
|
|
|
return $this->buildResult($drawn_gifts);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 巡乐会抽奖-从可用礼物中选择一个
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function selectGiftFromAvailable(array &$available_gifts)
|
|
|
|
|
|
{
|
|
|
|
|
|
$remaining = array_sum(array_column($available_gifts, 'remaining_number'));
|
|
|
|
|
|
if ($remaining <= 0) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 循环尝试直到抽中有效礼物
|
|
|
|
|
|
$max_attempts = 5; // 最大尝试次数,防止无限循环
|
|
|
|
|
|
$attempt = 0;
|
|
|
|
|
|
$selected_gift = null;
|
|
|
|
|
|
while ($attempt < $max_attempts && !$selected_gift) {
|
|
|
|
|
|
$rand_value = mt_rand(1, $remaining);
|
|
|
|
|
|
$current_sum = 0;
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($available_gifts as $gift) {
|
|
|
|
|
|
if ($gift['remaining_number'] <= 0) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
$current_sum += $gift['remaining_number'];
|
|
|
|
|
|
if ($rand_value <= $current_sum) {
|
|
|
|
|
|
$selected_gift = $gift;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
$attempt++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $selected_gift;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 巡乐会抽奖-计算结束时间
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function calculateEndTime($pan_xlh_num, $ext, $room_id)
|
|
|
|
|
|
{
|
2025-10-15 16:39:05 +08:00
|
|
|
|
$cache_key = 'selected_gift_id_' . $ext['locking_condition']['selected_gift_id'];
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
|
|
|
|
|
if ($pan_xlh_num <= 1) {
|
|
|
|
|
|
$add_end_time = $ext['locking_time']['tow_no_locking_time'] * 60;
|
|
|
|
|
|
Cache::set($cache_key, $add_end_time, $add_end_time);
|
|
|
|
|
|
return $add_end_time;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (Cache::get($cache_key)) {
|
|
|
|
|
|
$erci_xlh_num = Cache::get($cache_key);
|
|
|
|
|
|
$add_end_time = $erci_xlh_num - $ext['locking_time']['next_time'] * 60;
|
|
|
|
|
|
Cache::set($cache_key, $add_end_time, $add_end_time);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$add_end_time = ($ext['locking_time']['tow_no_locking_time'] - $ext['locking_time']['next_time']) * 60;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ($add_end_time <= 30) {
|
|
|
|
|
|
Cache::set($cache_key, 30, 30);
|
|
|
|
|
|
return 30;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $add_end_time;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 巡乐会抽奖-重新加载奖池
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function reloadGiftPool($room_id, $gift_bag_id)
|
|
|
|
|
|
{
|
|
|
|
|
|
return db::name("vs_gift_bag_detail")
|
|
|
|
|
|
->field('id,quantity,remaining_number,weight,foreign_id,gift_bag_id')
|
|
|
|
|
|
->where(['gift_bag_id'=>$gift_bag_id])
|
|
|
|
|
|
->select();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 巡乐会抽奖-获取礼物价格
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function getGiftPrice($gift_id)
|
|
|
|
|
|
{
|
|
|
|
|
|
static $gift_prices = [];
|
|
|
|
|
|
|
|
|
|
|
|
if (!isset($gift_prices[$gift_id])) {
|
|
|
|
|
|
$gift_prices[$gift_id] = db::name("vs_gift")
|
|
|
|
|
|
->where(['gid' => $gift_id])
|
|
|
|
|
|
->value('gift_price');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $gift_prices[$gift_id];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 巡乐会抽奖-处理中奖通知
|
|
|
|
|
|
*/
|
2025-10-14 10:21:22 +08:00
|
|
|
|
private function handlePrizeNotification($user_id,$gift_id, $room_id, $pan_xlh_num, $end_time,$locking_gift_id)
|
2025-10-12 10:09:07 +08:00
|
|
|
|
{
|
|
|
|
|
|
$FromUserInfo = db::name('user')->field('nickname,avatar')->where(['id' => $user_id])->find();
|
|
|
|
|
|
$gift_info = db::name('vs_gift')->field('gift_name')->where(['gid' => $gift_id])->find();
|
|
|
|
|
|
$locking_gift = db::name('vs_gift')->field('gift_name')->where(['gid' => $locking_gift_id])->find();
|
2025-10-14 10:21:22 +08:00
|
|
|
|
$room_data = db::name('vs_room')->field('room_name,user_id')->where(['id' => $room_id])->find();
|
|
|
|
|
|
$room_user = db::name('user')->where('id',$room_data['user_id'])->field('nickname,avatar')->find();
|
2025-10-12 10:09:07 +08:00
|
|
|
|
//锁定礼物
|
|
|
|
|
|
|
2025-10-15 16:12:22 +08:00
|
|
|
|
// $text = [
|
|
|
|
|
|
// 'gift_num' => $pan_xlh_num,
|
|
|
|
|
|
// 'FromUserInfo' => $FromUserInfo,
|
|
|
|
|
|
// 'end_time' => $end_time,
|
|
|
|
|
|
// 'text' => $FromUserInfo['nickname'] . ' 在 ' . ' 巡乐会活动中 获得' . $gift_info['gift_name'] . '礼物 x 1'
|
|
|
|
|
|
// ];
|
|
|
|
|
|
//
|
|
|
|
|
|
// model('Chat')->sendMsg(1057, $room_id, $text);
|
2025-10-12 10:09:07 +08:00
|
|
|
|
|
|
|
|
|
|
//推送礼物横幅
|
2025-10-14 10:21:22 +08:00
|
|
|
|
$text = $FromUserInfo['nickname'] .' 巡乐会活动中锁定礼物'.$locking_gift['gift_name'].' x ' .$pan_xlh_num ;
|
2025-10-12 10:09:07 +08:00
|
|
|
|
$push = new Push(0, $room_id);
|
|
|
|
|
|
$text_list_new = [
|
|
|
|
|
|
'text' => $text,
|
|
|
|
|
|
'room_id' => $room_id,
|
2025-10-15 10:20:50 +08:00
|
|
|
|
'from_type' => 103,
|
2025-10-14 10:21:22 +08:00
|
|
|
|
'gift_num' => $pan_xlh_num,
|
|
|
|
|
|
'FromUserInfo' => $FromUserInfo,
|
|
|
|
|
|
'end_time' => $end_time,
|
|
|
|
|
|
'room_user' => $room_user
|
2025-10-12 10:09:07 +08:00
|
|
|
|
];
|
|
|
|
|
|
$push->xunlehui($text_list_new);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 巡乐会抽奖-构建返回结果
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function buildResult($drawn_gifts)
|
|
|
|
|
|
{
|
|
|
|
|
|
$result_list = [];
|
|
|
|
|
|
$gift_info_cache = [];
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($drawn_gifts as $gift_id => $count) {
|
|
|
|
|
|
if (!isset($gift_info_cache[$gift_id])) {
|
|
|
|
|
|
$gift_info = db::name('vs_gift')
|
|
|
|
|
|
->where(['gid' => $gift_id])
|
|
|
|
|
|
->find();
|
|
|
|
|
|
$gift_info_cache[$gift_id] = $gift_info;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$gift_info = $gift_info_cache[$gift_id];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$result_list[] = [
|
|
|
|
|
|
'gift_id' => $gift_id,
|
|
|
|
|
|
'gift_name' => $gift_info['gift_name'],
|
|
|
|
|
|
'base_image' => $gift_info['base_image'],
|
|
|
|
|
|
'gift_price' => $gift_info['gift_price'],
|
|
|
|
|
|
'count' => $count,
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 按价格降序排序
|
|
|
|
|
|
usort($result_list, function($a, $b) {
|
|
|
|
|
|
return $b['gift_price'] <=> $a['gift_price'];
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return ['code' => 1, 'msg' => '成功', 'data' => $result_list];
|
|
|
|
|
|
}
|
|
|
|
|
|
//错误日志记录-------------------------------------------------------
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 记录抽奖错误日志到Redis
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function recordDrawErrorToRedis($expectedCount, $actualCount, $room_id, $user_id, $gift_bag_id, $num, $gift_user_ids, $precomputedResults)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查Redis连接是否可用
|
|
|
|
|
|
if (!$this->redis) {
|
|
|
|
|
|
Log::record('Redis连接不可用,无法记录错误日志', 'error');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
$errorData = [
|
|
|
|
|
|
'timestamp' => time(),
|
|
|
|
|
|
'expected_count' => $expectedCount,
|
|
|
|
|
|
'actual_count' => $actualCount,
|
|
|
|
|
|
'room_id' => $room_id,
|
|
|
|
|
|
'user_id' => $user_id,
|
|
|
|
|
|
'gift_bag_id' => $gift_bag_id,
|
|
|
|
|
|
'num' => $num,
|
|
|
|
|
|
'gift_user_ids' => $gift_user_ids,
|
|
|
|
|
|
'precomputed_results_count' => count($precomputedResults),
|
|
|
|
|
|
'precomputed_results' => $precomputedResults,
|
|
|
|
|
|
'error_type' => 'draw_count_mismatch'
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// 使用正确的Redis方法存储数据
|
|
|
|
|
|
$key = 'blind_box_draw_world_errors_' . date('Y-m-d-H-i-s');
|
|
|
|
|
|
$this->redis->setex($key, 86400 * 7, json_encode($errorData));
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
Log::record('Redis操作失败: ' . $e->getMessage(), 'error');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-13 11:26:30 +08:00
|
|
|
|
|
2025-10-12 10:09:07 +08:00
|
|
|
|
}
|