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

- 新增 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

@@ -1,6 +1,6 @@
# 🎮 聊天室游戏开发进度
> 更新时间2026-03-03
> 更新时间2026-03-04
---
@@ -76,9 +76,9 @@
## 📌 通用待办(所有游戏共用)
- [ ] 后台游戏管理页面(`/admin/game-configs`)显示各游戏实时统计数据
- [ ] 各游戏历史记录在后台可查(管理员视角)
- [ ] 生产环境部署:`php artisan db:seed --class=GameConfigSeeder`(初始化游戏配置) 已经完成了
- [x] 后台游戏管理页面(`/admin/game-configs`)显示各游戏实时统计数据(点击"加载实时统计"异步加载各游戏汇总卡片)
- [x] 各游戏历史记录在后台可查(管理员视角,新增 `/admin/game-history/` 路由组,支持百家乐/老虎机/赛马/神秘箱子/占卜各自的历史记录列表及详情页,含筛选分页
- [x] 生产环境部署:`php artisan db:seed --class=GameConfigSeeder`(初始化游戏配置) 已经完成了
- [ ] 百家乐/老虎机 全面测试(多用户并发下注)
---

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'));
}
}

View File

@@ -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

View File

@@ -0,0 +1,115 @@
@extends('admin.layouts.app')
@section('title', "百家乐第 #{$round->id} 局下注明细")
@section('content')
<div class="space-y-6">
{{-- 页头 --}}
<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">🎲 百家乐 #{{ $round->id }} 局下注明细</h2>
<p class="text-xs text-gray-500 mt-1">
结算时间:{{ $round->settled_at?->format('Y-m-d H:i:s') ?? '未结算' }}
&nbsp;·&nbsp;结果:
<span class="font-bold text-indigo-600">{{ $round->resultLabel() }}</span>
&nbsp;·&nbsp;总点数:<span class="font-bold">{{ $round->total_points }}</span>
</p>
</div>
<a href="{{ route('admin.game-history.baccarat') }}"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg text-sm font-bold hover:bg-gray-200 transition">
返回历史列表
</a>
</div>
{{-- 本局摘要 --}}
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-indigo-600">{{ number_format($round->bet_count ?? 0) }}</div>
<div class="text-xs text-gray-500 mt-1">参与下注人数</div>
</div>
<div class="bg-red-50 rounded-xl shadow-sm border border-red-100 p-5">
<div class="text-2xl font-bold text-red-600">{{ number_format($round->total_bet_big ?? 0) }}</div>
<div class="text-xs text-red-400 mt-1">押大总金额</div>
</div>
<div class="bg-blue-50 rounded-xl shadow-sm border border-blue-100 p-5">
<div class="text-2xl font-bold text-blue-600">{{ number_format($round->total_bet_small ?? 0) }}</div>
<div class="text-xs text-blue-400 mt-1">押小总金额</div>
</div>
<div class="bg-amber-50 rounded-xl shadow-sm border border-amber-100 p-5">
<div class="text-2xl font-bold text-amber-600">{{ number_format($round->total_payout ?? 0) }}</div>
<div class="text-xs text-amber-400 mt-1">本局派奖总额</div>
</div>
</div>
{{-- 下注明细表 --}}
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b border-gray-100">
<tr>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">玩家</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">押注方向</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">押注金额</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">实际获得</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">是否中奖</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">下注时间</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-50">
@forelse ($bets as $bet)
@php
$betLabels = ['big' => '大', 'small' => '小', 'triple' => '豹子'];
$betColors = [
'big' => 'bg-red-100 text-red-700',
'small' => 'bg-blue-100 text-blue-700',
'triple' => 'bg-purple-100 text-purple-700',
];
$won = $bet->payout > 0;
@endphp
<tr class="hover:bg-gray-50 transition">
<td class="px-4 py-3 font-medium text-gray-800">
{{ $bet->user?->username ?? '已注销' }}
</td>
<td class="px-4 py-3 text-center">
<span
class="px-2 py-0.5 rounded-full text-xs font-bold {{ $betColors[$bet->bet_type] ?? 'bg-gray-100 text-gray-600' }}">
{{ $betLabels[$bet->bet_type] ?? $bet->bet_type }}
</span>
</td>
<td class="px-4 py-3 text-right font-mono text-sm text-gray-700">
{{ number_format($bet->amount) }}
</td>
<td
class="px-4 py-3 text-right font-mono text-sm {{ $won ? 'text-emerald-600 font-bold' : 'text-gray-400' }}">
{{ $won ? '+' . number_format($bet->payout) : '0' }}
</td>
<td class="px-4 py-3 text-center">
@if ($won)
<span
class="px-2 py-0.5 rounded-full text-xs font-bold bg-emerald-100 text-emerald-700">🎉
中奖</span>
@else
<span
class="px-2 py-0.5 rounded-full text-xs font-bold bg-gray-100 text-gray-500">未中</span>
@endif
</td>
<td class="px-4 py-3 text-right text-xs text-gray-400">
{{ $bet->created_at->format('H:i:s') }}
</td>
</tr>
@empty
<tr>
<td colspan="6" class="px-4 py-10 text-center text-gray-400 text-sm">本局无人下注</td>
</tr>
@endforelse
</tbody>
</table>
@if ($bets->hasPages())
<div class="px-4 py-3 border-t border-gray-100">
{{ $bets->links() }}
</div>
@endif
</div>
</div>
@endsection

