盲盒转盘-岁月之城,时空之旅,抽奖结果转落包。
This commit is contained in:
@@ -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];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user