Files
chatroom/app/Http/Controllers/HorseRaceController.php

225 lines
7.5 KiB
PHP
Raw Normal View History

<?php
/**
* 文件功能:赛马竞猜前台控制器
*
* 提供用户在聊天室内参与赛马的 API 接口:
* - 查询当前场次信息(马匹、注池、赔率)
* - 提交下注(扣除金币 + 写入下注记录)
* - 查询本人下注状态
* - 查询最近历史记录
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace App\Http\Controllers;
use App\Enums\CurrencySource;
use App\Models\GameConfig;
use App\Models\HorseBet;
use App\Models\HorseRace;
use App\Services\UserCurrencyService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class HorseRaceController extends Controller
{
public function __construct(
private readonly UserCurrencyService $currency,
) {}
/**
* 获取当前进行中的场次信息(前端轮询或事件触发后调用)。
*/
public function currentRace(Request $request): JsonResponse
{
$race = HorseRace::currentRace();
if (! $race) {
return response()->json(['race' => null]);
}
$user = $request->user();
$myBet = HorseBet::query()
->where('race_id', $race->id)
->where('user_id', $user->id)
->first();
// 计算各马匹当前注额
$config = GameConfig::forGame('horse_racing')?->params ?? [];
$houseTake = (int) ($config['house_take_percent'] ?? 5);
$horsePools = HorseBet::query()
->where('race_id', $race->id)
->groupBy('horse_id')
->selectRaw('horse_id, SUM(amount) as pool')
->pluck('pool', 'horse_id')
->toArray();
// 计算实时赔率
$horses = $race->horses ?? [];
$horsesWithBets = array_map(function ($horse) use ($horsePools, $houseTake) {
$horsePool = (int) ($horsePools[$horse['id']] ?? 0);
$totalPool = array_sum(array_values($horsePools));
$netPool = $totalPool * (1 - $houseTake / 100);
$odds = $horsePool > 0 ? round($netPool / $horsePool, 2) : null;
return [
'id' => $horse['id'],
'name' => $horse['name'],
'emoji' => $horse['emoji'],
'pool' => $horsePool,
'odds' => $odds,
];
}, $horses);
return response()->json([
'race' => [
'id' => $race->id,
'status' => $race->status,
'bet_closes_at' => $race->bet_closes_at?->toIso8601String(),
'seconds_left' => $race->status === 'betting'
? max(0, (int) now()->diffInSeconds($race->bet_closes_at, false))
: 0,
'horses' => $horsesWithBets,
'total_pool' => $race->total_pool + array_sum(array_values($horsePools)),
'my_bet' => $myBet ? [
'horse_id' => $myBet->horse_id,
'amount' => $myBet->amount,
] : null,
],
]);
}
/**
* 用户提交下注。
*
* 同一场每人限下一注,下注成功后立即扣除金币。
*/
public function bet(Request $request): JsonResponse
{
if (! GameConfig::isEnabled('horse_racing')) {
return response()->json(['ok' => false, 'message' => '赛马竞猜当前未开启。']);
}
$data = $request->validate([
'race_id' => 'required|integer|exists:horse_races,id',
'horse_id' => 'required|integer|min:1',
'amount' => 'required|integer|min:1',
]);
$config = GameConfig::forGame('horse_racing')?->params ?? [];
$minBet = (int) ($config['min_bet'] ?? 100);
$maxBet = (int) ($config['max_bet'] ?? 100000);
if ($data['amount'] < $minBet || $data['amount'] > $maxBet) {
return response()->json(['ok' => false, 'message' => "押注金额须在 {$minBet}~{$maxBet} 金币之间。"]);
}
$race = HorseRace::find($data['race_id']);
if (! $race || ! $race->isBettingOpen()) {
return response()->json(['ok' => false, 'message' => '当前不在下注时间内。']);
}
// 验证马匹 ID 是否有效
$horses = $race->horses ?? [];
$validIds = array_column($horses, 'id');
if (! in_array($data['horse_id'], $validIds, true)) {
return response()->json(['ok' => false, 'message' => '无效的马匹编号。']);
}
$user = $request->user();
$currency = $this->currency;
// 校验余额
if (($user->jjb ?? 0) < $data['amount']) {
return response()->json(['ok' => false, 'message' => '金币不足,无法下注。']);
}
return DB::transaction(function () use ($user, $race, $data, $currency, $horses): JsonResponse {
// 幂等:同一场只能下一注
$existing = HorseBet::query()
->where('race_id', $race->id)
->where('user_id', $user->id)
->lockForUpdate()
->exists();
if ($existing) {
return response()->json(['ok' => false, 'message' => '本场您已下注,请等待开奖。']);
}
// 找出马匹名称
$horseName = '';
foreach ($horses as $horse) {
if ((int) $horse['id'] === (int) $data['horse_id']) {
$horseName = ($horse['emoji'] ?? '').($horse['name'] ?? '');
break;
}
}
// 扣除金币
$currency->change(
$user,
'gold',
-$data['amount'],
CurrencySource::HORSE_BET,
"赛马 #{$race->id} 押注 {$horseName}",
);
// 写入下注记录
HorseBet::create([
'race_id' => $race->id,
'user_id' => $user->id,
'horse_id' => $data['horse_id'],
'amount' => $data['amount'],
'status' => 'pending',
]);
return response()->json([
'ok' => true,
'message' => "✅ 已押注「{$horseName}{$data['amount']} 金币,等待开跑!",
'amount' => $data['amount'],
'horse_id' => $data['horse_id'],
]);
});
}
/**
* 查询最近10场历史记录前端展示胜负趋势
*/
public function history(): JsonResponse
{
$races = HorseRace::query()
->where('status', 'settled')
->orderByDesc('id')
->limit(10)
->get(['id', 'horses', 'winner_horse_id', 'total_pool', 'total_bets', 'settled_at']);
// 转换获胜马匹名称
$history = $races->map(function ($race) {
$winnerName = '未知';
foreach (($race->horses ?? []) as $horse) {
if (($horse['id'] ?? 0) === (int) $race->winner_horse_id) {
$winnerName = ($horse['emoji'] ?? '').($horse['name'] ?? '');
break;
}
}
return [
'id' => $race->id,
'winner_id' => $race->winner_horse_id,
'winner_name' => $winnerName,
'total_pool' => $race->total_pool,
'total_bets' => $race->total_bets,
'settled_at' => $race->settled_at?->toDateTimeString(),
];
});
return response()->json(['history' => $history]);
}
}