View File

@@ -0,0 +1,139 @@
@extends('admin.layouts.app')
@section('title', '百家乐历史记录')
@section('content')
<div class="space-y-6">
{{-- 页头 --}}
<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>
<a href="{{ route('admin.game-configs.index') }}"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg text-sm font-bold hover:bg-gray-200 transition">
⚙️ 游戏配置
</a>
</div>
{{-- 统计卡片 --}}
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-indigo-600">{{ number_format($summary['total_rounds']) }}</div>
<div class="text-xs text-gray-500 mt-1">历史总局数</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-amber-600">{{ number_format($summary['total_bets']) }}</div>
<div class="text-xs text-gray-500 mt-1">历史总下注次</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-emerald-600">{{ number_format($summary['total_payout']) }}</div>
<div class="text-xs text-gray-500 mt-1">累计派奖金币</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-sm font-bold text-gray-700 mb-2">结果分布</div>
<div class="space-y-1">
@foreach (['big' => '大', 'small' => '小', 'triple' => '豹子', 'kill' => '收割'] as $key => $label)
<div class="flex justify-between text-xs">
<span class="text-gray-500">{{ $label }}</span>
<span class="font-bold text-gray-700">{{ $summary['result_dist'][$key] ?? 0 }} </span>
</div>
@endforeach
</div>
</div>
</div>
{{-- 局次列表 --}}
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b border-gray-100">
<tr>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">局次ID</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">结算时间</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">骰子</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">点数</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">结果</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">押注笔数</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">押大//</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">派奖金币</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">操作</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-50">
@forelse ($rounds as $round)
<tr class="hover:bg-gray-50 transition">
<td class="px-4 py-3 text-gray-400 text-xs font-mono">#{{ $round->id }}</td>
<td class="px-4 py-3 text-xs text-gray-600">
{{ $round->settled_at ? $round->settled_at->format('m-d H:i') : '—' }}
</td>
<td class="px-4 py-3 text-center text-lg">
@if ($round->dice1)
{{ $round->dice1 }} {{ $round->dice2 }} {{ $round->dice3 }}
@else
@endif
</td>
<td class="px-4 py-3 text-center font-bold text-gray-700">
{{ $round->total_points ?? '—' }}
</td>
<td class="px-4 py-3 text-center">
@php
$resultColors = [
'big' => 'bg-red-100 text-red-700',
'small' => 'bg-blue-100 text-blue-700',
'triple' => 'bg-purple-100 text-purple-700',
'kill' => 'bg-gray-100 text-gray-600',
];
$label = match ($round->result) {
'big' => '大',
'small' => '小',
'triple' => '豹子',
'kill' => '收割',
default => $round->result ?? '—',
};
$colorClass = $resultColors[$round->result] ?? 'bg-gray-100 text-gray-600';
@endphp
@if ($round->result)
<span class="px-2 py-0.5 rounded-full text-xs font-bold {{ $colorClass }}">
{{ $label }}
</span>
@else
<span class="text-gray-400 text-xs">未结算</span>
@endif
</td>
<td class="px-4 py-3 text-center text-xs font-bold text-indigo-600">
{{ number_format($round->bet_count ?? 0) }}
</td>
<td class="px-4 py-3 text-right text-xs text-gray-500 font-mono">
<span class="text-red-500">{{ number_format($round->total_bet_big ?? 0) }}</span> /
<span class="text-blue-500">{{ number_format($round->total_bet_small ?? 0) }}</span> /
<span class="text-purple-500">{{ number_format($round->total_bet_triple ?? 0) }}</span>
</td>
<td class="px-4 py-3 text-right font-mono text-xs text-amber-600 font-bold">
{{ number_format($round->total_payout ?? 0) }}
</td>
<td class="px-4 py-3 text-right">
<a href="{{ route('admin.game-history.baccarat.round', $round->id) }}"
class="px-3 py-1 bg-indigo-50 text-indigo-700 rounded text-xs font-bold hover:bg-indigo-100 transition">
下注明细
</a>
</td>
</tr>
@empty
<tr>
<td colspan="9" class="px-4 py-10 text-center text-gray-400 text-sm">暂无记录</td>
</tr>
@endforelse
</tbody>
</table>
{{-- 分页 --}}
@if ($rounds->hasPages())
<div class="px-4 py-3 border-t border-gray-100">
{{ $rounds->links() }}
</div>
@endif
</div>
</div>
@endsection

