From 0f0bfef2a8a49ae66248c024a9b58a6b3abcfd3f Mon Sep 17 00:00:00 2001 From: lkddi Date: Fri, 24 Apr 2026 21:17:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=81=8A=E5=A4=A9=E5=AE=A4?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E4=B8=8E=E5=8A=9F=E8=83=BD=E5=BF=AB=E6=8D=B7?= =?UTF-8?q?=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Events/UserStatusUpdated.php | 57 ++ .../Admin/AiProviderController.php | 17 +- app/Http/Controllers/ChatController.php | 58 +- app/Http/Controllers/UserController.php | 64 ++ .../Requests/UpdateDailyStatusRequest.php | 60 ++ app/Models/User.php | 3 + app/Services/ChatUserPresenceService.php | 69 ++ app/Support/ChatDailyStatusCatalog.php | 180 ++++++ ...add_daily_status_fields_to_users_table.php | 43 ++ resources/js/chat.js | 7 + resources/views/chat/frame.blade.php | 19 +- .../chat/partials/layout/input-bar.blade.php | 93 ++- .../chat/partials/layout/toolbar.blade.php | 21 +- .../views/chat/partials/scripts.blade.php | 604 +++++++++++++++++- routes/channels.php | 20 +- routes/web.php | 1 + tests/Feature/ChatControllerTest.php | 58 ++ tests/Feature/UserControllerTest.php | 111 ++++ 18 files changed, 1361 insertions(+), 124 deletions(-) create mode 100644 app/Events/UserStatusUpdated.php create mode 100644 app/Http/Requests/UpdateDailyStatusRequest.php create mode 100644 app/Services/ChatUserPresenceService.php create mode 100644 app/Support/ChatDailyStatusCatalog.php create mode 100644 database/migrations/2026_04_24_204456_add_daily_status_fields_to_users_table.php diff --git a/app/Events/UserStatusUpdated.php b/app/Events/UserStatusUpdated.php new file mode 100644 index 0000000..8ac0942 --- /dev/null +++ b/app/Events/UserStatusUpdated.php @@ -0,0 +1,57 @@ + $user 最新在线名单载荷 + */ + public function __construct( + public readonly int $roomId, + public readonly string $username, + public readonly array $user, + ) {} + + /** + * 获取广播频道。 + * + * @return array + */ + public function broadcastOn(): array + { + return [ + new PresenceChannel('room.'.$this->roomId), + ]; + } + + /** + * 获取广播数据。 + * + * @return array + */ + public function broadcastWith(): array + { + return [ + 'username' => $this->username, + 'user' => $this->user, + ]; + } +} diff --git a/app/Http/Controllers/Admin/AiProviderController.php b/app/Http/Controllers/Admin/AiProviderController.php index 64c6c2b..c75c3f6 100644 --- a/app/Http/Controllers/Admin/AiProviderController.php +++ b/app/Http/Controllers/Admin/AiProviderController.php @@ -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)); diff --git a/app/Http/Controllers/ChatController.php b/app/Http/Controllers/ChatController.php index 2742c7f..b83c52c 100644 --- a/app/Http/Controllers/ChatController.php +++ b/app/Http/Controllers/ChatController.php @@ -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([ diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index e2dfc92..6810962 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -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) */ diff --git a/app/Http/Requests/UpdateDailyStatusRequest.php b/app/Http/Requests/UpdateDailyStatusRequest.php new file mode 100644 index 0000000..d0d9f10 --- /dev/null +++ b/app/Http/Requests/UpdateDailyStatusRequest.php @@ -0,0 +1,60 @@ +user() !== null; + } + + /** + * 获取聊天室当日状态设置的验证规则。 + * + * @return array + */ + 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 + */ + public function messages(): array + { + return [ + 'room_id.required' => '缺少当前房间信息。', + 'room_id.integer' => '房间编号格式无效。', + 'room_id.exists' => '当前房间不存在。', + 'action.required' => '缺少状态操作类型。', + 'action.in' => '不支持的状态操作类型。', + 'status_key.required' => '请选择要设置的状态。', + 'status_key.in' => '请选择系统支持的状态。', + ]; + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 89d24ba..e52826b 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -54,6 +54,8 @@ class User extends Authenticatable 'custom_join_effect', 'custom_leave_effect', 'chat_preferences', + 'daily_status_key', + 'daily_status_expires_at', 'user_level', 'inviter_id', 'room_id', @@ -107,6 +109,7 @@ class User extends Authenticatable 'q3_time' => 'datetime', 'has_received_new_gift' => 'boolean', 'chat_preferences' => 'array', + 'daily_status_expires_at' => 'datetime', ]; } diff --git a/app/Services/ChatUserPresenceService.php b/app/Services/ChatUserPresenceService.php new file mode 100644 index 0000000..d99e1b7 --- /dev/null +++ b/app/Services/ChatUserPresenceService.php @@ -0,0 +1,69 @@ + + */ + public function build(User $user): array + { + $superLevel = (int) Sysparam::getValue('superlevel', '100'); + $activePosition = $user->activePosition; + $position = $activePosition?->position; + $payload = [ + 'id' => $user->id, + 'user_id' => $user->id, + 'username' => $user->username, + 'level' => $user->user_level, + 'user_level' => $user->user_level, + 'sex' => $user->sex, + 'headface' => $user->headface, + 'headface_url' => $user->headfaceUrl, + 'headfaceUrl' => $user->headfaceUrl, + 'vip_icon' => $user->vipIcon(), + 'vip_name' => $user->vipName(), + 'vip_color' => $user->isVip() ? ($user->vipLevel?->color ?? '') : '', + 'is_admin' => $user->user_level >= $superLevel, + 'position_icon' => $position?->icon ?? '', + 'position_name' => $position?->name ?? '', + 'department_name' => $position?->department?->name ?? '', + ]; + + $activeStatus = $this->currentDailyStatus($user); + if ($activeStatus !== null) { + $payload['daily_status_key'] = $activeStatus['key']; + $payload['daily_status_label'] = $activeStatus['label']; + $payload['daily_status_icon'] = $activeStatus['icon']; + $payload['daily_status_group'] = $activeStatus['group']; + $payload['daily_status_expires_at'] = $activeStatus['expires_at']; + } + + return $payload; + } + + /** + * 读取用户当前仍然有效的当日状态。 + * + * @return array{key: string, label: string, icon: string, group: string, expires_at: string}|null + */ + public function currentDailyStatus(User $user): ?array + { + return ChatDailyStatusCatalog::resolveActiveStatus( + $user->daily_status_key, + $user->daily_status_expires_at, + ); + } +} diff --git a/app/Support/ChatDailyStatusCatalog.php b/app/Support/ChatDailyStatusCatalog.php new file mode 100644 index 0000000..f8f289e --- /dev/null +++ b/app/Support/ChatDailyStatusCatalog.php @@ -0,0 +1,180 @@ +}> + */ + public static function groupedOptions(): array + { + return [ + [ + 'group' => '心情想法', + 'items' => [ + ['key' => 'feeling_great', 'label' => '美滋滋', 'icon' => '🙂'], + ['key' => 'heartbroken', 'label' => '裂开', 'icon' => '💔'], + ['key' => 'lucky_fish', 'label' => '求锦鲤', 'icon' => '🐟'], + ['key' => 'waiting_sun', 'label' => '等天晴', 'icon' => '☀️'], + ['key' => 'tired', 'label' => '疲惫', 'icon' => '😮‍💨'], + ['key' => 'dazed', 'label' => '发呆', 'icon' => '💭'], + ['key' => 'charging', 'label' => '冲', 'icon' => '🚀'], + ['key' => 'emo', 'label' => 'emo', 'icon' => '🥀'], + ['key' => 'overthinking', 'label' => '胡思乱想', 'icon' => '☁️'], + ['key' => 'energetic', 'label' => '元气满满', 'icon' => '✨'], + ['key' => 'bot', 'label' => 'bot', 'icon' => '🤖'], + ], + ], + [ + 'group' => '工作学习', + 'items' => [ + ['key' => 'working_hard', 'label' => '搬砖', 'icon' => '🧱'], + ['key' => 'studying', 'label' => '沉迷学习', 'icon' => '📚'], + ['key' => 'busy', 'label' => '忙', 'icon' => '🙆'], + ['key' => 'slacking', 'label' => '摸鱼', 'icon' => '🐟'], + ['key' => 'business_trip', 'label' => '出差', 'icon' => '✈️'], + ['key' => 'running_home', 'label' => '飞奔回家', 'icon' => '🏃'], + ['key' => 'do_not_disturb', 'label' => '勿扰模式', 'icon' => '🌙'], + ], + ], + [ + 'group' => '活动', + 'items' => [ + ['key' => 'wandering', 'label' => '浪', 'icon' => '🌊'], + ['key' => 'clocking_in', 'label' => '打卡', 'icon' => '✌️'], + ['key' => 'exercising', 'label' => '运动', 'icon' => '🏃‍♂️'], + ['key' => 'coffee', 'label' => '喝咖啡', 'icon' => '☕'], + ['key' => 'milk_tea', 'label' => '喝奶茶', 'icon' => '🧋'], + ['key' => 'eating', 'label' => '干饭', 'icon' => '🍚'], + ['key' => 'parenting', 'label' => '带娃', 'icon' => '🧒'], + ['key' => 'save_world', 'label' => '拯救世界', 'icon' => '🦸'], + ['key' => 'selfie', 'label' => '自拍', 'icon' => '🤳'], + ], + ], + [ + 'group' => '休息', + 'items' => [ + ['key' => 'retreat', 'label' => '闭关', 'icon' => '🧘'], + ['key' => 'staying_home', 'label' => '宅', 'icon' => '🛋️'], + ['key' => 'sleeping', 'label' => '睡觉', 'icon' => '💤'], + ['key' => 'cat_time', 'label' => '吸猫', 'icon' => '🐈'], + ['key' => 'walk_dog', 'label' => '遛狗', 'icon' => '🐕'], + ['key' => 'gaming', 'label' => '玩游戏', 'icon' => '🎮'], + ['key' => 'listening_music', 'label' => '听歌', 'icon' => '🎧'], + ], + ], + ]; + } + + /** + * 返回全部合法状态键。 + * + * @return array + */ + public static function keys(): array + { + return array_values(array_map( + static fn (array $item): string => $item['key'], + self::flatOptions() + )); + } + + /** + * 根据状态键查找对应配置。 + * + * @return array{key: string, label: string, icon: string, group: string}|null + */ + public static function find(?string $key): ?array + { + if ($key === null || $key === '') { + return null; + } + + foreach (self::flatOptions() as $item) { + if ($item['key'] === $key) { + return $item; + } + } + + return null; + } + + /** + * 解析当前仍处于有效期内的状态数据。 + * + * @param CarbonInterface|string|null $expiresAt 到期时间 + * @return array{key: string, label: string, icon: string, group: string, expires_at: string}|null + */ + public static function resolveActiveStatus(?string $key, CarbonInterface|string|null $expiresAt): ?array + { + $option = self::find($key); + if ($option === null) { + return null; + } + + $expiresAtCarbon = self::normalizeExpiresAt($expiresAt); + if ($expiresAtCarbon === null || $expiresAtCarbon->isPast()) { + return null; + } + + return [ + 'key' => $option['key'], + 'label' => $option['label'], + 'icon' => $option['icon'], + 'group' => $option['group'], + 'expires_at' => $expiresAtCarbon->toIso8601String(), + ]; + } + + /** + * 将分组目录整理为扁平列表,便于校验与查找。 + * + * @return array + */ + private static function flatOptions(): array + { + $flat = []; + + foreach (self::groupedOptions() as $group) { + foreach ($group['items'] as $item) { + $flat[] = [ + 'key' => $item['key'], + 'label' => $item['label'], + 'icon' => $item['icon'], + 'group' => $group['group'], + ]; + } + } + + return $flat; + } + + /** + * 规整状态到期时间为 Carbon 对象。 + * + * @param CarbonInterface|string|null $expiresAt 原始到期时间 + */ + private static function normalizeExpiresAt(CarbonInterface|string|null $expiresAt): ?CarbonInterface + { + if ($expiresAt instanceof CarbonInterface) { + return $expiresAt; + } + + if ($expiresAt === null || $expiresAt === '') { + return null; + } + + return Carbon::parse($expiresAt); + } +} diff --git a/database/migrations/2026_04_24_204456_add_daily_status_fields_to_users_table.php b/database/migrations/2026_04_24_204456_add_daily_status_fields_to_users_table.php new file mode 100644 index 0000000..f28732a --- /dev/null +++ b/database/migrations/2026_04_24_204456_add_daily_status_fields_to_users_table.php @@ -0,0 +1,43 @@ +string('daily_status_key', 50) + ->nullable() + ->after('chat_preferences') + ->comment('聊天室当日状态键'); + $table->dateTime('daily_status_expires_at') + ->nullable() + ->after('daily_status_key') + ->comment('聊天室当日状态到期时间'); + }); + } + + /** + * 回滚 users 表中的聊天室当日状态字段。 + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn([ + 'daily_status_key', + 'daily_status_expires_at', + ]); + }); + } +}; diff --git a/resources/js/chat.js b/resources/js/chat.js index d59c657..86e7fa2 100644 --- a/resources/js/chat.js +++ b/resources/js/chat.js @@ -243,6 +243,13 @@ export function initChat(roomId) { new CustomEvent("chat:screen-cleared", { detail: e }), ); }) + // 监听用户状态变更,实时刷新右侧在线名单。 + .listen("UserStatusUpdated", (e) => { + console.log("用户状态更新:", e); + window.dispatchEvent( + new CustomEvent("chat:user-status-updated", { detail: e }), + ); + }) // 监听站长触发的全员刷新 .listen("BrowserRefreshRequested", (e) => { console.log("全员刷新:", e); diff --git a/resources/views/chat/frame.blade.php b/resources/views/chat/frame.blade.php index d5bbdb8..999ff22 100644 --- a/resources/views/chat/frame.blade.php +++ b/resources/views/chat/frame.blade.php @@ -57,20 +57,8 @@ if ($chatbotEnabledState) { $botUser = \App\Models\User::where('username', 'AI小班长')->first(); if ($botUser) { - $botUserData = [ - 'user_id' => $botUser->id, - 'username' => $botUser->username, - 'level' => $botUser->user_level, - 'sex' => $botUser->sex, - 'headface' => $botUser->headface, - 'headfaceUrl' => $botUser->headfaceUrl, - 'vip_icon' => $botUser->vipIcon(), - 'vip_name' => $botUser->vipName(), - 'vip_color' => $botUser->isVip() ? ($botUser->vipLevel?->color ?? '') : '', - 'is_admin' => false, - 'position_icon' => '', - 'position_name' => '', - ]; + $botUserData = app(\App\Services\ChatUserPresenceService::class)->build($botUser); + $botUserData['headfaceUrl'] = $botUser->headfaceUrl; } } @endphp @@ -106,9 +94,12 @@ rewardQuotaUrl: "{{ route('command.reward_quota') }}", refreshAllUrl: "{{ route('command.refresh_all') }}", chatPreferencesUrl: "{{ route('user.update_chat_preferences') }}", + dailyStatusUpdateUrl: "{{ route('user.update_daily_status') }}", userJjb: {{ (int) $user->jjb }}, // 当前用户金币(求婚前金额预检查用) myGold: {{ (int) $user->jjb }}, // 赠金币面板显示余额用(赠送成功后前端更新) chatPreferences: @json($user->chat_preferences ?? []), + currentDailyStatus: @json($activeDailyStatus), + dailyStatusCatalog: @json($dailyStatusCatalog), // ─── 婚姻系统 ────────────────────────────── minWeddingCost: {{ (int) \App\Models\WeddingTier::where('is_active', true)->orderBy('amount')->value('amount') ?? 0 }}, diff --git a/resources/views/chat/partials/layout/input-bar.blade.php b/resources/views/chat/partials/layout/input-bar.blade.php index 5b023c3..21ce367 100644 --- a/resources/views/chat/partials/layout/input-bar.blade.php +++ b/resources/views/chat/partials/layout/input-bar.blade.php @@ -15,6 +15,7 @@ $canSendRedPacket = $roomPermissionMap[\App\Support\PositionPermissionRegistry::ROOM_RED_PACKET] ?? false; $canManageLossCover = $roomPermissionMap[\App\Support\PositionPermissionRegistry::ROOM_BACCARAT_LOSS_COVER] ?? false; $canTriggerFullscreenEffect = $roomPermissionMap[\App\Support\PositionPermissionRegistry::ROOM_FULLSCREEN_EFFECT] ?? false; + $currentDailyStatusKey = $activeDailyStatus['key'] ?? ''; @endphp
@@ -165,6 +166,94 @@ $welcomeMessages = [
+
+ + +
+ + + @if (! empty($hasRoomManagementPermission))
@endif - - {{-- 第二行:输入框 + 发送 --}} diff --git a/resources/views/chat/partials/layout/toolbar.blade.php b/resources/views/chat/partials/layout/toolbar.blade.php index b31c249..2f31122 100644 --- a/resources/views/chat/partials/layout/toolbar.blade.php +++ b/resources/views/chat/partials/layout/toolbar.blade.php @@ -24,7 +24,7 @@
婚姻
好友
头像
-
设置 +
设置
反馈
留言
@@ -89,9 +89,10 @@ {{-- ═══════════ 个人设置弹窗 ═══════════ --}}