盲盒转盘-岁月之城,时空之旅,抽奖结果转落包。

This commit is contained in:
2025-12-19 18:40:29 +08:00
parent 1c2b981649
commit cd69ab6b73
6 changed files with 391 additions and 18 deletions

View File

@@ -391,16 +391,29 @@ class BlindBoxTurntableGiftDrawWorldNew extends Model
*/
private function resetPoolAndReload($gift_bag_id)
{
// 重置剩余数量
Db::name("vs_gift_bag_detail")
->where(['gift_bag_id' => $gift_bag_id])
->update(['remaining_number' => Db::raw('quantity')]);
$lockKey = "reset_pool_lock_".$gift_bag_id;
// 清除缓存
Cache::rm("pan_gift_bag_detail".$gift_bag_id);
// 尝试获取分布式锁
if ($this->redis->set($lockKey, 1, ['NX', 'EX' => 10])) {
try {
// 重置剩余数量
Db::name("vs_gift_bag_detail")
->where(['gift_bag_id' => $gift_bag_id])
->update(['remaining_number' => Db::raw('quantity')]);
// 重新获取可用礼物
return $this->getAvailableGifts($gift_bag_id);
// 清除缓存
Cache::rm("pan_gift_bag_detail".$gift_bag_id);
// 重新获取可用礼物
return $this->getAvailableGifts($gift_bag_id);
} finally {
// 释放锁
$this->redis->del($lockKey);
}
} else {
// 如果无法获取锁,等待一段时间后重试或返回错误
throw new \Exception('系统繁忙,请稍后再试');
}
}
/**
@@ -496,9 +509,13 @@ class BlindBoxTurntableGiftDrawWorldNew extends Model
*/
private function updateGiftRemainingNumber($gift_bag_detail_id)
{
Db::name("vs_gift_bag_detail")
$result = Db::name("vs_gift_bag_detail")
->where(['id' => $gift_bag_detail_id])
->where('remaining_number', '>', 0) // 确保库存充足
->setDec('remaining_number', 1);
if (!$result) {
throw new \Exception('礼物库存不足');
}
}
/**
@@ -650,7 +667,7 @@ class BlindBoxTurntableGiftDrawWorldNew extends Model
db::startTrans();
// 按照固定顺序处理事务步骤
// 1. 扣除用户金币(优先处理)
$this->deductUserCoins($user_id, $total_price, $room_id);
$this->deductUserCoins($user_id, $total_price, $room_id,$bag_data);
// 2. 创建抽奖记录
$boxTurntableLog = db::name('vs_blind_box_turntable_log')->insertGetId([
'user_id' => $user_id,
@@ -696,7 +713,7 @@ class BlindBoxTurntableGiftDrawWorldNew extends Model
/**
* 扣除用户金币
*/
private function deductUserCoins($user_id, $bagGiftPrice, $room_id)
private function deductUserCoins($user_id, $bagGiftPrice, $room_id , $bag_data=[])
{
// 使用悲观锁查询用户钱包
$userWallet = db::name('user_wallet')
@@ -711,7 +728,7 @@ class BlindBoxTurntableGiftDrawWorldNew extends Model
$room_id,
1,
10,
'盲盒转盘抽奖消耗'
$bag_data['gift_bag_name'] .'抽奖消耗'
);
if (!$walletUpdate) {
throw new \Exception('扣除用户金币失败');
@@ -1175,7 +1192,13 @@ class BlindBoxTurntableGiftDrawWorldNew extends Model
// 批量更新库存
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);
$result = db::name("vs_gift_bag_detail")
->where('id',$detail_id)
->where('remaining_number', '>=', $count) // 确保库存充足
->setDec('remaining_number', $count);
if (!$result) {
throw new \Exception('礼物库存不足');
}
}
// 处理主奖品更新
if (!empty($main_prize_updates)) {
@@ -1405,4 +1428,302 @@ class BlindBoxTurntableGiftDrawWorldNew extends Model
return ['code' => 1, 'msg' => '成功', 'data' => $result_list];
}
/** ***********2025-12-19*****************
* 盲盒转盘-11-12-直接落包抽奖
*/
public function draw_gift_drop_bag($gift_bag_id, $user_id, $num = 1, $room_id = 0)
{
try {
// 参数验证
$validation_result = $this->validateDrawParams($gift_bag_id, $user_id, $num);
if ($validation_result['code'] !== 1) {
return $validation_result;
}
// 获取转盘信息和用户钱包
$bag_data = $this->getCachedGiftBag($gift_bag_id);
$user_wallet = Db::name('user_wallet')->where(['user_id' => $user_id])->find();
if (!$user_wallet) {
return ['code' => 0, 'msg' => '用户钱包不存在', 'data' => null];
}
$total_price = $bag_data['gift_price'] * $num;
if ($user_wallet['coin'] < $total_price) {
return ['code' => 0, 'msg' => '用户金币不足', 'data' => null];
}
// 检查奖池状态并准备礼物
$pool_status = $this->prepareGiftPool($gift_bag_id, $num);
if ($pool_status['code'] !== 1) {
throw new \Exception($pool_status['msg']);
}
$available_gifts = $pool_status['data']['available_gifts'];
$remaining_available_gifts = $pool_status['data']['remaining_gifts'] ?? [];
$periods = $bag_data['periods'];
// 开始事务处理
Db::startTrans();
try {
// 扣除用户金币
$this->deductUserCoins($user_id, $total_price, $room_id, $bag_data);
// 抽取礼物
$draw_result = $this->performGiftDrawing(
$gift_bag_id,
$available_gifts,
$remaining_available_gifts,
$num,
$periods
);
if ($draw_result['code'] !== 1) {
throw new \Exception($draw_result['msg']);
}
$precomputed_results = $draw_result['data']['precomputed_results'];
$drawn_gifts = $draw_result['data']['drawn_gifts'];
$available_giftss = $draw_result['data']['available_giftss'];
// 创建抽奖记录
$log_id = $this->createDrawLog($user_id, $gift_bag_id, $num, $room_id, $bag_data);
if (!$log_id) {
throw new \Exception('添加盲盒转盘记录失败');
}
// 批量更新库存
$this->batchUpdateGiftInventory($gift_bag_id, $available_giftss);
// 批量插入礼包发放记录
$this->batchInsertGiftBagReceiveLog($bag_data, $user_id, $log_id, $room_id, $precomputed_results);
// 发送礼物给用户
$send_result = $this->sendGiftsToRecipientsPack($drawn_gifts, $user_id, $bag_data);
if (isset($send_result['code']) && $send_result['code'] !== 1) {
throw new \Exception($send_result['msg']);
}
$gift_counts = $send_result['data'];
// 插入抽奖结果
$this->batchInsertBlindBoxResults($precomputed_results, $log_id);
Db::commit();
// 返回结果
return $this->buildDrawResult($log_id, $gift_counts);
} catch (\Exception $e) {
Db::rollback();
return ['code' => 0, 'msg' => $e->getMessage(), 'data' => null];
}
} catch (\Exception $e) {
$key = 'blind_box_draw_errors_' . date('Y-m-d-H-i-s');
$error_data = [
'gift_bag_id' => $gift_bag_id,
'user_id' => $user_id,
'num' => $num,
'room_id' => $room_id
];
if ($this->redis) {
$this->redis->setex($key, 86400 * 7, $e->getMessage() . ' ' . json_encode($error_data));
}
return ['code' => 0, 'msg' => "网络加载失败,请重试!", 'data' => null];
}
}
/**
* 验证抽奖参数
*/
private function validateDrawParams($gift_bag_id, $user_id, $num)
{
if (empty($user_id)) {
return ['code' => 0, 'msg' => '用户ID不能为空', 'data' => null];
}
if (empty($gift_bag_id)) {
return ['code' => 0, 'msg' => '盲盒转盘ID不能为空', 'data' => null];
}
if ($num <= 0) {
return ['code' => 0, 'msg' => '抽奖数量必须大于0', 'data' => null];
}
return ['code' => 1, 'msg' => '验证通过', 'data' => null];
}
/**
* 准备奖池
*/
private function prepareGiftPool($gift_bag_id, $num)
{
$pan_total_draw_times = $this->getCachedPanDrawTimes($gift_bag_id);
$pan_total_remaining = $this->getCachedPanTotalRemaining($gift_bag_id);
$available_gifts = $this->getAvailableGifts($gift_bag_id, $pan_total_draw_times + $num);
$remaining_gifts = [];
// 如果没有可用礼物或者剩余数量为0重置奖池
if (empty($available_gifts) || $pan_total_remaining == 0) {
$available_gifts = $this->resetPoolAndReload($gift_bag_id);
if (empty($available_gifts)) {
return ['code' => 0, 'msg' => '奖池无可用礼物', 'data' => null];
}
$this->getCachedPanDrawTimes($gift_bag_id, "clear");
} else if ($pan_total_remaining < $num) {
// 如果剩余数量不足,保存当前剩余礼物作为上期剩余
$remaining_gifts = $available_gifts;
$available_gifts = $this->resetPoolAndReload($gift_bag_id);
if (empty($available_gifts)) {
return ['code' => 0, 'msg' => '奖池无可用礼物', 'data' => null];
}
$this->getCachedPanDrawTimes($gift_bag_id, "clear");
}
return [
'code' => 1,
'msg' => '奖池准备完成',
'data' => [
'available_gifts' => $available_gifts,
'remaining_gifts' => $remaining_gifts
]
];
}
/**
* 执行礼物抽取
*/
private function performGiftDrawing($gift_bag_id, $available_gifts, $remaining_gifts, $num, $periods)
{
$drawn_gifts = [];
$precomputed_results = [];
$available_giftss = [];
$inventory_updates = [];
// 先处理上期剩余的礼物
if (!empty($remaining_gifts)) {
foreach ($remaining_gifts as $remaining_gift) {
$gift_id = $remaining_gift['foreign_id'];
$drawn_gifts[$gift_id] = ($drawn_gifts[$gift_id] ?? 0) + 1;
$precomputed_results[] = [
'gift_id' => $gift_id,
'gift_detail_id' => $remaining_gift['id'],
'gift_bag_detail' => $remaining_gift,
'gift' => Db::name('vs_gift')->where(['gid' => $gift_id])->find(),
'gift_user_id' => 0,
'periods' => $periods,
];
}
}
// 计算还需要抽取的数量
$remaining_count = $num - count($remaining_gifts);
// 抽取新礼物
for ($i = 0; $i < $remaining_count; $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) {
return ['code' => 0, 'msg' => '预计算抽奖失败,重置后无可用礼物', 'data' => null];
}
}
// 记录库存变化
$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;
// 记录完整结果
$result_entry = [
'gift_id' => $gift_id,
'gift_detail_id' => $selected_gift['id'],
'gift_bag_detail' => $selected_gift,
'gift' => Db::name('vs_gift')->where(['gid' => $gift_id])->find(),
'gift_user_id' => 0,
'periods' => $periods,
];
$available_giftss[] = $result_entry;
$precomputed_results[] = $result_entry;
$this->updateCachedGiftBagDetail($gift_bag_id, $selected_gift['id']);
$this->getCachedPanTotalRemaining($gift_bag_id, "set");
$this->getCachedPanDrawTimes($gift_bag_id, "set");
}
// 检查是否需要重置奖池
$total_remaining = $this->getCachedPanTotalRemaining($gift_bag_id);
if ($total_remaining <= 0) {
$new_gifts = $this->resetPoolAndReload($gift_bag_id);
if (empty($new_gifts)) {
return ['code' => 0, 'msg' => '重置奖池后仍无可用礼物', 'data' => null];
}
}
return [
'code' => 1,
'msg' => '抽奖完成',
'data' => [
'drawn_gifts' => $drawn_gifts,
'precomputed_results' => $precomputed_results,
'available_giftss' => $available_giftss
]
];
}
/**
* 创建抽奖日志
*/
private function createDrawLog($user_id, $gift_bag_id, $num, $room_id, $bag_data)
{
return Db::name('vs_blind_box_turntable_log')->insertGetId([
'user_id' => $user_id,
'gift_bag_id' => $bag_data['gift_bag_id'],
'num' => $num,
'room_id' => $room_id,
'bag_price' => $bag_data['gift_price'],
'createtime' => time()
]);
}
/**
* 发送礼物给接收者
*/
private function sendGiftsToRecipientsPack($drawn_gifts,$user_id,$bag_data)
{
$giftCounts =[];
// 批量发送礼物
foreach ($drawn_gifts as $gift_id => $count) {
$res = model('api/UserGiftPack')->change_user_gift_pack(
$user_id,
$gift_id,
$count,
model('api/UserGiftPack')::BLANK_BOX_DRAW_GIFT_GET,
$bag_data['gift_bag_name']."抽奖所得"
);
if ($res['code'] != 1) {
throw new \Exception($res['msg']);
}
$giftCounts[]=[
'gift_id' => $gift_id,
'count' => $count
];
}
return ['code' => 1, 'msg' => '发送礼物成功', 'data' => $giftCounts];
}
}