View File

@@ -0,0 +1,163 @@
@extends('admin.layouts.app')
@section('title', '神秘占卜历史记录')
@section('content')
<div class="space-y-6">
{{-- 页头 --}}
<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>
<a href="{{ route('admin.game-configs.index') }}"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg text-sm font-bold hover:bg-gray-200 transition">
⚙️ 游戏配置
</a>
</div>
{{-- 统计卡片 --}}
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-indigo-600">{{ number_format($summary['total_times']) }}</div>
<div class="text-xs text-gray-500 mt-1">历史总占卜次</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-amber-500">{{ number_format($summary['grade_dist']['jackpot'] ?? 0) }}
</div>
<div class="text-xs text-gray-500 mt-1"> 上上签次数</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-red-500">{{ number_format($summary['grade_dist']['curse'] ?? 0) }}</div>
<div class="text-xs text-gray-500 mt-1">💀 大凶签次数</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-sm font-bold text-gray-700 mb-2">签文分布</div>
<div class="space-y-1">
@php
$gradeAll = [
'jackpot' => ['label' => '上上签', 'color' => 'text-amber-600'],
'good' => ['label' => '上签', 'color' => 'text-emerald-600'],
'normal' => ['label' => '中签', 'color' => 'text-gray-500'],
'bad' => ['label' => '下签', 'color' => 'text-orange-500'],
'curse' => ['label' => '大凶签', 'color' => 'text-red-600'],
];
@endphp
@foreach ($gradeAll as $key => $meta)
<div class="flex justify-between text-xs">
<span class="{{ $meta['color'] }}">{{ $meta['label'] }}</span>
<span class="font-bold text-gray-700">{{ $summary['grade_dist'][$key] ?? 0 }}</span>
</div>
@endforeach
</div>
</div>
</div>
{{-- 筛选 --}}
<form method="GET"
class="bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-wrap items-end gap-4">
<div>
<label class="block text-xs font-bold text-gray-600 mb-1">签文等级</label>
<select name="grade"
class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:border-indigo-400 min-w-[130px]">
<option value="">全部等级</option>
@foreach ($gradeAll as $key => $meta)
<option value="{{ $key }}" {{ request('grade') === $key ? 'selected' : '' }}>
{{ $meta['label'] }}
</option>
@endforeach
</select>
</div>
<div>
<label class="block text-xs font-bold text-gray-600 mb-1">玩家名称</label>
<input type="text" name="username" value="{{ request('username') }}" placeholder="模糊搜索..."
class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:border-indigo-400 w-40">
</div>
<div class="flex gap-2">
<button type="submit"
class="px-5 py-2 bg-indigo-600 text-white rounded-lg font-bold hover:bg-indigo-700 transition text-sm">
🔍 筛选
</button>
<a href="{{ route('admin.game-history.fortune') }}"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg font-bold hover:bg-gray-200 transition text-sm">
重置
</a>
</div>
</form>
{{-- 记录列表 --}}
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b border-gray-100">
<tr>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">时间</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">玩家</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">签文等级</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase w-1/3">签文内容</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">加成/减益</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">是否免费</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">消耗金币</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-50">
@forelse ($logs as $log)
@php
$gradeInfo = match ($log->grade) {
'jackpot' => ['label' => '✨ 上上签', 'color' => 'bg-amber-100 text-amber-700'],
'good' => ['label' => '🌸 上签', 'color' => 'bg-emerald-100 text-emerald-700'],
'normal' => ['label' => '📜 中签', 'color' => 'bg-gray-100 text-gray-600'],
'bad' => ['label' => '😞 下签', 'color' => 'bg-orange-100 text-orange-700'],
'curse' => ['label' => '💀 大凶签', 'color' => 'bg-red-100 text-red-700'],
default => ['label' => $log->grade, 'color' => 'bg-gray-100 text-gray-500'],
};
@endphp
<tr
class="hover:bg-gray-50 transition {{ in_array($log->grade, ['jackpot']) ? 'bg-amber-50/50' : (in_array($log->grade, ['curse']) ? 'bg-red-50/30' : '') }}">
<td class="px-4 py-3 text-xs text-gray-400">
{{ $log->created_at->format('m-d H:i') }}
</td>
<td class="px-4 py-3 font-medium text-gray-800">
{{ $log->user?->username ?? '已注销' }}
</td>
<td class="px-4 py-3 text-center">
<span class="px-2 py-0.5 rounded-full text-xs font-bold {{ $gradeInfo['color'] }}">
{{ $gradeInfo['label'] }}
</span>
</td>
<td class="px-4 py-3 text-xs text-gray-600 leading-relaxed">
{{ \Illuminate\Support\Str::limit($log->text, 50) }}
</td>
<td class="px-4 py-3 text-xs text-gray-500">
{{ $log->buff_desc ?? '—' }}
</td>
<td class="px-4 py-3 text-center">
@if ($log->is_free)
<span
class="px-2 py-0.5 rounded-full text-xs font-bold bg-emerald-100 text-emerald-700">免费</span>
@else
<span
class="px-2 py-0.5 rounded-full text-xs font-bold bg-amber-100 text-amber-700">付费</span>
@endif
</td>
<td
class="px-4 py-3 text-right font-mono text-xs {{ $log->cost > 0 ? 'text-red-500' : 'text-gray-400' }}">
{{ $log->cost > 0 ? '-' . number_format($log->cost) : '0' }}
</td>
</tr>
@empty
<tr>
<td colspan="7" class="px-4 py-10 text-center text-gray-400 text-sm">暂无记录</td>
</tr>
@endforelse
</tbody>
</table>
@if ($logs->hasPages())
<div class="px-4 py-3 border-t border-gray-100">
{{ $logs->links() }}
</div>
@endif
</div>
</div>
@endsection

