## 新功能 - 神秘箱子系统(MysteryBox)完整实现: - 新增 MysteryBox / MysteryBoxClaim 模型及迁移文件 - DropMysteryBoxJob / ExpireMysteryBoxJob 队列作业 - MysteryBoxController(/mystery-box/status + /mystery-box/claim) - 支持三种类型:普通箱(500~2000金)/ 稀有箱(5000~20000金)/ 黑化箱(陷阱扣200~1000金) - 调度器自动投放 + 管理员手动投放 - CurrencySource 新增 MYSTERY_BOX / MYSTERY_BOX_TRAP 枚举 - 婚姻状态弹窗(工具栏「婚姻」按钮): - 工具栏「呼叫」改为「婚姻」,点击打开婚姻状态弹窗 - 动态渲染三种状态:单身 / 求婚中 / 已婚 - 被求婚方可直接「答应 / 婉拒」;已婚可申请离婚(含二次确认) ## 优化修复 - frame.blade.php:Alpine.js CDN 补加 defer,修复所有组件初始化报错 - scripts.blade.php:神秘箱子暗号主动拦截(不依赖轮询),领取成功后弹 chatDialog 展示结果,更新金币余额 - MysteryBoxController:claim() 时 change() 补传 room_id 记录来源房间 - 后台游戏管理页(game-configs):投放箱子按钮颜色修复;弹窗替换为 window.adminDialog - admin/layouts:新增全局 adminDialog 弹窗组件(替代原生 alert/confirm) - baccarat-panel:FAB 拖动重构为 Alpine.js baccaratFab() 组件,与 slotFab 一致 - GAMES_TODO.md:神秘箱子移入已完成区,补全修复记录
123 lines
4.0 KiB
PHP
123 lines
4.0 KiB
PHP
<?php
|
||
|
||
/**
|
||
* 文件功能:投放神秘箱子队列任务
|
||
*
|
||
* 由调度器或管理员手动触发,执行以下操作:
|
||
* 1. 创建神秘箱记录(含暗号、类型、奖励范围)
|
||
* 2. 公屏广播暗号提示(全服可见)
|
||
* 3. 设定定时关闭任务(windows_seconds 秒后过期)
|
||
*
|
||
* @author ChatRoom Laravel
|
||
* @version 1.0.0
|
||
*/
|
||
|
||
namespace App\Jobs;
|
||
|
||
use App\Events\MessageSent;
|
||
use App\Models\GameConfig;
|
||
use App\Models\MysteryBox;
|
||
use App\Services\ChatStateService;
|
||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||
use Illuminate\Foundation\Queue\Queueable;
|
||
use Illuminate\Support\Str;
|
||
|
||
class DropMysteryBoxJob implements ShouldQueue
|
||
{
|
||
use Queueable;
|
||
|
||
/**
|
||
* 最大重试次数。
|
||
*/
|
||
public int $tries = 1;
|
||
|
||
/**
|
||
* @param string $boxType 箱子类型:normal | rare | trap
|
||
* @param int|null $roomId 投放目标房间,null 时默认 1
|
||
* @param string|null $passcode 手动指定暗号(null=自动生成)
|
||
* @param int|null $droppedBy 投放者用户ID(null=系统自动)
|
||
*/
|
||
public function __construct(
|
||
public readonly string $boxType = 'normal',
|
||
public readonly ?int $roomId = 1,
|
||
public readonly ?string $passcode = null,
|
||
public readonly ?int $droppedBy = null,
|
||
) {}
|
||
|
||
/**
|
||
* 执行投放逻辑。
|
||
*/
|
||
public function handle(ChatStateService $chatState): void
|
||
{
|
||
// 检查游戏是否开启
|
||
if (! GameConfig::isEnabled('mystery_box')) {
|
||
return;
|
||
}
|
||
|
||
$config = GameConfig::forGame('mystery_box')?->params ?? [];
|
||
$claimWindow = (int) ($config['claim_window_seconds'] ?? 120);
|
||
$targetRoom = $this->roomId ?? 1;
|
||
|
||
// 自动生成随机暗号(若未指定)
|
||
$passcode = $this->passcode ?? strtoupper(Str::random(6));
|
||
|
||
// 根据类型确定奖励范围
|
||
[$rewardMin, $rewardMax] = match ($this->boxType) {
|
||
'rare' => [
|
||
(int) ($config['rare_reward_min'] ?? 5000),
|
||
(int) ($config['rare_reward_max'] ?? 20000),
|
||
],
|
||
'trap' => [
|
||
(int) ($config['trap_penalty_min'] ?? 200),
|
||
(int) ($config['trap_penalty_max'] ?? 1000),
|
||
],
|
||
default => [
|
||
(int) ($config['normal_reward_min'] ?? 500),
|
||
(int) ($config['normal_reward_max'] ?? 2000),
|
||
],
|
||
};
|
||
|
||
// 创建箱子记录
|
||
$box = MysteryBox::create([
|
||
'box_type' => $this->boxType,
|
||
'passcode' => $passcode,
|
||
'reward_min' => $rewardMin,
|
||
'reward_max' => $rewardMax,
|
||
'status' => 'open',
|
||
'expires_at' => now()->addSeconds($claimWindow),
|
||
'dropped_by' => $this->droppedBy,
|
||
]);
|
||
|
||
// 公屏广播暗号提示
|
||
$emoji = $box->typeEmoji();
|
||
$typeName = $box->typeName();
|
||
$source = $this->droppedBy ? '管理员' : '系统';
|
||
|
||
$content = "{$emoji}【{$typeName}】{$source}投放了一个神秘箱子!"
|
||
. "发送暗号「{$passcode}」即可开箱!限时 {$claimWindow} 秒,先到先得!";
|
||
|
||
$msg = [
|
||
'id' => $chatState->nextMessageId($targetRoom),
|
||
'room_id' => $targetRoom,
|
||
'from_user' => '系统传音',
|
||
'to_user' => '大家',
|
||
'content' => $content,
|
||
'is_secret' => false,
|
||
'font_color' => match ($this->boxType) {
|
||
'rare' => '#c4b5fd',
|
||
'trap' => '#f87171',
|
||
default => '#34d399',
|
||
},
|
||
'action' => '大声宣告',
|
||
'sent_at' => now()->toDateTimeString(),
|
||
];
|
||
|
||
$chatState->pushMessage($targetRoom, $msg);
|
||
broadcast(new MessageSent($targetRoom, $msg));
|
||
SaveMessageJob::dispatch($msg);
|
||
|
||
// 定时关闭任务:到期后将箱子标记为 expired
|
||
ExpireMysteryBoxJob::dispatch($box->id)->delay(now()->addSeconds($claimWindow + 5));
|
||
}
|
||
}
|