feat(im):fetchFriends 加 pending 去重 + FRIEND_APPLICATION 重命名 RECEIVED + inflight 命名调整为 pending

im
YunaiV 2026-05-04 19:14:51 +08:00
parent cfd152addf
commit b242b017c0
3 changed files with 38 additions and 26 deletions

View File

@ -25,14 +25,16 @@ import { getCurrentUserId, imStorage, setQuietly, StorageKeys } from '../../util
import { getFriendDisplayName } from '../../utils/user' import { getFriendDisplayName } from '../../utils/user'
import type { Friend, FriendRequest } from '../types' import type { Friend, FriendRequest } from '../types'
/** 当前正在进行的好友列表拉取;多 dispatcher 同时触发时复用同一 Promise避免雪崩重拉 */
let pendingFetchFriends: Promise<void> | null = null
/** 当前正在进行的好友申请列表拉取;多端连续多条申请到达时复用同一 Promise避免雪崩重拉 */ /** 当前正在进行的好友申请列表拉取;多端连续多条申请到达时复用同一 Promise避免雪崩重拉 */
let inflightFetchRequests: Promise<void> | null = null let pendingFetchRequests: Promise<void> | null = null
/** 好友通知 payload对齐后端 BaseFriendNotification + 子类裁减后的字段) */ /** 好友通知 payload对齐后端 BaseFriendNotification + 子类裁减后的字段) */
export interface FriendNotificationPayload { export interface FriendNotificationPayload {
operatorUserId: number operatorUserId: number
friendUserId: number friendUserId: number
// FRIEND_APPLICATION 系列:申请记录的核心字段(避免 payload 携带完整 DO // FRIEND_REQUEST_* 系列:申请记录的核心字段(避免 payload 携带完整 DO
requestId?: number requestId?: number
applyContent?: string applyContent?: string
handleContent?: string handleContent?: string
@ -128,24 +130,34 @@ export const useFriendStore = defineStore('imFriendStore', {
// ==================== 远端拉取 ==================== // ==================== 远端拉取 ====================
/** 从后端拉取并覆盖本地列表;同步刷新对应私聊会话的展示名 / 头像 + 落 IDB */ /** 从后端拉取并覆盖本地列表;同步刷新对应私聊会话的展示名 / 头像 + 落 IDBpending 期间复用同一 Promise */
async fetchFriends(force = false) { async fetchFriends(force = false) {
if (this.loaded && !force) { if (this.loaded && !force) {
return return
} }
const list = await apiGetMyFriendList() if (pendingFetchFriends) {
this.friends = (list || []).map(convertFriend) return pendingFetchFriends
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() 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 最新) */ /** 按 friendUserId 获取详情并合并到本地(保证 nickname / avatar 最新) */
@ -195,19 +207,19 @@ export const useFriendStore = defineStore('imFriendStore', {
} }
}, },
/** 拉取「我相关」的好友申请列表(页面打开时 / 收到 FRIEND_APPLICATION 时刷新in-flight 期间复用同一 Promise */ /** 拉取「我相关」的好友申请列表(页面打开时 / 收到 FRIEND_REQUEST_RECEIVED 时刷新pending 期间复用同一 Promise */
async fetchFriendRequests() { async fetchFriendRequests() {
if (inflightFetchRequests) { if (pendingFetchRequests) {
return inflightFetchRequests return pendingFetchRequests
} }
inflightFetchRequests = apiGetMyFriendRequestList() pendingFetchRequests = apiGetMyFriendRequestList()
.then((list) => { .then((list) => {
this.friendRequests = (list || []).map(convertFriendRequest) this.friendRequests = (list || []).map(convertFriendRequest)
}) })
.finally(() => { .finally(() => {
inflightFetchRequests = null pendingFetchRequests = null
}) })
return inflightFetchRequests return pendingFetchRequests
}, },
/** 按 id 查申请记录;列表是按 id 倒序的小列表O(n) find 即可,不再维护 Map 索引 */ /** 按 id 查申请记录;列表是按 id 倒序的小列表O(n) find 即可,不再维护 Map 索引 */
@ -320,8 +332,8 @@ export const useFriendStore = defineStore('imFriendStore', {
// ==================== WebSocket 事件 dispatcher1201-1210 段) ==================== // ==================== WebSocket 事件 dispatcher1201-1210 段) ====================
/** FRIEND_APPLICATION(1203)收到新申请payload 已裁减为核心字段,本地拉一次列表补齐 fromUser 等聚合字段 */ /** FRIEND_REQUEST_RECEIVED(1203)收到新申请payload 已裁减为核心字段,本地拉一次列表补齐 fromUser 等聚合字段 */
applyFriendRequestNotification(_payload: FriendNotificationPayload) { applyFriendRequestReceivedNotification(_payload: FriendNotificationPayload) {
this.fetchFriendRequests().catch(() => undefined) this.fetchFriendRequests().catch(() => undefined)
}, },

View File

@ -486,8 +486,8 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', {
const payload = JSON.parse(websocketMessage.content || '{}') as FriendNotificationPayload const payload = JSON.parse(websocketMessage.content || '{}') as FriendNotificationPayload
const friendStore = useFriendStore() const friendStore = useFriendStore()
switch (websocketMessage.type) { switch (websocketMessage.type) {
case ImMessageType.FRIEND_APPLICATION: case ImMessageType.FRIEND_REQUEST_RECEIVED:
friendStore.applyFriendRequestNotification(payload) friendStore.applyFriendRequestReceivedNotification(payload)
break break
case ImMessageType.FRIEND_REQUEST_APPROVED: case ImMessageType.FRIEND_REQUEST_APPROVED:
friendStore.applyFriendRequestApprovedNotification(payload) friendStore.applyFriendRequestApprovedNotification(payload)

View File

@ -12,7 +12,7 @@ export const ImMessageType = {
// ========== 好友通知1201-1210 直接复用 OpenIM 段位编号) ========== // ========== 好友通知1201-1210 直接复用 OpenIM 段位编号) ==========
FRIEND_REQUEST_APPROVED: 1201, // 好友申请被同意 FRIEND_REQUEST_APPROVED: 1201, // 好友申请被同意
FRIEND_REQUEST_REJECTED: 1202, // 好友申请被拒绝 FRIEND_REQUEST_REJECTED: 1202, // 好友申请被拒绝
FRIEND_APPLICATION: 1203, // 收到新的好友申请 FRIEND_REQUEST_RECEIVED: 1203, // 收到新的好友申请
FRIEND_ADD: 1204, // 新增好友(双方建立关系) FRIEND_ADD: 1204, // 新增好友(双方建立关系)
FRIEND_DELETE: 1205, // 好友被删除 FRIEND_DELETE: 1205, // 好友被删除
// 1206 对应 OpenIM FriendRemarkSetNotification本系统并入 FRIEND_UPDATE(1210) 统一推送,单一字段变更不再独立通道 // 1206 对应 OpenIM FriendRemarkSetNotification本系统并入 FRIEND_UPDATE(1210) 统一推送,单一字段变更不再独立通道