View File

@@ -0,0 +1,124 @@
@extends('admin.layouts.app')
@section('title', "赛马第 #{$race->id} 场下注明细")
@section('content')
<div class="space-y-6">
{{-- 页头 --}}
@php
$horses = $race->horses ?? [];
$winner = collect($horses)->firstWhere('id', $race->winner_horse_id);
@endphp
<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">🐎 赛马 #{{ $race->id }} 场下注明细</h2>
<p class="text-xs text-gray-500 mt-1">
结算时间:{{ $race->settled_at?->format('Y-m-d H:i:s') ?? '未结算' }}
@if ($winner)
&nbsp;·&nbsp;胜者:<span class="font-bold text-indigo-600">{{ $winner['emoji'] ?? '' }}
{{ $winner['name'] ?? '' }}</span>
@endif
&nbsp;·&nbsp;注池:<span class="font-bold text-amber-600">{{ number_format($race->total_pool ?? 0) }}
金币</span>
</p>
</div>
<a href="{{ route('admin.game-history.horse') }}"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg text-sm font-bold hover:bg-gray-200 transition">
返回场次列表
</a>
</div>
{{-- 马匹信息 --}}
@if (count($horses) > 0)
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-sm font-bold text-gray-700 mb-3">参赛马匹</div>
<div class="flex flex-wrap gap-3">
@foreach ($horses as $horse)
<div
class="px-4 py-2 rounded-lg {{ $horse['id'] === $race->winner_horse_id ? 'bg-amber-100 border-2 border-amber-400' : 'bg-gray-100' }}">
<div class="text-lg">{{ $horse['emoji'] ?? '🐎' }}</div>
<div
class="text-xs font-bold {{ $horse['id'] === $race->winner_horse_id ? 'text-amber-700' : 'text-gray-600' }}">
{{ $horse['name'] }}
@if ($horse['id'] === $race->winner_horse_id)
🏆
@endif
</div>
</div>
@endforeach
</div>
</div>
@endif
{{-- 下注明细表 --}}
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b border-gray-100">
<tr>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">玩家</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">押注马匹</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">押注金额</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">实际获得</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">是否中奖</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">下注时间</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-50">
@forelse ($bets as $bet)
@php
$betHorse = collect($horses)->firstWhere('id', $bet->horse_id);
$isWinner = $bet->horse_id === $race->winner_horse_id;
$won = ($bet->payout ?? 0) > 0;
@endphp
<tr class="hover:bg-gray-50 transition {{ $won ? 'bg-amber-50/50' : '' }}">
<td class="px-4 py-3 font-medium text-gray-800">
{{ $bet->user?->username ?? '已注销' }}
</td>
<td class="px-4 py-3 text-center">
@if ($betHorse)
<span
class="px-2 py-0.5 rounded-full text-xs font-bold {{ $isWinner ? 'bg-amber-100 text-amber-700' : 'bg-gray-100 text-gray-600' }}">
{{ $betHorse['emoji'] ?? '' }} {{ $betHorse['name'] ?? '' }}
</span>
@else
<span class="text-gray-400 text-xs">#{{ $bet->horse_id }}</span>
@endif
</td>
<td class="px-4 py-3 text-right font-mono text-sm text-gray-700">
{{ number_format($bet->amount) }}
</td>
<td
class="px-4 py-3 text-right font-mono text-sm {{ $won ? 'text-emerald-600 font-bold' : 'text-gray-400' }}">
{{ $won ? '+' . number_format($bet->payout) : '0' }}
</td>
<td class="px-4 py-3 text-center">
@if ($won)
<span
class="px-2 py-0.5 rounded-full text-xs font-bold bg-emerald-100 text-emerald-700">🎉
中奖</span>
@else
<span
class="px-2 py-0.5 rounded-full text-xs font-bold bg-gray-100 text-gray-500">未中</span>
@endif
</td>
<td class="px-4 py-3 text-right text-xs text-gray-400">
{{ $bet->created_at->format('H:i:s') }}
</td>
</tr>
@empty
<tr>
<td colspan="6" class="px-4 py-10 text-center text-gray-400 text-sm">本场无人下注</td>
</tr>
@endforelse
</tbody>
</table>
@if ($bets->hasPages())
<div class="px-4 py-3 border-t border-gray-100">
{{ $bets->links() }}
</div>
@endif
</div>
</div>
@endsection

