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

- 新增 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
@@ -6,9 +6,22 @@
<div class="space-y-6">
{{-- 页头 --}}
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<h2 class="text-lg font-bold text-gray-800">🎮 游戏管理</h2>
<p class="text-xs text-gray-500 mt-1">统一管理聊天室所有娱乐游戏的开关状态与核心参数,所有游戏默认关闭。</p>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6 flex justify-between items-center">
<div>
<h2 class="text-lg font-bold text-gray-800">🎮 游戏管理</h2>
<p class="text-xs text-gray-500 mt-1">统一管理聊天室所有娱乐游戏的开关状态与核心参数,所有游戏默认关闭。</p>
</div>
<button onclick="loadGameStats()"
class="px-4 py-2 bg-indigo-50 text-indigo-700 rounded-lg text-sm font-bold hover:bg-indigo-100 transition">
📊 加载实时统计
</button>
</div>
{{-- 实时统计摘要区(AJAX 异步加载,默认隐藏) --}}
<div id="game-stats-panel" class="hidden">
<div class="grid grid-cols-2 md:grid-cols-5 gap-4" id="game-stats-grid">
{{-- JS 动态填充 --}}
</div>
</div>
@if (session('success'))
@@ -41,13 +54,33 @@
<div class="text-xs text-gray-500 mt-0.5">{{ $game->description }}</div>
</div>
</div>
{{-- 大开关按钮 --}}
<button onclick="toggleGame('{{ $game->game_key }}', {{ $game->id }})"
id="toggle-btn-{{ $game->game_key }}"
class="px-5 py-2 rounded-lg font-bold text-sm transition shadow-sm
{{ $game->enabled ? 'bg-red-500 hover:bg-red-600 text-white' : 'bg-emerald-500 hover:bg-emerald-600 text-white' }}">
{{ $game->enabled ? '⏸ 关闭游戏' : '▶ 开启游戏' }}
</button>
{{-- 操作按钮 --}}
<div class="flex items-center gap-2">
{{-- 历史记录链接 --}}
@php
$historyRoute = match ($game->game_key) {
'baccarat' => 'admin.game-history.baccarat',
'slot_machine' => 'admin.game-history.slot',
'mystery_box' => 'admin.game-history.mystery-box',
'horse_racing' => 'admin.game-history.horse',
'fortune_telling' => 'admin.game-history.fortune',
default => null,
};
@endphp
@if ($historyRoute)
<a href="{{ route($historyRoute) }}"
class="px-4 py-2 bg-indigo-50 text-indigo-700 rounded-lg font-bold text-sm hover:bg-indigo-100 transition shadow-sm">
📋 历史记录
</a>
@endif
{{-- 大开关按钮 --}}
<button onclick="toggleGame('{{ $game->game_key }}', {{ $game->id }})"
id="toggle-btn-{{ $game->game_key }}"
class="px-5 py-2 rounded-lg font-bold text-sm transition shadow-sm
{{ $game->enabled ? 'bg-red-500 hover:bg-red-600 text-white' : 'bg-emerald-500 hover:bg-emerald-600 text-white' }}">
{{ $game->enabled ? '⏸ 关闭游戏' : '▶ 开启游戏' }}
</button>
</div>
</div>
{{-- 参数配置区域 --}}
@@ -231,6 +264,139 @@
);
}
</script>
<script>
/**
* 加载各游戏实时统计摘要并渲染到顶部面板
*/
function loadGameStats() {
const panel = document.getElementById('game-stats-panel');
const grid = document.getElementById('game-stats-grid');
grid.innerHTML = '<div class="col-span-5 text-center text-gray-400 text-sm py-4">⏳ 加载中...</div>';
panel.classList.remove('hidden');
fetch('{{ route('admin.game-history.stats') }}', {
headers: {
'Accept': 'application/json'
}
})
.then(r => r.json())
.then(data => {
const cards = [{
icon: '🎲',
title: '百家乐',
items: [{
label: '总局数',
value: data.baccarat.total_rounds.toLocaleString()
},
{
label: '总下注',
value: data.baccarat.total_bets.toLocaleString() + ' 笔'
},
{
label: '今日局数',
value: data.baccarat.today_rounds.toLocaleString()
},
],
color: 'border-red-200 bg-red-50',
},
{
icon: '🎰',
title: '老虎机',
items: [{
label: '总转动',
value: data.slot.total_spins.toLocaleString() + ' 次'
},
{
label: '三7大奖',
value: data.slot.jackpot_count.toLocaleString() + ' 次'
},
{
label: '今日转动',
value: data.slot.today_spins.toLocaleString()
},
],
color: 'border-amber-200 bg-amber-50',
},
{
icon: '🐎',
title: '赛马竞猜',
items: [{
label: '总场次',
value: data.horse.total_races.toLocaleString()
},
{
label: '总注池',
value: data.horse.total_pool.toLocaleString() + ' 金'
},
{
label: '今日场次',
value: data.horse.today_races.toLocaleString()
},
],
color: 'border-emerald-200 bg-emerald-50',
},
{
icon: '📦',
title: '神秘箱子',
items: [{
label: '总投放',
value: data.mystery_box.total_dropped.toLocaleString()
},
{
label: '已领取',
value: data.mystery_box.total_claimed.toLocaleString()
},
{
label: '今日投放',
value: data.mystery_box.today_dropped.toLocaleString()
},
],
color: 'border-purple-200 bg-purple-50',
},
{
icon: '🔮',
title: '神秘占卜',
items: [{
label: '总占卜',
value: data.fortune.total_times.toLocaleString() + ' 次'
},
{
label: '上上签',
value: data.fortune.jackpot_count.toLocaleString()
},
{
label: '今日次数',
value: data.fortune.today_times.toLocaleString()
},
],
color: 'border-indigo-200 bg-indigo-50',
},
];
grid.innerHTML = cards.map(card => `
<div class="bg-white rounded-xl shadow-sm border ${card.color} p-4">
<div class="flex items-center gap-2 mb-3">
<span class="text-xl">${card.icon}</span>
<span class="font-bold text-gray-700 text-sm">${card.title}</span>
</div>
<div class="space-y-1.5">
${card.items.map(item => `
<div class="flex justify-between text-xs">
<span class="text-gray-500">${item.label}</span>
<span class="font-bold text-gray-700">${item.value}</span>
</div>
`).join('')}
</div>
</div>
`).join('');
})
.catch(() => {
grid.innerHTML = '<div class="col-span-5 text-center text-red-400 text-sm py-4">❌ 加载失败,请重试</div>';
});
}
</script>
@endsection
@php