功能:后台游戏历史记录查询中心 + 游戏管理页实时统计

- 新增 GameHistoryController,提供各游戏历史记录查询接口
  - 百家乐:局次列表 + 单局下注明细(含结果分布统计)
  - 老虎机:转动记录含图案分布,支持结果类型/玩家名筛选
  - 赛马:场次列表 + 单场下注明细(含马匹信息展示)
  - 神秘箱子:投放/领取历史,支持箱子类型/领取状态筛选
  - 神秘占卜:签文等级分布统计 + 历史记录,支持等级/玩家名筛选
- 新增 /admin/game-history/ 路由组(stats + 各游戏历史 + 单局详情共9条路由)
- 游戏管理页(/admin/game-configs)优化:
  - 每个游戏卡片新增「📋 历史记录」直达按钮
  - 新增「📊 加载实时统计」按钮,AJAX 异步拉取并展示各游戏汇总卡片
- 更新 GAMES_TODO.md,标记通用待办已完成
This commit is contained in:
2026-03-03 23:40:31 +08:00
parent f45483bcba
commit b62a9f6240
11 changed files with 1442 additions and 14 deletions

View File

@@ -0,0 +1,244 @@
<?php
/**
* 文件功能:游戏历史记录后台查询控制器
*
* 提供百家乐、老虎机、赛马竞猜、神秘箱子、神秘占卜各游戏
* 的历史记录查询页面及统计摘要接口,供管理员查阅。
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\BaccaratBet;
use App\Models\BaccaratRound;
use App\Models\FortuneLog;
use App\Models\HorseBet;
use App\Models\HorseRace;
use App\Models\MysteryBox;
use App\Models\SlotMachineLog;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class GameHistoryController extends Controller
{
/**
* 各游戏实时统计摘要JSON 接口,供 game-configs 首页加载)。
*/
public function stats(): JsonResponse
{
// 百家乐最近30天
$baccarat = [
'total_rounds' => BaccaratRound::query()->where('status', 'settled')->count(),
'total_bets' => BaccaratBet::query()->count(),
'total_payout' => BaccaratRound::query()->where('status', 'settled')->sum('total_payout'),
'today_rounds' => BaccaratRound::query()->where('status', 'settled')->whereDate('settled_at', today())->count(),
];
// 老虎机
$slot = [
'total_spins' => SlotMachineLog::query()->count(),
'total_cost' => SlotMachineLog::query()->sum('cost'),
'total_payout' => SlotMachineLog::query()->sum('payout'),
'jackpot_count' => SlotMachineLog::query()->where('result_type', 'jackpot')->count(),
'today_spins' => SlotMachineLog::query()->whereDate('created_at', today())->count(),
];
// 赛马
$horse = [
'total_races' => HorseRace::query()->where('status', 'settled')->count(),
'total_bets' => HorseBet::query()->count(),
'total_pool' => HorseRace::query()->where('status', 'settled')->sum('total_pool'),
'today_races' => HorseRace::query()->where('status', 'settled')->whereDate('settled_at', today())->count(),
];
// 神秘箱子
$mysteryBox = [
'total_dropped' => MysteryBox::query()->count(),
'total_claimed' => MysteryBox::query()->where('status', 'claimed')->count(),
'total_expired' => MysteryBox::query()->where('status', 'expired')->count(),
'today_dropped' => MysteryBox::query()->whereDate('created_at', today())->count(),
];
// 占卜
$fortune = [
'total_times' => FortuneLog::query()->count(),
'jackpot_count' => FortuneLog::query()->where('grade', 'jackpot')->count(),
'curse_count' => FortuneLog::query()->where('grade', 'curse')->count(),
'today_times' => FortuneLog::query()->whereDate('created_at', today())->count(),
];
return response()->json([
'baccarat' => $baccarat,
'slot' => $slot,
'horse' => $horse,
'mystery_box' => $mysteryBox,
'fortune' => $fortune,
]);
}
/**
* 百家乐历史记录页面(局次列表,支持分页)。
*/
public function baccarat(Request $request): View
{
// 各局统计摘要
$summary = [
'total_rounds' => BaccaratRound::query()->where('status', 'settled')->count(),
'total_bets' => BaccaratBet::query()->count(),
'total_payout' => (int) BaccaratRound::query()->where('status', 'settled')->sum('total_payout'),
'result_dist' => BaccaratRound::query()
->where('status', 'settled')
->select('result', \Illuminate\Support\Facades\DB::raw('count(*) as cnt'))
->groupBy('result')
->pluck('cnt', 'result'),
];
$rounds = BaccaratRound::query()
->latest()
->paginate(20);
return view('admin.game-history.baccarat', compact('rounds', 'summary'));
}
/**
* 百家乐单局下注明细。
*/
public function baccaratRound(BaccaratRound $round): View
{
$bets = $round->bets()->with('user')->latest()->paginate(30);
return view('admin.game-history.baccarat-round', compact('round', 'bets'));
}
/**
* 老虎机历史记录页面(支持按结果类型筛选/分页)。
*/
public function slot(Request $request): View
{
// 统计摘要
$summary = [
'total_spins' => SlotMachineLog::query()->count(),
'total_cost' => (int) SlotMachineLog::query()->sum('cost'),
'total_payout' => (int) SlotMachineLog::query()->sum('payout'),
'net_income' => (int) SlotMachineLog::query()->sum('cost') - (int) SlotMachineLog::query()->sum('payout'),
'result_dist' => SlotMachineLog::query()
->select('result_type', \Illuminate\Support\Facades\DB::raw('count(*) as cnt'))
->groupBy('result_type')
->pluck('cnt', 'result_type'),
];
$query = SlotMachineLog::query()->with('user')->latest();
// 按结果类型筛选
if ($request->filled('result_type')) {
$query->where('result_type', $request->input('result_type'));
}
// 按用户名筛选
if ($request->filled('username')) {
$query->whereHas('user', function ($q) use ($request) {
$q->where('username', 'like', '%'.$request->input('username').'%');
});
}
$logs = $query->paginate(30)->withQueryString();
return view('admin.game-history.slot', compact('logs', 'summary'));
}
/**
* 赛马竞猜历史记录页面(场次列表,支持分页)。
*/
public function horse(Request $request): View
{
$summary = [
'total_races' => HorseRace::query()->where('status', 'settled')->count(),
'total_bets' => HorseBet::query()->count(),
'total_pool' => (int) HorseRace::query()->sum('total_pool'),
];
$races = HorseRace::query()
->latest()
->paginate(20);
return view('admin.game-history.horse', compact('races', 'summary'));
}
/**
* 赛马单场下注明细。
*/
public function horseRace(HorseRace $race): View
{
$bets = $race->bets()->with('user')->latest()->paginate(30);
return view('admin.game-history.horse-race', compact('race', 'bets'));
}
/**
* 神秘箱子历史记录(投放/领取列表,支持分页和类型筛选)。
*/
public function mysteryBox(Request $request): View
{
$summary = [
'total_dropped' => MysteryBox::query()->count(),
'total_claimed' => MysteryBox::query()->where('status', 'claimed')->count(),
'total_expired' => MysteryBox::query()->where('status', 'expired')->count(),
'type_dist' => MysteryBox::query()
->select('box_type', \Illuminate\Support\Facades\DB::raw('count(*) as cnt'))
->groupBy('box_type')
->pluck('cnt', 'box_type'),
];
$query = MysteryBox::query()->with(['claim.user'])->latest();
if ($request->filled('box_type')) {
$query->where('box_type', $request->input('box_type'));
}
if ($request->filled('status')) {
$query->where('status', $request->input('status'));
}
$boxes = $query->paginate(20)->withQueryString();
return view('admin.game-history.mystery-box', compact('boxes', 'summary'));
}
/**
* 神秘占卜历史记录(支持按用户/签文等级筛选,分页)。
*/
public function fortune(Request $request): View
{
$summary = [
'total_times' => FortuneLog::query()->count(),
'grade_dist' => FortuneLog::query()
->select('grade', \Illuminate\Support\Facades\DB::raw('count(*) as cnt'))
->groupBy('grade')
->pluck('cnt', 'grade'),
'total_cost' => (int) FortuneLog::query()->sum('cost'),
'free_count' => FortuneLog::query()->where('is_free', true)->count(),
];
$query = FortuneLog::query()->with('user')->latest();
if ($request->filled('grade')) {
$query->where('grade', $request->input('grade'));
}
if ($request->filled('username')) {
$query->whereHas('user', function ($q) use ($request) {
$q->where('username', 'like', '%'.$request->input('username').'%');
});
}
$logs = $query->paginate(30)->withQueryString();
return view('admin.game-history.fortune', compact('logs', 'summary'));
}
}