From 6359118132a888f63c15acb1650aea4d2e9c0a37 Mon Sep 17 00:00:00 2001 From: Forrest Date: Thu, 22 Jan 2026 19:36:57 +0800 Subject: [PATCH] =?UTF-8?q?fix(chatService):=20=E4=BC=98=E5=8C=96=E5=A4=B4?= =?UTF-8?q?=E5=83=8F=E5=8A=A0=E8=BD=BD=E5=85=9C=E5=BA=95=E6=9C=BA=E5=88=B6?= =?UTF-8?q?=EF=BC=9A=E6=94=B6=E9=9B=86=E6=97=A0=20URL=20=E7=9A=84=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=90=8D=EF=BC=8C=E4=BB=8E=20head=5Fimage.db=20?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E8=8E=B7=E5=8F=96=E5=B9=B6=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=E4=B8=BA=20base64=20=E6=A0=BC=E5=BC=8F=EF=BC=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=A4=B4=E5=83=8F=E7=BC=93=E5=AD=98=E5=B9=B6=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E8=81=8A=E5=A4=A9=E7=95=8C=E9=9D=A2=E5=A4=B4=E5=83=8F?= =?UTF-8?q?=E7=BC=BA=E5=A4=B1=E3=80=82=EF=BC=88=E8=A7=A3=E5=86=B3=E4=BA=86?= =?UTF-8?q?=E9=83=A8=E5=88=86=EF=BC=8C=E6=88=91=E7=94=B5=E8=84=91=E4=B8=8A?= =?UTF-8?q?=E6=9C=89=E5=87=A0=E4=B8=AA=E4=B8=8D=E6=98=BE=E7=A4=BA=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/services/chatService.ts | 107 ++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/electron/services/chatService.ts b/electron/services/chatService.ts index 330cf57..e37540a 100644 --- a/electron/services/chatService.ts +++ b/electron/services/chatService.ts @@ -343,9 +343,17 @@ class ChatService { wcdbService.getAvatarUrls(missing) ]) + // 收集没有头像 URL 的用户名 + const missingAvatars: string[] = [] + for (const username of missing) { const displayName = displayNames.success && displayNames.map ? displayNames.map[username] : undefined - const avatarUrl = avatarUrls.success && avatarUrls.map ? avatarUrls.map[username] : undefined + let avatarUrl = avatarUrls.success && avatarUrls.map ? avatarUrls.map[username] : undefined + + // 如果没有头像 URL,记录下来稍后从 head_image.db 获取 + if (!avatarUrl) { + missingAvatars.push(username) + } const cacheEntry: ContactCacheEntry = { displayName: displayName || username, @@ -357,6 +365,23 @@ class ChatService { this.avatarCache.set(username, cacheEntry) updatedEntries[username] = cacheEntry } + + // 从 head_image.db 获取缺失的头像 + if (missingAvatars.length > 0) { + const headImageAvatars = await this.getAvatarsFromHeadImageDb(missingAvatars) + for (const username of missingAvatars) { + const avatarUrl = headImageAvatars[username] + if (avatarUrl) { + result[username].avatarUrl = avatarUrl + const cached = this.avatarCache.get(username) + if (cached) { + cached.avatarUrl = avatarUrl + updatedEntries[username] = cached + } + } + } + } + if (Object.keys(updatedEntries).length > 0) { this.contactCacheService.setEntries(updatedEntries) } @@ -368,6 +393,86 @@ class ChatService { } } + /** + * 从 head_image.db 批量获取头像(转换为 base64 data URL) + */ + private async getAvatarsFromHeadImageDb(usernames: string[]): Promise> { + const result: Record = {} + if (usernames.length === 0) return result + + try { + const dbPath = this.configService.get('dbPath') + const wxid = this.configService.get('myWxid') + if (!dbPath || !wxid) { + console.log('[头像加载] 配置缺失,无法加载头像', { dbPath: !!dbPath, wxid: !!wxid }) + return result + } + + // 使用 resolveAccountDir 获取正确的账号目录 + const accountDir = this.resolveAccountDir(dbPath, wxid) + if (!accountDir) { + console.log('[头像加载] 无法解析账号目录', { dbPath, wxid }) + return result + } + + // head_image.db 可能在账号目录下或 db_storage 子目录下 + const headImageDbPaths = [ + join(accountDir, 'head_image.db'), + join(accountDir, 'db_storage', 'head_image.db') + ] + + let headImageDbPath: string | null = null + for (const path of headImageDbPaths) { + if (existsSync(path)) { + headImageDbPath = path + break + } + } + + if (!headImageDbPath) { + console.log('[头像加载] 未找到 head_image.db', { + accountDir, + checkedPaths: headImageDbPaths + }) + return result + } + + console.log(`[头像加载] 使用数据库: ${headImageDbPath},查询 ${usernames.length} 个用户`) + + const Database = require('better-sqlite3') + const db = new Database(headImageDbPath, { readonly: true }) + + try { + const stmt = db.prepare('SELECT username, image_buffer FROM head_image WHERE username = ?') + + for (const username of usernames) { + try { + const row = stmt.get(username) as any + if (row && row.image_buffer) { + const buffer = Buffer.from(row.image_buffer) + const base64 = buffer.toString('base64') + result[username] = `data:image/jpeg;base64,${base64}` + } else { + // 只输出没有找到头像的 + console.log(`[头像加载] 未找到头像: ${username}`, { + hasRow: !!row, + hasBuffer: row ? !!row.image_buffer : false + }) + } + } catch (e) { + console.error(`[头像加载] 查询失败: ${username}`, e) + } + } + } finally { + db.close() + } + } catch (e) { + console.error('从 head_image.db 获取头像失败:', e) + } + + return result + } + /** * 补充联系人信息(私有方法,保持向后兼容) */