Files
chatroom/app/Jobs/CloseBaccaratRoundJob.php

180 lines
6.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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);
}
}