From ab2fa4e6b878233d1e6b0c9f8abb79a6e45080d2 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 18 Jun 2026 06:57:05 -0700 Subject: [PATCH] =?UTF-8?q?fix(im)=EF=BC=9A=E7=BE=A4=E6=98=B5=E7=A7=B0?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=94=B9=E4=B8=BA=E9=9D=99=E9=BB=98=E5=90=8C?= =?UTF-8?q?=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端将 GROUP_MEMBER_NICKNAME_UPDATE 改为非持久化事件,避免写入群聊历史消息 - 保留群昵称变更的 WebSocket 在线同步,继续更新成员 displayUserName - Vue3 聊天侧栏从当前群成员的 displayUserName 回填「我在本群的昵称」 - Vue3 WebSocket 收到 GROUP_MEMBER_NICKNAME_UPDATE 时只同步 groupStore,不再插入消息列表 - 补充后端单测,覆盖群昵称变更事件不入库但仍推送 --- .../components/message/MessagePanel.vue | 3 +++ src/views/im/home/store/websocketStore.ts | 15 +++++++++++++- src/views/im/utils/db.ts | 20 ++++++++++++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/views/im/home/pages/conversation/components/message/MessagePanel.vue b/src/views/im/home/pages/conversation/components/message/MessagePanel.vue index 4113e9abb..61224a6ba 100644 --- a/src/views/im/home/pages/conversation/components/message/MessagePanel.vue +++ b/src/views/im/home/pages/conversation/components/message/MessagePanel.vue @@ -259,6 +259,7 @@ import { createCall } from '@/api/im/rtc' import { ImRtcCallMediaType, ImRtcCallStatus, ImConversationType } from '@/views/im/utils/constants' import { resolveCallEndReasonText } from '@/views/im/utils/message' import { getClientConversationId } from '@/views/im/utils/db' +import { getCurrentUserId } from '@/utils/auth' import { useRtcStore } from '../../../../store/rtcStore' import { useMessageStore } from '../../../../store/messageStore' @@ -403,12 +404,14 @@ const groupInfo = computed< return undefined } const group = groupStore.getGroup(conversation.targetId) + const selfMember = group?.members?.find((member) => member.userId === getCurrentUserId()) return { id: conversation.targetId, name: group?.name || conversation.name, showGroupName: group?.name || conversation.name, showImage: group?.avatar || conversation.avatar, notice: group?.notice, + remarkNickName: selfMember?.displayUserName, groupRemark: group?.groupRemark, ownerId: group?.ownerUserId, memberCount: group?.memberCount, diff --git a/src/views/im/home/store/websocketStore.ts b/src/views/im/home/store/websocketStore.ts index da0178d78..3bf68edb8 100644 --- a/src/views/im/home/store/websocketStore.ts +++ b/src/views/im/home/store/websocketStore.ts @@ -171,6 +171,7 @@ const convertGroupMessage = ( * - 已读 / 回执(READ / RECEIPT):多端已读同步、对方读后回执 * - 好友变更(FRIEND_*):同步 friendStore + 级联刷新私聊会话;FRIEND_ADD / FRIEND_DELETE 额外插入会话气泡 * - 群个人信号(GROUP_MEMBER_SETTING_UPDATE):同步 groupStore + 级联刷新群聊会话 + * - 群成员昵称变更(GROUP_MEMBER_NICKNAME_UPDATE):同步 groupStore,不插入消息列表 * - 群广播事件(GROUP_*):走 handleGroupMessage + applyGroupNotification 旁路(含 DISSOLVE / QUIT / KICK 自判清群) */ export const useImWebSocketStore = defineStore('imWebSocketStore', { @@ -496,7 +497,7 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', { /** * 群聊统一帧分发:按 payload.type(ImContentType)分到已读 / 回执 / 群个人信号 / 普通消息 * - * 1530 GROUP_MEMBER_SETTING_UPDATE 是个人信号;其它(普通消息 + 1501-1520 段位群广播事件)走 handleGroupMessage 入库 + 触发 applyGroupNotification 旁路 + * GROUP_MEMBER_SETTING_UPDATE / GROUP_MEMBER_NICKNAME_UPDATE 是成员资料同步信号;其它群广播事件走 handleGroupMessage 入库 + 触发 applyGroupNotification 旁路 */ dispatchGroupFrame(websocketMessage: ImGroupMessageNotification) { try { @@ -510,6 +511,9 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', { case ImContentType.GROUP_MEMBER_SETTING_UPDATE: this.handleGroupMemberSettingUpdate(websocketMessage) break + case ImContentType.GROUP_MEMBER_NICKNAME_UPDATE: + this.handleGroupMemberNicknameUpdate(websocketMessage) + break case ImContentType.RTC_CALL_START: // 入库 + 渲染聊天 tip;胶囊条状态走 1602/1603,本帧不动 rtcStore,避免与首次填充竞争 ignoreRealtimePersistError(this.handleGroupMessage(websocketMessage)) @@ -921,6 +925,15 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', { } }, + /** GROUP_MEMBER_NICKNAME_UPDATE:同步成员在群里的昵称 */ + handleGroupMemberNicknameUpdate(websocketMessage: ImGroupMessageNotification) { + useGroupStore().applyGroupNotification( + websocketMessage.groupId, + websocketMessage.type, + websocketMessage.content + ) + }, + // ==================== 心跳 / 重连 ==================== /** 心跳包:纯文本 'ping',对应服务端 'pong'(后端这层用纯字符串约定,避免 JSON 解析开销) */ diff --git a/src/views/im/utils/db.ts b/src/views/im/utils/db.ts index 1f0c3c82c..f6761a6fc 100644 --- a/src/views/im/utils/db.ts +++ b/src/views/im/utils/db.ts @@ -202,7 +202,25 @@ function guardSession(session: number) { /** 克隆可入库对象 */ function toDbValue(value: T): T { - return toRaw(value) as T + return cloneDbValue(value) as T +} + +/** 转换为 IndexedDB 可克隆对象 */ +function cloneDbValue(value: unknown): unknown { + const raw = toRaw(value) + if (Array.isArray(raw)) { + return raw.map((item) => cloneDbValue(item)) + } + if (!raw || typeof raw !== 'object') { + return raw + } + const prototype = Object.getPrototypeOf(raw) + if (prototype !== Object.prototype && prototype !== null) { + return raw + } + return Object.fromEntries( + Object.entries(raw as Record).map(([key, item]) => [key, cloneDbValue(item)]) + ) } class DbClient {