perf(export): speed up session detail stats loading

This commit is contained in:
aits2026
2026-03-05 15:39:59 +08:00
parent 5945942acd
commit 2a9f0f24fd
2 changed files with 56 additions and 92 deletions

View File

@@ -5209,39 +5209,36 @@ class ChatService {
return { success: true, detail: cachedDetail.detail } return { success: true, detail: cachedDetail.detail }
} }
const [tableStatsResult, statsResult] = await Promise.allSettled([ const tableStatsResult = await wcdbService.getMessageTableStats(normalizedSessionId)
wcdbService.getMessageTableStats(normalizedSessionId),
(async (): Promise<ExportSessionStats | null> => {
const cachedStats = this.getSessionStatsCacheEntry(normalizedSessionId)
if (cachedStats && this.supportsRequestedRelation(cachedStats.entry, false)) {
return this.fromSessionStatsCacheStats(cachedStats.entry.stats)
}
const myWxid = this.configService.get('myWxid') || ''
const selfIdentitySet = new Set<string>(this.buildIdentityKeys(myWxid))
const stats = await this.getOrComputeSessionExportStats(normalizedSessionId, false, selfIdentitySet)
this.setSessionStatsCacheEntry(normalizedSessionId, stats, false)
return stats
})()
])
const statsSnapshot = statsResult.status === 'fulfilled'
? statsResult.value
: null
const firstMessageTime = statsSnapshot && Number.isFinite(statsSnapshot.firstTimestamp)
? Math.max(0, Math.floor(statsSnapshot.firstTimestamp as number))
: undefined
const latestMessageTime = statsSnapshot && Number.isFinite(statsSnapshot.lastTimestamp)
? Math.max(0, Math.floor(statsSnapshot.lastTimestamp as number))
: undefined
const messageTables: { dbName: string; tableName: string; count: number }[] = [] const messageTables: { dbName: string; tableName: string; count: number }[] = []
if (tableStatsResult.status === 'fulfilled' && tableStatsResult.value.success && tableStatsResult.value.tables) { let firstMessageTime: number | undefined
for (const row of tableStatsResult.value.tables) { let latestMessageTime: number | undefined
if (tableStatsResult.success && tableStatsResult.tables) {
for (const row of tableStatsResult.tables) {
messageTables.push({ messageTables.push({
dbName: basename(row.db_path || ''), dbName: basename(row.db_path || ''),
tableName: row.table_name || '', tableName: row.table_name || '',
count: parseInt(row.count || '0', 10) count: parseInt(row.count || '0', 10)
}) })
const firstTs = this.getRowInt(
row,
['first_timestamp', 'firstTimestamp', 'first_time', 'firstTime', 'min_create_time', 'minCreateTime'],
0
)
if (firstTs > 0 && (firstMessageTime === undefined || firstTs < firstMessageTime)) {
firstMessageTime = firstTs
}
const lastTs = this.getRowInt(
row,
['last_timestamp', 'lastTimestamp', 'last_time', 'lastTime', 'max_create_time', 'maxCreateTime'],
0
)
if (lastTs > 0 && (latestMessageTime === undefined || lastTs > latestMessageTime)) {
latestMessageTime = lastTs
}
} }
} }

View File