View File

@@ -0,0 +1,113 @@
@extends('admin.layouts.app')
@section('title', '赛马竞猜历史记录')
@section('content')
<div class="space-y-6">
{{-- 页头 --}}
<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>
<a href="{{ route('admin.game-configs.index') }}"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg text-sm font-bold hover:bg-gray-200 transition">
⚙️ 游戏配置
</a>
</div>
{{-- 统计卡片 --}}
<div class="grid grid-cols-2 md:grid-cols-3 gap-4">
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-indigo-600">{{ number_format($summary['total_races']) }}</div>
<div class="text-xs text-gray-500 mt-1">历史总场次</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-amber-600">{{ number_format($summary['total_bets']) }}</div>
<div class="text-xs text-gray-500 mt-1">历史总下注笔</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-emerald-600">{{ number_format($summary['total_pool']) }}</div>
<div class="text-xs text-gray-500 mt-1">累计注池金币</div>
</div>
</div>
{{-- 场次列表 --}}
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b border-gray-100">
<tr>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">场次ID</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">结算时间</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">参赛马匹</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">胜者</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">状态</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">下注总笔</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">注池总额</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">操作</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-50">
@forelse ($races as $race)
@php
$horses = $race->horses ?? [];
$winner = collect($horses)->firstWhere('id', $race->winner_horse_id);
$statusLabel = match ($race->status) {
'betting' => ['label' => '押注中', 'color' => 'bg-blue-100 text-blue-700'],
'running' => ['label' => '比赛中', 'color' => 'bg-amber-100 text-amber-700'],
'settled' => ['label' => '已结算', 'color' => 'bg-emerald-100 text-emerald-700'],
'canceled' => ['label' => '已取消', 'color' => 'bg-red-100 text-red-600'],
default => ['label' => $race->status, 'color' => 'bg-gray-100 text-gray-500'],
};
@endphp
<tr class="hover:bg-gray-50 transition">
<td class="px-4 py-3 text-gray-400 text-xs font-mono">#{{ $race->id }}</td>
<td class="px-4 py-3 text-xs text-gray-600">
{{ $race->settled_at ? $race->settled_at->format('m-d H:i') : '—' }}
</td>
<td class="px-4 py-3 text-center text-sm">
{{ collect($horses)->pluck('emoji')->implode('') ?: '—' }}
<span class="text-xs text-gray-400">{{ count($horses) }} 匹)</span>
</td>
<td class="px-4 py-3 text-center font-bold text-indigo-700">
@if ($winner)
{{ $winner['emoji'] ?? '' }} {{ $winner['name'] ?? '' }}
@else
<span class="text-gray-400"></span>
@endif
</td>
<td class="px-4 py-3 text-center">
<span class="px-2 py-0.5 rounded-full text-xs font-bold {{ $statusLabel['color'] }}">
{{ $statusLabel['label'] }}
</span>
</td>
<td class="px-4 py-3 text-right font-mono text-xs text-indigo-600 font-bold">
{{ number_format($race->total_bets ?? 0) }}
</td>
<td class="px-4 py-3 text-right font-mono text-xs text-amber-600 font-bold">
{{ number_format($race->total_pool ?? 0) }}
</td>
<td class="px-4 py-3 text-right">
<a href="{{ route('admin.game-history.horse.race', $race->id) }}"
class="px-3 py-1 bg-indigo-50 text-indigo-700 rounded text-xs font-bold hover:bg-indigo-100 transition">
下注明细
</a>
</td>
</tr>
@empty
<tr>
<td colspan="8" class="px-4 py-10 text-center text-gray-400 text-sm">暂无记录</td>
</tr>
@endforelse
</tbody>
</table>
@if ($races->hasPages())
<div class="px-4 py-3 border-t border-gray-100">
{{ $races->links() }}
</div>
@endif
</div>
</div>
@endsection

View File

