Files
chatroom/resources/views/admin/game-configs/index.blade.php

703 lines
39 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.
@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>
<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'))
<div class="bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-lg text-sm">
{{ session('success') }}
</div>
@endif
{{-- 游戏卡片列表 --}}
<div class="grid grid-cols-1 gap-6">
@foreach ($games as $game)
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden"
id="game-card-{{ $game->game_key }}">
{{-- 卡片头部:游戏名 + 开关 --}}
<div
class="flex items-center justify-between p-5 border-b border-gray-100
{{ $game->enabled ? 'bg-emerald-50' : 'bg-gray-50' }}">
<div class="flex items-center gap-3">
<span class="text-3xl">{{ $game->icon }}</span>
<div>
<div class="font-bold text-gray-800 flex items-center gap-2">
{{ $game->name }}
<span id="badge-{{ $game->game_key }}"
class="text-xs px-2 py-0.5 rounded-full font-bold
{{ $game->enabled ? 'bg-emerald-100 text-emerald-700' : 'bg-gray-200 text-gray-500' }}">
{{ $game->enabled ? '运行中' : '已关闭' }}
</span>
</div>
<div class="text-xs text-gray-500 mt-0.5">{{ $game->description }}</div>
</div>
</div>
{{-- 操作按钮组 --}}
<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>
{{-- 参数配置区域 --}}
<div class="p-5">
<form action="{{ route('admin.game-configs.params', $game) }}" method="POST">
@csrf
@php
$params = $game->params ?? [];
$labels = gameParamLabels($game->game_key);
@endphp
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
@foreach ($params as $paramKey => $paramValue)
@php $meta = $labels[$paramKey] ?? ['label' => $paramKey, 'type' => 'number', 'unit' => ''] @endphp
<div>
<label class="block text-xs font-bold text-gray-600 mb-1">
{{ $meta['label'] }}
@if ($meta['unit'])
<span class="font-normal text-gray-400">{{ $meta['unit'] }}</span>
@endif
</label>
@if ($meta['type'] === 'boolean')
<select name="params[{{ $paramKey }}]"
class="w-full border border-gray-300 rounded-lg p-2 text-sm focus:border-indigo-400">
<option value="1" {{ $paramValue ? 'selected' : '' }}></option>
<option value="0" {{ !$paramValue ? 'selected' : '' }}></option>
</select>
@elseif ($meta['type'] === 'array')
<input type="text" name="params[{{ $paramKey }}]"
value="{{ implode(',', (array) $paramValue) }}"
class="w-full border border-gray-300 rounded-lg p-2 text-sm focus:border-indigo-400"
placeholder="多个值用逗号分隔">
@else
<input type="{{ $meta['type'] }}" name="params[{{ $paramKey }}]"
value="{{ $paramValue }}" step="{{ $meta['step'] ?? 1 }}"
min="{{ $meta['min'] ?? 0 }}"
class="w-full border border-gray-300 rounded-lg p-2 text-sm focus:border-indigo-400">
@endif
</div>
@endforeach
</div>
<div class="mt-4 flex items-center gap-3">
<button type="submit"
class="px-6 py-2 bg-indigo-600 text-white rounded-lg font-bold hover:bg-indigo-700 transition text-sm shadow-sm">
💾 保存参数
</button>
<span class="text-xs text-gray-400">修改后立即生效缓存60秒刷新</span>
</div>
</form>
{{-- 神秘箱子:手动投放区域 --}}
@if ($game->game_key === 'mystery_box')
<div class="mt-4 pt-4 border-t border-gray-100">
<div class="text-xs font-bold text-gray-600 mb-2">🎯 手动投放箱子</div>
<div class="flex items-center gap-3 flex-wrap">
<button onclick="dropBox('normal', {{ $game->id }})"
style="padding:8px 16px; background:linear-gradient(135deg,#059669,#10b981); color:#fff; border:none; border-radius:8px; font-size:13px; font-weight:700; cursor:pointer; transition:opacity .15s;"
onmouseover="this.style.opacity='.85'" onmouseout="this.style.opacity='1'">
📦 投放普通箱
</button>
<button onclick="dropBox('rare', {{ $game->id }})"
style="padding:8px 16px; background:linear-gradient(135deg,#7c3aed,#a78bfa); color:#fff; border:none; border-radius:8px; font-size:13px; font-weight:700; cursor:pointer; transition:opacity .15s;"
onmouseover="this.style.opacity='.85'" onmouseout="this.style.opacity='1'">
💎 投放稀有箱
</button>
<button onclick="dropBox('trap', {{ $game->id }})"
style="padding:8px 16px; background:linear-gradient(135deg,#7f1d1d,#ef4444); color:#fff; border:none; border-radius:8px; font-size:13px; font-weight:700; cursor:pointer; transition:opacity .15s;"
onmouseover="this.style.opacity='.85'" onmouseout="this.style.opacity='1'">
☠️ 投放黑化箱
</button>
<span class="text-xs text-gray-400">直接向 #1 房间投放,立即广播暗号</span>
</div>
</div>
@endif
{{-- 双色球彩票:手动操作区域 --}}
@if ($game->game_key === 'lottery')
<div class="mt-4 pt-4 border-t border-gray-100">
<div class="text-xs font-bold text-gray-600 mb-3">🎟️ 手动操作</div>
{{-- 当前期次状态展示 --}}
<div id="lottery-issue-status"
class="mb-3 text-xs text-gray-500 bg-red-50 border border-red-100 rounded-lg p-3">
<span class="text-red-400"> 点击下方「加载期次状态」查看当前状态</span>
</div>
<div class="flex items-center gap-3 flex-wrap">
<button onclick="lotteryLoadStatus()"
style="padding:8px 16px; background:linear-gradient(135deg,#475569,#64748b); color:#fff; border:none; border-radius:8px; font-size:13px; font-weight:700; cursor:pointer; transition:opacity .15s;"
onmouseover="this.style.opacity='.85'" onmouseout="this.style.opacity='1'">
🔄 加载期次状态
</button>
<button onclick="lotteryOpenIssue()"
style="padding:8px 16px; background:linear-gradient(135deg,#059669,#10b981); color:#fff; border:none; border-radius:8px; font-size:13px; font-weight:700; cursor:pointer; transition:opacity .15s;"
onmouseover="this.style.opacity='.85'" onmouseout="this.style.opacity='1'">
手动开新期
</button>
<button onclick="lotteryForceDraw()"
style="padding:8px 16px; background:linear-gradient(135deg,#dc2626,#ef4444); color:#fff; border:none; border-radius:8px; font-size:13px; font-weight:700; cursor:pointer; transition:opacity .15s;"
onmouseover="this.style.opacity='.85'" onmouseout="this.style.opacity='1'">
🎊 立即强制开奖
</button>
<span class="text-xs text-gray-400">开新期仅在无进行中期次时生效;强制开奖将提前结束当期</span>
</div>
</div>
@endif
</div>
</div>
@endforeach
@if ($games->isEmpty())
<div class="bg-white rounded-xl border border-gray-100 p-12 text-center text-gray-400">
暂无游戏配置,请先运行 <code class="bg-gray-100 px-2 py-1 rounded">php artisan db:seed
--class=GameConfigSeeder</code>
</div>
@endif
</div>
</div>
<script>
/**
* 切换游戏开启/关闭状态
*/
function toggleGame(gameKey, gameId) {
fetch(`/admin/game-configs/${gameId}/toggle`, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json',
},
})
.then(r => r.json())
.then(data => {
if (!data.ok) return;
const enabled = data.enabled;
const card = document.getElementById(`game-card-${gameKey}`);
const badge = document.getElementById(`badge-${gameKey}`);
const btn = document.getElementById(`toggle-btn-${gameKey}`);
const header = card?.querySelector('.flex.items-center.justify-between');
// 更新徽章
badge.textContent = enabled ? '运行中' : '已关闭';
badge.className = `text-xs px-2 py-0.5 rounded-full font-bold ${enabled
? 'bg-emerald-100 text-emerald-700'
: 'bg-gray-200 text-gray-500'}`;
// 更新按钮
btn.textContent = enabled ? '⏸ 关闭游戏' : '▶ 开启游戏';
btn.className = `px-5 py-2 rounded-lg font-bold text-sm transition shadow-sm ${enabled
? 'bg-red-500 hover:bg-red-600 text-white'
: 'bg-emerald-500 hover:bg-emerald-600 text-white'}`;
// 更新头部背景
if (header) {
header.classList.toggle('bg-emerald-50', enabled);
header.classList.toggle('bg-gray-50', !enabled);
}
// 全局弹窗提示
window.adminDialog.alert(data.message, enabled ? '游戏已开启' : '游戏已关闭', enabled ? '✅' : '⏸');
});
}
/**
* 管理员手动投放神秘箱子
*
* @param {string} boxType 箱子类型normal | rare | trap
*/
function dropBox(boxType) {
const typeNames = {
normal: '普通箱',
rare: '稀有箱',
trap: '黑化箱'
};
const typeIcons = {
normal: '📦',
rare: '💎',
trap: '☠️'
};
const name = typeNames[boxType] || boxType;
const icon = typeIcons[boxType] || '📦';
window.adminDialog.confirm(
`确定要向 <b>#1 房间</b> 投放一个「${name}」吗?<br><span style="color:#64748b; font-size:12px;">箱子投放后将立即在公屏广播暗号,用户限时领取。</span>`,
`投放${name}`,
() => {
fetch('/admin/mystery-box/drop', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
box_type: boxType
}),
})
.then(r => r.json())
.then(data => {
window.adminDialog.alert(
data.message || (data.ok ? '投放成功!' : '投放失败'),
data.ok ? '投放成功' : '投放失败',
data.ok ? icon : '❌'
);
})
.catch(() => window.adminDialog.alert('网络错误,请重试', '网络错误', '🌐'));
},
icon
);
}
/**
* 加载双色球当前期次状态
*/
function lotteryLoadStatus() {
const box = document.getElementById('lottery-issue-status');
box.innerHTML = '<span class="text-gray-400">⏳ 加载中…</span>';
fetch('/lottery/current', {
headers: {
'Accept': 'application/json'
}
})
.then(r => r.json())
.then(data => {
if (!data.issue) {
box.innerHTML = '<span class="text-orange-500">⚠️ 当前无进行中期次,可手动开新期</span>';
return;
}
const iss = data.issue;
const pool = Number(iss.pool_amount).toLocaleString();
const statusMap = {
open: '🟢 购票中',
closed: '🔴 已停售',
settled: '✅ 已开奖'
};
const superTag = iss.is_super_issue ? ' 🎊<b>超级期</b>' : '';
const drawAt = iss.draw_at ? iss.draw_at.replace('T', ' ') : '--';
box.innerHTML = `第 <b>${iss.issue_no}</b> 期${superTag} &nbsp;·&nbsp; 状态:${statusMap[iss.status] || iss.status}
&nbsp;·&nbsp; 奖池:<b style="color:#dc2626">💰 ${pool} 金币</b>
&nbsp;·&nbsp; 预计开奖:${drawAt}
&nbsp;·&nbsp; 已购:${iss.total_tickets || 0} 注`;
})
.catch(() => {
box.innerHTML = '<span class="text-red-400">❌ 加载失败</span>';
});
}
/**
* 手动开启新一期双色球
*/
function lotteryOpenIssue() {
window.adminDialog.confirm(
'确定要手动开启新一期双色球吗?<br><span style="color:#64748b;font-size:12px;">仅在无进行中期次时生效,开奖时间将使用当前配置的 draw_hour:draw_minute。</span>',
'手动开新期', () => {
fetch('/admin/lottery/open-issue', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json'
},
})
.then(r => r.json())
.then(d => {
window.adminDialog.alert(d.message, d.ok ? '操作成功' : '操作失败', d.ok ? '✅' : '❌');
if (d.ok) lotteryLoadStatus();
})
.catch(() => window.adminDialog.alert('网络错误,请重试', '网络错误', '🌐'));
}, ''
);
}
/**
* 强制提前开奖(用于测试或管理需要)
*/
function lotteryForceDraw() {
window.adminDialog.confirm(
'确定要<b style="color:red">立即强制开奖</b>吗?<br><span style="color:#64748b;font-size:12px;">将对当前 closed 或 open 期次立即执行开奖,此操作不可撤销!</span>',
'强制开奖确认', () => {
fetch('/admin/lottery/force-draw', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json'
},
})
.then(r => r.json())
.then(d => {
window.adminDialog.alert(d.message, d.ok ? '开奖完成' : '操作失败', d.ok ? '🎊' : '❌');
if (d.ok) lotteryLoadStatus();
})
.catch(() => window.adminDialog.alert('网络错误,请重试', '网络错误', '🌐'));
}, '🎊'
);
}
</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
/**
* 返回各游戏参数的中文标签说明。
*
* @param string $gameKey
* @return array<string, array{label: string, type: string, unit: string}>
*/
function gameParamLabels(string $gameKey): array
{
return match ($gameKey) {
'baccarat' => [
'interval_minutes' => ['label' => '开局间隔', 'type' => 'number', 'unit' => '分钟', 'min' => 1],
'bet_window_seconds' => ['label' => '押注窗口', 'type' => 'number', 'unit' => '秒', 'min' => 10],
'min_bet' => ['label' => '最低押注', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'max_bet' => ['label' => '最高押注', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'payout_big' => ['label' => '大赔率1:N', 'type' => 'number', 'unit' => '', 'min' => 1],
'payout_small' => ['label' => '小赔率1:N', 'type' => 'number', 'unit' => '', 'min' => 1],
'payout_triple' => ['label' => '豹子赔率1:N', 'type' => 'number', 'unit' => '', 'min' => 1],
'kill_points' => ['label' => '庄家收割点数', 'type' => 'array', 'unit' => '逗号分隔'],
],
'slot_machine' => [
'cost_per_spin' => ['label' => '每次旋转消耗', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'house_edge_percent' => [
'label' => '庄家边际',
'type' => 'number',
'unit' => '%',
'min' => 0,
'step' => 0.1,
],
'daily_limit' => ['label' => '每日转动上限', 'type' => 'number', 'unit' => '次0=不限)', 'min' => 0],
'jackpot_payout' => ['label' => '三个7赔率1:N', 'type' => 'number', 'unit' => '', 'min' => 1],
'triple_payout' => ['label' => '三💎赔率1:N', 'type' => 'number', 'unit' => '', 'min' => 1],
'same_payout' => ['label' => '其他三同1:N', 'type' => 'number', 'unit' => '', 'min' => 1],
'pair_payout' => ['label' => '两同赔率1:N', 'type' => 'number', 'unit' => '', 'min' => 1],
'curse_enabled' => ['label' => '开启诅咒(三💀)', 'type' => 'boolean', 'unit' => ''],
],
'mystery_box' => [
'auto_drop_enabled' => ['label' => '自动定时投放', 'type' => 'boolean', 'unit' => ''],
'auto_interval_hours' => ['label' => '自动投放间隔', 'type' => 'number', 'unit' => '小时', 'min' => 1],
'claim_window_seconds' => ['label' => '领取窗口', 'type' => 'number', 'unit' => '秒', 'min' => 10],
// 新键名
'normal_reward_min' => ['label' => '普通箱最低奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'normal_reward_max' => ['label' => '普通箱最高奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'rare_reward_min' => ['label' => '稀有箱最低奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'rare_reward_max' => ['label' => '稀有箱最高奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'trap_penalty_min' => ['label' => '黑化箱最低惩罚', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'trap_penalty_max' => ['label' => '黑化箱最高惩罚', 'type' => 'number', 'unit' => '金币', 'min' => 1],
// 旧键名兼容(数据库中已存在的旧配置)
'min_reward' => ['label' => '普通箱最低奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'max_reward' => ['label' => '普通箱最高奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'rare_min_reward' => ['label' => '稀有箱最低奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'rare_max_reward' => ['label' => '稀有箱最高奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'trap_chance_percent' => [
'label' => '黑化箱概率',
'type' => 'number',
'unit' => '%',
'min' => 0,
'max' => 100,
],
],
'horse_racing' => [
'interval_minutes' => ['label' => '比赛间隔', 'type' => 'number', 'unit' => '分钟', 'min' => 5],
'bet_window_seconds' => ['label' => '押注窗口', 'type' => 'number', 'unit' => '秒', 'min' => 10],
'race_duration' => ['label' => '跑马动画时长', 'type' => 'number', 'unit' => '秒', 'min' => 10],
'horse_count' => ['label' => '参赛马匹数', 'type' => 'number', 'unit' => '匹', 'min' => 2, 'max' => 8],
'min_bet' => ['label' => '最低押注', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'max_bet' => ['label' => '最高押注', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'house_take_percent' => [
'label' => '庄家抽水',
'type' => 'number',
'unit' => '%',
'min' => 0,
'max' => 20,
],
],
'fortune_telling' => [
'free_count_per_day' => ['label' => '每日免费次数', 'type' => 'number', 'unit' => '次', 'min' => 0],
'extra_cost' => ['label' => '额外次数消耗', 'type' => 'number', 'unit' => '金币', 'min' => 0],
'buff_duration_hours' => ['label' => '加成持续时间', 'type' => 'number', 'unit' => '小时', 'min' => 1],
'jackpot_chance' => [
'label' => '上上签概率',
'type' => 'number',
'unit' => '%',
'min' => 0,
'max' => 100,
],
'good_chance' => ['label' => '上签概率', 'type' => 'number', 'unit' => '%', 'min' => 0, 'max' => 100],
'bad_chance' => ['label' => '下签概率', 'type' => 'number', 'unit' => '%', 'min' => 0, 'max' => 100],
'curse_chance' => [
'label' => '大凶签概率',
'type' => 'number',
'unit' => '%',
'min' => 0,
'max' => 100,
],
],
'fishing' => [
'fishing_cost' => ['label' => '每次抛竿消耗', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'fishing_wait_min' => ['label' => '浮漂等待最短', 'type' => 'number', 'unit' => '秒', 'min' => 1],
'fishing_wait_max' => ['label' => '浮漂等待最长', 'type' => 'number', 'unit' => '秒', 'min' => 1],
'fishing_cooldown' => ['label' => '收竿后冷却时间', 'type' => 'number', 'unit' => '秒', 'min' => 10],
],
'lottery' => [
// ── 开奖时间 ──
'draw_hour' => [
'label' => '开奖时24h制',
'type' => 'number',
'unit' => '时',
'min' => 0,
'max' => 23,
],
'draw_minute' => ['label' => '开奖分', 'type' => 'number', 'unit' => '分', 'min' => 0, 'max' => 59],
'stop_sell_minutes' => ['label' => '停售提前', 'type' => 'number', 'unit' => '分钟', 'min' => 1],
// ── 购票限制 ──
'ticket_price' => ['label' => '每注金额', 'type' => 'number', 'unit' => '金币', 'min' => 1],
'max_tickets_per_user' => ['label' => '单人每期上限', 'type' => 'number', 'unit' => '注', 'min' => 1],
'max_tickets_per_buy' => ['label' => '单次购买上限', 'type' => 'number', 'unit' => '注', 'min' => 1],
// ── 奖池分配 ──
'pool_ratio' => [
'label' => '购票进奖池比例',
'type' => 'number',
'unit' => '%',
'min' => 1,
'max' => 100,
],
'prize_1st_ratio' => [
'label' => '一等奖占奖池',
'type' => 'number',
'unit' => '%',
'min' => 1,
'max' => 100,
],
'prize_2nd_ratio' => [
'label' => '二等奖占奖池',
'type' => 'number',
'unit' => '%',
'min' => 1,
'max' => 100,
],
'prize_3rd_ratio' => [
'label' => '三等奖占奖池',
'type' => 'number',
'unit' => '%',
'min' => 1,
'max' => 100,
],
'carry_ratio' => ['label' => '强制滚存', 'type' => 'number', 'unit' => '%', 'min' => 0, 'max' => 50],
// ── 固定小奖 ──
'prize_4th_fixed' => ['label' => '四等奖固定金额/注', 'type' => 'number', 'unit' => '金币', 'min' => 0],
'prize_5th_fixed' => ['label' => '五等奖固定金额/注', 'type' => 'number', 'unit' => '金币', 'min' => 0],
// ── 超级期 ──
'super_issue_threshold' => [
'label' => '超级期触发连续无一等奖',
'type' => 'number',
'unit' => '期',
'min' => 1,
],
'super_issue_inject' => [
'label' => '超级期系统注入上限',
'type' => 'number',
'unit' => '金币',
'min' => 0,
],
],
'gomoku' => [
// ── PvP 随机对战 ──
'pvp_reward' => ['label' => 'PvP 胜利奖励', 'type' => 'number', 'unit' => '金币', 'min' => 0],
// ── 人机对战:简单 ──
'pve_easy_fee' => ['label' => 'AI简单 入场费', 'type' => 'number', 'unit' => '金币', 'min' => 0],
'pve_easy_reward' => ['label' => 'AI简单 胜利奖励', 'type' => 'number', 'unit' => '金币', 'min' => 0],
// ── 人机对战:普通 ──
'pve_normal_fee' => ['label' => 'AI普通 入场费', 'type' => 'number', 'unit' => '金币', 'min' => 0],
'pve_normal_reward' => ['label' => 'AI普通 胜利奖励', 'type' => 'number', 'unit' => '金币', 'min' => 0],
// ── 人机对战:困难 ──
'pve_hard_fee' => ['label' => 'AI困难 入场费', 'type' => 'number', 'unit' => '金币', 'min' => 0],
'pve_hard_reward' => ['label' => 'AI困难 胜利奖励', 'type' => 'number', 'unit' => '金币', 'min' => 0],
// ── 人机对战:专家 ──
'pve_expert_fee' => ['label' => 'AI专家 入场费', 'type' => 'number', 'unit' => '金币', 'min' => 0],
'pve_expert_reward' => ['label' => 'AI专家 胜利奖励', 'type' => 'number', 'unit' => '金币', 'min' => 0],
// ── 超时配置 ──
'invite_timeout' => ['label' => 'PvP邀请超时', 'type' => 'number', 'unit' => '秒', 'min' => 10],
'move_timeout' => ['label' => '每步落子超时', 'type' => 'number', 'unit' => '秒', 'min' => 10],
],
default => [],
};
}
@endphp