diff --git a/electron/services/chatService.ts b/electron/services/chatService.ts index 67de6e0..15d42d0 100644 --- a/electron/services/chatService.ts +++ b/electron/services/chatService.ts @@ -29,6 +29,7 @@ export interface ChatSession { sortTimestamp: number // 用于排序 lastTimestamp: number // 用于显示时间 lastMsgType: number + messageCountHint?: number displayName?: string avatarUrl?: string lastMsgSender?: string @@ -421,6 +422,21 @@ class ChatService { const summary = this.cleanString(row.summary || row.digest || row.last_msg || row.lastMsg || '') const lastMsgType = parseInt(row.last_msg_type || row.lastMsgType || '0', 10) + const messageCountHintRaw = + row.message_count ?? + row.messageCount ?? + row.msg_count ?? + row.msgCount ?? + row.total_count ?? + row.totalCount ?? + row.n_msg ?? + row.nMsg ?? + row.message_num ?? + row.messageNum + const parsedMessageCountHint = Number(messageCountHintRaw) + const messageCountHint = Number.isFinite(parsedMessageCountHint) && parsedMessageCountHint >= 0 + ? Math.floor(parsedMessageCountHint) + : undefined // 先尝试从缓存获取联系人信息(快速路径) let displayName = username @@ -439,6 +455,7 @@ class ChatService { sortTimestamp: sortTs, lastTimestamp: lastTs, lastMsgType, + messageCountHint, displayName, avatarUrl, lastMsgSender: row.last_msg_sender, @@ -812,23 +829,30 @@ class ChatService { } } - await this.forEachWithConcurrency(pendingSessionIds, 16, async (sessionId) => { + const batchSize = 320 + for (let i = 0; i < pendingSessionIds.length; i += batchSize) { + const batch = pendingSessionIds.slice(i, i + batchSize) + let batchCounts: Record = {} try { - const result = await wcdbService.getMessageCount(sessionId) - const nextCount = result.success && typeof result.count === 'number' ? result.count : 0 + const result = await wcdbService.getMessageCounts(batch) + if (result.success && result.counts) { + batchCounts = result.counts + } + } catch { + // noop + } + + const nowTs = Date.now() + for (const sessionId of batch) { + const nextCountRaw = batchCounts[sessionId] + const nextCount = Number.isFinite(nextCountRaw) ? Math.max(0, Math.floor(nextCountRaw)) : 0 counts[sessionId] = nextCount this.sessionMessageCountCache.set(sessionId, { count: nextCount, - updatedAt: Date.now() - }) - } catch { - counts[sessionId] = 0 - this.sessionMessageCountCache.set(sessionId, { - count: 0, - updatedAt: Date.now() + updatedAt: nowTs }) } - }) + } return { success: true, counts } } catch (e) { diff --git a/electron/services/wcdbCore.ts b/electron/services/wcdbCore.ts index 1c47b4c..80bdbc1 100644 --- a/electron/services/wcdbCore.ts +++ b/electron/services/wcdbCore.ts @@ -1144,6 +1144,40 @@ export class WcdbCore { } } + async getMessageCounts(sessionIds: string[]): Promise<{ success: boolean; counts?: Record; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + + const normalizedSessionIds = Array.from( + new Set( + (sessionIds || []) + .map((id) => String(id || '').trim()) + .filter(Boolean) + ) + ) + if (normalizedSessionIds.length === 0) { + return { success: true, counts: {} } + } + + try { + const counts: Record = {} + for (let i = 0; i < normalizedSessionIds.length; i += 1) { + const sessionId = normalizedSessionIds[i] + const outCount = [0] + const result = this.wcdbGetMessageCount(this.handle, sessionId, outCount) + counts[sessionId] = result === 0 && Number.isFinite(outCount[0]) ? Math.max(0, Math.floor(outCount[0])) : 0 + + if (i > 0 && i % 160 === 0) { + await new Promise(resolve => setImmediate(resolve)) + } + } + return { success: true, counts } + } catch (e) { + return { success: false, error: String(e) } + } + } + async getDisplayNames(usernames: string[]): Promise<{ success: boolean; map?: Record; error?: string }> { if (!this.ensureReady()) { return { success: false, error: 'WCDB 未连接' } @@ -2093,4 +2127,3 @@ export class WcdbCore { }) } } - diff --git a/electron/services/wcdbService.ts b/electron/services/wcdbService.ts index b8834f6..a77378e 100644 --- a/electron/services/wcdbService.ts +++ b/electron/services/wcdbService.ts @@ -218,6 +218,10 @@ export class WcdbService { return this.callWorker('getMessageCount', { sessionId }) } + async getMessageCounts(sessionIds: string[]): Promise<{ success: boolean; counts?: Record; error?: string }> { + return this.callWorker('getMessageCounts', { sessionIds }) + } + /** * 获取联系人昵称 */ diff --git a/electron/wcdbWorker.ts b/electron/wcdbWorker.ts index d95f5f6..f2a4ddd 100644 --- a/electron/wcdbWorker.ts +++ b/electron/wcdbWorker.ts @@ -54,6 +54,9 @@ if (parentPort) { case 'getMessageCount': result = await core.getMessageCount(payload.sessionId) break + case 'getMessageCounts': + result = await core.getMessageCounts(payload.sessionIds) + break case 'getDisplayNames': result = await core.getDisplayNames(payload.usernames) break diff --git a/src/pages/ExportPage.tsx b/src/pages/ExportPage.tsx index d0d1c25..a2305b8 100644 --- a/src/pages/ExportPage.tsx +++ b/src/pages/ExportPage.tsx @@ -624,6 +624,10 @@ function ExportPage() { const count = prev[session.username] if (typeof count === 'number') { next[session.username] = count + continue + } + if (typeof session.messageCountHint === 'number' && Number.isFinite(session.messageCountHint) && session.messageCountHint >= 0) { + next[session.username] = Math.floor(session.messageCountHint) } } return next diff --git a/src/types/models.ts b/src/types/models.ts index 7eec446..0727b93 100644 --- a/src/types/models.ts +++ b/src/types/models.ts @@ -7,6 +7,7 @@ export interface ChatSession { sortTimestamp: number // 用于排序 lastTimestamp: number // 用于显示时间 lastMsgType: number + messageCountHint?: number // 会话总消息数提示(若底层直接可取) displayName?: string avatarUrl?: string lastMsgSender?: string