json(['active' => false]); } $box = MysteryBox::currentOpenBox(); if (! $box) { return response()->json(['active' => false]); } // 计算剩余时间 $secondsLeft = $box->expires_at ? max(0, now()->diffInSeconds($box->expires_at, false)) : null; return response()->json([ 'active' => true, 'box_id' => $box->id, 'box_type' => $box->box_type, 'type_name' => $box->typeName(), 'type_emoji' => $box->typeEmoji(), 'passcode' => $box->passcode, 'seconds_left' => $secondsLeft, ]); } /** * 用户用暗号领取箱子。 */ public function claim(Request $request): JsonResponse { if (! GameConfig::isEnabled('mystery_box')) { return response()->json(['ok' => false, 'message' => '神秘箱子功能未开放。']); } $passcode = strtoupper(trim((string) $request->input('passcode', ''))); if ($passcode === '') { return response()->json(['ok' => false, 'message' => '请输入暗号。']); } $user = $request->user(); return DB::transaction(function () use ($user, $passcode): JsonResponse { // 查找匹配暗号的可领取箱子(加锁防并发) $box = MysteryBox::query() ->where('passcode', $passcode) ->where('status', 'open') ->where(fn ($q) => $q->whereNull('expires_at')->orWhere('expires_at', '>', now())) ->lockForUpdate() ->first(); if (! $box) { return response()->json(['ok' => false, 'message' => '暗号不正确,或箱子已被领走/已过期。']); } // ① 随机奖励金额 $reward = $box->rollReward(); // ② 货币变更 $source = $reward >= 0 ? CurrencySource::MYSTERY_BOX : CurrencySource::MYSTERY_BOX_TRAP; $remark = $reward >= 0 ? "神秘箱子【{$box->typeName()}】奖励" : "神秘箱子【黑化箱】陷阱扣除"; $this->currency->change($user, 'gold', $reward, $source, $remark, $box->room_id); // ③ 写领取记录 + 更新箱子状态 MysteryBoxClaim::create([ 'mystery_box_id' => $box->id, 'user_id' => $user->id, 'reward_amount' => $reward, ]); $box->update(['status' => 'claimed']); // ④ 公屏广播结果 $user->refresh(); $this->broadcastResult($box, $user->username, $reward); return response()->json([ 'ok' => true, 'reward' => $reward, 'balance' => $user->jjb ?? 0, 'message' => $reward >= 0 ? "🎉 恭喜!开箱获得 +{$reward} 金币!" : "☠️ 中了黑化陷阱!扣除 " . abs($reward) . ' 金币!', ]); }); } /** * 公屏广播开箱结果。 * * @param MysteryBox $box 箱子实例 * @param string $username 领取者用户名 * @param int $reward 奖励金额(正/负) */ private function broadcastResult(MysteryBox $box, string $username, int $reward): void { $emoji = $box->typeEmoji(); $typeName = $box->typeName(); if ($reward >= 0) { $content = "{$emoji}【开箱播报】恭喜 【{$username}】 抢到了神秘{$typeName}!" . "获得 🪙" . number_format($reward) . " 金币!"; $color = $box->box_type === 'rare' ? '#c4b5fd' : '#34d399'; } else { $content = "☠️【黑化陷阱】haha!【{$username}】 中了神秘黑化箱的陷阱!" . "被扣除 🪙" . number_format(abs($reward)) . " 金币!点背~"; $color = '#f87171'; } $msg = [ 'id' => $this->chatState->nextMessageId(1), 'room_id' => 1, 'from_user' => '系统传音', 'to_user' => '大家', 'content' => $content, 'is_secret' => false, 'font_color' => $color, 'action' => '大声宣告', 'sent_at' => now()->toDateTimeString(), ]; $this->chatState->pushMessage(1, $msg); broadcast(new MessageSent(1, $msg)); SaveMessageJob::dispatch($msg); } }