新增聊天室状态与功能快捷菜单

This commit is contained in:
2026-04-24 21:17:44 +08:00
parent d7ec42a025
commit 0f0bfef2a8
18 changed files with 1361 additions and 124 deletions
@@ -20,6 +20,7 @@ use App\Http\Controllers\Controller;
use App\Models\AiProviderConfig;
use App\Models\Sysparam;
use App\Services\ChatStateService;
use App\Services\ChatUserPresenceService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@@ -33,6 +34,7 @@ class AiProviderController extends Controller
*/
public function __construct(
private readonly ChatStateService $chatState,
private readonly ChatUserPresenceService $chatUserPresenceService,
) {}
/**
@@ -283,19 +285,8 @@ class AiProviderController extends Controller
]);
}
$userData = [
'user_id' => $user->id,
'username' => $user->username,
'level' => $user->user_level,
'sex' => $user->sex,
'headface' => $user->headface,
'vip_icon' => $user->vipIcon(),
'vip_name' => $user->vipName(),
'vip_color' => $user->isVip() ? ($user->vipLevel?->color ?? '') : '',
'is_admin' => false,
'position_icon' => '',
'position_name' => '',
];
// 机器人在线载荷也统一走聊天室展示服务,避免名单字段口径逐步漂移。
$userData = $this->chatUserPresenceService->build($user);
// 广播机器人进出事件(供前端名单增删)
broadcast(new \App\Events\ChatBotToggled($userData, $isEnabled));
+11 -47
View File
@@ -25,11 +25,13 @@ use App\Models\Sysparam;
use App\Models\User;
use App\Services\AppointmentService;
use App\Services\ChatStateService;
use App\Services\ChatUserPresenceService;
use App\Services\MessageFilterService;
use App\Services\PositionPermissionService;
use App\Services\RoomBroadcastService;
use App\Services\UserCurrencyService;
use App\Services\VipService;
use App\Support\ChatDailyStatusCatalog;
use App\Support\PositionPermissionRegistry;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -54,6 +56,7 @@ class ChatController extends Controller
*/
public function __construct(
private readonly ChatStateService $chatState,
private readonly ChatUserPresenceService $chatUserPresenceService,
private readonly MessageFilterService $filter,
private readonly VipService $vipService,
private readonly \App\Services\ShopService $shopService,
@@ -105,21 +108,7 @@ class ChatController extends Controller
}
// 2. 将当前用户加入到 Redis 房间在线列表(包含 VIP 和管理员信息)
$superLevel = (int) Sysparam::getValue('superlevel', '100');
// 获取当前在职职务信息(用于内容显示)
$activePosition = $user->activePosition;
$userData = [
'user_id' => $user->id,
'level' => $user->user_level,
'sex' => $user->sex,
'headface' => $user->headface,
'vip_icon' => $user->vipIcon(),
'vip_name' => $user->vipName(),
'vip_color' => $user->isVip() ? ($user->vipLevel?->color ?? '') : '',
'is_admin' => $user->user_level >= $superLevel,
'position_icon' => $activePosition?->position?->icon ?? '',
'position_name' => $activePosition?->position?->name ?? '',
];
$userData = $this->chatUserPresenceService->build($user);
$this->chatState->userJoin($id, $user->username, $userData);
// 记录重新加入房间的精确时间戳(微秒),用于防抖判断(刷新的时候避免闪退闪进播报)
\Illuminate\Support\Facades\Redis::set("room:{$id}:join_time:{$user->username}", microtime(true));
@@ -296,6 +285,8 @@ class ChatController extends Controller
'pendingDivorce' => $pendingDivorceData,
'roomPermissionMap' => $roomPermissionMap,
'hasRoomManagementPermission' => in_array(true, $roomPermissionMap, true),
'dailyStatusCatalog' => ChatDailyStatusCatalog::groupedOptions(),
'activeDailyStatus' => $this->chatUserPresenceService->currentDailyStatus($user),
]);
}
@@ -526,18 +517,7 @@ class ChatController extends Controller
// 3. 将新的等级反馈给当前用户的在线名单上
// 确保刚刚升级后别人查看到的也是最准确等级
$activePosition = $user->activePosition;
$this->chatState->userJoin($id, $user->username, [
'level' => $user->user_level,
'sex' => $user->sex,
'headface' => $user->headface,
'vip_icon' => $user->vipIcon(),
'vip_name' => $user->vipName(),
'vip_color' => $user->isVip() ? ($user->vipLevel?->color ?? '') : '',
'is_admin' => $user->user_level >= $superLevel,
'position_icon' => $activePosition?->position?->icon ?? '',
'position_name' => $activePosition?->position?->name ?? '',
]);
$this->chatState->userJoin($id, $user->username, $this->chatUserPresenceService->build($user));
// 4. 如果突破境界,向全房系统喊话广播!
if ($leveledUp) {
@@ -806,18 +786,10 @@ class ChatController extends Controller
// 将新头像同步到 Redis 在线用户列表中(所有房间)
// 通过更新 Redis 的用户信息,使得其他用户和自己刷新后都能看到新头像
$superLevel = (int) Sysparam::getValue('superlevel', '100');
$rooms = $this->chatState->getUserRooms($user->username);
foreach ($rooms as $roomId) {
$this->chatState->userJoin((int) $roomId, $user->username, [
'level' => $user->user_level,
'sex' => $user->sex,
'headface' => $headface,
'vip_icon' => $user->vipIcon(),
'vip_name' => $user->vipName(),
'vip_color' => $user->isVip() ? ($user->vipLevel?->color ?? '') : '',
'is_admin' => $user->user_level >= $superLevel,
]);
// 头像更新后,统一通过在线载荷服务刷新所有扩展字段,避免状态或职务字段丢失。
$this->chatState->userJoin((int) $roomId, $user->username, $this->chatUserPresenceService->build($user));
}
return response()->json([
@@ -872,18 +844,10 @@ class ChatController extends Controller
}
// 同步 Redis 状态
$superLevel = (int) Sysparam::getValue('superlevel', '100');
$rooms = $this->chatState->getUserRooms($user->username);
foreach ($rooms as $roomId) {
$this->chatState->userJoin((int) $roomId, $user->username, [
'level' => $user->user_level,
'sex' => $user->sex,
'headface' => $user->headface, // Use accessor
'vip_icon' => $user->vipIcon(),
'vip_name' => $user->vipName(),
'vip_color' => $user->isVip() ? ($user->vipLevel?->color ?? '') : '',
'is_admin' => $user->user_level >= $superLevel,
]);
// 自定义头像上传成功后,同步覆盖在线名单中的全部展示字段。
$this->chatState->userJoin((int) $roomId, $user->username, $this->chatUserPresenceService->build($user));
}
return response()->json([
+64
View File
@@ -19,20 +19,35 @@ namespace App\Http\Controllers;
use App\Events\UserKicked;
use App\Events\UserMuted;
use App\Events\UserStatusUpdated;
use App\Http\Requests\ChangePasswordRequest;
use App\Http\Requests\UpdateChatPreferencesRequest;
use App\Http\Requests\UpdateDailyStatusRequest;
use App\Http\Requests\UpdateProfileRequest;
use App\Models\Room;
use App\Models\Sysparam;
use App\Models\User;
use App\Services\ChatStateService;
use App\Services\ChatUserPresenceService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Redis;
/**
* 类功能:处理用户资料、聊天室偏好、当日状态与基础管理动作。
*/
class UserController extends Controller
{
/**
* 构造用户控制器依赖。
*/
public function __construct(
private readonly ChatStateService $chatState,
private readonly ChatUserPresenceService $chatUserPresenceService,
) {}
/**
* 查看其他用户资料片 (对应 USERinfo.ASP)
*/
@@ -230,6 +245,55 @@ class UserController extends Controller
]);
}
/**
* 保存聊天室当日状态,并同步当前在线名单显示。
*/
public function updateDailyStatus(UpdateDailyStatusRequest $request): JsonResponse
{
$user = Auth::user();
$data = $request->validated();
$roomId = (int) $data['room_id'];
// 仅允许当前确实在线的用户从聊天室内修改状态,避免离线脏请求写入。
if (! $this->chatState->isUserInRoom($roomId, $user->username)) {
return response()->json([
'status' => 'error',
'message' => '请先进入聊天室后再设置状态。',
], 422);
}
if ($data['action'] === 'clear') {
$user->update([
'daily_status_key' => null,
'daily_status_expires_at' => null,
]);
} else {
// 状态有效期固定维持到当天结束,次日自动失效。
$user->update([
'daily_status_key' => $data['status_key'],
'daily_status_expires_at' => now()->endOfDay(),
]);
}
$user->refresh();
$presencePayload = $this->chatUserPresenceService->build($user);
$roomIds = $this->chatState->getUserRooms($user->username);
foreach ($roomIds as $activeRoomId) {
// 所有当前在线房间都刷新 Redis 载荷,确保头像、会员与状态显示口径一致。
$this->chatState->userJoin((int) $activeRoomId, $user->username, $presencePayload);
broadcast(new UserStatusUpdated((int) $activeRoomId, $user->username, $presencePayload));
}
return response()->json([
'status' => 'success',
'message' => $data['action'] === 'clear' ? '状态已清除。' : '状态已更新。',
'data' => [
'status' => $this->chatUserPresenceService->currentDailyStatus($user),
],
]);
}
/**
* 修改密码 (对应 chpasswd.asp)
*/
@@ -0,0 +1,60 @@
<?php
/**
* 文件功能:聊天室用户状态设置验证器
* 负责校验用户在聊天室内提交的当日状态设置与清除请求。
*/
namespace App\Http\Requests;
use App\Support\ChatDailyStatusCatalog;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UpdateDailyStatusRequest extends FormRequest
{
/**
* 允许已登录用户保存自己的当日状态。
*/
public function authorize(): bool
{
return $this->user() !== null;
}
/**
* 获取聊天室当日状态设置的验证规则。
*
* @return array<string, mixed>
*/
public function rules(): array
{
return [
'room_id' => ['required', 'integer', 'exists:rooms,id'],
'action' => ['required', 'string', Rule::in(['set', 'clear'])],
'status_key' => [
Rule::requiredIf(fn (): bool => $this->input('action') === 'set'),
'nullable',
'string',
Rule::in(ChatDailyStatusCatalog::keys()),
],
];
}
/**
* 获取聊天室当日状态设置的中文错误提示。
*
* @return array<string, string>
*/
public function messages(): array
{
return [
'room_id.required' => '缺少当前房间信息。',
'room_id.integer' => '房间编号格式无效。',
'room_id.exists' => '当前房间不存在。',
'action.required' => '缺少状态操作类型。',
'action.in' => '不支持的状态操作类型。',
'status_key.required' => '请选择要设置的状态。',
'status_key.in' => '请选择系统支持的状态。',
];
}
}