Files
chatroom/app/Jobs/CloseHorseRaceJob.php
lkddi f45483bcba 功能更新与UI优化:游戏图标移除、用户名片修复、婚礼红包界面重设计
- 移除聊天室右下角浮动游戏图标(占卜、百家乐、赛马、老虎机)
- 用户名片按钮区:修复已婚/已好友时按钮换行问题,统一单行显示
- 婚礼红包弹窗:重设计为喜庆鲜红背景,领取按钮改为圆形米黄样式
- 新增婚礼红包恢复接口(/wedding/pending-envelopes),刷新后自动恢复领取按钮
- 修复 Alpine :style 字符串覆盖静态 style 导致圆形按钮失效的问题
- 撤职后用户等级改为根据经验值重新计算,不再无条件重置为1
- 管理员修改用户经验值后自动重算等级,有职务用户等级锁定
- 娱乐大厅钓鱼游戏按钮直接调用 startFishing() 简化操作流程
- 新增赛马、占卜、百家乐游戏及相关后端逻辑
2026-03-03 23:19:59 +08:00

172 lines
5.0 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\HorseRaceSettled;
use App\Events\MessageSent;
use App\Models\GameConfig;
use App\Models\HorseBet;
use App\Models\HorseRace;
use App\Services\ChatStateService;
use App\Services\UserCurrencyService;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\DB;
class CloseHorseRaceJob implements ShouldQueue
{
use Queueable;
/**
* 最大重试次数。
*/
public int $tries = 3;
/**
* @param HorseRace $race 要结算的场次
*/
public function __construct(
public readonly HorseRace $race,
) {}
/**
* 执行结算逻辑。
*/
public function handle(
UserCurrencyService $currency,
ChatStateService $chatState,
): void {
$race = $this->race->fresh();
// 防止重复结算
if (! $race || $race->status !== 'running') {
return;
}
// CAS 改状态为 settled
$updated = HorseRace::query()
->where('id', $race->id)
->where('status', 'running')
->update(['status' => 'settled', 'settled_at' => now()]);
if (! $updated) {
return;
}
$race->refresh();
$config = GameConfig::forGame('horse_racing')?->params ?? [];
$houseTake = (int) ($config['house_take_percent'] ?? 5);
$winnerId = (int) $race->winner_horse_id;
// 按马匹统计各匹下注金额
$horsePools = HorseBet::query()
->where('race_id', $race->id)
->groupBy('horse_id')
->selectRaw('horse_id, SUM(amount) as pool')
->pluck('pool', 'horse_id')
->toArray();
$totalPool = array_sum($horsePools);
$winnerPool = (int) ($horsePools[$winnerId] ?? 0);
$netPool = (int) ($totalPool * (1 - $houseTake / 100));
// 结算:遍历所有下注记录
$bets = HorseBet::query()
->where('race_id', $race->id)
->where('status', 'pending')
->with('user')
->get();
$totalPayout = 0;
DB::transaction(function () use ($bets, $winnerId, $netPool, $winnerPool, $currency, &$totalPayout) {
foreach ($bets as $bet) {
if ((int) $bet->horse_id !== $winnerId) {
// 未中奖(本金已在下注时扣除)
$bet->update(['status' => 'lost', 'payout' => 0]);
continue;
}
// 中奖:按注额比例分配净注池
if ($winnerPool > 0) {
$payout = (int) round($netPool * ($bet->amount / $winnerPool));
} else {
$payout = 0;
}
$bet->update(['status' => 'won', 'payout' => $payout]);
if ($payout > 0 && $bet->user) {
$currency->change(
$bet->user,
'gold',
$payout,
CurrencySource::HORSE_WIN,
"赛马 #{$this->race->id}{$bet->horse_id}号马」中奖",
);
}
$totalPayout += $payout;
}
});
// 公屏公告
$this->pushResultMessage($race, $chatState, $totalPayout);
// 广播结算事件
broadcast(new HorseRaceSettled($race));
}
/**
* 向公屏发送赛果系统消息。
*/
private function pushResultMessage(HorseRace $race, ChatStateService $chatState, int $totalPayout): void
{
// 找出胜利马匹名称
$horses = $race->horses ?? [];
$winnerName = '未知';
foreach ($horses as $horse) {
if (($horse['id'] ?? 0) === (int) $race->winner_horse_id) {
$winnerName = ($horse['emoji'] ?? '').($horse['name'] ?? '');
break;
}
}
$payoutText = $totalPayout > 0
? '共派发 🪙'.number_format($totalPayout).' 金币'
: '本场无人获奖';
$content = "🏆 【赛马】第 #{$race->id} 场结束!冠军:{$winnerName}{$payoutText}";
$msg = [
'id' => $chatState->nextMessageId(1),
'room_id' => 1,
'from_user' => '系统传音',
'to_user' => '大家',
'content' => $content,
'is_secret' => false,
'font_color' => '#f59e0b',
'action' => '大声宣告',
'sent_at' => now()->toDateTimeString(),
];
$chatState->pushMessage(1, $msg);
broadcast(new MessageSent(1, $msg));
SaveMessageJob::dispatch($msg);
}
}