功能:单次特效卡支持赠送——送礼弹框、广播给指定用户/全员、公屏系统消息、购买后关闭商店展示特效

This commit is contained in:
2026-02-27 16:19:21 +08:00
parent 1e2c304754
commit 6a8ba4fbc8
4 changed files with 241 additions and 28 deletions
+17 -11
View File
@@ -3,12 +3,12 @@
/**
* 文件功能:聊天室全屏特效广播事件
*
* 管理员触发烟花/下雨/雷电等特效后,
* 通过 WebSocket 广播给房间内所有在线用户,前端收到后播放对应 Canvas 动画
* 管理员或用户购买单次卡后触发,通过 WebSocket 广播给房间内用户播放 Canvas 动画。
* 支持指定接收者(target_username null 则全员播放)
*
* @package App\Events
* @author ChatRoom Laravel
* @version 1.0.0
*
* @version 2.0.0
*/
namespace App\Events;
@@ -26,19 +26,23 @@ class EffectBroadcast implements ShouldBroadcastNow
/**
* 支持的特效类型列表(用于校验)
*/
public const TYPES = ['fireworks', 'rain', 'lightning'];
public const TYPES = ['fireworks', 'rain', 'lightning', 'snow'];
/**
* 构造函数
*
* @param int $roomId 房间 ID
* @param string $type 特效类型:fireworks / rain / lightning
* @param string $operator 触发特效的管理员用户名
* @param int $roomId 房间 ID
* @param string $type 特效类型:fireworks / rain / lightning / snow
* @param string $operator 触发特效的用户名(购买者)
* @param string|null $targetUsername 接收者用户名(null = 全员)
* @param string|null $giftMessage 附带赠言
*/
public function __construct(
public readonly int $roomId,
public readonly string $type,
public readonly string $operator,
public readonly ?string $targetUsername = null,
public readonly ?string $giftMessage = null,
) {}
/**
@@ -49,20 +53,22 @@ class EffectBroadcast implements ShouldBroadcastNow
public function broadcastOn(): array
{
return [
new PresenceChannel('room.' . $this->roomId),
new PresenceChannel('room.'.$this->roomId),
];
}
/**
* 广播数据:特效类型操作者
* 广播数据:特效类型操作者、目标用户、赠言
*
* @return array<string, mixed>
*/
public function broadcastWith(): array
{
return [
'type' => $this->type,
'type' => $this->type,
'operator' => $this->operator,
'target_username' => $this->targetUsername, // null = 全员
'gift_message' => $this->giftMessage,
];
}
}
+59 -3
View File
@@ -2,11 +2,13 @@
/**
* 文件功能:商店控制器
* 提供商品列表查询、商品购买、改名卡使用 三个接口
* 提供商品列表查询、商品购买(含赠送特效广播)、改名卡使用 三个接口
*/
namespace App\Http\Controllers;
use App\Events\EffectBroadcast;
use App\Events\MessageSent;
use App\Models\ShopItem;
use App\Services\ShopService;
use Illuminate\Http\JsonResponse;
@@ -53,7 +55,11 @@ class ShopController extends Controller
/**
* 购买商品
*
* @param Request $request item_id
* 单次特效卡额外支持:
* - recipient 接收者用户名(传 "all" 或留空则全员可见)
* - message 公屏赠言(可选)
*
* @param Request $request item_id, recipient?, message?
*/
public function buy(Request $request): JsonResponse
{
@@ -72,9 +78,59 @@ class ShopController extends Controller
$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' => '❄️',
];
$icon = $icons[$result['play_effect']] ?? '✨';
$toStr = $targetUsername ? "{$targetUsername}" : '全体聊友';
$msgText = $message
? "{$message}"
: '';
$sysContent = "{$icon}{$user->username}】送出了一张 [{$item->name}],赠给 {$toStr}{$msgText}";
// 广播系统消息到公屏
broadcast(new MessageSent(
roomId: $roomId,
message: [
'id' => 0,
'room_id' => $roomId,
'username' => '系统',
'content' => $sysContent,
'type' => 'sys',
'color' => '#cc6600',
'created_at' => now()->toDateTimeString(),
]
));
}
}
// 返回最新金币余额