fix: 优化 Windows 启动性能与聊天图片缓存

- 增加聊天图片磁盘持久化缓存,重启后直接复用
- 将 DAT 图片解密移至 Worker,避免阻塞主进程
- 增加图片加载优先级和并发控制
- 优化会话目录与图片文件的异步查找
- 使用本地媒体协议加载缓存图片,减少 Base64 IPC 开销
- 优化启动缓存与数据库初始化流程,降低窗口未响应时间

(cherry picked from commit 82bcc8d32ca674de38c745cc9925ed2309887dc7)
This commit is contained in:
电摇小子
2026-08-03 10:03:19 +08:00
committed by Wxw-Gu
parent 8a6d443acc
commit a3955d691d
10 changed files with 1615 additions and 362 deletions
+70 -4
View File
@@ -241,6 +241,8 @@ export class Wcdb4Client {
private groupNicknameCache = new Map<string, Map<string, string>>()
private cachedSessions: Wcdb4Session[] | null = null
private cachedChatTables: { name: string; db_number: string }[] | null = null
private sessionsInFlight: Promise<Wcdb4Session[]> | null = null
private sessionCacheGeneration = 0
private wcdbShutdown: (() => number) | null = null
private wcdbOpenAccount:
@@ -531,7 +533,11 @@ export class Wcdb4Client {
this.handle = handleOut[0]
if (this.wcdbSetMyWxid) {
try {
this.wcdbSetMyWxid(this.handle, this.wxid)
await this.callAsyncCode(
this.wcdbSetMyWxid as unknown as KoffiAsyncFunction,
this.handle,
this.wxid
)
} catch {
// Optional helper. Failure does not block message reads.
}
@@ -557,14 +563,16 @@ export class Wcdb4Client {
this.groupNicknameCache.clear()
}
startMonitor(callback: (type: string, json: string) => void): boolean {
async startMonitor(callback: (type: string, json: string) => void): Promise<boolean> {
if (!this.wcdbStartMonitorPipe || !this.wcdbGetMonitorPipeName || !this.koffi) return false
this.stopMonitor()
this.monitorCallback = callback
try {
const startResult = this.wcdbStartMonitorPipe()
const startResult = await this.callAsyncCode(
this.wcdbStartMonitorPipe as unknown as KoffiAsyncFunction
)
if (startResult !== 0) {
this.monitorCallback = null
console.warn(`[WCDB4] wcdb_start_monitor_pipe 失败,错误码: ${startResult}`)
@@ -573,7 +581,10 @@ export class Wcdb4Client {
this.monitorStarted = true
const outName: WcdbVoidOut = [null]
const nameResult = this.wcdbGetMonitorPipeName(outName)
const nameResult = await this.callAsyncCode(
this.wcdbGetMonitorPipeName as unknown as KoffiAsyncFunction,
outName
)
if (nameResult !== 0 || !outName[0]) {
console.warn(`[WCDB4] wcdb_get_monitor_pipe_name 失败,错误码: ${nameResult}`)
this.stopMonitor()
@@ -715,9 +726,45 @@ export class Wcdb4Client {
return this.cachedSessions
}
async getSessionsAsync(): Promise<Wcdb4Session[]> {
if (this.cachedSessions) return this.cachedSessions
if (this.sessionsInFlight) return this.sessionsInFlight
if (!this.wcdbGetSessions) return []
const generation = this.sessionCacheGeneration
const request = (async (): Promise<Wcdb4Session[]> => {
const rows = await this.callJsonAsync<Record<string, unknown>[]>(
this.wcdbGetSessions as unknown as KoffiAsyncFunction
)
const sessions = (Array.isArray(rows) ? rows : [])
.map((row) => this.normalizeSession(row))
.filter((session) => session.username)
await this.hydrateDisplayNamesAsync(
sessions
.filter((session) => this.shouldHydrateSessionDisplayName(session))
.map((session) => session.username)
)
const hydrated = sessions.map((session) => ({
...session,
nickname:
this.displayNameCache.get(session.username) || session.nickname || session.username
}))
if (generation === this.sessionCacheGeneration) this.cachedSessions = hydrated
return hydrated
})()
this.sessionsInFlight = request
try {
return await request
} finally {
if (this.sessionsInFlight === request) this.sessionsInFlight = null
}
}
invalidateSessionCache(): void {
this.sessionCacheGeneration += 1
this.cachedSessions = null
this.cachedChatTables = null
this.sessionsInFlight = null
}
getChatTables(): { name: string; db_number: string }[] {
@@ -1527,6 +1574,25 @@ export class Wcdb4Client {
}
}
async resolveImageHardlinkAsync(md5: string): Promise<Wcdb4ImageHardlink | null> {
if (!this.wcdbResolveImageHardlink) return null
const normalizedMd5 = String(md5 || '')
.trim()
.toLowerCase()
if (!normalizedMd5) return null
try {
return await this.callJsonAsync<Wcdb4ImageHardlink>(
this.wcdbResolveImageHardlink as unknown as KoffiAsyncFunction,
normalizedMd5,
this.accountRoot
)
} catch (error) {
console.warn('[WCDB4] async image hardlink resolve failed:', error)
return null
}
}
resolveVideoHardlink(md5: string, dbPath: string): Wcdb4VideoHardlink | null {
if (!this.wcdbResolveVideoHardlink) return null
const normalizedMd5 = String(md5 || '')