功能:钓鱼游戏后台管理系统
一、钓鱼全局开关
- 钓鱼纳入 GameConfig(game_key=fishing),游戏管理页可一键开关
- cast() 接口加开关校验,关闭时返回 403 友好提示
- GameConfigSeeder 新增 fishing 配置(含4个参数)
二、钓鱼事件数据库化
- 新建 fishing_events 表(emoji/name/message/exp/jjb/weight/is_active/sort)
- FishingEvent 模型含 rollOne() 加权随机方法
- FishingEventSeeder 填充7条初始事件(经验降低、金币提升)
- FishingController::randomFishResult() 改为读数据库事件
三、钓鱼参数迁移至 GameConfig
- fishing_cost/wait_min/wait_max/cooldown 改为 GameConfig::param() 读取
- 保留 Sysparam fallback 兼容旧数据
四、后台管理页面
- 新建 FishingEventController(CRUD + AJAX toggle)
- 新建 admin/fishing/index.blade.php(事件列表+概率显示+编辑弹窗)
- 侧边栏「游戏管理」下方新增「🎣 钓鱼事件」入口
- 游戏管理视图 gameParamLabels 新增钓鱼参数标签
This commit is contained in:
108
app/Http/Controllers/Admin/FishingEventController.php
Normal file
108
app/Http/Controllers/Admin/FishingEventController.php
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件功能:钓鱼事件后台管理控制器
|
||||||
|
* 提供钓鱼事件的列表展示、创建、编辑、删除、启用/禁用功能
|
||||||
|
*
|
||||||
|
* @author ChatRoom Laravel
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\FishingEvent;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
class FishingEventController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 显示所有钓鱼事件列表
|
||||||
|
*/
|
||||||
|
public function index(): View
|
||||||
|
{
|
||||||
|
$events = FishingEvent::orderBy('sort')->orderBy('id')->get();
|
||||||
|
$totalWeight = $events->where('is_active', true)->sum('weight');
|
||||||
|
|
||||||
|
return view('admin.fishing.index', compact('events', 'totalWeight'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建新钓鱼事件
|
||||||
|
*/
|
||||||
|
public function store(Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$data = $request->validate([
|
||||||
|
'emoji' => 'required|string|max:10',
|
||||||
|
'name' => 'required|string|max:100',
|
||||||
|
'message' => 'required|string|max:255',
|
||||||
|
'exp' => 'required|integer',
|
||||||
|
'jjb' => 'required|integer',
|
||||||
|
'weight' => 'required|integer|min:1|max:9999',
|
||||||
|
'sort' => 'required|integer|min:0',
|
||||||
|
'is_active' => 'boolean',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$data['is_active'] = $request->boolean('is_active', true);
|
||||||
|
FishingEvent::create($data);
|
||||||
|
|
||||||
|
return redirect()->route('admin.fishing.index')->with('success', '钓鱼事件已添加!');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新钓鱼事件
|
||||||
|
*
|
||||||
|
* @param FishingEvent $fishing 路由模型绑定
|
||||||
|
*/
|
||||||
|
public function update(Request $request, FishingEvent $fishing): RedirectResponse
|
||||||
|
{
|
||||||
|
$data = $request->validate([
|
||||||
|
'emoji' => 'required|string|max:10',
|
||||||
|
'name' => 'required|string|max:100',
|
||||||
|
'message' => 'required|string|max:255',
|
||||||
|
'exp' => 'required|integer',
|
||||||
|
'jjb' => 'required|integer',
|
||||||
|
'weight' => 'required|integer|min:1|max:9999',
|
||||||
|
'sort' => 'required|integer|min:0',
|
||||||
|
'is_active' => 'boolean',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$data['is_active'] = $request->boolean('is_active');
|
||||||
|
$fishing->update($data);
|
||||||
|
|
||||||
|
return redirect()->route('admin.fishing.index')->with('success', "事件「{$fishing->name}」已更新!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换事件启用/禁用状态(AJAX)
|
||||||
|
*
|
||||||
|
* @param FishingEvent $fishing 路由模型绑定
|
||||||
|
*/
|
||||||
|
public function toggle(FishingEvent $fishing): JsonResponse
|
||||||
|
{
|
||||||
|
$fishing->update(['is_active' => ! $fishing->is_active]);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'ok' => true,
|
||||||
|
'is_active' => $fishing->is_active,
|
||||||
|
'message' => $fishing->is_active ? "「{$fishing->name}」已启用" : "「{$fishing->name}」已禁用",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除钓鱼事件
|
||||||
|
*
|
||||||
|
* @param FishingEvent $fishing 路由模型绑定
|
||||||
|
*/
|
||||||
|
public function destroy(FishingEvent $fishing): RedirectResponse
|
||||||
|
{
|
||||||
|
$name = $fishing->name;
|
||||||
|
$fishing->delete();
|
||||||
|
|
||||||
|
return redirect()->route('admin.fishing.index')->with('success', "事件「{$name}」已删除!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,8 @@ namespace App\Http\Controllers;
|
|||||||
|
|
||||||
use App\Enums\CurrencySource;
|
use App\Enums\CurrencySource;
|
||||||
use App\Events\MessageSent;
|
use App\Events\MessageSent;
|
||||||
|
use App\Models\FishingEvent;
|
||||||
|
use App\Models\GameConfig;
|
||||||
use App\Models\Sysparam;
|
use App\Models\Sysparam;
|
||||||
use App\Services\ChatStateService;
|
use App\Services\ChatStateService;
|
||||||
use App\Services\ShopService;
|
use App\Services\ShopService;
|
||||||
@@ -56,6 +58,11 @@ class FishingController extends Controller
|
|||||||
return response()->json(['status' => 'error', 'message' => '请先登录'], 401);
|
return response()->json(['status' => 'error', 'message' => '请先登录'], 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查钓鱼全局开关
|
||||||
|
if (! GameConfig::isEnabled('fishing')) {
|
||||||
|
return response()->json(['status' => 'error', 'message' => '钓鱼功能暂未开放。'], 403);
|
||||||
|
}
|
||||||
|
|
||||||
// 1. 检查冷却时间(Redis TTL)
|
// 1. 检查冷却时间(Redis TTL)
|
||||||
$cooldownKey = "fishing:cd:{$user->id}";
|
$cooldownKey = "fishing:cd:{$user->id}";
|
||||||
if (Redis::exists($cooldownKey)) {
|
if (Redis::exists($cooldownKey)) {
|
||||||
@@ -69,7 +76,7 @@ class FishingController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. 检查金币是否足够
|
// 2. 检查金币是否足够
|
||||||
$cost = (int) Sysparam::getValue('fishing_cost', '5');
|
$cost = (int) (GameConfig::param('fishing', 'fishing_cost') ?? Sysparam::getValue('fishing_cost', '5'));
|
||||||
if (($user->jjb ?? 0) < $cost) {
|
if (($user->jjb ?? 0) < $cost) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
@@ -87,8 +94,8 @@ class FishingController extends Controller
|
|||||||
$user->refresh();
|
$user->refresh();
|
||||||
|
|
||||||
// 4. 生成一次性 token,存入 Redis(TTL = 等待时间 + 收竿窗口 + 缓冲)
|
// 4. 生成一次性 token,存入 Redis(TTL = 等待时间 + 收竿窗口 + 缓冲)
|
||||||
$waitMin = (int) Sysparam::getValue('fishing_wait_min', '8');
|
$waitMin = (int) (GameConfig::param('fishing', 'fishing_wait_min') ?? Sysparam::getValue('fishing_wait_min', '8'));
|
||||||
$waitMax = (int) Sysparam::getValue('fishing_wait_max', '15');
|
$waitMax = (int) (GameConfig::param('fishing', 'fishing_wait_max') ?? Sysparam::getValue('fishing_wait_max', '15'));
|
||||||
$waitTime = rand($waitMin, $waitMax);
|
$waitTime = rand($waitMin, $waitMax);
|
||||||
$token = Str::random(32);
|
$token = Str::random(32);
|
||||||
$tokenKey = "fishing:token:{$user->id}";
|
$tokenKey = "fishing:token:{$user->id}";
|
||||||
@@ -169,7 +176,7 @@ class FishingController extends Controller
|
|||||||
Redis::del($tokenKey);
|
Redis::del($tokenKey);
|
||||||
|
|
||||||
// 2. 设置冷却时间
|
// 2. 设置冷却时间
|
||||||
$cooldown = (int) Sysparam::getValue('fishing_cooldown', '300');
|
$cooldown = (int) (GameConfig::param('fishing', 'fishing_cooldown') ?? Sysparam::getValue('fishing_cooldown', '300'));
|
||||||
Redis::setex("fishing:cd:{$user->id}", $cooldown, time());
|
Redis::setex("fishing:cd:{$user->id}", $cooldown, time());
|
||||||
|
|
||||||
// 3. 随机决定钓鱼结果
|
// 3. 随机决定钓鱼结果
|
||||||
@@ -229,57 +236,31 @@ class FishingController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 随机钓鱼结果(复刻原版概率分布)
|
* 随机钓鱼结果(从数据库 fishing_events 加权随机抽取)
|
||||||
|
*
|
||||||
|
* 若数据库中无激活事件,回退到兜底结果。
|
||||||
*
|
*
|
||||||
* @return array{emoji: string, message: string, exp: int, jjb: int}
|
* @return array{emoji: string, message: string, exp: int, jjb: int}
|
||||||
*/
|
*/
|
||||||
private function randomFishResult(): array
|
private function randomFishResult(): array
|
||||||
{
|
{
|
||||||
$roll = rand(1, 100);
|
$event = FishingEvent::rollOne();
|
||||||
|
|
||||||
return match (true) {
|
// 数据库无事件时的兜底
|
||||||
$roll <= 15 => [
|
if (! $event) {
|
||||||
'emoji' => '🦈',
|
return [
|
||||||
'message' => '钓到一条大鲨鱼!增加经验100、金币20',
|
'emoji' => '🐟',
|
||||||
'exp' => 100,
|
'message' => '钓到一条小鱼,获得金币10',
|
||||||
'jjb' => 20,
|
'exp' => 0,
|
||||||
],
|
'jjb' => 10,
|
||||||
$roll <= 30 => [
|
];
|
||||||
'emoji' => '🐟',
|
}
|
||||||
'message' => '钓到一条娃娃鱼,到集市卖得30个金币',
|
|
||||||
'exp' => 0,
|
return [
|
||||||
'jjb' => 30,
|
'emoji' => $event->emoji,
|
||||||
],
|
'message' => $event->message,
|
||||||
$roll <= 50 => [
|
'exp' => $event->exp,
|
||||||
'emoji' => '🐠',
|
'jjb' => $event->jjb,
|
||||||
'message' => '钓到一只大草鱼,吃下增加经验50',
|
];
|
||||||
'exp' => 50,
|
|
||||||
'jjb' => 0,
|
|
||||||
],
|
|
||||||
$roll <= 70 => [
|
|
||||||
'emoji' => '🐡',
|
|
||||||
'message' => '钓到一条小鲤鱼,增加经验50、金币10',
|
|
||||||
'exp' => 50,
|
|
||||||
'jjb' => 10,
|
|
||||||
],
|
|
||||||
$roll <= 85 => [
|
|
||||||
'emoji' => '💧',
|
|
||||||
'message' => '鱼没钓到,摔到河里经验减少50',
|
|
||||||
'exp' => -50,
|
|
||||||
'jjb' => 0,
|
|
||||||
],
|
|
||||||
$roll <= 95 => [
|
|
||||||
'emoji' => '👊',
|
|
||||||
'message' => '偷钓鱼塘被主人发现,一阵殴打!经验减少20、金币减少3',
|
|
||||||
'exp' => -20,
|
|
||||||
'jjb' => -3,
|
|
||||||
],
|
|
||||||
default => [
|
|
||||||
'emoji' => '🎉',
|
|
||||||
'message' => '运气爆棚!钓到大鲨鱼、大草鱼、小鲤鱼各一条!经验+150,金币+50!',
|
|
||||||
'exp' => 150,
|
|
||||||
'jjb' => 50,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
89
app/Models/FishingEvent.php
Normal file
89
app/Models/FishingEvent.php
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件功能:钓鱼事件模型
|
||||||
|
*
|
||||||
|
* 对应 fishing_events 表,每条记录是一个钓鱼随机奖惩事件。
|
||||||
|
* 概率由 weight(权重)字段决定,权重越大被选中概率越高。
|
||||||
|
*
|
||||||
|
* @author ChatRoom Laravel
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class FishingEvent extends Model
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 可批量赋值字段
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'emoji',
|
||||||
|
'name',
|
||||||
|
'message',
|
||||||
|
'exp',
|
||||||
|
'jjb',
|
||||||
|
'weight',
|
||||||
|
'is_active',
|
||||||
|
'sort',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段类型转换
|
||||||
|
*
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'exp' => 'integer',
|
||||||
|
'jjb' => 'integer',
|
||||||
|
'weight' => 'integer',
|
||||||
|
'is_active' => 'boolean',
|
||||||
|
'sort' => 'integer',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 作用域:只查询已启用的事件
|
||||||
|
*/
|
||||||
|
public function scopeActive(Builder $query): Builder
|
||||||
|
{
|
||||||
|
return $query->where('is_active', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据权重随机抽取一个激活的钓鱼事件
|
||||||
|
*
|
||||||
|
* 实现加权随机:权重越大,被选中概率越高。
|
||||||
|
* 若无任何激活事件,返回 null。
|
||||||
|
*/
|
||||||
|
public static function rollOne(): ?static
|
||||||
|
{
|
||||||
|
$events = static::active()->orderBy('sort')->get();
|
||||||
|
if ($events->isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算总权重后加权随机
|
||||||
|
$totalWeight = $events->sum('weight');
|
||||||
|
$roll = random_int(1, max($totalWeight, 1));
|
||||||
|
$cumulative = 0;
|
||||||
|
|
||||||
|
foreach ($events as $event) {
|
||||||
|
$cumulative += $event->weight;
|
||||||
|
if ($roll <= $cumulative) {
|
||||||
|
return $event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兜底:返回最后一个
|
||||||
|
return $events->last();
|
||||||
|
}
|
||||||
|
}
|
||||||
23
database/factories/FishingEventFactory.php
Normal file
23
database/factories/FishingEventFactory.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\FishingEvent>
|
||||||
|
*/
|
||||||
|
class FishingEventFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件功能:创建钓鱼事件表
|
||||||
|
*
|
||||||
|
* 将钓鱼随机事件从硬编码迁移到数据库,支持后台增删改。
|
||||||
|
* weight 字段为权重(不是百分比),由所有激活事件的权重之和决定概率。
|
||||||
|
*
|
||||||
|
* @author ChatRoom Laravel
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 创建 fishing_events 表
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('fishing_events', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('emoji', 10)->comment('事件表情符号');
|
||||||
|
$table->string('name', 100)->comment('事件名称(后台展示用)');
|
||||||
|
$table->string('message', 255)->comment('聊天室播报内容');
|
||||||
|
$table->integer('exp')->default(0)->comment('经验变化(正为获得,负为扣除)');
|
||||||
|
$table->integer('jjb')->default(0)->comment('金币变化(正为获得,负为扣除)');
|
||||||
|
$table->unsignedSmallInteger('weight')->default(10)->comment('权重(权重越大概率越高)');
|
||||||
|
$table->boolean('is_active')->default(true)->comment('是否启用');
|
||||||
|
$table->unsignedTinyInteger('sort')->default(0)->comment('排序(越小越靠前)');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回滚:删除 fishing_events 表
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('fishing_events');
|
||||||
|
}
|
||||||
|
};
|
||||||
123
database/seeders/FishingEventSeeder.php
Normal file
123
database/seeders/FishingEventSeeder.php
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件功能:钓鱼事件初始数据填充
|
||||||
|
*
|
||||||
|
* 将原硬编码的 7 个钓鱼事件迁移至数据库,
|
||||||
|
* 调整奖励:经验减少,金币提升,游戏更加休闲娱乐。
|
||||||
|
*
|
||||||
|
* 执行方式:php artisan db:seed --class=FishingEventSeeder
|
||||||
|
*
|
||||||
|
* @author ChatRoom Laravel
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\FishingEvent;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class FishingEventSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 填充钓鱼事件数据
|
||||||
|
*
|
||||||
|
* weight 权重说明(总权重 100):
|
||||||
|
* 15 → 大鲨鱼(幸运)
|
||||||
|
* 15 → 娃娃鱼(卖鱼)
|
||||||
|
* 20 → 大草鱼(经验)
|
||||||
|
* 20 → 小鲤鱼(双收)
|
||||||
|
* 15 → 掉河(惩罚)
|
||||||
|
* 10 → 被打(惩罚)
|
||||||
|
* 5 → 超级大奖
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
// 避免重复 Seed
|
||||||
|
if (FishingEvent::count() > 0) {
|
||||||
|
$this->command->info('钓鱼事件已存在,跳过填充。');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$events = [
|
||||||
|
[
|
||||||
|
'sort' => 1,
|
||||||
|
'emoji' => '🦈',
|
||||||
|
'name' => '大鲨鱼',
|
||||||
|
'message' => '钓到一条大鲨鱼!获得经验30、金币50',
|
||||||
|
'exp' => 30,
|
||||||
|
'jjb' => 50,
|
||||||
|
'weight' => 15,
|
||||||
|
'is_active' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'sort' => 2,
|
||||||
|
'emoji' => '🐟',
|
||||||
|
'name' => '娃娃鱼',
|
||||||
|
'message' => '钓到一条娃娃鱼,到集市卖得80个金币',
|
||||||
|
'exp' => 0,
|
||||||
|
'jjb' => 80,
|
||||||
|
'weight' => 15,
|
||||||
|
'is_active' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'sort' => 3,
|
||||||
|
'emoji' => '🐠',
|
||||||
|
'name' => '大草鱼',
|
||||||
|
'message' => '钓到一只大草鱼,吃下增加经验20、金币30',
|
||||||
|
'exp' => 20,
|
||||||
|
'jjb' => 30,
|
||||||
|
'weight' => 20,
|
||||||
|
'is_active' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'sort' => 4,
|
||||||
|
'emoji' => '🐡',
|
||||||
|
'name' => '小鲤鱼',
|
||||||
|
'message' => '钓到一条小鲤鱼,增加经验10、金币20',
|
||||||
|
'exp' => 10,
|
||||||
|
'jjb' => 20,
|
||||||
|
'weight' => 20,
|
||||||
|
'is_active' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'sort' => 5,
|
||||||
|
'emoji' => '💧',
|
||||||
|
'name' => '落水惨败',
|
||||||
|
'message' => '鱼没钓到,摔到河里损失金币30',
|
||||||
|
'exp' => 0,
|
||||||
|
'jjb' => -30,
|
||||||
|
'weight' => 15,
|
||||||
|
'is_active' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'sort' => 6,
|
||||||
|
'emoji' => '👊',
|
||||||
|
'name' => '被抓殴打',
|
||||||
|
'message' => '偷钓鱼塘被主人发现,一阵殴打!金币减少10',
|
||||||
|
'exp' => 0,
|
||||||
|
'jjb' => -10,
|
||||||
|
'weight' => 10,
|
||||||
|
'is_active' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'sort' => 7,
|
||||||
|
'emoji' => '🎉',
|
||||||
|
'name' => '超级大奖',
|
||||||
|
'message' => '运气爆棚!钓到大鲨鱼、大草鱼、小鲤鱼各一条!经验+50,金币+200!',
|
||||||
|
'exp' => 50,
|
||||||
|
'jjb' => 200,
|
||||||
|
'weight' => 5,
|
||||||
|
'is_active' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($events as $event) {
|
||||||
|
FishingEvent::create($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->command->info('✅ 钓鱼事件已填充(7 条)');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -116,6 +116,21 @@ class GameConfigSeeder extends Seeder
|
|||||||
'curse_chance' => 5, // 大凶签概率(%)
|
'curse_chance' => 5, // 大凶签概率(%)
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// ─── 钓鱼小游戏 ──────────────────────────────────────────────
|
||||||
|
[
|
||||||
|
'game_key' => 'fishing',
|
||||||
|
'name' => '钓鱼小游戏',
|
||||||
|
'icon' => '🎣',
|
||||||
|
'description' => '消耗金币抛竿,等待浮漂下沉后点击收竿,随机获得奖励或惩罚。持有自动钓鱼卡可自动循环。',
|
||||||
|
'enabled' => false,
|
||||||
|
'params' => [
|
||||||
|
'fishing_cost' => 5, // 每次抛竿消耗金币
|
||||||
|
'fishing_wait_min' => 8, // 浮漂等待最短秒数
|
||||||
|
'fishing_wait_max' => 15, // 浮漂等待最长秒数
|
||||||
|
'fishing_cooldown' => 300, // 收竿后冷却秒数
|
||||||
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($games as $game) {
|
foreach ($games as $game) {
|
||||||
|
|||||||
281
resources/views/admin/fishing/index.blade.php
Normal file
281
resources/views/admin/fishing/index.blade.php
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
@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">
|
||||||
|
管理钓鱼随机事件。权重越大被触发概率越高。
|
||||||
|
当前激活事件总权重:<strong class="text-indigo-600">{{ $totalWeight }}</strong>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
{{-- Flash --}}
|
||||||
|
@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="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-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 w-1/3">播报内容</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-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">
|
||||||
|
@foreach ($events as $event)
|
||||||
|
<tr id="row-{{ $event->id }}" class="{{ $event->is_active ? '' : 'opacity-50' }}">
|
||||||
|
<td class="px-4 py-3 text-gray-500 text-xs">{{ $event->sort }}</td>
|
||||||
|
<td class="px-4 py-3 text-xl">{{ $event->emoji }}</td>
|
||||||
|
<td class="px-4 py-3 font-medium text-gray-800">{{ $event->name }}</td>
|
||||||
|
<td class="px-4 py-3 text-gray-500 text-xs">{{ $event->message }}</td>
|
||||||
|
<td
|
||||||
|
class="px-4 py-3 text-center font-mono text-xs
|
||||||
|
{{ $event->exp > 0 ? 'text-green-600' : ($event->exp < 0 ? 'text-red-500' : 'text-gray-400') }}">
|
||||||
|
{{ $event->exp > 0 ? '+' : '' }}{{ $event->exp }}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class="px-4 py-3 text-center font-mono text-xs
|
||||||
|
{{ $event->jjb > 0 ? 'text-amber-600' : ($event->jjb < 0 ? 'text-red-500' : 'text-gray-400') }}">
|
||||||
|
{{ $event->jjb > 0 ? '+' : '' }}{{ $event->jjb }}
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-3 text-center text-xs text-indigo-600 font-bold">{{ $event->weight }}</td>
|
||||||
|
<td class="px-4 py-3 text-center text-xs text-gray-500">
|
||||||
|
@if ($totalWeight > 0 && $event->is_active)
|
||||||
|
{{ number_format(($event->weight / $totalWeight) * 100, 1) }}%
|
||||||
|
@else
|
||||||
|
—
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-3 text-center">
|
||||||
|
<button onclick="toggleEvent({{ $event->id }})" id="toggle-{{ $event->id }}"
|
||||||
|
class="px-2 py-1 rounded-full text-xs font-bold transition
|
||||||
|
{{ $event->is_active ? 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200' : 'bg-gray-100 text-gray-500 hover:bg-gray-200' }}">
|
||||||
|
{{ $event->is_active ? '启用' : '禁用' }}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-3 text-right">
|
||||||
|
<button onclick="openEdit({{ $event->id }})"
|
||||||
|
class="px-3 py-1 bg-indigo-50 text-indigo-700 rounded text-xs font-bold hover:bg-indigo-100 transition mr-1">
|
||||||
|
编辑
|
||||||
|
</button>
|
||||||
|
<form action="{{ route('admin.fishing.destroy', $event->id) }}" method="POST"
|
||||||
|
class="inline" onsubmit="return confirm('确定删除事件「{{ $event->name }}」?')">
|
||||||
|
@csrf @method('DELETE')
|
||||||
|
<button type="submit"
|
||||||
|
class="px-3 py-1 bg-red-50 text-red-600 rounded text-xs font-bold hover:bg-red-100 transition">
|
||||||
|
删除
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- 新增事件卡片 --}}
|
||||||
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||||
|
<div class="p-5 border-b border-gray-100 bg-gray-50">
|
||||||
|
<h3 class="font-bold text-gray-700 text-sm">➕ 新增钓鱼事件</h3>
|
||||||
|
</div>
|
||||||
|
<form action="{{ route('admin.fishing.store') }}" method="POST" class="p-5">
|
||||||
|
@csrf
|
||||||
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">Emoji 符号</label>
|
||||||
|
<input type="text" name="emoji" value="{{ old('emoji') }}" placeholder="🐟" required
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm focus:border-indigo-400">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">名称</label>
|
||||||
|
<input type="text" name="name" value="{{ old('name') }}" placeholder="小鲤鱼" required
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm focus:border-indigo-400">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">经验变化</label>
|
||||||
|
<input type="number" name="exp" value="{{ old('exp', 0) }}"
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm focus:border-indigo-400">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">金币变化</label>
|
||||||
|
<input type="number" name="jjb" value="{{ old('jjb', 0) }}"
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm focus:border-indigo-400">
|
||||||
|
</div>
|
||||||
|
<div class="md:col-span-2">
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">播报内容</label>
|
||||||
|
<input type="text" name="message" value="{{ old('message') }}" placeholder="钓到一条小鲤鱼..." required
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm focus:border-indigo-400">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">权重(概率)</label>
|
||||||
|
<input type="number" name="weight" value="{{ old('weight', 10) }}" min="1"
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm focus:border-indigo-400">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">排序</label>
|
||||||
|
<input type="number" name="sort" value="{{ old('sort', 0) }}" min="0"
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm focus:border-indigo-400">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 flex items-center gap-4">
|
||||||
|
<button type="submit"
|
||||||
|
class="px-5 py-2 bg-indigo-600 text-white rounded-lg font-bold hover:bg-indigo-700 transition text-sm shadow-sm">
|
||||||
|
💾 添加事件
|
||||||
|
</button>
|
||||||
|
<label class="flex items-center gap-2 text-sm text-gray-600 cursor-pointer">
|
||||||
|
<input type="checkbox" name="is_active" value="1" checked class="rounded">
|
||||||
|
立即启用
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- 编辑弹窗 --}}
|
||||||
|
<div id="edit-modal" class="hidden fixed inset-0 bg-black/40 z-50 flex items-center justify-center p-4">
|
||||||
|
<div class="bg-white rounded-xl w-full max-w-lg shadow-2xl">
|
||||||
|
<div class="p-5 border-b border-gray-100 flex justify-between items-center">
|
||||||
|
<h3 class="font-bold text-gray-800">✏️ 编辑钓鱼事件</h3>
|
||||||
|
<button onclick="closeEdit()" class="text-gray-400 hover:text-gray-600 text-xl">✕</button>
|
||||||
|
</div>
|
||||||
|
<form id="edit-form" method="POST" class="p-5">
|
||||||
|
@csrf @method('PUT')
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">Emoji</label>
|
||||||
|
<input type="text" name="emoji" id="edit-emoji" required
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">名称</label>
|
||||||
|
<input type="text" name="name" id="edit-name" required
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm">
|
||||||
|
</div>
|
||||||
|
<div class="col-span-2">
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">播报内容</label>
|
||||||
|
<input type="text" name="message" id="edit-message" required
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">经验变化</label>
|
||||||
|
<input type="number" name="exp" id="edit-exp"
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">金币变化</label>
|
||||||
|
<input type="number" name="jjb" id="edit-jjb"
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">权重</label>
|
||||||
|
<input type="number" name="weight" id="edit-weight" min="1"
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-600 mb-1">排序</label>
|
||||||
|
<input type="number" name="sort" id="edit-sort" min="0"
|
||||||
|
class="w-full border border-gray-300 rounded-lg p-2 text-sm">
|
||||||
|
</div>
|
||||||
|
<div class="col-span-2">
|
||||||
|
<label class="flex items-center gap-2 text-sm cursor-pointer">
|
||||||
|
<input type="checkbox" name="is_active" id="edit-is-active" value="1" class="rounded">
|
||||||
|
启用此事件
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5 flex gap-3">
|
||||||
|
<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>
|
||||||
|
<button type="button" onclick="closeEdit()"
|
||||||
|
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg font-bold hover:bg-gray-200 transition text-sm">
|
||||||
|
取消
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 事件数据(供编辑弹窗填充)
|
||||||
|
const fishingEvents = @json($events->keyBy('id'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开编辑弹窗并填充数据
|
||||||
|
*/
|
||||||
|
function openEdit(id) {
|
||||||
|
const e = fishingEvents[id];
|
||||||
|
if (!e) return;
|
||||||
|
document.getElementById('edit-form').action = `/admin/fishing/${id}`;
|
||||||
|
document.getElementById('edit-emoji').value = e.emoji;
|
||||||
|
document.getElementById('edit-name').value = e.name;
|
||||||
|
document.getElementById('edit-message').value = e.message;
|
||||||
|
document.getElementById('edit-exp').value = e.exp;
|
||||||
|
document.getElementById('edit-jjb').value = e.jjb;
|
||||||
|
document.getElementById('edit-weight').value = e.weight;
|
||||||
|
document.getElementById('edit-sort').value = e.sort;
|
||||||
|
document.getElementById('edit-is-active').checked = !!e.is_active;
|
||||||
|
document.getElementById('edit-modal').classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭编辑弹窗
|
||||||
|
*/
|
||||||
|
function closeEdit() {
|
||||||
|
document.getElementById('edit-modal').classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换事件启用/禁用状态
|
||||||
|
*/
|
||||||
|
function toggleEvent(id) {
|
||||||
|
fetch(`/admin/fishing/${id}/toggle`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (!data.ok) return;
|
||||||
|
const btn = document.getElementById(`toggle-${id}`);
|
||||||
|
const row = document.getElementById(`row-${id}`);
|
||||||
|
btn.textContent = data.is_active ? '启用' : '禁用';
|
||||||
|
btn.className = `px-2 py-1 rounded-full text-xs font-bold transition ${
|
||||||
|
data.is_active
|
||||||
|
? 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200'
|
||||||
|
: 'bg-gray-100 text-gray-500 hover:bg-gray-200'
|
||||||
|
}`;
|
||||||
|
row.classList.toggle('opacity-50', !data.is_active);
|
||||||
|
// 同步内存里的状态
|
||||||
|
if (fishingEvents[id]) fishingEvents[id].is_active = data.is_active;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
@endsection
|
||||||
@@ -248,6 +248,12 @@
|
|||||||
'max' => 100,
|
'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],
|
||||||
|
],
|
||||||
default => [],
|
default => [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,6 +82,10 @@
|
|||||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.game-configs.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.game-configs.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||||
{!! '🎮 游戏管理' !!}
|
{!! '🎮 游戏管理' !!}
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{{ route('admin.fishing.index') }}"
|
||||||
|
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.fishing.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||||
|
{!! '🎣 钓鱼事件' !!}
|
||||||
|
</a>
|
||||||
<a href="{{ route('admin.departments.index') }}"
|
<a href="{{ route('admin.departments.index') }}"
|
||||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.departments.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.departments.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||||
{!! '🏛️ 部门管理' !!}
|
{!! '🏛️ 部门管理' !!}
|
||||||
|
|||||||
@@ -349,6 +349,15 @@ Route::middleware(['chat.auth', 'chat.has_position'])->prefix('admin')->name('ad
|
|||||||
Route::post('/{gameConfig}/toggle', [\App\Http\Controllers\Admin\GameConfigController::class, 'toggle'])->name('toggle');
|
Route::post('/{gameConfig}/toggle', [\App\Http\Controllers\Admin\GameConfigController::class, 'toggle'])->name('toggle');
|
||||||
Route::post('/{gameConfig}/params', [\App\Http\Controllers\Admin\GameConfigController::class, 'updateParams'])->name('params');
|
Route::post('/{gameConfig}/params', [\App\Http\Controllers\Admin\GameConfigController::class, 'updateParams'])->name('params');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 🎣 钓鱼事件管理
|
||||||
|
Route::prefix('fishing')->name('fishing.')->group(function () {
|
||||||
|
Route::get('/', [\App\Http\Controllers\Admin\FishingEventController::class, 'index'])->name('index');
|
||||||
|
Route::post('/', [\App\Http\Controllers\Admin\FishingEventController::class, 'store'])->name('store');
|
||||||
|
Route::put('/{fishing}', [\App\Http\Controllers\Admin\FishingEventController::class, 'update'])->name('update');
|
||||||
|
Route::post('/{fishing}/toggle', [\App\Http\Controllers\Admin\FishingEventController::class, 'toggle'])->name('toggle');
|
||||||
|
Route::delete('/{fishing}', [\App\Http\Controllers\Admin\FishingEventController::class, 'destroy'])->name('destroy');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ──────────────────────────────────────────────────────────────
|
// ──────────────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user