From 27cd042df9aca0383a49f3514bc21958dd890912 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 29 六月 2026 15:42:23 +0800
Subject: [PATCH] 银川 1.联调产品维护页面 2.添加IM即时通讯模块
---
src/views/im/home/composables/useMessagePuller.ts | 464 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 464 insertions(+), 0 deletions(-)
diff --git a/src/views/im/home/composables/useMessagePuller.ts b/src/views/im/home/composables/useMessagePuller.ts
new file mode 100644
index 0000000..95cf36d
--- /dev/null
+++ b/src/views/im/home/composables/useMessagePuller.ts
@@ -0,0 +1,464 @@
+import type { Message } from '../types'
+
+import type { ImChannelMessageApi } from '#/api/im/message/channel'
+import type { ImGroupMessageApi } from '#/api/im/message/group'
+import type { ImPrivateMessageApi } from '#/api/im/message/private'
+
+import { watch } from 'vue'
+
+import { pullChannelMessageList as apiPullChannelMessageList } from '#/api/im/message/channel'
+import { pullGroupMessageList as apiPullGroupMessageList } from '#/api/im/message/group'
+import { getPrivateMaxReadMessageId as apiGetPrivateMaxReadMessageId, pullPrivateMessageList as apiPullPrivateMessageList } from '#/api/im/message/private'
+import { getCurrentUserId } from '#/views/im/utils/auth'
+
+import { buildChannelConversationStub } from '../../utils/channel'
+import {
+ MESSAGE_GROUP_PULL_SIZE,
+ MESSAGE_PRIVATE_PULL_SIZE,
+ MESSAGE_PRIVATE_READ_ENABLED
+} from '../../utils/config'
+import {
+ ImContentType,
+ ImConversationType,
+ ImMessageStatus,
+ isFriendChatTip,
+ isFriendNotification
+} from '../../utils/constants'
+import { generateClientMessageId, getPrivateMessagePeerId } from '../../utils/message'
+import { runMinIdPull } from '../../utils/pull'
+import { getFriendDisplayName, getGroupDisplayName } from '../../utils/user'
+import { useConversationStore } from '../store/conversationStore'
+import { useFriendStore } from '../store/friendStore'
+import { useGroupRequestStore } from '../store/groupRequestStore'
+import { useGroupStore } from '../store/groupStore'
+import { type PulledMessage, useMessageStore } from '../store/messageStore'
+import { useRtcStore } from '../store/rtcStore'
+import { useImWebSocketStore } from '../store/websocketStore'
+
+/** 涓夌被娑堟伅 pull 鎺ュ彛杩斿洖鐨勫師濮� VO 鑱斿悎绫诲瀷锛況unMinIdPull 鍙渶 id 鎺ㄨ繘娓告爣锛屽叿浣撳垎鍙戝湪 applyPage 鍐呮寜绫诲瀷 cast */
+type PulledRawMessage = ImChannelMessageApi.ChannelMessageRespVO | ImGroupMessageApi.GroupMessageRespVO | ImPrivateMessageApi.PrivateMessageRespVO
+
+/**
+ * 娑堟伅澧為噺鎷夊彇锛氱櫥褰曞悗鍒嗛〉鎷夊彇绂荤嚎鏈熼棿鐨勬柊娑堟伅
+ *
+ * 璁捐瑕佺偣锛�
+ * 1. 鍚屾椂鎷夊彇绉佽亰 + 缇よ亰锛屼娇鐢ㄥ悇鑷殑 `minId` 娓告爣锛坧rivateMessageMaxId / groupMessageMaxId锛�
+ * 2. 鍚庣涓�娆℃渶澶氳繑鍥� size 鏉★紱鍓嶇鎸� minId 鎸佺画缈婚〉锛岀洿鍒版帴鍙h繑鍥炵┖鍒楄〃涓烘
+ * 3. 鎷夊彇鏈熼棿 conversationStore.loading=true锛�
+ * - conversationStore 璺宠繃鎵归噺鎸佷箙鍖栵紝閬垮厤棰戠箒鍐欏叆鍗¢】
+ * - websocketStore 鎶婃柊鏉ョ殑 WS 鏅�氭秷鎭涪杩涚紦鍐插尯锛岀瓑寰幆缁撴潫鍚庣粺涓�鍥炴斁
+ * 4. WebSocket 閲嶈繛鍚庝細鍐嶈Е鍙戜竴娆℃媺鍙栵紝琛ラ綈鏂綉鏈熼棿閿欒繃鐨勬秷鎭�
+ */
+export const useMessagePuller = () => {
+ const conversationStore = useConversationStore()
+ const messageStore = useMessageStore()
+ const wsStore = useImWebSocketStore()
+ const friendStore = useFriendStore()
+ const groupStore = useGroupStore()
+ const groupRequestStore = useGroupRequestStore()
+ const rtcStore = useRtcStore()
+ const currentUserId = getCurrentUserId()
+
+ /** 鍒ゆ柇璇锋眰鏄惁琚富鍔ㄥ彇娑� */
+ const isAbortError = (e: unknown): boolean => {
+ const error = e as { code?: string; message?: string; name?: string; }
+ return (
+ error?.name === 'CanceledError' ||
+ error?.code === 'ERR_CANCELED' ||
+ error?.message === 'canceled'
+ )
+ }
+
+ /** 绉佽亰浼氳瘽褰掑睘锛氳嚜宸卞彂鐨勭畻"鍙戠粰 receiverId 鐨勪細璇�"锛屽惁鍒欑畻"鍙戦�佹柟鐨勪細璇�"锛沜urry currentUserId 杩涢棴鍖呭噺灏� 3 澶勮皟鐢ㄦ柟鐨勬牱鏉� */
+ const getPrivatePeerId = (message: ImPrivateMessageApi.PrivateMessageRespVO) =>
+ getPrivateMessagePeerId(message, currentUserId)
+
+ /** 鏈嶅姟绔鑱婃秷鎭� -> 鏈湴 Message锛歵argetId 鏄細璇濅富閿紙瀵圭 userId锛� */
+ const convertPrivateMessage = (message: ImPrivateMessageApi.PrivateMessageRespVO): Message => {
+ return {
+ id: message.id,
+ clientMessageId: message.clientMessageId || generateClientMessageId(),
+ type: message.type,
+ content: message.content,
+ status: message.status,
+ receiptStatus: message.receiptStatus,
+ sendTime: new Date(message.sendTime).getTime(),
+ senderId: message.senderId,
+ targetId: getPrivatePeerId(message),
+ selfSend: message.senderId === currentUserId
+ }
+ }
+
+ /** 鏈嶅姟绔兢鑱婃秷鎭� -> 鏈湴 Message */
+ const convertGroupMessage = (message: ImGroupMessageApi.GroupMessageRespVO): Message => {
+ return {
+ id: message.id,
+ clientMessageId: message.clientMessageId || generateClientMessageId(),
+ type: message.type,
+ content: message.content,
+ status: message.status,
+ sendTime: new Date(message.sendTime).getTime(),
+ senderId: message.senderId,
+ targetId: message.groupId,
+ selfSend: message.senderId === currentUserId,
+ atUserIds: message.atUserIds || [],
+ receiverUserIds: message.receiverUserIds || [],
+ receiptStatus: message.receiptStatus,
+ readCount: message.readCount
+ }
+ }
+
+ /** 鏈嶅姟绔閬撴秷鎭� -> 鏈湴 Message */
+ const convertChannelMessage = (message: ImChannelMessageApi.ChannelMessageRespVO): Message => {
+ return {
+ id: message.id,
+ clientMessageId: message.clientMessageId || generateClientMessageId(),
+ type: message.type,
+ content: message.content,
+ status: ImMessageStatus.NORMAL, // 棰戦亾鏃犳挙鍥烇紝鎭掍负姝e父
+ receiptStatus: message.receiptStatus, // 棰戦亾宸茶鎬侊細DONE 宸茶 / PENDING 鏈
+ sendTime: new Date(message.sendTime).getTime(),
+ senderId: 0, // 绯荤粺涓嬪彂锛屾棤鍙戦�佷汉
+ targetId: message.channelId, // 浼氳瘽褰掑睘鍒伴閬撶紪鍙�
+ selfSend: false,
+ materialId: message.materialId // 璇︽儏椤垫媺瀵屾枃鏈敤
+ }
+ }
+
+ /** 棰戦亾锛氫細璇濆綊灞炲埌 channelId锛沶ame / avatar 鏆傜敤鍗犱綅锛屽皢鏉ユ帴鍏� channelStore 鍚庡啀濉湡鍊� */
+ const convertChannelConversation = (message: ImChannelMessageApi.ChannelMessageRespVO) =>
+ buildChannelConversationStub(message.channelId)
+
+ /** 绉佽亰锛氫細璇濆綊灞炲埌瀵圭 userId */
+ const convertPrivateConversation = (message: ImPrivateMessageApi.PrivateMessageRespVO) => {
+ const targetId = getPrivatePeerId(message)
+ const friend = friendStore.getFriend(targetId)
+ return {
+ type: ImConversationType.PRIVATE,
+ targetId,
+ name: friend ? getFriendDisplayName(friend) : String(targetId), // 浼氳瘽鍒楄〃 / 椤堕儴鏍囬灞曠ず锛氬ソ鍙嬪娉� > 鐪熷疄鏄电О
+ avatar: friend?.avatar || '',
+ silent: friend?.silent
+ }
+ }
+
+ /** 缇よ亰锛氫細璇濆綊灞炲埌 groupId */
+ const convertGroupConversation = (message: ImGroupMessageApi.GroupMessageRespVO) => {
+ const group = groupStore.getGroup(message.groupId)
+ return {
+ type: ImConversationType.GROUP,
+ targetId: message.groupId,
+ name: group ? getGroupDisplayName(group) : String(message.groupId),
+ avatar: group?.avatar || '',
+ silent: group?.silent
+ }
+ }
+
+ /**
+ * 鍒嗙被鍨嬫媺鍙栫绾挎秷鎭細缈婚〉 / minId 娓告爣鎺ㄨ繘 / 绌洪〉鍋滅敱 runMinIdPull 璐熻矗锛岃繖閲屽彧鍋氭帴鍙e垎鏀� + 閫愭潯涓氬姟鍒嗗彂
+ * 锛堟挙鍥� / 濂藉弸閫氱煡 / 鏅�氭秷鎭級+ 鍏ュ簱銆�
+ *
+ * 鍙栨秷璇箟涓ゅ眰瀹堝崼锛岀粡 isActive 浼犲叆 runMinIdPull锛屼换涓�涓嶇瓑鍗充涪寮冩湰鎵逛笉鍏ュ簱銆佸仠姝㈢炕椤碉紝閬垮厤鏃� session 鍝嶅簲钀藉埌鏂� store锛�
+ * 1. startEpoch锛歝ancelPull() 閫掑 pullEpoch锛涚寮� IM / 鍒囪处鍙锋椂璺冲嚭
+ * 2. startUserId锛氭瘡鎵� await 鍚庢瘮瀵瑰綋鍓嶇櫥褰� userId锛涢槻寰� logout / 澶� tab 涓嬬敤鎴峰凡鍒囦絾 cancelPull 鏈Е鍙�
+ */
+ const pullByType = async (
+ conversationType: number,
+ startMinId: number,
+ startEpoch: number,
+ startUserId: number,
+ signal: AbortSignal
+ ) => {
+ // 绉佽亰 / 缇よ亰 / 棰戦亾鍚勮嚜涓�濂楁帴鍙o紱鎸� conversationType 鍒嗘敮璋冨害銆傜炕椤垫満鍒讹紙minId 娓告爣 / 绌洪〉鍒ゆ柇 / 闃叉缈伙級浜ょ粰 runMinIdPull
+ const isPrivate = conversationType === ImConversationType.PRIVATE
+ const isChannel = conversationType === ImConversationType.CHANNEL
+ const size = isPrivate ? MESSAGE_PRIVATE_PULL_SIZE : MESSAGE_GROUP_PULL_SIZE
+ const isStillValid = () =>
+ !signal.aborted && pullEpoch === startEpoch && getCurrentUserId() === startUserId
+ await runMinIdPull<PulledRawMessage>({
+ initialMinId: startMinId,
+ pageSize: size,
+ isActive: isStillValid,
+ fetchPage: ({ minId, size }) => {
+ if (isPrivate) {
+ return apiPullPrivateMessageList({ minId, size }, signal)
+ }
+ if (isChannel) {
+ return apiPullChannelMessageList({ minId, size }, signal)
+ }
+ return apiPullGroupMessageList({ minId, size }, signal)
+ },
+ applyPage: async (list, nextMinId) => {
+ const pulledMessages: PulledMessage[] = []
+ // 閫愭潯 dispatch锛氬師娑堟伅璧版壒閲� insert锛汻ECALL 淇″彿璧版壒閲� recall 鎶婂悓鎵瑰唴宸� insert 鐨勫師娑堟伅鏇存柊涓烘挙鍥炴彁绀恒��
+ // 鍚庣鎸� id 鍗囧簭杩斿洖锛屼笖淇″彿 id 涓�瀹� > 鍘熸秷鎭� id锛堝厛鏇存柊 status 鍐嶆彃淇″彿锛夛紝鎵�浠ュ師娑堟伅涓�瀹氬厛鍒般�乺ecallMessage 鎵惧緱鍒�
+ for (const raw of list) {
+ if (isChannel) {
+ const message = raw as ImChannelMessageApi.ChannelMessageRespVO
+ pulledMessages.push({
+ kind: 'insert',
+ conversationInfo: convertChannelConversation(message),
+ message: convertChannelMessage(message)
+ })
+ continue
+ }
+ if (isPrivate) {
+ const message = raw as ImPrivateMessageApi.PrivateMessageRespVO
+ // 鐗规畩锛氭挙鍥炴秷鎭殑澶勭悊
+ if (message.type === ImContentType.RECALL) {
+ pulledMessages.push({
+ kind: 'recall',
+ conversationType: ImConversationType.PRIVATE,
+ targetId: getPrivatePeerId(message),
+ recallSignalContent: message.content
+ })
+ continue
+ }
+ // 鐗规畩锛氬巻鍙插ソ鍙嬩簨浠跺彧杩樺師鑱婂ぉ姘旀场锛涘ソ鍙嬩富鏁版嵁鐢卞ソ鍙嬪閲忚ˉ鍋垮悓姝�
+ // 浠� FRIEND_ADD / FRIEND_DELETE 鎵嶄綔涓轰細璇濇皵娉″叆娑堟伅鍒楄〃
+ if (isFriendNotification(message.type) && !isFriendChatTip(message.type)) {
+ continue
+ }
+ // 鍏跺畠娑堟伅姝e父鍏ヤ細璇濇秷鎭垪琛�
+ pulledMessages.push({
+ kind: 'insert',
+ conversationInfo: convertPrivateConversation(message),
+ message: convertPrivateMessage(message)
+ })
+ } else {
+ const message = raw as ImGroupMessageApi.GroupMessageRespVO
+ // 鐗规畩锛氭挙鍥炴秷鎭殑澶勭悊
+ if (message.type === ImContentType.RECALL) {
+ pulledMessages.push({
+ kind: 'recall',
+ conversationType: ImConversationType.GROUP,
+ targetId: message.groupId,
+ recallSignalContent: message.content
+ })
+ continue
+ }
+ pulledMessages.push({
+ kind: 'insert',
+ conversationInfo: convertGroupConversation(message),
+ message: convertGroupMessage(message)
+ })
+ }
+ }
+ // 鍏ュ簱 + 鎺ㄨ繘 messageMaxId锛沶extMinId 涓虹┖锛堟湰鎵规棤鏈夋晥 id锛夋椂涓嶆帹杩涙父鏍囷紝涓庢棫閫昏緫涓�鑷�
+ await messageStore.applyPulledMessageList(pulledMessages, conversationType, nextMinId)
+ }
+ })
+ }
+
+ /** 鍚屼竴鏃跺埢鍙厑璁镐竴娆� pull锛歩ndex.vue 鐨勬墜鍔ㄨ皟鐢ㄤ笌閲嶈繛 watch 瑙﹀彂鍙兘骞跺彂锛屽叡鐢ㄥ悓涓�涓� promise 鍗冲彲鍘婚噸 */
+ let pullPromise: null | Promise<void> = null
+ let pullAbortController: AbortController | null = null
+
+ /**
+ * 棣栨 pull 鏄惁宸插畬鎴愩�備粎鍦ㄧ疆 true 鍚庯紝isConnected watch 鎵嶄細瑙﹀彂 pull銆�
+ * 闃叉 socket onopen 姣� friendStore/groupStore 棰勬媺鍏堝埌杈炬椂锛寃atcher 鎶㈣窇閫犳垚娑堟伅鎻掑叆鏃╀簬浼氳瘽鍏冩暟鎹彲瑙�
+ */
+ let initialPulled = false
+
+ /**
+ * pull 杞璁℃暟锛涘垏璐﹀彿 / 绂诲紑 IM 鏃� cancelPull() 閫掑锛屾棫 pullByType 寰幆鎸� epoch 鑷鍚庤烦鍑�
+ * 閬垮厤鏃� session 鐨勬帴鍙e搷搴斿湪鏂� session 钀藉湴锛岄�犳垚璺ㄨ处鍙锋秷鎭硠婕�
+ *
+ * 娉ㄦ剰锛氭櫘閫氭柇杩烇紙WS 鐭柇锛変笉鍙栨秷 pull鈥斺�旂綉缁滄姈鍔� / 鏈嶅姟绔噸鍚兘灞炰簬鏈处鍙锋甯哥敓鍛藉懆鏈燂紝
+ * 鍙栨秷浼氬鑷撮鎷夎涓柇鍚� initialPulled 姘歌繙鍋滃湪 false锛屽悗缁噸杩� watcher 涓嶅啀琛ユ媺
+ */
+ let pullEpoch = 0
+
+ /** 鏄惧紡鍙栨秷锛氫粎鐢� index.vue onUnmounted锛堢寮� IM / 鍒囪处鍙� / 璺敱璺冲嚭锛夎皟鐢� */
+ const cancelPull = () => {
+ pullEpoch++
+ pullAbortController?.abort()
+ pullAbortController = null
+ // 鏃� promise 浠嶅湪 finally 闃舵璺戯紝浣� epoch 瀹堝崼宸查樆鏂悗缁壇浣滅敤锛涜繖閲岀珛鍒昏 pullPromise = null 璁╂柊涓�杞彲閲嶅叆
+ pullPromise = null
+ // 鍚屾涓㈠純 WS 缂撳啿甯э紱鏃� pull 宸蹭笉浼� flushBuffer锛岃嫢涓嶆竻涓嬫杩� IM 绗竴娆� pullOnce 浼氭妸鏃� session 鐨勫抚鍥炴斁杩涙柊 store
+ wsStore.discardBuffer()
+ }
+
+ /**
+ * 鐘舵�佷簨浠惰ˉ鍋匡細濂藉弸 / 濂藉弸鐢宠璧板閲忥紱缇ゅ垪琛ㄥ拰缇ょ敵璇风孩鐐硅蛋蹇収鍒锋柊
+ *
+ * 棣栫櫥涓绘暟鎹敱 index.vue 椹卞姩锛岄噸杩炴椂鍚� store 宸插氨浣嶏紝澶氳矾 allSettled 骞跺彂浜掍笉褰卞搷锛屽崟璺け璐ヤ粎璁版棩蹇椼��
+ * 缇ゆ垚鍛樹笉鍋氬叏灞�澧為噺鍚屾锛岄噸杩炲彧鏍囪鏈湴缇ゆ垚鍛� cache 杩囨湡锛岃繘鍏ョ兢浼氳瘽鎴栨垚鍛樺垪琛ㄦ椂鍐嶆寜 groupId 鍒锋柊銆�
+ */
+ const pullStateEvents = async (): Promise<void> => {
+ // 1. 娓呯悊杩炴帴绾х紦瀛�
+ messageStore.clearPrivateReadMaxIdCache()
+ rtcStore.clearGroupCallCache()
+ groupStore.markAllGroupActiveCallsExpired()
+ groupStore.markAllGroupInfoExpired()
+ groupStore.markAllGroupMembersExpired()
+ // 2. 骞跺彂琛ュ伩杩滅鐘舵��
+ const results = await Promise.allSettled([
+ friendStore.pullFriends(),
+ friendStore.pullFriendRequests(),
+ conversationStore.pullConversationReads(),
+ groupStore.fetchGroupList(true),
+ groupRequestStore.pullGroupRequests(),
+ groupRequestStore.fetchUnhandledGroupRequestList()
+ ])
+ for (const result of results) {
+ if (result.status === 'rejected') {
+ console.warn('[IM] 鐘舵�佷簨浠跺閲忚ˉ鍋垮け璐�', result.reason)
+ }
+ }
+ }
+
+ /** 鎵ц涓�娆″叏閲忓閲忔媺鍙栵紙閲嶅叆瀹夊叏锛氳繘琛屼腑鍐嶆璋冪敤澶嶇敤鍚屼竴涓� promise锛� */
+ const pullOnce = (): Promise<void> => {
+ if (!currentUserId) {
+ return Promise.resolve()
+ }
+ if (pullPromise) {
+ return pullPromise
+ }
+ const startEpoch = pullEpoch
+ // 鍚姩鏃剁殑鐢ㄦ埛蹇収锛沺ullByType 姣忔壒 await 鍚庢瘮瀵瑰綋鍓嶇櫥褰曠敤鎴凤紝璐﹀彿鍙樹簡绔嬪埢涓㈠純
+ const startUserId = currentUserId
+ const abortController = new AbortController()
+ pullAbortController = abortController
+ // 鏈疆 pull 浠嶅睘浜庡綋鍓� session锛歟poch 鏈紓 + 鐢ㄦ埛鏈垏锛涗换浣曞姩鏂� store 鐘舵�佺殑鍓綔鐢ㄩ兘瑕佸厛杩囪繖閬撳叧
+ const isCurrentPull = () =>
+ !abortController.signal.aborted &&
+ pullEpoch === startEpoch &&
+ getCurrentUserId() === startUserId
+ pullPromise = (async () => {
+ try {
+ // 鏃� puller 鍦� cancelPull 鏈Е鍙戠殑寮傚父璺緞涓婂啀杩涙潵鏃讹紝鍏堜簬浠讳綍鍓綔鐢ㄩ��鍑猴紝閬垮厤姹℃煋鏂� session 鐨� loading
+ if (!isCurrentPull()) {
+ return
+ }
+ conversationStore.loading = true
+ let messagePullSucceeded = false
+ try {
+ // 骞跺彂鎷夊彇绉佽亰 + 缇よ亰 + 棰戦亾娑堟伅锛岄檷浣庡垵濮嬪姞杞借�楁椂
+ await Promise.all([
+ pullByType(
+ ImConversationType.PRIVATE,
+ messageStore.privateMessageMaxId,
+ startEpoch,
+ startUserId,
+ abortController.signal
+ ),
+ pullByType(
+ ImConversationType.GROUP,
+ messageStore.groupMessageMaxId,
+ startEpoch,
+ startUserId,
+ abortController.signal
+ ),
+ pullByType(
+ ImConversationType.CHANNEL,
+ messageStore.channelMessageMaxId,
+ startEpoch,
+ startUserId,
+ abortController.signal
+ )
+ ])
+ messagePullSucceeded = true
+ } catch (error) {
+ if (isAbortError(error)) {
+ return
+ }
+ console.error('[IM] 鎷夊彇绂荤嚎娑堟伅澶辫触:', error)
+ } finally {
+ // 浠嶅睘鏈疆鎵嶅浣� loading锛涙棫杞 cancel / 鍒囪处鍙锋椂鐢辨柊涓�杞嚜绠★紝閬垮厤瑕嗙洊鏂� session 鐨� true
+ if (isCurrentPull()) {
+ conversationStore.loading = false
+ }
+ }
+
+ // 鍙栨秷 / 鍒囪处鍙峰悗璺宠繃 flushBuffer / 鎺掑簭 / 宸茶浣嶇疆琛ラ綈
+ if (!isCurrentPull()) {
+ return
+ }
+ if (!messagePullSucceeded) {
+ return
+ }
+
+ // 鍥炴斁 WebSocket 鍦� loading 鏈熼棿鏀跺埌鐨勭紦鍐叉秷鎭�
+ const buffered = wsStore.flushBuffer()
+ const replayPersistPromises: Promise<void>[] = []
+ for (const item of buffered) {
+ if (item.conversationType === ImConversationType.PRIVATE) {
+ replayPersistPromises.push(wsStore.handlePrivateMessage(item.payload))
+ } else if (item.conversationType === ImConversationType.CHANNEL) {
+ replayPersistPromises.push(wsStore.handleChannelMessage(item.payload))
+ } else {
+ replayPersistPromises.push(wsStore.handleGroupMessage(item.payload))
+ }
+ }
+ await Promise.all(replayPersistPromises)
+
+ // pull + replay 閮藉畬鎴愬悗鍐嶆帓搴忥紝閬垮厤鍥炴斁娑堟伅鎵撲贡椤哄簭
+ conversationStore.sortConversationList()
+
+ // 閲嶈繛 / 鍐峰惎鍔ㄥ悗琛ラ綈褰撳墠婵�娲荤鑱婁細璇濈殑銆屽鏂瑰凡璇讳綅缃��
+ // 绂荤嚎鏈熼棿閿欒繃鐨� RECEIPT 鎺ㄩ�佷細琚繖閲岃ˉ鍥烇紱鍏朵粬绉佽亰浼氳瘽绛夌敤鎴风偣寮�鏃剁敱 index.vue 鐨� watch 瑙﹀彂
+ // 绉佽亰宸茶鍏抽棴鏃惰烦杩囷紝閬垮厤鎵撳埌宸茬鐢ㄦ帴鍙hЕ鍙戦敊璇棩蹇�
+ const active = conversationStore.activeConversation
+ if (MESSAGE_PRIVATE_READ_ENABLED && active && active.type === ImConversationType.PRIVATE) {
+ try {
+ const maxReadId = await apiGetPrivateMaxReadMessageId(
+ active.targetId,
+ abortController.signal
+ )
+ if (!isCurrentPull()) {
+ return
+ }
+ messageStore.updatePrivateReadMaxId(active.targetId, maxReadId)
+ if (maxReadId) {
+ messageStore.applyMessageReadReceipt({
+ conversationType: ImConversationType.PRIVATE,
+ targetId: active.targetId,
+ privateReadMaxId: maxReadId
+ })
+ }
+ } catch (error) {
+ if (isAbortError(error)) {
+ return
+ }
+ console.warn('[IM] 鎷夊彇瀵规柟宸茶浣嶇疆澶辫触', error)
+ }
+ }
+ } finally {
+ // 浠嶅睘鏈疆锛氭甯稿畬鎴愰鎷夛紱epoch 绛変絾 userId 鍒囦簡锛氭竻 pullPromise 闃插崱姝汇�佷笉鏍囬鎷夛紱epoch 婕傦細cancelPull 宸叉竻锛宯o-op
+ if (isCurrentPull()) {
+ pullPromise = null
+ initialPulled = true
+ if (pullAbortController === abortController) {
+ pullAbortController = null
+ }
+ } else if (pullEpoch === startEpoch) {
+ pullPromise = null
+ if (pullAbortController === abortController) {
+ pullAbortController = null
+ }
+ }
+ }
+ })()
+ return pullPromise
+ }
+
+ /**
+ * 鏂綉鏈熼棿 WS 鏀朵笉鍒版帹閫侊細閲嶈繛鍚庢棦瑕佹寜 minId 琛ラ綈娑堟伅锛屼篃瑕佹寜 update_time + id 琛ラ綈濂藉弸 / 缇� / 缇ょ敵璇风姸鎬併��
+ * 棣栨杩炴帴鐢� index.vue 鏄惧紡椹卞姩锛坧ullOnce 鎷夋秷鎭� + 鍚� store 棣栨媺锛夛紝杩欓噷浠呰鐩栦箣鍚庣殑閲嶈繛銆�
+ * 閲嶈繛鏃� store 宸插氨浣嶏紝pullStateEvents 涓� pullOnce 骞跺彂鍗冲彲锛屾棤闇�銆屽厛灏变綅鍐嶆媺娑堟伅銆嶇殑棣栫櫥椤哄簭绾︽潫銆�
+ */
+ watch(
+ () => wsStore.isConnected,
+ (isConnected) => {
+ if (isConnected && initialPulled) {
+ void pullOnce()
+ void pullStateEvents()
+ }
+ }
+ )
+
+ return { pullOnce, cancelPull, convertPrivateMessage, convertGroupMessage }
+}
--
Gitblit v1.9.3