From 396dfeaa07a6bb8d3ae6d6969fa3e392932bb691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E9=92=8A?= Date: Tue, 21 Oct 2025 09:52:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E9=9C=80=E6=B1=82-=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E4=BC=98=E5=8C=96-=E7=9B=B2=E7=9B=92=E8=BD=AC=E7=9B=98?= =?UTF-8?q?=E8=B6=85=E6=8A=BD=E9=97=AE=E9=A2=98=E8=A7=A3=E5=86=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/BlindBoxTurntableGiftDrawWorld.php | 291 +++++++++--------- 1 file changed, 149 insertions(+), 142 deletions(-) diff --git a/application/api/model/BlindBoxTurntableGiftDrawWorld.php b/application/api/model/BlindBoxTurntableGiftDrawWorld.php index 87c42c2..b205a7f 100644 --- a/application/api/model/BlindBoxTurntableGiftDrawWorld.php +++ b/application/api/model/BlindBoxTurntableGiftDrawWorld.php @@ -37,98 +37,116 @@ class BlindBoxTurntableGiftDrawWorld extends Model 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']; + 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; + // 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]; + } + foreach ($precomputedResults as $result) { + $key = $result['gift_user_id'] . '_' . $result['gift_bag_detail']['foreign_id']; + if (!isset($giftUserCountsJianCha[$key])) { + $giftUserCountsJianCha[$key] = [ + 'gift_user_id' => $result['gift_user_id'], + 'gift_id' => $result['gift_bag_detail']['foreign_id'], + 'count' => 0, + 'gift_price' => $result['gift']['gift_price'], + 'quantity' => $result['gift_bag_detail']['quantity'], + ]; } - $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) { + $giftUserCountsJianCha[$key]['count']++; + if($giftUserCountsJianCha[$key]['count'] > $result['gift_bag_detail']['quantity']){ // 记录错误到Redis - $this->recordDrawErrorToRedis($expectedCount, count($precomputedResults), $room_id, $user_id, $gift_bag_id, $num, $gift_user_ids, $precomputedResults); + // 使用正确的Redis方法存储数据 + $key = 'blind_box_draw_world_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, + 'giftUserCountsJianCha' => $giftUserCountsJianCha, + ]; + $this->redis->setex($key, 86400 * 7, "礼物数量超出限制 ".json_encode($errorData)); 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]; } + // 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)); + } + return ['code' => 0, 'msg' => "网络加载失败,请重试!", 'data' => null]; } } /** @@ -495,65 +513,55 @@ class BlindBoxTurntableGiftDrawWorld extends Model $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); + 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() - ]); + // 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]; + 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(); + return ['code' => 0, 'msg' => $e->getMessage(), 'data' => null]; } - return ['code' => 0, 'msg' => '操作超时,请重试', 'data' => null]; } /** @@ -692,7 +700,6 @@ class BlindBoxTurntableGiftDrawWorld extends Model } $giftUserCounts[$key]['count']++; } - // 批量插入 $batchInsertData = []; foreach ($giftUserCounts as $userGift) {