From f09ab1bbcceb7256b07e7d73309b52ea0f5dde7a Mon Sep 17 00:00:00 2001 From: xuncha <1658671838@qq.com> Date: Wed, 14 Jan 2026 13:31:51 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BA=86=E7=BE=A4?= =?UTF-8?q?=E5=A4=B4=E5=83=8F=E5=AF=BC=E5=87=BA=E4=B8=A2=E5=A4=B1=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + electron/services/exportService.ts | 84 +++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3433fcf..66440f0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ dist dist-electron dist-ssr *.local +test/ # Editor directories and files .vscode/* diff --git a/electron/services/exportService.ts b/electron/services/exportService.ts index de87244..4a5550d 100644 --- a/electron/services/exportService.ts +++ b/electron/services/exportService.ts @@ -19,6 +19,7 @@ interface ChatLabMeta { platform: string type: 'group' | 'private' groupId?: string + groupAvatar?: string } interface ChatLabMember { @@ -425,6 +426,81 @@ class ExportService { return { rows, memberSet, firstTime, lastTime } } + // 补齐群成员,避免只导出发言者导致头像缺失 + private async mergeGroupMembers( + chatroomId: string, + memberSet: Map, + includeAvatars: boolean + ): Promise { + const result = await wcdbService.getGroupMembers(chatroomId) + if (!result.success || !result.members || result.members.length === 0) return + + const rawMembers = result.members as Array<{ + username?: string + avatarUrl?: string + nickname?: string + displayName?: string + remark?: string + originalName?: string + }> + const usernames = rawMembers + .map((member) => member.username) + .filter((username): username is string => Boolean(username)) + if (usernames.length === 0) return + + const lookupUsernames = new Set() + for (const username of usernames) { + lookupUsernames.add(username) + const cleaned = this.cleanAccountDirName(username) + if (cleaned && cleaned !== username) { + lookupUsernames.add(cleaned) + } + } + + const [displayNames, avatarUrls] = await Promise.all([ + wcdbService.getDisplayNames(Array.from(lookupUsernames)), + includeAvatars ? wcdbService.getAvatarUrls(Array.from(lookupUsernames)) : Promise.resolve({ success: true, map: {} }) + ]) + + for (const member of rawMembers) { + const username = member.username + if (!username) continue + + const cleaned = this.cleanAccountDirName(username) + const displayName = displayNames.success && displayNames.map + ? (displayNames.map[username] || (cleaned ? displayNames.map[cleaned] : undefined) || username) + : username + const groupNickname = member.nickname || member.displayName || member.remark || member.originalName + const avatarUrl = includeAvatars && avatarUrls.success && avatarUrls.map + ? (avatarUrls.map[username] || (cleaned ? avatarUrls.map[cleaned] : undefined) || member.avatarUrl) + : member.avatarUrl + + const existing = memberSet.get(username) + if (existing) { + if (displayName && existing.member.accountName === existing.member.platformId && displayName !== existing.member.platformId) { + existing.member.accountName = displayName + } + if (groupNickname && !existing.member.groupNickname) { + existing.member.groupNickname = groupNickname + } + if (!existing.avatarUrl && avatarUrl) { + existing.avatarUrl = avatarUrl + } + memberSet.set(username, existing) + continue + } + + const chatlabMember: ChatLabMember = { + platformId: username, + accountName: displayName + } + if (groupNickname) { + chatlabMember.groupNickname = groupNickname + } + memberSet.set(username, { member: chatlabMember, avatarUrl }) + } + } + private resolveAvatarFile(avatarUrl?: string): { data?: Buffer; sourcePath?: string; sourceUrl?: string; ext: string; mime?: string } | null { if (!avatarUrl) return null if (avatarUrl.startsWith('data:')) { @@ -567,6 +643,9 @@ class ExportService { const collected = await this.collectMessages(sessionId, cleanedMyWxid, options.dateRange) const allMessages = collected.rows + if (isGroup) { + await this.mergeGroupMembers(sessionId, collected.memberSet, options.exportAvatars === true) + } allMessages.sort((a, b) => a.createTime - b.createTime) @@ -585,6 +664,7 @@ class ExportService { return { sender: msg.senderUsername, accountName: memberInfo.accountName, + groupNickname: memberInfo.groupNickname, timestamp: msg.createTime, type: this.convertMessageType(msg.localType, msg.content), content: this.parseMessageContent(msg.content, msg.localType) @@ -603,6 +683,7 @@ class ExportService { ) : new Map() + const sessionAvatar = avatarMap.get(sessionId) const members = Array.from(collected.memberSet.values()).map((info) => { const avatar = avatarMap.get(info.member.platformId) return avatar ? { ...info.member, avatar } : info.member @@ -618,7 +699,8 @@ class ExportService { name: sessionInfo.displayName, platform: 'wechat', type: isGroup ? 'group' : 'private', - ...(isGroup && { groupId: sessionId }) + ...(isGroup && { groupId: sessionId }), + ...(sessionAvatar && { groupAvatar: sessionAvatar }) }, members, messages: chatLabMessages