@@ -0,0 +1,174 @@
@extends('admin.layouts.app')
@section('title', '神秘箱子历史记录')
@section('content')
<div class="space-y-6">
{{-- 页头 --}}
<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>
<a href="{{ route('admin.game-configs.index') }}"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg text-sm font-bold hover:bg-gray-200 transition">
⚙️ 游戏配置
</a>
</div>
{{-- 统计卡片 --}}
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-indigo-600">{{ number_format($summary['total_dropped']) }}</div>
<div class="text-xs text-gray-500 mt-1">历史总投放数</div>
</div>
<div class="bg-emerald-50 rounded-xl shadow-sm border border-emerald-100 p-5">
<div class="text-2xl font-bold text-emerald-600">{{ number_format($summary['total_claimed']) }}</div>
<div class="text-xs text-emerald-500 mt-1">已被领取</div>
</div>
<div class="bg-gray-50 rounded-xl shadow-sm border border-gray-200 p-5">
<div class="text-2xl font-bold text-gray-500">{{ number_format($summary['total_expired']) }}</div>
<div class="text-xs text-gray-400 mt-1">已过期/未领取</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-sm font-bold text-gray-700 mb-2">类型分布</div>
<div class="space-y-1">
<div class="flex justify-between text-xs">
<span class="text-gray-500">📦 普通箱</span>
<span class="font-bold">{{ $summary['type_dist']['normal'] ?? 0 }}</span>
</div>
<div class="flex justify-between text-xs">
<span class="text-gray-500">💎 稀有箱</span>
<span class="font-bold text-purple-600">{{ $summary['type_dist']['rare'] ?? 0 }}</span>
</div>
<div class="flex justify-between text-xs">
<span class="text-gray-500">☠️ 黑化箱</span>
<span class="font-bold text-red-600">{{ $summary['type_dist']['trap'] ?? 0 }}</span>
</div>
</div>
</div>
</div>
{{-- 筛选条件 --}}
<form method="GET"
class="bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-wrap items-end gap-4">
<div>
<label class="block text-xs font-bold text-gray-600 mb-1">箱子类型</label>
<select name="box_type"
class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:border-indigo-400 min-w-[130px]">
<option value="">全部类型</option>
<option value="normal" {{ request('box_type') === 'normal' ? 'selected' : '' }}>📦 普通箱</option>
<option value="rare" {{ request('box_type') === 'rare' ? 'selected' : '' }}>💎 稀有箱</option>
<option value="trap" {{ request('box_type') === 'trap' ? 'selected' : '' }}>☠️ 黑化箱</option>
</select>
</div>
<div>
<label class="block text-xs font-bold text-gray-600 mb-1">状态</label>
<select name="status"
class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:border-indigo-400 min-w-[120px]">
<option value="">全部状态</option>
<option value="open" {{ request('status') === 'open' ? 'selected' : '' }}>等待领取</option>
<option value="claimed" {{ request('status') === 'claimed' ? 'selected' : '' }}>已领取</option>
<option value="expired" {{ request('status') === 'expired' ? 'selected' : '' }}>已过期</option>
</select>
</div>
<div class="flex gap-2">
<button type="submit"
class="px-5 py-2 bg-indigo-600 text-white rounded-lg font-bold hover:bg-indigo-700 transition text-sm">
🔍 筛选
</button>
<a href="{{ route('admin.game-history.mystery-box') }}"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg font-bold hover:bg-gray-200 transition text-sm">
重置
</a>
</div>
</form>
{{-- 箱子列表 --}}
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b border-gray-100">
<tr>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">投放时间</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">类型</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">奖励范围</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">暗号</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">状态</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">领取者</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">实际奖励</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">领取时间</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-50">
@forelse ($boxes as $box)
@php
$typeInfo = match ($box->box_type) {
'normal' => ['label' => '📦 普通箱', 'color' => 'bg-emerald-100 text-emerald-700'],
'rare' => ['label' => '💎 稀有箱', 'color' => 'bg-purple-100 text-purple-700'],
'trap' => ['label' => '☠️ 黑化箱', 'color' => 'bg-red-100 text-red-700'],
default => ['label' => '📦 未知', 'color' => 'bg-gray-100 text-gray-600'],
};
$statusInfo = match ($box->status) {
'open' => ['label' => '等待领取', 'color' => 'bg-blue-100 text-blue-700'],
'claimed' => ['label' => '✅ 已领取', 'color' => 'bg-emerald-100 text-emerald-700'],
'expired' => ['label' => '⏰ 已过期', 'color' => 'bg-gray-100 text-gray-500'],
default => ['label' => $box->status, 'color' => 'bg-gray-100 text-gray-500'],
};
$claim = $box->claim;
@endphp
<tr class="hover:bg-gray-50 transition">
<td class="px-4 py-3 text-xs text-gray-400">
{{ $box->created_at->format('m-d H:i:s') }}
</td>
<td class="px-4 py-3 text-center">
<span class="px-2 py-0.5 rounded-full text-xs font-bold {{ $typeInfo['color'] }}">
{{ $typeInfo['label'] }}
</span>
</td>
<td class="px-4 py-3 text-center text-xs text-gray-600 font-mono">
{{ number_format($box->reward_min) }} ~ {{ number_format($box->reward_max) }}
</td>
<td class="px-4 py-3 text-center">
<code class="bg-gray-100 px-2 py-0.5 rounded text-xs text-indigo-700 font-bold">
{{ $box->passcode ?? '—' }}
</code>
</td>
<td class="px-4 py-3 text-center">
<span class="px-2 py-0.5 rounded-full text-xs font-bold {{ $statusInfo['color'] }}">
{{ $statusInfo['label'] }}
</span>
</td>
<td class="px-4 py-3 font-medium text-gray-700 text-sm">
{{ $claim?->user?->username ?? '—' }}
</td>
<td class="px-4 py-3 text-right font-mono text-xs">
@if ($claim)
<span
class="{{ $claim->reward_amount > 0 ? 'text-emerald-600 font-bold' : 'text-red-500 font-bold' }}">
{{ $claim->reward_amount > 0 ? '+' : '' }}{{ number_format($claim->reward_amount) }}
</span>
@else
<span class="text-gray-400"></span>
@endif
</td>
<td class="px-4 py-3 text-xs text-gray-400">
{{ $claim?->created_at?->format('m-d H:i:s') ?? '—' }}
</td>
</tr>
@empty
<tr>
<td colspan="8" class="px-4 py-10 text-center text-gray-400 text-sm">暂无记录</td>
</tr>
@endforelse
</tbody>
</table>
@if ($boxes->hasPages())
<div class="px-4 py-3 border-t border-gray-100">
{{ $boxes->links() }}
</div>
@endif
</div>
</div>
@endsection

