Files
chatroom/app/Jobs/CloseBaccaratRoundJob.php

180 lines
6.5 KiB
PHP
Raw Normal View History

<?php
/**
* 文件功能:百家乐结算队列任务
*
* 在押注截止时间到达后自动触发:
* 摇三颗骰子 判断大//豹子/庄家收割
* 遍历下注记录逐一发放赔付 广播结果 公屏公告
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace App\Jobs;
use App\Enums\CurrencySource;
use App\Events\BaccaratRoundSettled;
use App\Events\MessageSent;
use App\Models\BaccaratBet;
use App\Models\BaccaratRound;
use App\Models\GameConfig;
use App\Services\ChatStateService;
use App\Services\UserCurrencyService;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\DB;
class CloseBaccaratRoundJob implements ShouldQueue
{
use Queueable;
/**
* 最大重试次数。
*/
public int $tries = 3;
/**
* @param BaccaratRound $round 要结算的局次
*/
public function __construct(
public readonly BaccaratRound $round,
) {}
/**
* 执行结算逻辑。
*/
public function handle(
UserCurrencyService $currency,
ChatStateService $chatState,
): void {
$round = $this->round->fresh();
// 防止重复结算
if (! $round || $round->status !== 'betting') {
return;
}
$config = GameConfig::forGame('baccarat')?->params ?? [];
// 乐观锁CAS 先把状态改为 settling
$updated = BaccaratRound::query()
->where('id', $round->id)
->where('status', 'betting')
->update(['status' => 'settling']);
if (! $updated) {
return; // 已被其他进程处理
}
// ── 摇骰子 ──────────────────────────────────────────────────
$dice = [random_int(1, 6), random_int(1, 6), random_int(1, 6)];
$total = array_sum($dice);
// ── 判断结果 ────────────────────────────────────────────────
$killPoints = $config['kill_points'] ?? [3, 18];
if (! is_array($killPoints)) {
$killPoints = explode(',', (string) $killPoints);
}
$killPoints = array_map('intval', $killPoints);
$result = match (true) {
$dice[0] === $dice[1] && $dice[1] === $dice[2] => 'triple', // 豹子(优先判断)
in_array($total, $killPoints, true) => 'kill', // 庄家收割
$total >= 11 && $total <= 17 => 'big', // 大
default => 'small', // 小
};
// ── 结算下注记录 ─────────────────────────────────────────────
$bets = BaccaratBet::query()->where('round_id', $round->id)->where('status', 'pending')->with('user')->get();
$totalPayout = 0;
DB::transaction(function () use ($bets, $result, $config, $currency, &$totalPayout) {
foreach ($bets as $bet) {
if ($result === 'kill') {
// 庄家收割:全灭无退款
$bet->update(['status' => 'lost', 'payout' => 0]);
continue;
}
if ($bet->bet_type === $result) {
// 中奖:计算赔付(含本金返还)
$payout = BaccaratRound::calcPayout($bet->bet_type, $bet->amount, $config);
$bet->update(['status' => 'won', 'payout' => $payout]);
// 金币入账
$currency->change(
$bet->user,
'gold',
$payout,
CurrencySource::BACCARAT_WIN,
"百家乐 #{$this->round->id}{$bet->betTypeLabel()} 中奖",
);
$totalPayout += $payout;
} else {
$bet->update(['status' => 'lost', 'payout' => 0]);
}
}
});
// ── 更新局次记录 ─────────────────────────────────────────────
$round->update([
'dice1' => $dice[0],
'dice2' => $dice[1],
'dice3' => $dice[2],
'total_points' => $total,
'result' => $result,
'status' => 'settled',
'settled_at' => now(),
'total_payout' => $totalPayout,
]);
$round->refresh();
// ── 广播结算事件 ─────────────────────────────────────────────
broadcast(new BaccaratRoundSettled($round));
// ── 公屏公告 ─────────────────────────────────────────────────
$this->pushResultMessage($round, $chatState);
}
/**
* 向公屏发送开奖结果系统消息。
*/
private function pushResultMessage(BaccaratRound $round, ChatStateService $chatState): void
{
$diceStr = "{$round->dice1}】【{$round->dice2}】【{$round->dice3}";
$resultText = match ($round->result) {
'big' => "🔵 大({$round->total_points} 点)",
'small' => "🟡 小({$round->total_points} 点)",
'triple' => "💥 豹子!({$round->dice1}{$round->dice1}{$round->dice1}",
'kill' => "☠️ 庄家收割!({$round->total_points} 点)全灭",
default => '',
};
$payoutText = $round->total_payout > 0
? '共派发 🪙'.number_format($round->total_payout).' 金币'
: '本局无人获奖';
$content = "🎲 【百家乐】第 #{$round->id} 局开奖!{$diceStr} 总点 {$round->total_points}{$resultText}{$payoutText}";
$msg = [
'id' => $chatState->nextMessageId(1),
'room_id' => 1,
'from_user' => '系统传音',
'to_user' => '大家',
'content' => $content,
'is_secret' => false,
'font_color' => '#8b5cf6',
'action' => '大声宣告',
'sent_at' => now()->toDateTimeString(),
];
$chatState->pushMessage(1, $msg);
broadcast(new MessageSent(1, $msg));
SaveMessageJob::dispatch($msg);
}
}