map(fn ($item) => [ 'id' => $item->id, 'name' => $item->name, 'slug' => $item->slug, 'description' => $item->description, 'icon' => $item->icon, 'price' => $item->price, 'type' => $item->type, 'duration_days' => $item->duration_days, 'duration_minutes' => $item->duration_minutes, 'intimacy_bonus' => $item->intimacy_bonus, 'charm_bonus' => $item->charm_bonus, ]); // 统计背包中各戒指持有数量 $ringCounts = \App\Models\UserPurchase::query() ->where('user_id', $user->id) ->where('status', 'active') ->whereHas('item', fn ($q) => $q->where('type', 'ring')) ->selectRaw('shop_item_id, count(*) as qty') ->groupBy('shop_item_id') ->pluck('qty', 'shop_item_id') ->toArray(); return response()->json([ 'items' => $items, 'user_jjb' => $user->jjb ?? 0, 'active_week_effect' => $this->shopService->getActiveWeekEffect($user), 'has_rename_card' => $this->shopService->hasRenameCard($user), 'ring_counts' => $ringCounts, 'auto_fishing_minutes_left' => $this->shopService->getActiveAutoFishingMinutesLeft($user), ]); } /** * 购买商品 * * 单次特效卡额外支持: * - recipient 接收者用户名(传 "all" 或留空则全员可见) * - message 公屏赠言(可选) * * @param Request $request 含 item_id, recipient?, message? */ public function buy(Request $request): JsonResponse { $request->validate(['item_id' => 'required|integer|exists:shop_items,id']); $item = ShopItem::find($request->item_id); if (! $item->is_active) { return response()->json(['status' => 'error', 'message' => '该商品已下架。'], 400); } $result = $this->shopService->buyItem(Auth::user(), $item); if (! $result['ok']) { return response()->json(['status' => 'error', 'message' => $result['message']], 400); } $response = ['status' => 'success', 'message' => $result['message']]; // ── 单次特效卡:广播给指定用户或全员 ──────────────────────── if (isset($result['play_effect'])) { $user = Auth::user(); $roomId = (int) $request->room_id; $recipient = trim($request->input('recipient', '')); // 空字符串 = 全员 $message = trim($request->input('message', '')); // recipient 为空或 "all" 表示全员 $targetUsername = ($recipient === '' || $recipient === 'all') ? null : $recipient; // 广播特效事件(全员频道) broadcast(new EffectBroadcast( roomId: $roomId, type: $result['play_effect'], operator: $user->username, targetUsername: $targetUsername, giftMessage: $message ?: null, ))->toOthers(); // 同时前端也需要播放(自己也要看到) $response['play_effect'] = $result['play_effect']; $response['target_username'] = $targetUsername; $response['gift_message'] = $message ?: null; // 公屏系统消息 if ($roomId > 0) { $icons = [ 'fireworks' => '🎆', 'rain' => '🌧', 'lightning' => '⚡', 'snow' => '❄️', ]; // 赠礼消息文案(改成"为XX触发了一场特效") $icon = $icons[$result['play_effect']] ?? '✨'; $toStr = $targetUsername ? "【{$targetUsername}】" : '全体聊友'; $remarkPart = $message ? " 「{$message}」" : ''; $sysContent = "{$icon} {$user->username} 为 {$toStr} 燃放了一场【{$item->name}】特效!{$remarkPart}"; // 广播系统消息到公屏(字段名与前端 appendMessage() 保持一致) $sysMsgEvent = new MessageSent( roomId: $roomId, message: [ 'id' => 0, 'room_id' => $roomId, 'from_user' => '系统传音', // 触发金色左边框样式(已有处理分支) 'to_user' => '大家', 'content' => $sysContent, 'font_color' => '#cc6600', 'sent_at' => now()->format('H:i:s'), 'is_secret' => false, 'action' => null, ] ); broadcast($sysMsgEvent); } } else { // ── 其他类型:广播购买通知到公屏 ──────────────────────────── $user = Auth::user(); $roomId = (int) $request->room_id; if ($roomId > 0) { // auto_fishing 有效期文案(提前算好,避免在 match 内写复杂三元表达式) $fishDuration = ''; if ($item->type === 'auto_fishing') { $mins = (int) ($item->duration_minutes ?? 0); $fishDuration = $mins >= 60 ? floor($mins / 60).'小时' : $mins.'分钟'; } // 根据商品类型生成不同通知文案 $sysContent = match ($item->type) { 'duration' => "📅 【{$user->username}】购买了全屏特效周卡「{$item->name}」,登录时将自动触发!", 'one_time' => "🎫 【{$user->username}】购买了「{$item->name}」道具!", 'ring' => "💍 【{$user->username}】在商店购买了一枚「{$item->name}」,不知道打算送给谁呢?", 'auto_fishing' => "🎣 【{$user->username}】购买了「{$item->name}」,开启了 {$fishDuration} 的自动钓鱼模式!", default => "🛒 【{$user->username}】购买了「{$item->name}」。", }; broadcast(new MessageSent( roomId: $roomId, message: [ 'id' => 0, 'room_id' => $roomId, 'from_user' => '系统传音', 'to_user' => '大家', 'content' => $sysContent, 'font_color' => '#7c3aed', 'sent_at' => now()->format('H:i:s'), 'is_secret' => false, 'action' => null, ] )); } } // 返回最新金币余额 $response['jjb'] = Auth::user()->fresh()->jjb; return response()->json($response); } /** * 使用改名卡修改昵称 * * @param Request $request 含 new_name */ public function rename(Request $request): JsonResponse { $request->validate([ 'new_name' => 'required|string|min:1|max:10', ]); $result = $this->shopService->useRenameCard(Auth::user(), $request->new_name); if (! $result['ok']) { return response()->json(['status' => 'error', 'message' => $result['message']], 400); } return response()->json(['status' => 'success', 'message' => $result['message']]); } }