聊天室管理权限统一为职务权限
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
* 文件功能:管理员聊天室实时命令控制器
|
||||
*
|
||||
* 提供管理员在聊天室内对用户执行的管理操作:
|
||||
* 警告(=J)、踢出(=T)、禁言(=B)、冻结(=Y)、查看私信(=S)、职务公屏讲话。
|
||||
* 警告(=J)、踢出(=T)、禁言(=B)、封号、封IP、查看私信(=S)、职务公屏讲话。
|
||||
*
|
||||
* 对应原 ASP 文件:DOUSER.ASP / KILLUSER.ASP / LOCKIP.ASP / NEWSAY.ASP
|
||||
*
|
||||
@@ -278,14 +278,14 @@ class AdminCommandController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* 冻结用户账号(=Y 理由)
|
||||
* 封禁用户账号。
|
||||
*
|
||||
* 将用户账号状态设为冻结,禁止登录。
|
||||
* 将目标账号设为封禁状态,并将其从当前在线房间中移出。
|
||||
*
|
||||
* @param Request $request 请求对象,需包含 username, reason
|
||||
* @param Request $request 请求对象,需包含 username, room_id, reason
|
||||
* @return JsonResponse 操作结果
|
||||
*/
|
||||
public function freeze(Request $request): JsonResponse
|
||||
public function ban(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'username' => 'required|string',
|
||||
@@ -301,12 +301,12 @@ class AdminCommandController extends Controller
|
||||
return response()->json(['status' => 'error', 'message' => $roomAuthorization['message']], 403);
|
||||
}
|
||||
|
||||
$reason = ChatContentSanitizer::htmlText($request->input('reason', '违反聊天室规则'));
|
||||
$reason = ChatContentSanitizer::htmlText($request->input('reason', '严重违规'));
|
||||
$safeTargetUsername = ChatContentSanitizer::htmlText($targetUsername);
|
||||
$operatorDisplay = $this->buildOperatorDisplayHtml($admin);
|
||||
|
||||
// 权限检查:必须拥有职务权限,且不能处理职务高于自己的用户。
|
||||
$authorization = $this->authorizeModerationAction($admin, $targetUsername, PositionPermissionRegistry::USER_FREEZE, '冻结');
|
||||
$authorization = $this->authorizeModerationAction($admin, $targetUsername, PositionPermissionRegistry::USER_BAN, '封禁');
|
||||
if (! $authorization['ok']) {
|
||||
return response()->json(['status' => 'error', 'message' => $authorization['message']], 403);
|
||||
}
|
||||
@@ -316,37 +316,30 @@ class AdminCommandController extends Controller
|
||||
return response()->json(['status' => 'error', 'message' => $targetAuthorization['message']], 403);
|
||||
}
|
||||
|
||||
// 冻结用户账号(将等级设为 -1 表示冻结)
|
||||
$target = $authorization['target'];
|
||||
$target->user_level = -1;
|
||||
$target->save();
|
||||
|
||||
// 先给被冻结用户补发私聊提示,再将其移出各房间并强制下线。
|
||||
$this->pushTargetToastMessage(
|
||||
roomId: (int) $roomId,
|
||||
roomId: $roomId,
|
||||
targetUsername: $targetUsername,
|
||||
content: "🧊 {$operatorDisplay} 已冻结你的账号。原因:{$reason}",
|
||||
title: '🧊 账号已冻结',
|
||||
toastMessage: "{$operatorDisplay} 已冻结你的账号。<br>原因:{$reason}",
|
||||
color: '#3b82f6',
|
||||
icon: '🧊',
|
||||
content: "⛔ {$operatorDisplay} 已封禁你的账号。原因:{$reason}",
|
||||
title: '⛔ 账号已封禁',
|
||||
toastMessage: "{$operatorDisplay} 已封禁你的账号。<br>原因:{$reason}",
|
||||
color: '#991b1b',
|
||||
icon: '⛔',
|
||||
);
|
||||
|
||||
// 从所有房间移除
|
||||
$rooms = $this->chatState->getUserRooms($targetUsername);
|
||||
foreach ($rooms as $rid) {
|
||||
$this->chatState->userLeave($rid, $targetUsername);
|
||||
}
|
||||
$this->removeUserFromAllRooms($targetUsername);
|
||||
|
||||
// 广播冻结消息
|
||||
$msg = [
|
||||
'id' => $this->chatState->nextMessageId($roomId),
|
||||
'room_id' => $roomId,
|
||||
'from_user' => '系统传音',
|
||||
'to_user' => '大家',
|
||||
'content' => "🧊 {$operatorDisplay} 已冻结 <b>{$safeTargetUsername}</b> 的账号。原因:{$reason}",
|
||||
'content' => "⛔ {$operatorDisplay} 已封禁 <b>{$safeTargetUsername}</b> 的账号。原因:{$reason}",
|
||||
'is_secret' => false,
|
||||
'font_color' => '#dc2626',
|
||||
'font_color' => '#991b1b',
|
||||
'action' => '',
|
||||
'sent_at' => now()->toDateTimeString(),
|
||||
];
|
||||
@@ -354,10 +347,93 @@ class AdminCommandController extends Controller
|
||||
broadcast(new MessageSent($roomId, $msg));
|
||||
SaveMessageJob::dispatch($msg);
|
||||
|
||||
// 广播踢出事件
|
||||
broadcast(new \App\Events\UserKicked($roomId, $targetUsername, "账号已被冻结:{$reason}"));
|
||||
broadcast(new \App\Events\UserKicked($roomId, $targetUsername, "账号已被封禁:{$reason}"));
|
||||
|
||||
return response()->json(['status' => 'success', 'message' => "已冻结 {$targetUsername} 的账号"]);
|
||||
return response()->json(['status' => 'success', 'message' => "已封禁 {$targetUsername} 的账号"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 封禁用户 IP。
|
||||
*
|
||||
* 将目标账号设为封禁状态并把最近登录 IP 写入黑名单。
|
||||
*
|
||||
* @param Request $request 请求对象,需包含 username, room_id, reason
|
||||
* @return JsonResponse 操作结果
|
||||
*/
|
||||
public function banIp(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'username' => 'required|string',
|
||||
'room_id' => 'required|integer',
|
||||
'reason' => 'nullable|string|max:200',
|
||||
]);
|
||||
|
||||
$admin = Auth::user();
|
||||
$targetUsername = $request->input('username');
|
||||
$roomId = (int) $request->input('room_id');
|
||||
$roomAuthorization = $this->authorizeManagedRoom($roomId, $admin);
|
||||
if (! $roomAuthorization['ok']) {
|
||||
return response()->json(['status' => 'error', 'message' => $roomAuthorization['message']], 403);
|
||||
}
|
||||
|
||||
$reason = ChatContentSanitizer::htmlText($request->input('reason', '严重违规'));
|
||||
$safeTargetUsername = ChatContentSanitizer::htmlText($targetUsername);
|
||||
$operatorDisplay = $this->buildOperatorDisplayHtml($admin);
|
||||
|
||||
// 权限检查:必须拥有职务权限,且不能处理职务高于自己的用户。
|
||||
$authorization = $this->authorizeModerationAction($admin, $targetUsername, PositionPermissionRegistry::USER_BANIP, '封禁 IP ');
|
||||
if (! $authorization['ok']) {
|
||||
return response()->json(['status' => 'error', 'message' => $authorization['message']], 403);
|
||||
}
|
||||
|
||||
$targetAuthorization = $this->authorizeTargetOnlineInRoom($roomId, $targetUsername);
|
||||
if (! $targetAuthorization['ok']) {
|
||||
return response()->json(['status' => 'error', 'message' => $targetAuthorization['message']], 403);
|
||||
}
|
||||
|
||||
$target = $authorization['target'];
|
||||
$targetIp = (string) ($target->last_ip ?? '');
|
||||
if ($targetIp !== '') {
|
||||
Redis::sadd('banned_ips', $targetIp);
|
||||
}
|
||||
|
||||
$target->user_level = -1;
|
||||
$target->save();
|
||||
|
||||
$this->pushTargetToastMessage(
|
||||
roomId: $roomId,
|
||||
targetUsername: $targetUsername,
|
||||
content: "🌐 {$operatorDisplay} 已封禁你的 IP 并冻结账号。原因:{$reason}",
|
||||
title: '🌐 IP 已封禁',
|
||||
toastMessage: "{$operatorDisplay} 已封禁你的 IP 并冻结账号。<br>原因:{$reason}",
|
||||
color: '#7c2d12',
|
||||
icon: '🌐',
|
||||
);
|
||||
|
||||
$this->removeUserFromAllRooms($targetUsername);
|
||||
|
||||
$ipSuffix = $targetIp !== '' ? "(IP:{$targetIp})" : '(未记录有效 IP)';
|
||||
$msg = [
|
||||
'id' => $this->chatState->nextMessageId($roomId),
|
||||
'room_id' => $roomId,
|
||||
'from_user' => '系统传音',
|
||||
'to_user' => '大家',
|
||||
'content' => "🌐 {$operatorDisplay} 已封禁 <b>{$safeTargetUsername}</b> 的 IP 并冻结账号。原因:{$reason} {$ipSuffix}",
|
||||
'is_secret' => false,
|
||||
'font_color' => '#9a3412',
|
||||
'action' => '',
|
||||
'sent_at' => now()->toDateTimeString(),
|
||||
];
|
||||
$this->chatState->pushMessage($roomId, $msg);
|
||||
broadcast(new MessageSent($roomId, $msg));
|
||||
SaveMessageJob::dispatch($msg);
|
||||
|
||||
broadcast(new \App\Events\UserKicked($roomId, $targetUsername, "IP 已被封禁:{$reason}"));
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => $targetIp !== '' ? "已封禁 {$targetUsername} 的 IP 与账号" : "已封禁 {$targetUsername} 的账号,但未记录可封禁 IP",
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -555,7 +631,7 @@ class AdminCommandController extends Controller
|
||||
string $permissionCode,
|
||||
string $actionLabel,
|
||||
): array {
|
||||
if (! $this->positionPermissionService->hasPermission($admin, $permissionCode)) {
|
||||
if ($admin->id !== 1 && ! $this->positionPermissionService->hasPermission($admin, $permissionCode)) {
|
||||
return ['ok' => false, 'message' => "当前职务无权{$actionLabel}用户"];
|
||||
}
|
||||
|
||||
@@ -583,6 +659,18 @@ class AdminCommandController extends Controller
|
||||
return ['ok' => true, 'message' => '校验通过', 'target' => $target];
|
||||
}
|
||||
|
||||
/**
|
||||
* 将目标用户从当前在线的全部房间中移除。
|
||||
*/
|
||||
private function removeUserFromAllRooms(string $targetUsername): void
|
||||
{
|
||||
$rooms = $this->chatState->getUserRooms($targetUsername);
|
||||
|
||||
foreach ($rooms as $rid) {
|
||||
$this->chatState->userLeave((int) $rid, $targetUsername);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断操作者是否可以按职务位阶处理目标用户。
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user