Files
chatroom/app/Models/MysteryBox.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

125 lines
3.3 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
/**
* 文件功能:神秘箱子模型
*
* 管理聊天室内投放的神秘箱记录,提供领取状态管理及类型标签工具方法。
* 对应表mystery_boxes
*
* @author ChatRoom Laravel
* @version 1.0.0
*/
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
class MysteryBox extends Model
{
protected $fillable = [
'box_type',
'passcode',
'reward_min',
'reward_max',
'status',
'expires_at',
'dropped_by',
];
/**
* 属性类型转换。
*/
protected function casts(): array
{
return [
'reward_min' => 'integer',
'reward_max' => 'integer',
'expires_at' => 'datetime',
];
}
// ─── 关联关系 ────────────────────────────────────────────────────
/**
* 领取记录(一个箱子只能被一人领取,但关联为 HasOne
*/
public function claim(): HasOne
{
return $this->hasOne(MysteryBoxClaim::class);
}
/**
* 所有领取记录(逻辑上只有一条,保留 HasMany 供统计使用)
*/
public function claims(): HasMany
{
return $this->hasMany(MysteryBoxClaim::class);
}
// ─── 查询作用域 ──────────────────────────────────────────────────
/**
* 当前可领取open 状态 + 未过期)的箱子。
*/
public static function currentOpenBox(): ?static
{
return static::query()
->where('status', 'open')
->where(fn ($q) => $q->whereNull('expires_at')->orWhere('expires_at', '>', now()))
->latest()
->first();
}
// ─── 工具方法 ────────────────────────────────────────────────────
/**
* 返回箱子类型的 emoji 前缀。
*/
public function typeEmoji(): string
{
return match ($this->box_type) {
'normal' => '📦',
'rare' => '💎',
'trap' => '☠️',
default => '📦',
};
}
/**
* 返回箱子类型中文名称。
*/
public function typeName(): string
{
return match ($this->box_type) {
'normal' => '普通箱',
'rare' => '稀有箱',
'trap' => '黑化箱',
default => '神秘箱',
};
}
/**
* 随机生成奖励金额trap 类型为负数)。
*/
public function rollReward(): int
{
$amount = random_int(
min(abs($this->reward_min), abs($this->reward_max)),
max(abs($this->reward_min), abs($this->reward_max)),
);
// trap 类型:倒扣金币(负数)
return $this->box_type === 'trap' ? -$amount : $amount;
}
/**
* 判断箱子是否已过期
*/
public function isExpired(): bool
{
return $this->expires_at !== null && $this->expires_at->isPast();
}
}