diff --git a/src/views/im/home/store/friendStore.ts b/src/views/im/home/store/friendStore.ts index ca963204f..f0052114e 100644 --- a/src/views/im/home/store/friendStore.ts +++ b/src/views/im/home/store/friendStore.ts @@ -25,14 +25,16 @@ import { getCurrentUserId, imStorage, setQuietly, StorageKeys } from '../../util import { getFriendDisplayName } from '../../utils/user' import type { Friend, FriendRequest } from '../types' +/** 当前正在进行的好友列表拉取;多 dispatcher 同时触发时复用同一 Promise,避免雪崩重拉 */ +let pendingFetchFriends: Promise | null = null /** 当前正在进行的好友申请列表拉取;多端连续多条申请到达时复用同一 Promise,避免雪崩重拉 */ -let inflightFetchRequests: Promise | null = null +let pendingFetchRequests: Promise | null = null /** 好友通知 payload(对齐后端 BaseFriendNotification + 子类裁减后的字段) */ export interface FriendNotificationPayload { operatorUserId: number friendUserId: number - // FRIEND_APPLICATION 系列:申请记录的核心字段(避免 payload 携带完整 DO) + // FRIEND_REQUEST_* 系列:申请记录的核心字段(避免 payload 携带完整 DO) requestId?: number applyContent?: string handleContent?: string @@ -128,24 +130,34 @@ export const useFriendStore = defineStore('imFriendStore', { // ==================== 远端拉取 ==================== - /** 从后端拉取并覆盖本地列表;同步刷新对应私聊会话的展示名 / 头像 + 落 IDB */ + /** 从后端拉取并覆盖本地列表;同步刷新对应私聊会话的展示名 / 头像 + 落 IDB;pending 期间复用同一 Promise */ async fetchFriends(force = false) { if (this.loaded && !force) { return } - const list = await apiGetMyFriendList() - this.friends = (list || []).map(convertFriend) - this.loaded = true - // 同步 conversationStore 私聊会话的展示名 / 头像 / 免打扰 - const conversationStore = useConversationStore() - for (const friend of this.friends) { - conversationStore.updateConversation(ImConversationType.PRIVATE, friend.friendUserId, { - name: getFriendDisplayName(friend), - avatar: friend.avatar, - muted: friend.muted - }) + if (pendingFetchFriends) { + return pendingFetchFriends } - this.saveFriends() + pendingFetchFriends = apiGetMyFriendList() + .then((list) => { + this.friends = (list || []).map(convertFriend) + this.loaded = true + // 同步 conversationStore 私聊会话的展示名 / 头像 / 免打扰 + const conversationStore = useConversationStore() + for (const friend of this.friends) { + conversationStore.updateConversation(ImConversationType.PRIVATE, friend.friendUserId, { + name: getFriendDisplayName(friend), + avatar: friend.avatar, + muted: friend.muted + }) + } + // 落本地缓存 + this.saveFriends() + }) + .finally(() => { + pendingFetchFriends = null + }) + return pendingFetchFriends }, /** 按 friendUserId 获取详情并合并到本地(保证 nickname / avatar 最新) */ @@ -195,19 +207,19 @@ export const useFriendStore = defineStore('imFriendStore', { } }, - /** 拉取「我相关」的好友申请列表(页面打开时 / 收到 FRIEND_APPLICATION 时刷新);in-flight 期间复用同一 Promise */ + /** 拉取「我相关」的好友申请列表(页面打开时 / 收到 FRIEND_REQUEST_RECEIVED 时刷新);pending 期间复用同一 Promise */ async fetchFriendRequests() { - if (inflightFetchRequests) { - return inflightFetchRequests + if (pendingFetchRequests) { + return pendingFetchRequests } - inflightFetchRequests = apiGetMyFriendRequestList() + pendingFetchRequests = apiGetMyFriendRequestList() .then((list) => { this.friendRequests = (list || []).map(convertFriendRequest) }) .finally(() => { - inflightFetchRequests = null + pendingFetchRequests = null }) - return inflightFetchRequests + return pendingFetchRequests }, /** 按 id 查申请记录;列表是按 id 倒序的小列表,O(n) find 即可,不再维护 Map 索引 */ @@ -320,8 +332,8 @@ export const useFriendStore = defineStore('imFriendStore', { // ==================== WebSocket 事件 dispatcher(1201-1210 段) ==================== - /** FRIEND_APPLICATION(1203):收到新申请;payload 已裁减为核心字段,本地拉一次列表补齐 fromUser 等聚合字段 */ - applyFriendRequestNotification(_payload: FriendNotificationPayload) { + /** FRIEND_REQUEST_RECEIVED(1203):收到新申请;payload 已裁减为核心字段,本地拉一次列表补齐 fromUser 等聚合字段 */ + applyFriendRequestReceivedNotification(_payload: FriendNotificationPayload) { this.fetchFriendRequests().catch(() => undefined) }, diff --git a/src/views/im/home/store/websocketStore.ts b/src/views/im/home/store/websocketStore.ts index 156b575fd..cb1b22dd4 100644 --- a/src/views/im/home/store/websocketStore.ts +++ b/src/views/im/home/store/websocketStore.ts @@ -486,8 +486,8 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', { const payload = JSON.parse(websocketMessage.content || '{}') as FriendNotificationPayload const friendStore = useFriendStore() switch (websocketMessage.type) { - case ImMessageType.FRIEND_APPLICATION: - friendStore.applyFriendRequestNotification(payload) + case ImMessageType.FRIEND_REQUEST_RECEIVED: + friendStore.applyFriendRequestReceivedNotification(payload) break case ImMessageType.FRIEND_REQUEST_APPROVED: friendStore.applyFriendRequestApprovedNotification(payload) diff --git a/src/views/im/utils/constants.ts b/src/views/im/utils/constants.ts index 73a013d37..b11538a17 100644 --- a/src/views/im/utils/constants.ts +++ b/src/views/im/utils/constants.ts @@ -12,7 +12,7 @@ export const ImMessageType = { // ========== 好友通知(1201-1210 直接复用 OpenIM 段位编号) ========== FRIEND_REQUEST_APPROVED: 1201, // 好友申请被同意 FRIEND_REQUEST_REJECTED: 1202, // 好友申请被拒绝 - FRIEND_APPLICATION: 1203, // 收到新的好友申请 + FRIEND_REQUEST_RECEIVED: 1203, // 收到新的好友申请 FRIEND_ADD: 1204, // 新增好友(双方建立关系) FRIEND_DELETE: 1205, // 好友被删除 // 1206 对应 OpenIM FriendRemarkSetNotification;本系统并入 FRIEND_UPDATE(1210) 统一推送,单一字段变更不再独立通道