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/store/groupStore.ts | 1017 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1,017 insertions(+), 0 deletions(-)
diff --git a/src/views/im/home/store/groupStore.ts b/src/views/im/home/store/groupStore.ts
new file mode 100644
index 0000000..c9e2786
--- /dev/null
+++ b/src/views/im/home/store/groupStore.ts
@@ -0,0 +1,1017 @@
+import type { Group, GroupDO, GroupMember, Message } from '../types'
+
+import type { ImGroupApi } from '#/api/im/group'
+import type { ImGroupMemberApi } from '#/api/im/group/member'
+
+import { CommonStatusEnum } from '#/packages/constants/src'
+
+import { acceptHMRUpdate, defineStore } from 'pinia'
+
+import { getGroup as apiGetGroup, getMyGroupList as apiGetMyGroupList } from '#/api/im/group'
+import { getGroupMember as apiGetGroupMember, getGroupMemberList as apiGetGroupMemberList, updateGroupMember as apiUpdateGroupMember } from '#/api/im/group/member'
+import { getCurrentUserId } from '#/views/im/utils/auth'
+
+import {
+ ImContentType,
+ ImConversationType,
+ ImGroupMemberRole,
+ ImMessageStatus
+} from '../../utils/constants'
+import { getDb } from '../../utils/db'
+import { type GroupNotificationPayload } from '../../utils/message'
+import { getGroupDisplayName } from '../../utils/user'
+import { useConversationStore } from './conversationStore'
+import { useGroupRequestStore } from './groupRequestStore'
+
+/** clear() 鏃堕�掑锛涙棫璐﹀彿 in-flight 鐨勬垚鍛樿姹傝繑鍥炲悗姣斿涓�鑷存墠鍐� store */
+let storeEpoch = 0
+
+/**
+ * fetchGroupMemberList 骞跺彂鍘婚噸琛細鍚� groupId 鍚屾椂杩涚殑璇锋眰鍏辩敤涓�涓� Promise
+ *
+ * key 蹇呴』甯� userId鈥斺�旇处鍙峰垏鎹㈡椂 A 鐨勮姹備笉鑳借 B 澶嶇敤锛屽惁鍒� IIFE 鍐呴儴鐨� saveGroupMemberList 浼氭妸 A 鐨勬垚鍛樻暟鎹啓杩� B 鐨� IDB 妗�
+ */
+const pendingMemberFetches = new Map<string, Promise<GroupMember[]>>()
+const pendingMemberKey = (userId: number, groupId: number) => `${userId}:${groupId}`
+
+/**
+ * fetchGroupMember 鍗曟垚鍛樺苟鍙戝幓閲嶈〃锛氬悓 (groupId, memberUserId) 鍚屾椂杩涚殑璇锋眰鍏辩敤涓�涓� Promise
+ *
+ * 璺熸暣缇よ〃鍒嗗紑锛氬崟鎴愬憳 fetch 璺熸暣缇� fetch 璇箟涓嶅悓锛堝崟鎴愬憳涓嶅洖濉� me 鐨� silent锛夛紝涓嶈兘浜掔浉浠f浛
+ */
+const pendingSingleMemberFetches = new Map<string, Promise<GroupMember | null>>()
+
+const pendingSingleMemberKey = (userId: number, groupId: number, memberUserId: number) =>
+ `${userId}:${groupId}:${memberUserId}`
+
+/** 鏋勫缓缇� IndexedDB 璁板綍 */
+function buildGroupDO(group: Group): GroupDO {
+ const {
+ activeCallExpired: _activeCallExpired,
+ activeCallLoaded: _activeCallLoaded,
+ infoLoaded: _infoLoaded,
+ members: _members,
+ membersLoaded: _membersLoaded,
+ membersExpired: _membersExpired,
+ ...record
+ } = group
+ return record
+}
+
+/** 鍒ゆ柇褰撳墠鐢ㄦ埛鏄惁鍦� payload.memberUserIds 閲岋紙GROUP_CREATE / INVITE / KICK 鑷垽鐢級 */
+function isSelfInPayloadMembers(payload: GroupNotificationPayload): boolean {
+ const selfUserId = getCurrentUserId()
+ return !!selfUserId && (payload.memberUserIds || []).includes(selfUserId)
+}
+
+/** 鍒锋柊鎴戠鐞嗙殑缇ょ敵璇风孩鐐� */
+function refreshUnhandledGroupRequests(): void {
+ useGroupRequestStore()
+ .fetchUnhandledGroupRequestList()
+ .catch(() => undefined)
+}
+
+/**
+ * IM 缇� Store
+ *
+ * 璐熻矗锛�
+ * - 鎷夊彇 / 缂撳瓨褰撳墠鐧诲綍鐢ㄦ埛鍔犲叆鐨勭兢鍒楄〃
+ * - 鎸� groupId 鎳掑姞杞界兢鎴愬憳锛堜緵 ConversationGroupSide / MentionPicker / MessageReadStatus 娑堣垂锛�
+ * - 鎴愬憳"宸茶 / 鏈"绛夎仛鍚堟煡璇㈢敱 MessageReadStatus 鍙﹁缁勫悎
+ */
+export const useGroupStore = defineStore('imGroupStore', {
+ state: () => ({
+ groups: [] as Group[],
+ loaded: false, // 浠� fetchGroupList 鎴愬姛鍚庣疆浣嶏紱loadGroupList锛圛DB锛変笉缃綅锛屽惁鍒欏悗鍙� SWR 鍒锋柊浼氳缂撳瓨鍛戒腑璺宠繃
+ groupMembersExpired: false // 杩涘叆 IM / 閲嶈繛鍚庣疆浣嶏紱IDB 閲岀殑鎴愬憳妗跺欢杩熷姞杞藉埌鍐呭瓨鏃讹紝涔熻鎸夎繃鏈熷鐞�
+ }),
+
+ getters: {
+ getGroup:
+ (state) =>
+ (id: number): Group | undefined => {
+ return state.groups.find((g) => g.id === id)
+ },
+ /** 缇ゆ垚鍛� userId 鈫� GroupMember 绱㈠紩锛涜皟鐢ㄦ柟鎸� userId 鍙嶆煡鏄电О / 澶村儚绛夊厓淇℃伅 */
+ getGroupMemberMap:
+ (state) =>
+ (id: number): Map<number, GroupMember> => {
+ const group = state.groups.find((g) => g.id === id)
+ return new Map((group?.members || []).map((m) => [m.userId, m]))
+ }
+ },
+
+ actions: {
+ // ==================== 鏈湴缂撳瓨 ====================
+
+ /** 浠� IndexedDB 鎭㈠缇ゅ垪琛� */
+ async loadGroupList(): Promise<boolean> {
+ try {
+ const cached = await getDb().getAll<GroupDO>('groups')
+ if (!cached || cached.length === 0) {
+ return false
+ }
+ this.groups = cached
+ return true
+ } catch (error) {
+ console.warn('[IM groupStore] 鏈湴缇ょ紦瀛樿鍙栧け璐�', error)
+ return false
+ }
+ },
+
+ /** 淇濆瓨缇ゅ垪琛� */
+ saveGroupList(): void {
+ void getDb()
+ .transaction(['groups'], 'readwrite', async (tx) => {
+ const db = getDb()
+ await db.clearStore('groups', tx)
+ for (const group of this.groups) {
+ await db.put('groups', buildGroupDO(group), tx)
+ }
+ })
+ .catch((error) => console.warn('[IM groupStore] 鏈湴缇ょ紦瀛樺啓鍏ュけ璐�', error))
+ },
+
+ /** 淇濆瓨鍗曚釜缇� */
+ async saveGroupRecord(group: Group | undefined): Promise<void> {
+ if (!group) {
+ return
+ }
+ await getDb().put('groups', buildGroupDO(group))
+ },
+
+ /** 淇濆瓨鍗曚釜缇� */
+ saveGroup(group: Group | undefined): void {
+ void this.saveGroupRecord(group).catch((error) =>
+ console.warn('[IM groupStore] 鏈湴缇ゅ啓鍏ュけ璐�', error)
+ )
+ },
+
+ /** 浠� IndexedDB 鎭㈠鎸囧畾缇ゆ垚鍛� */
+ async loadGroupMemberList(groupId: number): Promise<GroupMember[] | null> {
+ // in-memory 宸�"瀹屾暣"鍔犺浇锛坒etchGroupMemberList 璺戣繃鎴栦笂娆″喎鍚姩浠� IDB 鏁存《鎭㈠杩囷級锛氱洿鎺ュ鐢紱
+ // 鍗曟垚鍛樿ˉ榻愶紙fetchGroupMember锛夊啓杩涚殑 partial members 涓嶅湪姝よ繑鍥炵紦瀛樷�斺�斿叾 membersLoaded=false
+ const cachedGroup = this.getGroup(groupId)
+ if (cachedGroup?.members && cachedGroup.membersLoaded) {
+ return cachedGroup.members
+ }
+ try {
+ const cached = await getDb().getAllByIndex<GroupMember>(
+ 'groupMembers',
+ 'groupId',
+ groupId
+ )
+ if (!cached || cached.length === 0) {
+ return null
+ }
+ // 鎶� IDB 鎷垮埌鐨勬垚鍛樿惤鍒板搴� group
+ const group = this.getGroup(groupId)
+ if (group) {
+ group.members = cached
+ group.memberCount = cached.length
+ group.membersLoaded = true
+ group.membersExpired = this.groupMembersExpired
+ } else {
+ // group 杩樻病灏变綅锛氫粎 in-memory 鍗犱綅锛坣ame='' 琛ㄧず鏈煡锛夛紝涓嶈皟 upsertGroup 鈥斺�� 閬垮厤鎶婂亣鍚嶇亴杩� conversation.name + groups IDB 妗讹紱
+ // 鍚庣画锛岀瓑 fetchGroupList 娴呭悎骞舵椂锛岃鐪熷悕瑕嗙洊
+ this.groups.push({
+ id: groupId,
+ name: '',
+ members: cached,
+ memberCount: cached.length,
+ membersLoaded: true,
+ membersExpired: this.groupMembersExpired
+ })
+ }
+ return cached
+ } catch (error) {
+ console.warn('[IM groupStore] 鏈湴缇ゆ垚鍛樼紦瀛樿鍙栧け璐�', { groupId }, error)
+ return null
+ }
+ },
+
+ /** 淇濆瓨鎸囧畾缇ゆ垚鍛� */
+ saveGroupMemberList(groupId: number): void {
+ const members = this.getGroup(groupId)?.members
+ if (!members) {
+ return
+ }
+ void getDb()
+ .transaction(['groupMembers'], 'readwrite', async (tx) => {
+ const db = getDb()
+ await db.deleteByIndex('groupMembers', 'groupId', groupId, tx)
+ for (const member of members) {
+ if (member.id) {
+ await db.put('groupMembers', member, tx)
+ }
+ }
+ })
+ .catch((error) =>
+ console.warn(`[IM groupStore] 鏈湴缇ゆ垚鍛樼紦瀛樺啓鍏ュけ璐� (groupId=${groupId})`, error)
+ )
+ },
+
+ // ==================== 杩滅鎷夊彇 ====================
+
+ /** 鎷夊彇缇ゅ垪琛紱鍚屾鍒锋柊瀵瑰簲缇よ亰浼氳瘽鐨勫睍绀哄悕 / 澶村儚 + 钀� IDB */
+ async fetchGroupList(force = false) {
+ if (this.loaded && !force) {
+ return
+ }
+ const requestEpoch = storeEpoch
+ const requestUserId = getCurrentUserId()
+ // 鎷夊彇褰撳墠鐧诲綍鐢ㄦ埛鍔犲叆鐨勬墍鏈夌兢锛堜笉甯︽垚鍛橈紱鎴愬憳鎸夐渶鍐嶈蛋 fetchGroupMemberList锛�
+ const list = await apiGetMyGroupList()
+ if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
+ return
+ }
+ const fresh = (list || []).map((group) => convertGroup(group))
+ // 鍚堝苟鑰岄潪鍏ㄩ噺鏇挎崲锛氭垚鍛樼紦瀛樺彧鍦ㄦ垚鍛樺垪琛ㄦ帴鍙g淮鎶わ紝缇や釜浜鸿缃互缇ゅ垪琛ㄦ帴鍙d负鍑�
+ const groupMap = new Map(this.groups.map((group) => [group.id, group]))
+ this.groups = fresh.map((group) => {
+ const existing = groupMap.get(group.id)
+ if (!existing) {
+ return { ...group, activeCallExpired: true, infoLoaded: true }
+ }
+ return {
+ ...group,
+ infoLoaded: true,
+ activeCallExpired: existing.activeCallExpired,
+ activeCallLoaded: existing.activeCallLoaded,
+ members: existing.members,
+ memberCount: existing.memberCount ?? group.memberCount,
+ membersLoaded: existing.membersLoaded,
+ membersExpired: existing.membersExpired
+ }
+ })
+ this.loaded = true
+ const conversationStore = useConversationStore()
+ for (const group of this.groups) {
+ conversationStore.updateConversation(ImConversationType.GROUP, group.id, {
+ name: getGroupDisplayName(group),
+ avatar: group.avatar,
+ silent: group.silent
+ })
+ }
+ this.saveGroupList()
+ this.preloadMembersForEmptyAvatarGroups()
+ },
+
+ /** 澶辨晥鍏ㄩ儴缇よ鎯呯紦瀛� */
+ markAllGroupInfoExpired() {
+ for (const group of this.groups) {
+ group.infoLoaded = false
+ }
+ },
+
+ /** 棰勫姞杞界┖缇ゅご鍍忕殑鎴愬憳鍒楄〃锛屼緵 GroupAvatar 寮傛鍚堟垚缇ゅご鍍� */
+ preloadMembersForEmptyAvatarGroups() {
+ for (const group of this.groups) {
+ if (
+ group.avatar ||
+ group.joinStatus === CommonStatusEnum.DISABLE ||
+ (group.membersLoaded && !group.membersExpired && group.members?.length)
+ ) {
+ continue
+ }
+ const force = !!group.membersLoaded && !group.membersExpired && !group.members?.length
+ this.fetchGroupMemberList(group.id, force).catch((error) => {
+ console.warn('[IM groupStore] 棰勫姞杞界兢澶村儚鎴愬憳澶辫触', { groupId: group.id }, error)
+ })
+ }
+ },
+
+ /** 澶辨晥鍏ㄩ儴缇ゆ垚鍛樼紦瀛� */
+ markAllGroupMembersExpired() {
+ this.groupMembersExpired = true
+ for (const group of this.groups) {
+ if (group.membersLoaded) {
+ group.membersExpired = true
+ }
+ }
+ },
+
+ /** 澶辨晥鍏ㄩ儴缇ら�氳瘽鎺㈡祴缂撳瓨 */
+ markAllGroupActiveCallsExpired() {
+ for (const group of this.groups) {
+ group.activeCallExpired = true
+ }
+ },
+
+ /** 鏍囪缇ら�氳瘽鎺㈡祴宸插姞杞� */
+ markGroupActiveCallLoaded(groupId: number) {
+ const group = this.getGroup(groupId)
+ if (!group) {
+ return
+ }
+ group.activeCallLoaded = true
+ group.activeCallExpired = false
+ },
+
+ /** 鍒ゆ柇缇ら�氳瘽鏄惁闇�瑕侀噸鏂版帰娴� */
+ isGroupActiveCallExpired(groupId: number): boolean {
+ const group = this.getGroup(groupId)
+ return !group?.activeCallLoaded || !!group.activeCallExpired
+ },
+
+ /** 澶辨晥鎸囧畾缇ゆ垚鍛樼紦瀛� */
+ markGroupMembersExpired(groupId: number) {
+ const group = this.getGroup(groupId)
+ if (group?.membersLoaded) {
+ group.membersExpired = true
+ }
+ },
+
+ /** 鍗曠兢鍒锋柊锛氱敤 /im/group/get 鎷変竴浠芥渶鏂板厓鏁版嵁鍐� upsert锛屽父鐢ㄤ簬 GROUP_UPDATE 鎺ㄩ�佸悗鎴栨墜鍔� reload */
+ async fetchGroupInfo(groupId: number, force = false) {
+ const cached = this.getGroup(groupId)
+ if (cached?.infoLoaded && !force) {
+ return
+ }
+ try {
+ const data = await apiGetGroup(groupId)
+ if (!data) {
+ return
+ }
+ this.upsertGroup({ ...convertGroup(data), infoLoaded: true })
+ } catch (error) {
+ console.warn('[IM groupStore] fetchGroupInfo 澶辫触', error)
+ }
+ },
+
+ /** 鎸夌兢鎷夊彇鎴愬憳锛坕n-memory 缂撳瓨 + 骞跺彂鍘婚噸锛宖orce=true 寮哄埛锛�+ 钀� IDB */
+ fetchGroupMemberList(groupId: number, force = false): Promise<GroupMember[]> {
+ // in-memory "瀹屾暣"鍔犺浇杩囨墠鍛戒腑鈥斺�斿崟鎴愬憳琛ラ綈鍐欏叆鐨� partial members 涓嶅湪姝よ繑鍥烇紙membersLoaded=false锛�
+ const cached = this.getGroup(groupId)
+ if (cached && cached.members && cached.membersLoaded && !cached.membersExpired && !force) {
+ return Promise.resolve(cached.members)
+ }
+ // 鏈櫥褰曪細涓嶅彂璧疯姹備篃涓嶇櫥璁� in-flight锛岄伩鍏嶆薄鏌撳崟椋炶〃
+ const requestUserId = getCurrentUserId()
+ if (!requestUserId) {
+ return Promise.resolve([])
+ }
+ const requestEpoch = storeEpoch
+ // 鍚� (userId, groupId) 宸茬粡鏈夋鍦ㄩ鐨勮姹傦細鐩存帴澶嶇敤锛岄伩鍏嶉噸澶嶆墦鎺ュ彛
+ const key = pendingMemberKey(requestUserId, groupId)
+ const inflight = pendingMemberFetches.get(key)
+ if (inflight) {
+ return inflight
+ }
+ const promise = (async () => {
+ // 鎷夋帴鍙� + 鍗� pass 杞崲锛氬悓鏃舵崟鑾� me 鐨勫師濮� VO锛岀粰涓嬮潰鍥炲~ user-per-group 瀛楁锛坰ilent / groupRemark锛夌敤
+ const list = await apiGetGroupMemberList(groupId)
+ if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
+ return []
+ }
+ let meRaw: ImGroupMemberApi.GroupMemberRespVO | undefined
+ const members = (list || []).map((member) => {
+ if (member.userId === requestUserId) {
+ meRaw = member
+ }
+ return convertGroupMember(member, groupId)
+ })
+ const silent = !!meRaw?.silent
+ const groupRemark = meRaw?.groupRemark || ''
+
+ // 蹇呴』 await 涔嬪悗閲嶆柊 getGroup锛岄伩鍏� fetchGroupList 宸插苟鍙戝啓鍏ョ湡瀹� group 鐨� race
+ const group = this.getGroup(groupId)
+ const isPlaceholder = !group
+ let groupFieldsChanged = false
+ if (group) {
+ group.members = members
+ group.memberCount = members.length
+ group.membersLoaded = true
+ group.membersExpired = false
+ // silent / groupRemark 浠讳竴鍙樺寲鎵嶅悓姝ュ埌 conversation 鍜� IDB锛沢roupRemark 鍙樺寲瑕侀『甯﹀埛浼氳瘽鍚�
+ if (group.silent !== silent || group.groupRemark !== groupRemark) {
+ group.silent = silent
+ group.groupRemark = groupRemark
+ groupFieldsChanged = true
+ const conversationStore = useConversationStore()
+ conversationStore.updateConversation(ImConversationType.GROUP, groupId, {
+ name: getGroupDisplayName(group),
+ silent
+ })
+ }
+ } else {
+ // group 杩樻病灏变綅锛氫粎 in-memory push 鍗犱綅锛坣ame='' 琛ㄧず鏈煡锛夛紝涓嶈皟 upsertGroup鈥斺�旈伩鍏嶆妸鍋囧悕鐏岃繘 conversation.name + groups IDB 妗讹紱
+ // 鍚庣画锛岀瓑 fetchGroupList 娴呭悎骞舵椂锛岃鐪熷悕瑕嗙洊
+ this.groups.push({
+ id: groupId,
+ name: '',
+ members,
+ memberCount: members.length,
+ silent,
+ groupRemark,
+ membersLoaded: true,
+ membersExpired: false
+ })
+ }
+
+ // groups 妗朵粎鍦� user-per-group 瀛楁瀹為檯鍙樺寲鏃跺啓鈥斺�旈伩鍏嶄竴娆℃壒閲忚繘缇ゅ紩鍙戝娆℃暣妗堕噸鍐�
+ this.saveGroupMemberList(groupId)
+ if (!isPlaceholder && groupFieldsChanged) {
+ this.saveGroup(group)
+ }
+ return members
+ // 鏃犺鎴愬姛 / 澶辫触閮借浠庡崟椋炶〃娓呮帀锛屽惁鍒欏悗缁悓 group 璇锋眰姘歌繙鎷垮埌杩欎釜 stale Promise
+ })().finally(() => pendingMemberFetches.delete(key))
+
+ // 鎶� Promise 鐧昏杩涘崟椋炶〃锛岃姝ゅ悗鐭椂闂村唴鐨勫悓 (userId, groupId) 璇锋眰澶嶇敤
+ pendingMemberFetches.set(key, promise)
+ return promise
+ },
+
+ /**
+ * 鎸� (groupId, memberUserId) 鍗曟垚鍛樿ˉ榻愨�斺�攄eriveLastSenderDisplayName 鍏滃簳鍦烘櫙鐢�
+ *
+ * 璺� fetchGroupMemberList 鍖哄埆锛氬彧鎷夎繖涓�涓垚鍛橈紝涓嶅姩 me 鐨� silent / groupRemark锛堜笉鏄� me 鐨勮瘽鎷夸笉鍒帮級锛�
+ * 鍛戒腑鏃舵妸鎴愬憳 upsert 杩� group.members 鏁扮粍骞惰惤 IDB锛岃鍚庣画娓叉煋鑳界敤 displayUserName
+ */
+ fetchGroupMember(groupId: number, memberUserId: number): Promise<GroupMember | null> {
+ // in-memory 鍛戒腑鐩存帴杩斿洖锛屼笉鎵撴帴鍙�
+ const cached = this.getGroup(groupId)?.members?.find((m) => m.userId === memberUserId)
+ if (cached) {
+ return Promise.resolve(cached)
+ }
+ // 鏈櫥褰曪細涓嶅彂璧疯姹備篃涓嶇櫥璁� in-flight锛岄伩鍏嶆薄鏌撳崟椋炶〃
+ const requestUserId = getCurrentUserId()
+ if (!requestUserId) {
+ return Promise.resolve(null)
+ }
+ const requestEpoch = storeEpoch
+ // 鍚� (userId, groupId, memberUserId) 宸茬粡鏈夋鍦ㄩ鐨勮姹傦細鐩存帴澶嶇敤
+ const key = pendingSingleMemberKey(requestUserId, groupId, memberUserId)
+ const inflight = pendingSingleMemberFetches.get(key)
+ if (inflight) {
+ return inflight
+ }
+ const promise = (async () => {
+ const data = await apiGetGroupMember(groupId, memberUserId)
+ if (!data) {
+ return null
+ }
+ if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) {
+ return null
+ }
+ const member = convertGroupMember(data, groupId)
+ // 鎶婅繖涓�鏉� upsert 杩� group.members 浠呬緵 in-memory 娓叉煋鍏滃簳锛沢roup 杩樻病灏变綅鍒欑敤 placeholder
+ // 娉ㄦ剰锛氫笉鍐� IDB鈥斺�旀垚鍛樻《璇箟鏄�"鍏ㄩ噺"锛屽瓨"1 浜烘《"浼氭薄鏌撲笅娆″喎鍚姩鐨� loadGroupMemberList
+ const group = this.getGroup(groupId)
+ if (group) {
+ const memberList = group.members ?? []
+ const index = memberList.findIndex((m) => m.userId === memberUserId)
+ if (index === -1) {
+ memberList.push(member)
+ } else {
+ memberList[index] = member
+ }
+ group.members = memberList
+ } else {
+ // memberCount 涓嶈锛氬悗缁� fetchGroupList 鍚堝苟 `existing.memberCount ?? fresh.memberCount` 鏃讹紝
+ // 鍗犱綅鍊间細椤舵浛鐪熷疄鍊硷紙fresh 涓嶅甫 memberCount锛夛紝绛� fetchGroupMemberList 璺戣繃鎵嶈兘鎷垮埌鐪熷疄鏁�
+ this.groups.push({
+ id: groupId,
+ name: '',
+ members: [member]
+ })
+ }
+ return member
+ })().finally(() => pendingSingleMemberFetches.delete(key))
+ pendingSingleMemberFetches.set(key, promise)
+ return promise
+ },
+
+ /** 鎸� id 鎻掑叆鎴栧悎骞剁兢锛堝懡涓垯娴呭悎骞朵繚鐣欐棫瀛楁锛屾湭鍛戒腑鍒欒拷鍔狅級锛屽悓姝ユ妸灞曠ず鍚� / 澶村儚 / 鍏嶆墦鎵版帹鍒板搴斾細璇� */
+ upsertGroup(group: Group) {
+ void this.upsertGroupAndSave(group).catch((error) =>
+ console.warn('[IM groupStore] 鏈湴缇ゅ啓鍏ュけ璐�', error)
+ )
+ },
+
+ /** 鎸� id 鎻掑叆鎴栧悎骞剁兢 */
+ async upsertGroupAndSave(group: Group): Promise<void> {
+ const index = this.groups.findIndex((g) => g.id === group.id)
+ if (index === -1) {
+ this.groups.push(group)
+ } else {
+ this.groups[index] = { ...this.groups[index], ...group }
+ }
+ // 鍚屾鎺ㄥ埌 conversation锛氱兢鍚� / 澶村儚 / 鍏嶆墦鎵版槸浼氳瘽鍒楄〃灞曠ず鐢ㄧ殑锛屽繀椤荤揣闅� group 鍙樻洿
+ const merged = this.getGroup(group.id) ?? group
+ const conversationStore = useConversationStore()
+ conversationStore.updateConversation(ImConversationType.GROUP, group.id, {
+ name: getGroupDisplayName(merged),
+ avatar: merged.avatar,
+ silent: merged.silent
+ })
+ // 鎸佷箙鍖栧埌 IDB锛坒ire-and-forget锛�
+ await this.saveGroupRecord(merged)
+ },
+
+ /** 鏈湴绉婚櫎缇ょ紦瀛樺拰缇や細璇濓紱缇よВ鏁o紙GROUP_DEL锛夈�侀��缇ゃ�佽韪㈤兘澶嶇敤 */
+ removeGroup(id: number) {
+ // 鏈湴纭垹锛堝尯鍒簬濂藉弸鍒犻櫎鐨勮蒋鍒犱繚鐣欒褰曪級锛涚骇鑱旀竻缇よ亰浼氳瘽閬垮厤鍒楄〃閲岀暀姝荤兢
+ this.groups = this.groups.filter((g) => g.id !== id)
+ const conversationStore = useConversationStore()
+ conversationStore.removeGroupConversation(id)
+ void getDb()
+ .transaction(['groups', 'groupMembers'], 'readwrite', async (tx) => {
+ const db = getDb()
+ await db.delete('groups', id, tx)
+ await db.deleteByIndex('groupMembers', 'groupId', id, tx)
+ })
+ .catch((error) => console.warn(`[IM groupStore] 缇ょ紦瀛樺垹闄ゅけ璐� (groupId=${id})`, error))
+ },
+
+ /** 鍒囨崲鍏嶆墦鎵帮細鎺ㄥ悗绔� + 钀芥湰鍦� + 鍚屾浼氳瘽鍒楄〃鐨� silent锛岄伩鍏� silent 鍥炬爣 / 鎬绘湭璇� / 鎻愮ず闊冲垽鏂笌璁剧疆婕傜Щ锛涘拰 friendStore.setFriendSilent 瀵归綈 */
+ async setGroupSilent(id: number, silent: boolean) {
+ await apiUpdateGroupMember({ groupId: id, silent })
+ const group = this.getGroup(id)
+ if (!group) {
+ return
+ }
+ group.silent = silent
+ const conversationStore = useConversationStore()
+ conversationStore.updateConversation(ImConversationType.GROUP, id, { silent })
+ this.saveGroup(group)
+ },
+
+ /** 鎵归噺鏇存柊缇ゆ垚鍛樿鑹诧紱鏈湴涓嶅懡涓垯蹇界暐锛岀瓑 fetchGroupMemberList 鍏滃簳 */
+ updateGroupMemberRoleList(groupId: number, userIds: number[], role: number) {
+ const group = this.getGroup(groupId)
+ if (!group?.members?.length) {
+ return
+ }
+ // 鍛戒腑鐩爣涓旇鑹插凡鍙樺寲鎵嶆爣璁� changed锛岄伩鍏嶆棤鍙樺寲鏃舵暣鏁扮粍閲嶅缓瑙﹀彂鍝嶅簲寮�
+ const idSet = new Set(userIds)
+ let changed = false
+ const newMembers = group.members.map((member) => {
+ if (!idSet.has(member.userId) || member.role === role) {
+ return member
+ }
+ changed = true
+ return { ...member, role }
+ })
+ // 鏈夊彉鍖栨墠鏁寸粍鏇挎崲锛岃鍝嶅簲寮忓彧鍦ㄧ湡鏈夋洿鏂版椂閫氱煡涓嬫父
+ if (changed) {
+ group.members = newMembers
+ this.saveGroupMemberList(groupId)
+ }
+ },
+
+ /** 缇や富杞锛氱兢琛� ownerUserId 鏀逛负鏂板�硷紱鏃х兢涓� role 鈫� NORMAL锛涙柊缇や富 role 鈫� OWNER */
+ transferGroupOwner(groupId: number, oldOwnerId: number, newOwnerId: number) {
+ const group = this.getGroup(groupId)
+ if (!group) {
+ return
+ }
+ if (group.ownerUserId !== newOwnerId) {
+ group.ownerUserId = newOwnerId
+ }
+ this.updateGroupMemberRoleList(groupId, [oldOwnerId], ImGroupMemberRole.NORMAL)
+ this.updateGroupMemberRoleList(groupId, [newOwnerId], ImGroupMemberRole.OWNER)
+ this.saveGroup(group)
+ },
+
+ /** 鏈湴鍓旈櫎缇ゆ垚鍛橈紙GROUP_MEMBER_QUIT / KICK 浜嬩欢锛夛紱涓嶅懡涓垯绛� fetchGroupMemberList 鍏滃簳 */
+ removeLocalGroupMemberList(groupId: number, userIds: number[]) {
+ const group = this.getGroup(groupId)
+ if (!group?.members?.length || userIds.length === 0) {
+ return
+ }
+ const idSet = new Set(userIds)
+ const next = group.members.filter((member) => !idSet.has(member.userId))
+ if (next.length === group.members.length) {
+ return
+ }
+ group.members = next
+ group.memberCount = next.length
+ this.saveGroupMemberList(groupId)
+ },
+
+ /** 鏈湴鏇存柊缇ゆ垚鍛樼殑 status锛堣嚜宸遍��缇� / 琚涪鐨勬湰鍦伴缃紱璁� isMember 绔嬪嵆鏀舵暃鍒� stranger锛屼笉渚濊禆 removeGroup 鐨勬暣缇ょЩ闄わ級 */
+ updateGroupMemberStatus(groupId: number, userId: number, status: number) {
+ const group = this.getGroup(groupId)
+ const member = group?.members?.find((m) => m.userId === userId)
+ if (!member || member.status === status) {
+ return
+ }
+ member.status = status
+ this.saveGroupMemberList(groupId)
+ },
+
+ /** 鏈湴鏇存柊缇ゆ垚鍛樼殑 displayUserName锛圙ROUP_MEMBER_NICKNAME_UPDATE 浜嬩欢锛夛紱涓嶅懡涓垯绛� fetchGroupMemberList 鍏滃簳 */
+ updateGroupMemberDisplayUserName(groupId: number, userId: number, displayUserName: string) {
+ const group = this.getGroup(groupId)
+ const member = group?.members?.find((m) => m.userId === userId)
+ if (!member || member.displayUserName === displayUserName) {
+ return
+ }
+ member.displayUserName = displayUserName
+ this.saveGroupMemberList(groupId)
+ },
+
+ /** 灞�閮ㄦ洿鏂扮兢瀛楁锛坣ame / notice / avatar 绛夛級锛涙湭鍛戒腑鏈湴缂撳瓨鏃堕潤榛樺拷鐣ワ紝绛� fetchGroupList 鍏滃簳锛涙柊鍊艰窡鏃у�奸兘鐩稿悓鏃惰烦杩囧搷搴斿紡 + IDB 鍐� */
+ updateGroupFields(groupId: number, fields: Partial<Group>) {
+ const group = this.getGroup(groupId)
+ if (!group) {
+ return
+ }
+ const changed = (Object.keys(fields) as (keyof Group)[]).some((k) => group[k] !== fields[k])
+ if (!changed) {
+ return
+ }
+ Object.assign(group, fields)
+ const conversationStore = useConversationStore()
+ conversationStore.updateConversation(ImConversationType.GROUP, groupId, {
+ name: getGroupDisplayName(group),
+ avatar: group.avatar,
+ silent: group.silent
+ })
+ this.saveGroup(group)
+ },
+
+ /**
+ * 鎺ユ敹 GROUP_* 缇ゅ箍鎾簨浠讹紝鎸� type 鍒嗗彂鍒板搴旂鏈� action
+ *
+ * WebSocket 瀹炴椂鏀惰蛋 messageStore.insertMessage 鏃佽矾璋冪敤
+ * store 閲屾病缂撳瓨鐨勭兢闈欓粯蹇界暐锛岀瓑 fetchGroupList 鍏滃簳
+ */
+ applyGroupNotification(groupId: number, type: number, content?: string) {
+ if (!groupId) {
+ return
+ }
+ let payload: GroupNotificationPayload
+ try {
+ payload = content ? JSON.parse(content) : {}
+ } catch (error) {
+ console.warn(
+ '[IM groupStore] applyGroupNotification 瑙f瀽 content 澶辫触',
+ { groupId, type, contentLength: content?.length ?? 0 },
+ error
+ )
+ return
+ }
+ switch (type) {
+ case ImContentType.GROUP_ADMIN_ADD: {
+ this.updateGroupMemberRoleList(
+ groupId,
+ payload.memberUserIds || [],
+ ImGroupMemberRole.ADMIN
+ )
+ this.markGroupMembersExpired(groupId)
+ // 鑷繁琚姞涓虹鐞嗗憳锛屽師鏈湅涓嶅埌鐨勭兢涓嬫湭澶勭悊鐢宠鐜板湪鍙樺彲瑙侊紝閲嶆柊鎷変竴娆� unhandledList
+ if (isSelfInPayloadMembers(payload)) {
+ refreshUnhandledGroupRequests()
+ }
+ break
+ }
+ case ImContentType.GROUP_ADMIN_REMOVE: {
+ this.updateGroupMemberRoleList(
+ groupId,
+ payload.memberUserIds || [],
+ ImGroupMemberRole.NORMAL
+ )
+ this.markGroupMembersExpired(groupId)
+ if (isSelfInPayloadMembers(payload)) {
+ refreshUnhandledGroupRequests()
+ }
+ break
+ }
+ case ImContentType.GROUP_BANNED: {
+ this.updateGroupFields(groupId, { banned: !!payload.banned })
+ break
+ }
+ case ImContentType.GROUP_CANCEL_MUTED: {
+ this.updateGroupFields(groupId, { mutedAll: false })
+ break
+ }
+ case ImContentType.GROUP_CREATE: {
+ this.applyGroupCreateNotification(groupId, payload)
+ break
+ }
+ case ImContentType.GROUP_DISSOLVE: {
+ this.removeGroup(groupId)
+ break
+ }
+ case ImContentType.GROUP_INFO_UPDATE: {
+ this.applyGroupInfoUpdateNotification(groupId, payload)
+ break
+ }
+ case ImContentType.GROUP_MEMBER_CANCEL_MUTED: {
+ this.applyGroupMemberCancelMutedNotification(groupId, payload)
+ break
+ }
+ case ImContentType.GROUP_MEMBER_ENTER: {
+ this.applyGroupMemberEnterNotification(groupId, payload)
+ break
+ }
+ case ImContentType.GROUP_MEMBER_INVITE: {
+ this.applyGroupMemberInviteNotification(groupId, payload)
+ break
+ }
+ case ImContentType.GROUP_MEMBER_KICK: {
+ this.applyGroupMemberKickNotification(groupId, payload)
+ break
+ }
+ case ImContentType.GROUP_MEMBER_MUTED: {
+ this.applyGroupMemberMutedNotification(groupId, payload)
+ break
+ }
+ case ImContentType.GROUP_MEMBER_NICKNAME_UPDATE: {
+ this.applyGroupMemberNicknameUpdateNotification(groupId, payload)
+ break
+ }
+ case ImContentType.GROUP_MEMBER_QUIT: {
+ this.applyGroupMemberQuitNotification(groupId, payload)
+ break
+ }
+ case ImContentType.GROUP_MESSAGE_PIN: {
+ this.applyGroupMessagePinNotification(groupId, payload)
+ break
+ }
+ case ImContentType.GROUP_MESSAGE_UNPIN: {
+ this.applyGroupMessageUnpinNotification(groupId, payload)
+ break
+ }
+ case ImContentType.GROUP_MUTED: {
+ this.updateGroupFields(groupId, { mutedAll: true })
+ break
+ }
+ case ImContentType.GROUP_NAME_UPDATE: {
+ this.applyGroupNameUpdateNotification(groupId, payload)
+ break
+ }
+ case ImContentType.GROUP_NOTICE_UPDATE: {
+ this.applyGroupNoticeUpdateNotification(groupId, payload)
+ break
+ }
+ case ImContentType.GROUP_OWNER_TRANSFER: {
+ this.applyGroupOwnerTransferNotification(groupId, payload)
+ break
+ }
+ }
+ },
+
+ /** 鍒涘缓缇ゅ箍鎾細缇ゆ湭灏变綅鏃舵媺缇よ鎯� */
+ async applyGroupCreateNotification(groupId: number, payload: GroupNotificationPayload) {
+ if (!isSelfInPayloadMembers(payload)) {
+ return
+ }
+ const selfUserId = getCurrentUserId()
+ const selfIsOperator = !!selfUserId && payload.operatorUserId === selfUserId
+ if (selfIsOperator && this.getGroup(groupId)) {
+ return
+ }
+ await this.fetchGroupInfo(groupId, true)
+ },
+
+ /** 缇ゅ悕鍙樻洿锛氭寜 newName 灞�閮ㄦ洿鏂版湰鍦扮兢鍚� */
+ applyGroupNameUpdateNotification(groupId: number, payload: GroupNotificationPayload) {
+ if (payload.newName) {
+ this.updateGroupFields(groupId, { name: payload.newName })
+ }
+ },
+
+ /** 缇ゅ叕鍛婂彉鏇达細鎸� newNotice 灞�閮ㄦ洿鏂帮紙鍏佽绌轰覆浣滀负銆屾竻绌哄叕鍛娿�嶏級 */
+ applyGroupNoticeUpdateNotification(groupId: number, payload: GroupNotificationPayload) {
+ this.updateGroupFields(groupId, { notice: payload.newNotice ?? '' })
+ },
+
+ /** 缇や俊鎭彉鏇达細鍚屾澶村儚銆佽繘缇ゅ鎵� */
+ applyGroupInfoUpdateNotification(groupId: number, payload: GroupNotificationPayload) {
+ const fields: Partial<Group> = {}
+ if (payload.newAvatar) {
+ fields.avatar = payload.newAvatar
+ }
+ if (payload.newJoinApproval != null) {
+ fields.joinApproval = payload.newJoinApproval
+ }
+ if (Object.keys(fields).length > 0) {
+ this.updateGroupFields(groupId, fields)
+ }
+ },
+
+ /** 鎴愬憳鍔犲叆锛氳閭�璇疯�呮湰绔� group 鏈氨浣嶅厛 fetchGroupInfo 鍒濇鎷夊彇锛涙墍鏈変汉閮藉埛鎴愬憳鍒楄〃锛堟柊鎴愬憳 nickname / avatar 涓嶅湪 payload锛� */
+ async applyGroupMemberInviteNotification(groupId: number, payload: GroupNotificationPayload) {
+ // 鑷繁鍒氳鎷夎繘鏉ワ細蹇呴』 await fetchGroupInfo 璁╃兢鍏� state.groups锛屽惁鍒� fetchGroupMemberList 鐨� guard 浼氬厹绌�
+ if (isSelfInPayloadMembers(payload) && !this.getGroup(groupId)) {
+ await this.fetchGroupInfo(groupId, true)
+ }
+ this.markGroupMembersExpired(groupId)
+ this.fetchGroupMemberList(groupId, true).catch(() => undefined)
+ },
+
+ /** 鑷敱杩涚兢锛氳繘缇よ�呮湰绔� group 鏈氨浣嶅厛 fetchGroupInfo 鍒濇鎷夊彇锛涙墍鏈変汉閮藉埛鎴愬憳鍒楄〃 */
+ async applyGroupMemberEnterNotification(groupId: number, payload: GroupNotificationPayload) {
+ const selfUserId = getCurrentUserId()
+ // 鑷繁鑷敱杩涚兢锛氬繀椤� await fetchGroupInfo 璁╃兢鍏� state.groups锛屽惁鍒� fetchGroupMemberList 鐨� guard 浼氬厹绌�
+ if (selfUserId && payload.entrantUserId === selfUserId && !this.getGroup(groupId)) {
+ await this.fetchGroupInfo(groupId, true)
+ }
+ this.markGroupMembersExpired(groupId)
+ this.fetchGroupMemberList(groupId, true).catch(() => undefined)
+ },
+
+ /** 鎴愬憳閫�缇わ細閫�缇よ�呮湰浜哄厛鎶� self.status 缃� DISABLE 鍐� removeGroup锛堜繚鐣欑姸鎬佽涔� + 缁存寔 groups 鍒楄〃骞插噣锛夛紱鍏朵粬鎴愬憳浠庢湰鍦板垪琛ㄧЩ闄� quitter */
+ applyGroupMemberQuitNotification(groupId: number, payload: GroupNotificationPayload) {
+ const selfUserId = getCurrentUserId()
+ if (selfUserId && payload.operatorUserId === selfUserId) {
+ this.updateGroupMemberStatus(groupId, selfUserId, CommonStatusEnum.DISABLE)
+ this.removeGroup(groupId)
+ } else if (payload.operatorUserId) {
+ this.removeLocalGroupMemberList(groupId, [payload.operatorUserId])
+ this.markGroupMembersExpired(groupId)
+ }
+ },
+
+ /** 鎴愬憳琚Щ鍑猴細琚涪鑰呮湰浜哄厛鎶� self.status 缃� DISABLE 鍐� removeGroup锛涘叾浠栨垚鍛樹粠鏈湴鍒楄〃绉婚櫎琚涪鑰� */
+ applyGroupMemberKickNotification(groupId: number, payload: GroupNotificationPayload) {
+ const memberIds = payload.memberUserIds || []
+ const selfUserId = getCurrentUserId()
+ if (isSelfInPayloadMembers(payload)) {
+ if (selfUserId) {
+ this.updateGroupMemberStatus(groupId, selfUserId, CommonStatusEnum.DISABLE)
+ }
+ this.removeGroup(groupId)
+ } else if (memberIds.length > 0) {
+ this.removeLocalGroupMemberList(groupId, memberIds)
+ this.markGroupMembersExpired(groupId)
+ }
+ },
+
+ /** 鎴愬憳鏄电О鍙樻洿锛氭寜 operatorUserId 灞�閮ㄦ洿鏂板搴� member.displayUserName */
+ applyGroupMemberNicknameUpdateNotification(groupId: number, payload: GroupNotificationPayload) {
+ if (payload.operatorUserId) {
+ this.updateGroupMemberDisplayUserName(
+ groupId,
+ payload.operatorUserId,
+ payload.displayUserName ?? ''
+ )
+ this.markGroupMembersExpired(groupId)
+ }
+ },
+
+ /** 缇や富杞锛氭棫缇や富 鈫� NORMAL锛屾柊缇や富 鈫� OWNER锛涙柊缇や富鑷繁渚ч噸鏂版媺鐢宠鍒楄〃 */
+ applyGroupOwnerTransferNotification(groupId: number, payload: GroupNotificationPayload) {
+ if (payload.operatorUserId && payload.newOwnerUserId) {
+ this.transferGroupOwner(groupId, payload.operatorUserId, payload.newOwnerUserId)
+ this.markGroupMembersExpired(groupId)
+ }
+ // 鑷繁鎺ョ缇や富锛氬師鏈湅涓嶅埌鐨勭兢涓嬫湭澶勭悊鐢宠鐜板湪鍙樺彲瑙侊紝閲嶆柊鎷変竴娆� unhandledList
+ const selfUserId = getCurrentUserId()
+ if (selfUserId && payload.newOwnerUserId === selfUserId) {
+ refreshUnhandledGroupRequests()
+ } else if (selfUserId && payload.operatorUserId === selfUserId) {
+ refreshUnhandledGroupRequests()
+ }
+ },
+
+ /** 缇ゆ秷鎭疆椤讹細浠� payload 鍙栨秷鎭睍绀烘暟鎹姞鍏ョ疆椤跺垪琛� */
+ applyGroupMessagePinNotification(groupId: number, payload: GroupNotificationPayload) {
+ const message = payload.message
+ if (!message) {
+ return
+ }
+ const group = this.getGroup(groupId)
+ if (!group) {
+ return
+ }
+ // 骞傜瓑锛氬凡瀛樺湪鍚� messageId 涓嶉噸澶� push
+ const existing = group.pinnedMessages || []
+ if (existing.some((msg) => msg.id === message.id)) {
+ return
+ }
+ group.pinnedMessages = [
+ ...existing,
+ {
+ id: message.id,
+ clientMessageId: '',
+ senderId: message.senderId,
+ type: message.type,
+ content: message.content,
+ status: ImMessageStatus.NORMAL,
+ sendTime: new Date(message.sendTime).getTime(),
+ targetId: message.groupId || groupId,
+ selfSend: message.senderId === getCurrentUserId(),
+ atUserIds: message.atUserIds ? [...message.atUserIds] : [],
+ receiverUserIds: message.receiverUserIds ? [...message.receiverUserIds] : []
+ }
+ ]
+ this.saveGroup(group)
+ },
+
+ /** 缇ゆ秷鎭彇娑堢疆椤讹細鎸� messageId 浠庢湰鍦扮疆椤跺垪琛ㄤ腑绉婚櫎 */
+ applyGroupMessageUnpinNotification(groupId: number, payload: GroupNotificationPayload) {
+ if (!payload.messageId) {
+ return
+ }
+ const group = this.getGroup(groupId)
+ if (!group?.pinnedMessages?.length) {
+ return
+ }
+ const newPinnedMessages = group.pinnedMessages.filter((m) => m.id !== payload.messageId)
+ if (newPinnedMessages.length === group.pinnedMessages.length) {
+ return
+ }
+ group.pinnedMessages = newPinnedMessages
+ this.saveGroup(group)
+ },
+
+ /** 鍗曟垚鍛樼瑷�锛氭洿鏂扮洰鏍囨垚鍛樼殑 muteEndTime */
+ applyGroupMemberMutedNotification(groupId: number, payload: GroupNotificationPayload) {
+ const group = this.getGroup(groupId)
+ const member = group?.members?.find((m) => m.userId === payload.mutedUserId)
+ if (member && payload.muteEndTime) {
+ member.muteEndTime = payload.muteEndTime
+ this.saveGroupMemberList(groupId)
+ this.markGroupMembersExpired(groupId)
+ }
+ },
+
+ /** 鍗曟垚鍛樺彇娑堢瑷�锛氭竻绌虹洰鏍囨垚鍛樼殑 muteEndTime */
+ applyGroupMemberCancelMutedNotification(groupId: number, payload: GroupNotificationPayload) {
+ const group = this.getGroup(groupId)
+ const member = group?.members?.find((m) => m.userId === payload.mutedUserId)
+ if (member) {
+ member.muteEndTime = undefined
+ this.saveGroupMemberList(groupId)
+ this.markGroupMembersExpired(groupId)
+ }
+ },
+
+ /** 鍒囪处鍙锋椂浠呮竻 in-memory锛孖DB 鎸� userId 鍒嗘《澶╃劧闅旂锛屽洖鍒囩寮� */
+ clear() {
+ this.groups = []
+ this.loaded = false
+ this.groupMembersExpired = false
+ // 璐﹀彿鍒囨崲锛氶�掑 epoch 搴熷純鏃ц处鍙� in-flight 鐨勬垚鍛樿姹�
+ storeEpoch++
+ // 鍗曢琛ㄨ窡 in-memory state 涓�璧烽噸缃紱鏃ц处鍙� in-flight 鐨勮姹� finally 涔熶細鑷繁 delete key锛屾彁鍓嶆竻绌哄彧鏄洿骞茶剢
+ pendingMemberFetches.clear()
+ pendingSingleMemberFetches.clear()
+ }
+ }
+})
+
+function convertGroup(group: ImGroupApi.GroupRespVO): Group {
+ return {
+ id: group.id,
+ name: group.name,
+ avatar: group.avatar,
+ notice: group.notice,
+ ownerUserId: group.ownerUserId,
+ pinnedMessages: group.pinnedMessages?.map((message) => convertGroupMessageVO(message)),
+ mutedAll: group.mutedAll,
+ banned: group.banned,
+ joinApproval: group.joinApproval,
+ joinStatus: group.joinStatus,
+ groupRemark: group.groupRemark,
+ silent: group.silent
+ }
+}
+
+/** 鍚庣 ImGroupMessageApi.GroupMessageRespVO -> 鍓嶇 Message锛氳ˉ targetId / selfSend / sendTime 绛夋淳鐢熷瓧娈� */
+function convertGroupMessageVO(
+ message: NonNullable<ImGroupApi.GroupRespVO['pinnedMessages']>[number]
+): Message {
+ const currentUserId = getCurrentUserId()
+ return {
+ id: message.id,
+ clientMessageId: message.clientMessageId || '',
+ type: message.type,
+ content: message.content,
+ status: message.status,
+ sendTime: new Date(message.sendTime).getTime(),
+ senderId: message.senderId,
+ targetId: message.groupId,
+ selfSend: !!currentUserId && message.senderId === currentUserId,
+ atUserIds: message.atUserIds || [],
+ receiverUserIds: message.receiverUserIds || [],
+ receiptStatus: message.receiptStatus,
+ readCount: message.readCount
+ }
+}
+
+function convertGroupMember(member: ImGroupMemberApi.GroupMemberRespVO, groupId: number): GroupMember {
+ return {
+ id: member.id,
+ userId: member.userId,
+ groupId,
+ nickname: member.nickname || String(member.userId),
+ avatar: member.avatar,
+ displayUserName: member.displayUserName,
+ status: member.status,
+ role: member.role,
+ muteEndTime: member.muteEndTime
+ }
+}
+
+export const useGroupStoreWithOut = () => useGroupStore()
+
+// dev: 璁� Pinia 鐨� actions 鏀瑰姩鏀寔 HMR锛屽厤鍘绘瘡娆℃敼 store 閮借纭埛
+if (import.meta.hot) {
+ import.meta.hot.accept(acceptHMRUpdate(useGroupStore, import.meta.hot))
+}
--
Gitblit v1.9.3