Files
midi-php/application/api/model/BlindBoxTurntableGiftDrawWorld.php

1530 lines
59 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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;
}
}
/**
* 重构后的抽奖方法 - 优化响应速度
*/
public function draw_gift($gift_bag_id, $user_id, $gift_user_ids, $num = 1, $room_id = 0, $heart_id = 0,$auction_id = 0)
{
// 最大重试次数
$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;
}
// 添加以下检查以防止 null 错误
if (!isset($loadResult['data']) || !is_array($loadResult['data'])) {
return ['code' => 0, 'msg' => '数据加载失败', 'data' => null];
}
['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'];
$addcurrentXlhPeriodsNum = $precomputeResult['data']['addcurrentXlhPeriodsNum'];
$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,
$addcurrentXlhPeriodsNum,
$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];
}
}
}
/**
* 验证抽奖参数
*/
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();
if (!$bag_data || !is_array($bag_data) || !isset($bag_data['gift_price'])) {
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,
// 'user_waller' => $user_waller
]
];
}
/**
* 预计算抽奖结果
*/
private function precomputeDrawResults($bag_data, $gift_user_ids, $num)
{
$toarray = explode(',', $gift_user_ids);
// 1. 计算奖池信息
$poolInfo = $this->calculatePoolInfo($bag_data['id']);
if ($poolInfo['code'] !== 1) {
return $poolInfo;
}
$totalRemaining = $poolInfo['data']['total_remaining'];//奖池剩余数量
$periods = $poolInfo['data']['periods']; //奖池期数
$totalDrawTimes = $poolInfo['data']['total_draw_times']; //总抽奖次数
// 2. 获取可用礼物
$availableGifts = $this->getAvailableGifts($bag_data['id'], $totalDrawTimes);
if (empty($availableGifts)) {
$availableGifts = $this->resetPoolAndReload($bag_data['id']); //重置奖池并重新加载
if (empty($availableGifts)) {
throw new \Exception('重置奖池后仍无可用礼物');
}
$totalDrawTimes = 0;//总抽奖次数重置
}
// 在处理奖池重置逻辑之前添加检查
if (!is_array($availableGifts)) {
throw new \Exception('可用礼物数据格式错误');
}
// 3. 预加载礼物信息(减少后续查询)
$giftInfoMap = $this->preloadGiftInfo($availableGifts);
// 4. 处理奖池重置逻辑 - 优化版本
$needGiftNum = count($toarray) * $num; //总需要的礼物数
$remaining_available_gifts = [];
$remainingGiftCount = array_sum(array_column($availableGifts, 'remaining_number'));
if ($remainingGiftCount < $needGiftNum) {
// 如果当前奖池礼物不足以满足需求
// 保存当前剩余礼物作为上期剩余
$remaining_available_gifts = $availableGifts;
// 重置奖池
$availableGifts = $this->resetPoolAndReload($bag_data['id']);
if (empty($availableGifts)) {
throw new \Exception('重置奖池后仍无可用礼物');
}
$totalDrawTimes = 0; // 总抽奖次数重置
}
$current_xlh_periods_num = $this->getCachedXlhPeriodsNum("get");
// 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'],
'addcurrentXlhPeriodsNum' => $precomputedResults['addcurrentXlhPeriodsNum'],
]
];
}
/**
* 使用Alias Method预计算抽奖结果O(1)复杂度)
*/
private function precomputeResultsWithAliasMethod(
$toarray,
$num,
$availableGifts,
$giftInfoMap,
&$totalDrawTimes,
&$periods,
&$currentXlhPeriodsNum,
$remaining_available_gifts
) {
if (!is_array($toarray) || !is_array($availableGifts) || !is_array($giftInfoMap)) {
throw new \Exception('预计算参数格式错误');
}
$precomputedResults = [];
$precomputedResultss = [];
$addcurrentXlhPeriodsNum = 0;
// 计算上期剩余礼物总数
$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;
if (empty($gift)) {
$selectedGift = $this->selectGiftWithAliasMethod($aliasTableForRemaining);
$gift = $giftInfoMap[$selectedGift['foreign_id']] ?? null;
}
if(!$gift){
throw new \Exception('礼物信息不存在');
}
$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++;
$addcurrentXlhPeriodsNum++;
// 更新Alias表
$this->updateAliasTable($aliasTableForRemaining, $selectedGift['id']);
}
}
}
}
// 再从新奖池中分配剩余所需礼物
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++;
}
}
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++;
$addcurrentXlhPeriodsNum++;
// 更新Alias表
$this->updateAliasTable($aliasTableForNew, $selectedGift['id']);
}
}
}
}
return ['precomputedResults' => $precomputedResults, 'precomputedResultss' => $precomputedResultss, 'addcurrentXlhPeriodsNum' => $addcurrentXlhPeriodsNum];
}
/**
* 构建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;
}
}
/**
* 执行抽奖事务(核心操作)
*/
private function executeDrawTransaction($bag_data, $user_id, $room_id, $num, $precomputedResults,$availableGiftss,$gift_user_ids,$heart_id,$auction_id)
{
$gift_user_num = count(explode(',', $gift_user_ids)); //人数
$bagGiftPrice = $bag_data['gift_price'] * $num * $gift_user_num;
// 增加重试机制
$maxRetries = 3;
for ($retry = 0; $retry < $maxRetries; $retry++) {
try {
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('添加盲盒转盘记录失败');
}
// 3. 批量更新库存按ID排序避免死锁
$this->batchUpdateGiftInventory($availableGiftss, $room_id);
// 4. 批量插入礼包发放记录
$this->batchInsertGiftBagReceiveLog($user_id, $boxTurntableLog, $bag_data, $room_id, $precomputedResults);
// 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']);
}
db::commit();
// 统计礼物数量
$giftCounts = $this->countGifts($precomputedResults);
return [
'code' => 1,
'msg' => '事务执行成功',
'data' => [
'log_id' => $boxTurntableLog,
'gift_counts' => $giftCounts
]
];
} 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];
}
}
return ['code' => 0, 'msg' => '操作超时,请重试', 'data' => null];
}
/**
* 批量更新礼物库存
*/
private function batchUpdateGiftInventory($precomputedResults, $room_id)
{
// 按礼物ID分组统计需要减少的数量
$inventoryUpdates = [];
foreach ($precomputedResults as $result) {
$giftId = $result['gift_bag_detail']['id']??0;
$inventoryUpdates[$giftId] = ($inventoryUpdates[$giftId] ?? 0) + 1;
}
// 按ID排序避免死锁
ksort($inventoryUpdates);
// 批量更新
foreach ($inventoryUpdates as $giftId => $count) {
$ret = db::name("vs_gift_bag_detail")->where('id',$giftId)
->setDec('remaining_number', $count);
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)) {
$insertResult = db::name("vs_gift_bag_receive_pan_log")->insertAll($batchInsertData);
if (!$insertResult) {
throw new \Exception('插入礼包发放记录失败');
}
}
}
/**
* 扣除用户金币
*/
private function deductUserCoins($user_id, $bagGiftPrice, $room_id)
{
// 使用悲观锁查询用户钱包
$userWallet = db::name('user_wallet')
->where(['user_id' => $user_id])
->find();
if (!$userWallet || $userWallet['coin'] < $bagGiftPrice) {
throw new \Exception('用户金币不足');
}
$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,
$addcurrentXlhPeriodsNum,
$room
) {
// 1. 批量插入盲盒转盘结果记录
$this->batchInsertBlindBoxResults($precomputedResults, $boxTurntableLog);
// 3. 处理巡乐会相关操作
if (!empty($xlh_ext) && $xlh_ext['inlet_bag_id'] == $precomputedResults[0]['gift_bag_detail']['gift_bag_id']) {
$this->handleXlhOperations($room_id, $xlh_ext, $currentXlhPeriodsNum,$addcurrentXlhPeriodsNum,$room);
}
}
/**
* 批量插入盲盒转盘结果记录
*/
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);
}
}
/**
* 发送礼物给接收者
*/
private function sendGiftsToRecipients($precomputedResults, $room_id,$user_id,$heart_id,$auction_id)
{
// 统计每个用户每个礼物的数量
$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
];
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
);
}
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];
}
/**
* 处理巡乐会相关操作
*/
private function handleXlhOperations($room_id, $xlh_ext, $currentXlhPeriodsNum,$addcurrentXlhPeriodsNum,$room)
{
$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;
}
if($xlhPeriodsNum < $xlh_ext['open_condition']['start_num'] && $currentXlhPeriodsNum >= $xlh_ext['open_condition']['start_num']){
$xlhIsPiaoPing = 2;
}
// 更新房间巡乐会次数
$this->getCachedXlhPeriodsNum("set",$addcurrentXlhPeriodsNum);
// 处理飘屏
if ($xlhIsPiaoPing == 1 || $xlhIsPiaoPing == 2) {
$this->handleXlhPiaoPing($room_id, $xlh_ext, $xlhIsPiaoPing);
}
$this->updateAndPushXlhStatus($room_id, $xlh_ext, $currentXlhPeriodsNum);
}
private function handleXlhPiaoPing($room_id, $xlh_ext, $xlhIsPiaoPing){
if($xlhIsPiaoPing == 1){
// 即将开始推送飘屏
$text = "巡乐会即将开始...";
// 推送礼物横幅
$push = new Push(UID, $room_id);
$text_list_new = [
'text' => $text,
'room_id' => $room_id,
'from_type' => 101
];
$push->xunlehui($text_list_new);
}
if($xlhIsPiaoPing == 2){
// 正式开始推送飘屏
$text = "巡乐会游戏已正式开启...";
// 推送礼物横幅
$push = new Push(UID, $room_id);
$text_list_new = [
'text' => $text,
'room_id' => $room_id,
'from_type' => 102
];
$push->xunlehui($text_list_new);
// 巡乐会正式开始
$this_xlh_periods = $this->getCachedXlhPeriods('get');
$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' => []];
}
$this->getCachedXlhPeriods('set', $this_xlh_periods+1);//修改巡乐会期数
}
}
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:等待开始
//查询巡乐会信息
$pan_xlh = db::name('vs_room_pan_xlh')->where('send_time',0)->order('id desc')->find();
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();
}
$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;
}
// 推送进度条
$push = new Push(UID, $room_id);
$text_list_new = [
'xlh_data' => $xlh,
'text' => "",
'room_id' => $room_id,
'from_type' => 100
];
$push->xunlehui($text_list_new);
// 聊天室推送系统消息
// 推送
// $text = [
// 'xlh_data' => $xlh,
// 'text' => ""
// ];
// model('Chat')->sendMsg(1056,$room_id,$text);
}
/**
* 统计礼物数量
*/
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;
$totalDrawTimes = max(0, $totalQuantity - $totalRemaining); //总抽奖次数
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;
}
/**
* 获取缓存的巡乐会开启次数
*/
private function getCachedXlhPeriodsNum($type="get",$num=1) {
$cacheKey = "xlh_periods_num";
$xlh_periods_num = Cache::get($cacheKey) ?? 0;
if($type=="set"){
$xlh_periods_num = $xlh_periods_num + $num;
Cache::set($cacheKey, $xlh_periods_num, 0);
}
return $xlh_periods_num;
}
/**
* 获取缓存的巡乐会期数
*/
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;
}
/**
* 重置奖池
* @param int $room_id 房间ID
* @param int $gift_bag_id 礼物包ID
*/
private function reset_gift_pool($gift_bag_id,$remaining_available_gifts=[]) {
$bag_detail = db::name("vs_gift_bag_detail")->where('gift_bag_id',$gift_bag_id)->select();
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')
->where(['send_time' => 0])
->order('id', 'desc')
->find();
if (empty($pan_xlh)) {
return ['code' => 0, 'msg' => '本轮未开始', 'data' => null];
}
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])
->where(['remaining_number' => ['>', 0]])
->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'));
$total_draw_times = $total_quantity - $total_remaining; // 已抽奖次数
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);
$available_gifts = $this->getAvailableGifts($gift_bag_id);
}
// 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 = []; // 存储所有抽奖结果
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('扣除用户金币失败');
}
$user_level = model('Level')->user_level_data_update(
$user_id,
$bag_gift_price,
1,
$room_id
);
if (!$user_level) {
throw new \Exception('用户等级更新失败');
}
}
// 处理当前批次的抽奖
$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;
}
// 记录完整结果
$all_results[] = [
'gift_id' => $gift_id,
'gift_detail_id' => $selected_gift['id'],
'gift_bag_detail' => $selected_gift
];
// 更新可用礼物缓存
foreach ($available_gifts as &$gift) {
if ($gift['id'] == $selected_gift['id']) {
$gift['remaining_number']--;
break;
}
}
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('重置奖池后仍无可用礼物');
}
}
}
// 批量更新库存
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);
}
// 处理主奖品更新
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;
}else{
$is_zhong_jiang = 0;
}
$total_processed += $current_batch;
}
// 7. 批量处理结果记录
if(count($drawn_gifts) > $num){
$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));
return ['code' => 0, 'msg' => "抽奖中,请稍等...", 'data' => null];
}
// 批量插入礼包发放记录
$gift_records = [];
$periods = $this->getCachedXlhPeriods('get') ?? 0;
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)) {
db::name("vs_gift_bag_receive_pan_log")->insertAll($gift_records);
}
// 批量处理用户礼物包
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();
$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,
];
$this->redis->setex($key, 86400 * 7, $e->getMessage(). ' ' .json_encode($errorData));
return ['code' => 0, 'msg' => "抽奖中,请稍等...", 'data' => null];
}
// 8. 处理推送消息
if ($is_zhong_jiang == 1) {
$this->handlePrizeNotification($user_id,$gift_id, $room_id, $pan_xlh_num, $end_time,$ext['locking_condition']['locking_gift_id']);
}
// 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)
{
$cache_key = 'selected_gift_id_' . $ext['locking_condition']['selected_gift_id'];
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];
}
/**
* 巡乐会抽奖-处理中奖通知
*/
private function handlePrizeNotification($user_id,$gift_id, $room_id, $pan_xlh_num, $end_time,$locking_gift_id)
{
$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();
$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();
//锁定礼物
// $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);
//推送礼物横幅
$text = $FromUserInfo['nickname'] .' 巡乐会活动中锁定礼物'.$locking_gift['gift_name'].' x ' .$pan_xlh_num ;
$push = new Push(0, $room_id);
$text_list_new = [
'text' => $text,
'room_id' => $room_id,
'from_type' => 103,
'gift_num' => $pan_xlh_num,
'FromUserInfo' => $FromUserInfo,
'end_time' => $end_time,
'room_user' => $room_user
];
$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');
}
}
}