初始化
This commit is contained in:
158
app/service/DrawService.php
Normal file
158
app/service/DrawService.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
// 抽奖核心服务
|
||||
namespace app\service;
|
||||
|
||||
use addons\webman\model\DrawRecord;
|
||||
use addons\webman\model\Game;
|
||||
use addons\webman\model\Prize;
|
||||
use Exception;
|
||||
use support\Db;
|
||||
use support\Log;
|
||||
|
||||
class DrawService
|
||||
{
|
||||
/**
|
||||
* 执行抽奖
|
||||
* @param $player
|
||||
* @param int $gameId
|
||||
* @param int $departmentId
|
||||
* @param string $ip 用户IP
|
||||
* @return array 抽奖结果
|
||||
*/
|
||||
public function execute( $player, int $gameId, int $departmentId, string $ip): array
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
// 1. 查询有效奖品(加行锁防止并发问题)
|
||||
$prizes = Prize::query()->where('game_id', $gameId)
|
||||
->where('department_id', $departmentId)
|
||||
->where('status', 1)
|
||||
->where('total_remaining', '>', 0)
|
||||
->where('daily_remaining', '>', 0)
|
||||
->lockForUpdate()
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
if (empty($prizes)) {
|
||||
DB::rollBack();
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => '当前游戏不可用'
|
||||
];
|
||||
}
|
||||
|
||||
$game = Game::query()->select('id', 'game_type', 'consume')->where('id', $gameId)->first()->toArray();
|
||||
// 2. 计算中奖结果
|
||||
$result = $this->calculateWinning($player,$prizes, $game['consume']);
|
||||
$prizeId = $result['prize_id'];
|
||||
$prizeName = $result['prize_name'];
|
||||
$prizeType = $result['prize_type'];
|
||||
|
||||
$game['department_id'] = $departmentId;
|
||||
// 3. 记录抽奖信息
|
||||
$this->createRecord($player->id, $result, $game, $ip);
|
||||
|
||||
if ($result['prize_type'] == 3) {
|
||||
$message = '很遗憾,未中奖';
|
||||
} else {
|
||||
$message = '恭喜获得' . $prizeName;
|
||||
}
|
||||
DB::commit();
|
||||
return [
|
||||
'prize_id' => $prizeId,
|
||||
'prize_name' => $prizeName,
|
||||
'prize_type' => $prizeType,
|
||||
'consume' => $game['consume'],
|
||||
'message' => $message
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
Log::error("抽奖失败:{$e->getMessage()}", [
|
||||
'user_id' => $player->id,
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => '抽奖过程异常,请稍后再试'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算中奖结果
|
||||
* @param array $prizes 有效奖品列表
|
||||
* @return array 中奖信息
|
||||
* @throws Exception
|
||||
*/
|
||||
private function calculateWinning($player, array $prizes, $consume): array
|
||||
{
|
||||
// 计算总概率权重
|
||||
$totalProb = array_sum(array_column($prizes, 'probability'));
|
||||
if ($totalProb <= 0) {
|
||||
return ['prize_id' => null, 'type' => Prize::PRIZE_TYPE_LOSE, 'prize_name' => '未中奖', 'prize_pic' => null];
|
||||
}
|
||||
$player->wallet->decrement('money', $consume);
|
||||
// 生成随机数(0到总权重之间)
|
||||
$random = mt_rand(1, $totalProb * 10000) / 10000; // 提高精度
|
||||
|
||||
$currentSum = 0;
|
||||
foreach ($prizes as $prize) {
|
||||
$currentSum += $prize['probability'];
|
||||
if ($random <= $currentSum) {
|
||||
// 检查并扣减库存
|
||||
$this->deductStock($prize['id']);
|
||||
return [
|
||||
'prize_id' => $prize['id'],
|
||||
'prize_type' => $prize['type'],
|
||||
'prize_name' => $prize['name'],
|
||||
'prize_pic' => $prize['pic']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return ['prize_id' => null, 'type' => Prize::PRIZE_TYPE_LOSE, 'prize_name' => '未中奖', 'prize_pic' => null];
|
||||
}
|
||||
|
||||
/**
|
||||
* 扣减库存
|
||||
* @param int $prizeId 奖品ID
|
||||
* @throws Exception
|
||||
*/
|
||||
private function deductStock(int $prizeId): void
|
||||
{
|
||||
$prize = Prize::query()->findOrFail($prizeId);
|
||||
|
||||
// 再次检查库存(防止并发超卖)
|
||||
if ($prize->total_remaining <= 0 || $prize->daily_remaining <= 0) {
|
||||
throw new \Exception("奖品库存不足");
|
||||
}
|
||||
|
||||
$prize->decrement('total_remaining');
|
||||
$prize->decrement('daily_remaining');
|
||||
$prize->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建抽奖记录
|
||||
* @param int $userId 用户ID
|
||||
* @param array|null $prize 奖品
|
||||
* @param string $ip IP地址
|
||||
*/
|
||||
private function createRecord(int $userId, ?array $prize, array $game, string $ip): void
|
||||
{
|
||||
DrawRecord::query()->create([
|
||||
'uid' => $userId,
|
||||
'prize_id' => $prize['prize_id'],
|
||||
'prize_type' => $prize['prize_type'],
|
||||
'prize_name' => $prize['prize_name'],
|
||||
'prize_pic' => $prize['prize_pic'],
|
||||
'game_id' => $game['id'],
|
||||
'consume' => $game['consume'],
|
||||
'game_type' => $game['game_type'],
|
||||
'department_id' => $game['department_id'],
|
||||
'draw_time' => date('Y-m-d H:i:s'),
|
||||
'ip' => $ip
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user