Files
chatroom/app/Http/Controllers/ShopController.php
lkddi c8c1943f85 功能:商店购买其他商品类型时广播公屏通知
周卡 / 改名卡 / 求婚戒指 / 自动钓鱼卡购买后
各自向聊天室公屏(系统传音,紫色)广播欢迎语:
  📅 周卡:「登录时将自动触发!」
  🎫 一次性道具:「购买了XXX道具!」
  💍 戒指:「不知道打算送给谁呢?」
  🎣 钓鱼卡:「开启了X小时的自动钓鱼模式!」
单次特效卡仍由原 play_effect 分支广播(橙色)
2026-03-01 17:04:11 +08:00

216 lines
8.5 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
/**
* 文件功能:商店控制器
* 提供商品列表查询、商品购买(含赠送特效广播)、改名卡使用 三个接口
*/
namespace App\Http\Controllers;
use App\Events\EffectBroadcast;
use App\Events\MessageSent;
use App\Models\ShopItem;
use App\Services\ShopService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class ShopController extends Controller
{
/**
* 注入商店服务
*/
public function __construct(
private readonly ShopService $shopService,
) {}
/**
* 获取商店商品列表及当前用户状态
*
* 返回字段items商品列表、user_jjb当前金币
* active_week_effect当前周卡、has_rename_card是否持有改名卡
*/
public function items(): JsonResponse
{
$user = Auth::user();
$items = ShopItem::active()->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']]);
}
}