View File

@@ -0,0 +1,172 @@
@extends('admin.layouts.app')
@section('title', '老虎机历史记录')
@section('content')
<div class="space-y-6">
{{-- 页头 --}}
<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>
<a href="{{ route('admin.game-configs.index') }}"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg text-sm font-bold hover:bg-gray-200 transition">
⚙️ 游戏配置
</a>
</div>
{{-- 统计卡片 --}}
<div class="grid grid-cols-2 md:grid-cols-5 gap-4">
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-indigo-600">{{ number_format($summary['total_spins']) }}</div>
<div class="text-xs text-gray-500 mt-1">总转动次数</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-red-500">{{ number_format($summary['total_cost']) }}</div>
<div class="text-xs text-gray-500 mt-1">总消耗金币</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-emerald-600">{{ number_format($summary['total_payout']) }}</div>
<div class="text-xs text-gray-500 mt-1">总派奖金币</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-2xl font-bold text-amber-600">{{ number_format($summary['net_income']) }}</div>
<div class="text-xs text-gray-500 mt-1">庄家净收(消耗-派奖)</div>
</div>
<div class="bg-yellow-50 rounded-xl shadow-sm border border-yellow-100 p-5">
<div class="text-2xl font-bold text-yellow-600">{{ number_format($summary['result_dist']['jackpot'] ?? 0) }}
</div>
<div class="text-xs text-yellow-500 mt-1">🎉 三7大奖次数</div>
</div>
</div>
{{-- 结果分布 --}}
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-5">
<div class="text-sm font-bold text-gray-700 mb-3">结果类型分布</div>
<div class="flex flex-wrap gap-3">
@php
$resultLabels = [
'jackpot' => ['label' => '🎉 三个7', 'color' => 'bg-yellow-100 text-yellow-700'],
'triple_gem' => ['label' => '💎 三钻', 'color' => 'bg-blue-100 text-blue-700'],
'triple' => ['label' => '✨ 三同', 'color' => 'bg-green-100 text-green-700'],
'pair' => ['label' => '🎁 两同', 'color' => 'bg-indigo-100 text-indigo-700'],
'curse' => ['label' => '☠️ 三骷髅', 'color' => 'bg-red-100 text-red-700'],
'miss' => ['label' => '😔 未中奖', 'color' => 'bg-gray-100 text-gray-600'],
];
@endphp
@foreach ($resultLabels as $key => $meta)
<div class="px-4 py-2 rounded-lg {{ $meta['color'] }}">
<div class="text-sm font-bold">{{ $meta['label'] }}</div>
<div class="text-xs mt-0.5">{{ number_format($summary['result_dist'][$key] ?? 0) }} </div>
</div>
@endforeach
</div>
</div>
{{-- 筛选条件 --}}
<form method="GET"
class="bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-wrap items-end gap-4">
<div>
<label class="block text-xs font-bold text-gray-600 mb-1">结果类型</label>
<select name="result_type"
class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:border-indigo-400 min-w-[140px]">
<option value="">全部结果</option>
@foreach ($resultLabels as $key => $meta)
<option value="{{ $key }}" {{ request('result_type') === $key ? 'selected' : '' }}>
{{ $meta['label'] }}
</option>
@endforeach
</select>
</div>
<div>
<label class="block text-xs font-bold text-gray-600 mb-1">玩家名称</label>
<input type="text" name="username" value="{{ request('username') }}" placeholder="模糊搜索..."
class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:border-indigo-400 w-40">
</div>
<div class="flex gap-2">
<button type="submit"
class="px-5 py-2 bg-indigo-600 text-white rounded-lg font-bold hover:bg-indigo-700 transition text-sm">
🔍 筛选
</button>
<a href="{{ route('admin.game-history.slot') }}"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg font-bold hover:bg-gray-200 transition text-sm">
重置
</a>
</div>
</form>
{{-- 记录列表 --}}
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b border-gray-100">
<tr>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">时间</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">玩家</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">三列图案</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">结果</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">消耗</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">获得</th>
<th class="px-4 py-3 text-right text-xs font-bold text-gray-500 uppercase">净值</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-50">
@forelse ($logs as $log)
@php
$symbols = \App\Models\SlotMachineLog::symbols();
$r1Emoji = $symbols[$log->reel1]['emoji'] ?? $log->reel1;
$r2Emoji = $symbols[$log->reel2]['emoji'] ?? $log->reel2;
$r3Emoji = $symbols[$log->reel3]['emoji'] ?? $log->reel3;
$net = $log->payout - $log->cost;
$rowBg = match ($log->result_type) {
'jackpot' => 'bg-yellow-50',
'triple_gem' => 'bg-blue-50',
'curse' => 'bg-red-50',
default => '',
};
@endphp
<tr class="hover:bg-gray-50 transition {{ $rowBg }}">
<td class="px-4 py-3 text-xs text-gray-400">
{{ $log->created_at->format('m-d H:i:s') }}
</td>
<td class="px-4 py-3 font-medium text-gray-800">
{{ $log->user?->username ?? '已注销' }}
</td>
<td class="px-4 py-3 text-center text-lg tracking-widest">
{{ $r1Emoji }} {{ $r2Emoji }} {{ $r3Emoji }}
</td>
<td class="px-4 py-3 text-center">
<span
class="px-2 py-0.5 rounded-full text-xs font-bold {{ $resultLabels[$log->result_type]['color'] ?? 'bg-gray-100 text-gray-600' }}">
{{ $resultLabels[$log->result_type]['label'] ?? $log->result_type }}
</span>
</td>
<td class="px-4 py-3 text-right font-mono text-xs text-red-500">
-{{ number_format($log->cost) }}
</td>
<td
class="px-4 py-3 text-right font-mono text-xs {{ $log->payout > 0 ? 'text-emerald-600 font-bold' : 'text-gray-400' }}">
{{ $log->payout > 0 ? '+' . number_format($log->payout) : '0' }}
</td>
<td
class="px-4 py-3 text-right font-mono text-xs {{ $net >= 0 ? 'text-emerald-600' : 'text-red-500' }} font-bold">
{{ $net >= 0 ? '+' : '' }}{{ number_format($net) }}
</td>
</tr>
@empty
<tr>
<td colspan="7" class="px-4 py-10 text-center text-gray-400 text-sm">暂无记录</td>
</tr>
@endforelse
</tbody>
</table>
@if ($logs->hasPages())
<div class="px-4 py-3 border-t border-gray-100">
{{ $logs->links() }}
</div>
@endif
</div>
</div>
@endsection

