Files
chatroom/app/Jobs/DropMysteryBoxJob.php
lkddi 602dcd7cf1 feat: 神秘箱子系统完整实现 + 婚姻状态弹窗 + 工具栏优化
## 新功能
- 神秘箱子系统(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:神秘箱子移入已完成区,补全修复记录
2026-03-03 19:29:43 +08:00

123 lines
4.0 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.
<?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 投放者用户IDnull=系统自动)
*/
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));
}
}