@@ -3514,7 +3514,7 @@ function ExportPage() {
window.electronAPI.chat.getSessionDetailExtra(normalizedSessionId), window.electronAPI.chat.getSessionDetailExtra(normalizedSessionId),
window.electronAPI.chat.getExportSessionStats( window.electronAPI.chat.getExportSessionStats(
[normalizedSessionId], [normalizedSessionId],
{ includeRelations: false, forceRefresh: true, preferAccurateSpecialTypes: true } { includeRelations: false, allowStaleCache: true }
) )
]) ])
@@ -3535,59 +3535,56 @@ function ExportPage() {
} }
} }
let refreshIncludeRelations = false
let shouldRefreshStats = false
if (statsResultSettled.status === 'fulfilled' && statsResultSettled.value.success) { if (statsResultSettled.status === 'fulfilled' && statsResultSettled.value.success) {
const metric = statsResultSettled.value.data?.[normalizedSessionId] as SessionExportMetric | undefined const metric = statsResultSettled.value.data?.[normalizedSessionId] as SessionExportMetric | undefined
const cacheMeta = statsResultSettled.value.cache?.[normalizedSessionId] as SessionExportCacheMeta | undefined const cacheMeta = statsResultSettled.value.cache?.[normalizedSessionId] as SessionExportCacheMeta | undefined
refreshIncludeRelations = Boolean(cacheMeta?.includeRelations)
if (metric) { if (metric) {
applySessionDetailStats(normalizedSessionId, metric, cacheMeta, refreshIncludeRelations) applySessionDetailStats(normalizedSessionId, metric, cacheMeta, false)
} else if (cacheMeta) { } else if (cacheMeta) {
setSessionDetail((prev) => { setSessionDetail((prev) => {
if (!prev || prev.wxid !== normalizedSessionId) return prev if (!prev || prev.wxid !== normalizedSessionId) return prev
return { return {
...prev, ...prev,
relationStatsLoaded: refreshIncludeRelations || prev.relationStatsLoaded,
statsUpdatedAt: cacheMeta.updatedAt, statsUpdatedAt: cacheMeta.updatedAt,
statsStale: cacheMeta.stale statsStale: cacheMeta.stale
} }
}) })
} }
shouldRefreshStats = Array.isArray(statsResultSettled.value.needsRefresh) &&
statsResultSettled.value.needsRefresh.includes(normalizedSessionId)
} }
if (shouldRefreshStats) { setIsRefreshingSessionDetailStats(true)
setIsRefreshingSessionDetailStats(true) void (async () => {
void (async () => { try {
try { // 后台精确补算三类重字段(转账/红包/通话),不阻塞首屏基础统计显示。
const freshResult = await window.electronAPI.chat.getExportSessionStats( const freshResult = await window.electronAPI.chat.getExportSessionStats(
[normalizedSessionId], [normalizedSessionId],
{ includeRelations: refreshIncludeRelations, forceRefresh: true, preferAccurateSpecialTypes: true } { includeRelations: false, forceRefresh: true, preferAccurateSpecialTypes: true }
) )
if (requestSeq !== detailRequestSeqRef.current) return if (requestSeq !== detailRequestSeqRef.current) return
if (freshResult.success && freshResult.data) { if (freshResult.success && freshResult.data) {
const metric = freshResult.data[normalizedSessionId] as SessionExportMetric | undefined const metric = freshResult.data[normalizedSessionId] as SessionExportMetric | undefined
const cacheMeta = freshResult.cache?.[normalizedSessionId] as SessionExportCacheMeta | undefined const cacheMeta = freshResult.cache?.[normalizedSessionId] as SessionExportCacheMeta | undefined
if (metric) { if (metric) {
applySessionDetailStats( applySessionDetailStats(normalizedSessionId, metric, cacheMeta, false)
normalizedSessionId, } else if (cacheMeta) {
metric, setSessionDetail((prev) => {
cacheMeta, if (!prev || prev.wxid !== normalizedSessionId) return prev
refreshIncludeRelations ? true : undefined return {
) ...prev,
} statsUpdatedAt: cacheMeta.updatedAt,
} statsStale: cacheMeta.stale
} catch (error) { }
console.error('导出页刷新会话统计失败:', error) })
} finally {
if (requestSeq === detailRequestSeqRef.current) {
setIsRefreshingSessionDetailStats(false)
} }
} }
})() } catch (error) {
} console.error('导出页刷新会话统计失败:', error)
} finally {
if (requestSeq === detailRequestSeqRef.current) {
setIsRefreshingSessionDetailStats(false)
}
}
})()
} catch (error) { } catch (error) {
console.error('导出页加载会话详情补充统计失败:', error) console.error('导出页加载会话详情补充统计失败:', error)
} finally { } finally {
@@ -3619,36 +3616,6 @@ function ExportPage() {
if (metric) { if (metric) {
applySessionDetailStats(normalizedSessionId, metric, cacheMeta, true) applySessionDetailStats(normalizedSessionId, metric, cacheMeta, true)
} }
const needRefresh = relationResult.success &&
Array.isArray(relationResult.needsRefresh) &&
relationResult.needsRefresh.includes(normalizedSessionId)
if (needRefresh) {
setIsRefreshingSessionDetailStats(true)
void (async () => {
try {
const freshResult = await window.electronAPI.chat.getExportSessionStats(
[normalizedSessionId],
{ includeRelations: true, forceRefresh: true, preferAccurateSpecialTypes: true }
)
if (requestSeq !== detailRequestSeqRef.current) return
if (freshResult.success && freshResult.data) {
const freshMetric = freshResult.data[normalizedSessionId] as SessionExportMetric | undefined
const freshMeta = freshResult.cache?.[normalizedSessionId] as SessionExportCacheMeta | undefined
if (freshMetric) {
applySessionDetailStats(normalizedSessionId, freshMetric, freshMeta, true)
}
}
} catch (error) {
console.error('导出页刷新会话关系统计失败:', error)
} finally {
if (requestSeq === detailRequestSeqRef.current) {
setIsRefreshingSessionDetailStats(false)
}
}
})()
}
} catch (error) { } catch (error) {
console.error('导出页加载会话关系统计失败:', error) console.error('导出页加载会话关系统计失败:', error)
} finally { } finally {