View File

@@ -392,6 +392,24 @@ Route::middleware(['chat.auth', 'chat.has_position'])->prefix('admin')->name('ad
Route::post('/{gameConfig}/params', [\App\Http\Controllers\Admin\GameConfigController::class, 'updateParams'])->name('params');
});
// 📊 游戏历史记录查询(各游戏历史 + 实时统计摘要)
Route::prefix('game-history')->name('game-history.')->group(function () {
// 各游戏实时统计摘要JSON 接口,供游戏管理页 AJAX 加载)
Route::get('/stats', [\App\Http\Controllers\Admin\GameHistoryController::class, 'stats'])->name('stats');
// 百家乐局次历史
Route::get('/baccarat', [\App\Http\Controllers\Admin\GameHistoryController::class, 'baccarat'])->name('baccarat');
Route::get('/baccarat/{round}', [\App\Http\Controllers\Admin\GameHistoryController::class, 'baccaratRound'])->name('baccarat.round');
// 老虎机历史
Route::get('/slot', [\App\Http\Controllers\Admin\GameHistoryController::class, 'slot'])->name('slot');
// 赛马场次历史
Route::get('/horse', [\App\Http\Controllers\Admin\GameHistoryController::class, 'horse'])->name('horse');
Route::get('/horse/{race}', [\App\Http\Controllers\Admin\GameHistoryController::class, 'horseRace'])->name('horse.race');
// 神秘箱子投放历史
Route::get('/mystery-box', [\App\Http\Controllers\Admin\GameHistoryController::class, 'mysteryBox'])->name('mystery-box');
// 神秘占卜历史
Route::get('/fortune', [\App\Http\Controllers\Admin\GameHistoryController::class, 'fortune'])->name('fortune');
});
// 📦 神秘箱子:管理员手动投放
Route::post('/mystery-box/drop', [\App\Http\Controllers\Admin\GameConfigController::class, 'dropMysteryBox'])->name('mystery-box.drop');