mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 15:25:50 +00:00
fix:修复了头像加载失败的问题
This commit is contained in:
@@ -326,7 +326,11 @@ class ChatService {
|
|||||||
// 检查缓存
|
// 检查缓存
|
||||||
for (const username of usernames) {
|
for (const username of usernames) {
|
||||||
const cached = this.avatarCache.get(username)
|
const cached = this.avatarCache.get(username)
|
||||||
if (cached && now - cached.updatedAt < this.avatarCacheTtlMs) {
|
// 如果缓存有效且有头像,直接使用;如果没有头像,也需要重新尝试获取
|
||||||
|
// 额外检查:如果头像是无效的 hex 格式(以 ffd8 开头),也需要重新获取
|
||||||
|
const isValidAvatar = cached?.avatarUrl &&
|
||||||
|
!cached.avatarUrl.includes('base64,ffd8') // 检测错误的 hex 格式
|
||||||
|
if (cached && now - cached.updatedAt < this.avatarCacheTtlMs && isValidAvatar) {
|
||||||
result[username] = {
|
result[username] = {
|
||||||
displayName: cached.displayName,
|
displayName: cached.displayName,
|
||||||
avatarUrl: cached.avatarUrl
|
avatarUrl: cached.avatarUrl
|
||||||
@@ -403,22 +407,16 @@ class ChatService {
|
|||||||
try {
|
try {
|
||||||
const dbPath = this.configService.get('dbPath')
|
const dbPath = this.configService.get('dbPath')
|
||||||
const wxid = this.configService.get('myWxid')
|
const wxid = this.configService.get('myWxid')
|
||||||
if (!dbPath || !wxid) {
|
if (!dbPath || !wxid) return result
|
||||||
console.log('[头像加载] 配置缺失,无法加载头像', { dbPath: !!dbPath, wxid: !!wxid })
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用 resolveAccountDir 获取正确的账号目录
|
|
||||||
const accountDir = this.resolveAccountDir(dbPath, wxid)
|
const accountDir = this.resolveAccountDir(dbPath, wxid)
|
||||||
if (!accountDir) {
|
if (!accountDir) return result
|
||||||
console.log('[头像加载] 无法解析账号目录', { dbPath, wxid })
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// head_image.db 可能在账号目录下或 db_storage 子目录下
|
// head_image.db 可能在不同位置
|
||||||
const headImageDbPaths = [
|
const headImageDbPaths = [
|
||||||
join(accountDir, 'head_image.db'),
|
join(accountDir, 'db_storage', 'head_image', 'head_image.db'),
|
||||||
join(accountDir, 'db_storage', 'head_image.db')
|
join(accountDir, 'db_storage', 'head_image.db'),
|
||||||
|
join(accountDir, 'head_image.db')
|
||||||
]
|
]
|
||||||
|
|
||||||
let headImageDbPath: string | null = null
|
let headImageDbPath: string | null = null
|
||||||
@@ -429,42 +427,43 @@ class ChatService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!headImageDbPath) {
|
if (!headImageDbPath) return result
|
||||||
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 = ?')
|
|
||||||
|
|
||||||
|
// 使用 wcdbService.execQuery 查询加密的 head_image.db
|
||||||
for (const username of usernames) {
|
for (const username of usernames) {
|
||||||
try {
|
try {
|
||||||
const row = stmt.get(username) as any
|
const escapedUsername = username.replace(/'/g, "''")
|
||||||
if (row && row.image_buffer) {
|
const queryResult = await wcdbService.execQuery(
|
||||||
const buffer = Buffer.from(row.image_buffer)
|
'media',
|
||||||
const base64 = buffer.toString('base64')
|
headImageDbPath,
|
||||||
result[username] = `data:image/jpeg;base64,${base64}`
|
`SELECT image_buffer FROM head_image WHERE username = '${escapedUsername}' LIMIT 1`
|
||||||
|
)
|
||||||
|
|
||||||
|
if (queryResult.success && queryResult.rows && queryResult.rows.length > 0) {
|
||||||
|
const row = queryResult.rows[0] as any
|
||||||
|
if (row?.image_buffer) {
|
||||||
|
let base64Data: string
|
||||||
|
if (typeof row.image_buffer === 'string') {
|
||||||
|
// WCDB 返回的 BLOB 是十六进制字符串,需要转换为 base64
|
||||||
|
if (row.image_buffer.toLowerCase().startsWith('ffd8')) {
|
||||||
|
const buffer = Buffer.from(row.image_buffer, 'hex')
|
||||||
|
base64Data = buffer.toString('base64')
|
||||||
} else {
|
} else {
|
||||||
// 只输出没有找到头像的
|
base64Data = row.image_buffer
|
||||||
console.log(`[头像加载] 未找到头像: ${username}`, {
|
|
||||||
hasRow: !!row,
|
|
||||||
hasBuffer: row ? !!row.image_buffer : false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} else if (Buffer.isBuffer(row.image_buffer)) {
|
||||||
console.error(`[头像加载] 查询失败: ${username}`, e)
|
base64Data = row.image_buffer.toString('base64')
|
||||||
|
} else if (Array.isArray(row.image_buffer)) {
|
||||||
|
base64Data = Buffer.from(row.image_buffer).toString('base64')
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result[username] = `data:image/jpeg;base64,${base64Data}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} catch {
|
||||||
db.close()
|
// 静默处理单个用户的错误
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('从 head_image.db 获取头像失败:', e)
|
console.error('从 head_image.db 获取头像失败:', e)
|
||||||
@@ -1824,7 +1823,9 @@ class ChatService {
|
|||||||
const connectResult = await this.ensureConnected()
|
const connectResult = await this.ensureConnected()
|
||||||
if (!connectResult.success) return null
|
if (!connectResult.success) return null
|
||||||
const cached = this.avatarCache.get(username)
|
const cached = this.avatarCache.get(username)
|
||||||
if (cached && cached.avatarUrl && Date.now() - cached.updatedAt < this.avatarCacheTtlMs) {
|
// 检查缓存是否有效,且头像不是错误的 hex 格式
|
||||||
|
const isValidAvatar = cached?.avatarUrl && !cached.avatarUrl.includes('base64,ffd8')
|
||||||
|
if (cached && isValidAvatar && Date.now() - cached.updatedAt < this.avatarCacheTtlMs) {
|
||||||
return { avatarUrl: cached.avatarUrl, displayName: cached.displayName }
|
return { avatarUrl: cached.avatarUrl, displayName: cached.displayName }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3097,10 +3098,26 @@ class ChatService {
|
|||||||
|
|
||||||
private resolveAccountDir(dbPath: string, wxid: string): string | null {
|
private resolveAccountDir(dbPath: string, wxid: string): string | null {
|
||||||
const normalized = dbPath.replace(/[\\\\/]+$/, '')
|
const normalized = dbPath.replace(/[\\\\/]+$/, '')
|
||||||
|
|
||||||
|
// 如果 dbPath 本身指向 db_storage 目录下的文件(如某个 .db 文件)
|
||||||
|
// 则向上回溯到账号目录
|
||||||
|
if (basename(normalized).toLowerCase() === 'db_storage') {
|
||||||
|
return dirname(normalized)
|
||||||
|
}
|
||||||
const dir = dirname(normalized)
|
const dir = dirname(normalized)
|
||||||
if (basename(normalized).toLowerCase() === 'db_storage') return dir
|
if (basename(dir).toLowerCase() === 'db_storage') {
|
||||||
if (basename(dir).toLowerCase() === 'db_storage') return dirname(dir)
|
return dirname(dir)
|
||||||
return dir // 兜底
|
}
|
||||||
|
|
||||||
|
// 否则,dbPath 应该是数据库根目录(如 xwechat_files)
|
||||||
|
// 账号目录应该是 {dbPath}/{wxid}
|
||||||
|
const accountDirWithWxid = join(normalized, wxid)
|
||||||
|
if (existsSync(accountDirWithWxid)) {
|
||||||
|
return accountDirWithWxid
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兜底:返回 dbPath 本身(可能 dbPath 已经是账号目录)
|
||||||
|
return normalized
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findDatFile(accountDir: string, baseName: string, sessionId?: string): Promise<string | null> {
|
private async findDatFile(accountDir: string, baseName: string, sessionId?: string): Promise<string | null> {
|
||||||
|
|||||||
@@ -34,6 +34,14 @@ export class ContactCacheService {
|
|||||||
const raw = readFileSync(this.cacheFilePath, 'utf8')
|
const raw = readFileSync(this.cacheFilePath, 'utf8')
|
||||||
const parsed = JSON.parse(raw)
|
const parsed = JSON.parse(raw)
|
||||||
if (parsed && typeof parsed === 'object') {
|
if (parsed && typeof parsed === 'object') {
|
||||||
|
// 清除无效的头像数据(hex 格式而非正确的 base64)
|
||||||
|
for (const key of Object.keys(parsed)) {
|
||||||
|
const entry = parsed[key]
|
||||||
|
if (entry?.avatarUrl && entry.avatarUrl.includes('base64,ffd8')) {
|
||||||
|
// 这是错误的 hex 格式,清除它
|
||||||
|
entry.avatarUrl = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
this.cache = parsed
|
this.cache = parsed
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user