'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(); } }