收口聊天室安全边界并优化特效生命周期
This commit is contained in:
@@ -31,6 +31,7 @@ use App\Services\PositionPermissionService;
|
||||
use App\Services\RoomBroadcastService;
|
||||
use App\Services\UserCurrencyService;
|
||||
use App\Services\VipService;
|
||||
use App\Support\ChatContentSanitizer;
|
||||
use App\Support\ChatDailyStatusCatalog;
|
||||
use App\Support\PositionPermissionRegistry;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -353,6 +354,10 @@ class ChatController extends Controller
|
||||
$user = Auth::user();
|
||||
$imagePayload = null;
|
||||
|
||||
if ($response = $this->ensureUserCanActInRoom($id, $user, '请先进入当前房间后再发言。')) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// 0. 检查用户是否被禁言(Redis TTL 自动过期)
|
||||
$muteKey = "mute:{$id}:{$user->username}";
|
||||
if (Redis::exists($muteKey)) {
|
||||
@@ -884,13 +889,18 @@ class ChatController extends Controller
|
||||
return response()->json(['status' => 'error', 'message' => '权限不足,无法修改公告'], 403);
|
||||
}
|
||||
|
||||
if (! $this->chatState->isUserInRoom($id, $user->username)) {
|
||||
return response()->json(['status' => 'error', 'message' => '请先进入该房间后再修改公告'], 403);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'announcement' => 'required|string|max:500',
|
||||
]);
|
||||
|
||||
// 将发送者和发送时间追加到公告文本末尾,持久化存储,无需额外字段
|
||||
$room->announcement = trim($request->input('announcement'))
|
||||
.' ——'.$user->username.' '.now()->format('m-d H:i');
|
||||
$announcementText = trim((string) $request->input('announcement'));
|
||||
|
||||
// 将发送者和发送时间追加到公告文本末尾,持久化存储,无需额外字段。
|
||||
$room->announcement = $announcementText.' ——'.$user->username.' '.now()->format('m-d H:i');
|
||||
$room->save();
|
||||
|
||||
// 广播公告更新到所有在线用户
|
||||
@@ -899,7 +909,7 @@ class ChatController extends Controller
|
||||
'room_id' => $id,
|
||||
'from_user' => '系统公告',
|
||||
'to_user' => '大家',
|
||||
'content' => "📢 {$user->username} 更新了房间公告:{$room->announcement}",
|
||||
'content' => '📢 '.ChatContentSanitizer::htmlText($user->username).' 更新了房间公告:'.ChatContentSanitizer::htmlText($room->announcement),
|
||||
'is_secret' => false,
|
||||
'font_color' => '#cc0000',
|
||||
'action' => '',
|
||||
@@ -941,6 +951,10 @@ class ChatController extends Controller
|
||||
$giftId = $request->integer('gift_id');
|
||||
$count = $request->integer('count', 1);
|
||||
|
||||
if ($response = $this->ensureUserCanActInRoom((int) $roomId, $user, '请先进入当前房间后再送礼物。')) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// 不能给自己送花
|
||||
if ($toUsername === $user->username) {
|
||||
return response()->json(['status' => 'error', 'message' => '不能给自己送花哦~']);
|
||||
@@ -958,6 +972,10 @@ class ChatController extends Controller
|
||||
return response()->json(['status' => 'error', 'message' => '用户不存在']);
|
||||
}
|
||||
|
||||
if ($response = $this->ensureTargetOnlineInRoom((int) $roomId, (string) $toUsername)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$totalCost = $gift->cost * $count;
|
||||
$totalCharm = $gift->charm * $count;
|
||||
|
||||
@@ -981,12 +999,15 @@ class ChatController extends Controller
|
||||
|
||||
// 广播送花消息(含图片标记,前端识别后渲染图片)
|
||||
$countText = $count > 1 ? " {$count} 份" : '';
|
||||
$safeSender = ChatContentSanitizer::htmlText($user->username);
|
||||
$safeReceiver = ChatContentSanitizer::htmlText($toUsername);
|
||||
$safeGiftName = ChatContentSanitizer::htmlText($gift->name);
|
||||
$sysMsg = [
|
||||
'id' => $this->chatState->nextMessageId($roomId),
|
||||
'room_id' => $roomId,
|
||||
'from_user' => '送花播报',
|
||||
'to_user' => $toUsername,
|
||||
'content' => "{$gift->emoji} 【{$user->username}】 向 【{$toUsername}】 送出了{$countText}【{$gift->name}】!魅力 +{$totalCharm}!",
|
||||
'content' => "{$gift->emoji} 【{$safeSender}】 向 【{$safeReceiver}】 送出了{$countText}【{$safeGiftName}】!魅力 +{$totalCharm}!",
|
||||
'is_secret' => false,
|
||||
'font_color' => '#e91e8f',
|
||||
'action' => '',
|
||||
@@ -1255,6 +1276,10 @@ class ChatController extends Controller
|
||||
$roomId = $request->integer('room_id');
|
||||
$amount = $request->integer('amount');
|
||||
|
||||
if ($response = $this->ensureUserCanActInRoom($roomId, $sender, '请先进入当前房间后再赠送金币。')) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// 不能给自己转账
|
||||
if ($toName === $sender->username) {
|
||||
return response()->json(['status' => 'error', 'message' => '不能给自己赠送哦~']);
|
||||
@@ -1266,6 +1291,10 @@ class ChatController extends Controller
|
||||
return response()->json(['status' => 'error', 'message' => '用户不存在']);
|
||||
}
|
||||
|
||||
if ($response = $this->ensureTargetOnlineInRoom($roomId, (string) $toName)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// 余额校验
|
||||
if (($sender->jjb ?? 0) < $amount) {
|
||||
return response()->json([
|
||||
@@ -1292,7 +1321,7 @@ class ChatController extends Controller
|
||||
// 接收方收到消息时,在右下角弹到账提示卡片。
|
||||
'toast_notification' => [
|
||||
'title' => '💰 赠金币到账',
|
||||
'message' => "<b>{$sender->username}</b> 向你赠送了 <b>{$amount}</b> 枚金币!",
|
||||
'message' => '<b>'.ChatContentSanitizer::htmlText($sender->username)."</b> 向你赠送了 <b>{$amount}</b> 枚金币!",
|
||||
'icon' => '💰',
|
||||
'color' => '#f59e0b',
|
||||
'duration' => 8000,
|
||||
@@ -1313,4 +1342,37 @@ class ChatController extends Controller
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验用户是否能在指定房间执行聊天动作。
|
||||
*/
|
||||
private function ensureUserCanActInRoom(int $roomId, ?User $user, string $message): ?JsonResponse
|
||||
{
|
||||
if (! $user) {
|
||||
return response()->json(['status' => 'error', 'message' => '请先登录'], 401);
|
||||
}
|
||||
|
||||
$room = Room::query()->find($roomId);
|
||||
if (! $room) {
|
||||
return response()->json(['status' => 'error', 'message' => '房间不存在'], 404);
|
||||
}
|
||||
|
||||
if (! $room->canUserEnter($user) || ! $this->chatState->isUserInRoom($roomId, $user->username)) {
|
||||
return response()->json(['status' => 'error', 'message' => $message], 403);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验目标用户是否仍在当前房间在线,避免跨房间赠送和消息注入。
|
||||
*/
|
||||
private function ensureTargetOnlineInRoom(int $roomId, string $targetUsername): ?JsonResponse
|
||||
{
|
||||
if (! $this->chatState->isUserInRoom($roomId, $targetUsername)) {
|
||||
return response()->json(['status' => 'error', 'message' => '目标用户不在当前房间,无法执行该操作'], 403);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user