mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 15:25:50 +00:00
fix(stats): ensure accurate transfer red-packet and call counts in detail panels
This commit is contained in:
@@ -1159,6 +1159,7 @@ function registerIpcHandlers() {
|
|||||||
includeRelations?: boolean
|
includeRelations?: boolean
|
||||||
forceRefresh?: boolean
|
forceRefresh?: boolean
|
||||||
allowStaleCache?: boolean
|
allowStaleCache?: boolean
|
||||||
|
preferAccurateSpecialTypes?: boolean
|
||||||
}) => {
|
}) => {
|
||||||
return chatService.getExportSessionStats(sessionIds, options)
|
return chatService.getExportSessionStats(sessionIds, options)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
getSessionDetailExtra: (sessionId: string) => ipcRenderer.invoke('chat:getSessionDetailExtra', sessionId),
|
getSessionDetailExtra: (sessionId: string) => ipcRenderer.invoke('chat:getSessionDetailExtra', sessionId),
|
||||||
getExportSessionStats: (
|
getExportSessionStats: (
|
||||||
sessionIds: string[],
|
sessionIds: string[],
|
||||||
options?: { includeRelations?: boolean; forceRefresh?: boolean; allowStaleCache?: boolean }
|
options?: { includeRelations?: boolean; forceRefresh?: boolean; allowStaleCache?: boolean; preferAccurateSpecialTypes?: boolean }
|
||||||
) => ipcRenderer.invoke('chat:getExportSessionStats', sessionIds, options),
|
) => ipcRenderer.invoke('chat:getExportSessionStats', sessionIds, options),
|
||||||
getGroupMyMessageCountHint: (chatroomId: string) =>
|
getGroupMyMessageCountHint: (chatroomId: string) =>
|
||||||
ipcRenderer.invoke('chat:getGroupMyMessageCountHint', chatroomId),
|
ipcRenderer.invoke('chat:getGroupMyMessageCountHint', chatroomId),
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ interface ExportSessionStatsOptions {
|
|||||||
includeRelations?: boolean
|
includeRelations?: boolean
|
||||||
forceRefresh?: boolean
|
forceRefresh?: boolean
|
||||||
allowStaleCache?: boolean
|
allowStaleCache?: boolean
|
||||||
|
preferAccurateSpecialTypes?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExportSessionStatsCacheMeta {
|
interface ExportSessionStatsCacheMeta {
|
||||||
@@ -2365,9 +2366,59 @@ class ChatService {
|
|||||||
return this.extractXmlValue(content, 'type')
|
return this.extractXmlValue(content, 'type')
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildXmlTypeLikeExpr(columnName: string, xmlType: '2000' | '2001'): string {
|
private async collectSpecialMessageCountsByCursorScan(sessionId: string): Promise<{
|
||||||
const colExpr = `LOWER(CAST(COALESCE(${this.quoteSqlIdentifier(columnName)}, '') AS TEXT))`
|
transferMessages: number
|
||||||
return `${colExpr} LIKE '%<type>${xmlType}</type>%'`
|
redPacketMessages: number
|
||||||
|
callMessages: number
|
||||||
|
}> {
|
||||||
|
const counters = {
|
||||||
|
transferMessages: 0,
|
||||||
|
redPacketMessages: 0,
|
||||||
|
callMessages: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const cursorResult = await wcdbService.openMessageCursorLite(sessionId, 500, false, 0, 0)
|
||||||
|
if (!cursorResult.success || !cursorResult.cursor) {
|
||||||
|
return counters
|
||||||
|
}
|
||||||
|
|
||||||
|
const cursor = cursorResult.cursor
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
const batch = await wcdbService.fetchMessageBatch(cursor)
|
||||||
|
if (!batch.success) break
|
||||||
|
const rows = Array.isArray(batch.rows) ? batch.rows as Record<string, any>[] : []
|
||||||
|
for (const row of rows) {
|
||||||
|
const localType = this.getRowInt(row, ['local_type', 'localType', 'type', 'msg_type', 'msgType', 'WCDB_CT_local_type'], 1)
|
||||||
|
if (localType === 50) {
|
||||||
|
counters.callMessages += 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (localType === 8589934592049) {
|
||||||
|
counters.transferMessages += 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (localType === 8594229559345) {
|
||||||
|
counters.redPacketMessages += 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (localType !== 49) continue
|
||||||
|
|
||||||
|
const rawMessageContent = this.getRowField(row, ['message_content', 'messageContent', 'msg_content', 'msgContent', 'content', 'WCDB_CT_message_content'])
|
||||||
|
const rawCompressContent = this.getRowField(row, ['compress_content', 'compressContent', 'compressed_content', 'compressedContent', 'WCDB_CT_compress_content'])
|
||||||
|
const content = this.decodeMessageContent(rawMessageContent, rawCompressContent)
|
||||||
|
const xmlType = this.extractType49XmlTypeForStats(content)
|
||||||
|
if (xmlType === '2000') counters.transferMessages += 1
|
||||||
|
if (xmlType === '2001') counters.redPacketMessages += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!batch.hasMore || rows.length === 0) break
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await wcdbService.closeMessageCursor(cursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
return counters
|
||||||
}
|
}
|
||||||
|
|
||||||
private async collectSessionExportStatsByCursorScan(
|
private async collectSessionExportStatsByCursorScan(
|
||||||
@@ -2474,7 +2525,8 @@ class ChatService {
|
|||||||
|
|
||||||
private async collectSessionExportStats(
|
private async collectSessionExportStats(
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
selfIdentitySet: Set<string>
|
selfIdentitySet: Set<string>,
|
||||||
|
preferAccurateSpecialTypes: boolean = false
|
||||||
): Promise<ExportSessionStats> {
|
): Promise<ExportSessionStats> {
|
||||||
const stats: ExportSessionStats = {
|
const stats: ExportSessionStats = {
|
||||||
totalMessages: 0,
|
totalMessages: 0,
|
||||||
@@ -2511,18 +2563,6 @@ class ChatService {
|
|||||||
const timeCol = this.pickFirstColumn(columnSet, ['create_time', 'createtime', 'msg_create_time', 'time'])
|
const timeCol = this.pickFirstColumn(columnSet, ['create_time', 'createtime', 'msg_create_time', 'time'])
|
||||||
const senderCol = this.pickFirstColumn(columnSet, ['sender_username', 'senderusername', 'sender'])
|
const senderCol = this.pickFirstColumn(columnSet, ['sender_username', 'senderusername', 'sender'])
|
||||||
const isSendCol = this.pickFirstColumn(columnSet, ['computed_is_send', 'computedissend', 'is_send', 'issend'])
|
const isSendCol = this.pickFirstColumn(columnSet, ['computed_is_send', 'computedissend', 'is_send', 'issend'])
|
||||||
const messageContentCol = this.pickFirstColumn(columnSet, ['message_content', 'messagecontent', 'msg_content', 'msgcontent', 'content'])
|
|
||||||
const compressContentCol = this.pickFirstColumn(columnSet, ['compress_content', 'compresscontent', 'compressed_content', 'compressedcontent'])
|
|
||||||
|
|
||||||
const transferXmlConditions: string[] = []
|
|
||||||
if (messageContentCol) transferXmlConditions.push(this.buildXmlTypeLikeExpr(messageContentCol, '2000'))
|
|
||||||
if (compressContentCol) transferXmlConditions.push(this.buildXmlTypeLikeExpr(compressContentCol, '2000'))
|
|
||||||
const transferXmlCond = transferXmlConditions.length > 0 ? `(${transferXmlConditions.join(' OR ')})` : '0'
|
|
||||||
|
|
||||||
const redPacketXmlConditions: string[] = []
|
|
||||||
if (messageContentCol) redPacketXmlConditions.push(this.buildXmlTypeLikeExpr(messageContentCol, '2001'))
|
|
||||||
if (compressContentCol) redPacketXmlConditions.push(this.buildXmlTypeLikeExpr(compressContentCol, '2001'))
|
|
||||||
const redPacketXmlCond = redPacketXmlConditions.length > 0 ? `(${redPacketXmlConditions.join(' OR ')})` : '0'
|
|
||||||
|
|
||||||
const selectParts: string[] = [
|
const selectParts: string[] = [
|
||||||
'COUNT(*) AS total_messages',
|
'COUNT(*) AS total_messages',
|
||||||
@@ -2531,8 +2571,8 @@ class ChatService {
|
|||||||
typeCol ? `SUM(CASE WHEN ${this.quoteSqlIdentifier(typeCol)} = 43 THEN 1 ELSE 0 END) AS video_messages` : '0 AS video_messages',
|
typeCol ? `SUM(CASE WHEN ${this.quoteSqlIdentifier(typeCol)} = 43 THEN 1 ELSE 0 END) AS video_messages` : '0 AS video_messages',
|
||||||
typeCol ? `SUM(CASE WHEN ${this.quoteSqlIdentifier(typeCol)} = 47 THEN 1 ELSE 0 END) AS emoji_messages` : '0 AS emoji_messages',
|
typeCol ? `SUM(CASE WHEN ${this.quoteSqlIdentifier(typeCol)} = 47 THEN 1 ELSE 0 END) AS emoji_messages` : '0 AS emoji_messages',
|
||||||
typeCol ? `SUM(CASE WHEN ${this.quoteSqlIdentifier(typeCol)} = 50 THEN 1 ELSE 0 END) AS call_messages` : '0 AS call_messages',
|
typeCol ? `SUM(CASE WHEN ${this.quoteSqlIdentifier(typeCol)} = 50 THEN 1 ELSE 0 END) AS call_messages` : '0 AS call_messages',
|
||||||
typeCol ? `SUM(CASE WHEN ${this.quoteSqlIdentifier(typeCol)} = 8589934592049 THEN 1 WHEN ${this.quoteSqlIdentifier(typeCol)} = 49 AND ${transferXmlCond} THEN 1 ELSE 0 END) AS transfer_messages` : '0 AS transfer_messages',
|
typeCol ? `SUM(CASE WHEN ${this.quoteSqlIdentifier(typeCol)} = 8589934592049 THEN 1 ELSE 0 END) AS transfer_messages` : '0 AS transfer_messages',
|
||||||
typeCol ? `SUM(CASE WHEN ${this.quoteSqlIdentifier(typeCol)} = 8594229559345 THEN 1 WHEN ${this.quoteSqlIdentifier(typeCol)} = 49 AND ${redPacketXmlCond} THEN 1 ELSE 0 END) AS red_packet_messages` : '0 AS red_packet_messages',
|
typeCol ? `SUM(CASE WHEN ${this.quoteSqlIdentifier(typeCol)} = 8594229559345 THEN 1 ELSE 0 END) AS red_packet_messages` : '0 AS red_packet_messages',
|
||||||
timeCol ? `MIN(${this.quoteSqlIdentifier(timeCol)}) AS first_timestamp` : 'NULL AS first_timestamp',
|
timeCol ? `MIN(${this.quoteSqlIdentifier(timeCol)}) AS first_timestamp` : 'NULL AS first_timestamp',
|
||||||
timeCol ? `MAX(${this.quoteSqlIdentifier(timeCol)}) AS last_timestamp` : 'NULL AS last_timestamp'
|
timeCol ? `MAX(${this.quoteSqlIdentifier(timeCol)}) AS last_timestamp` : 'NULL AS last_timestamp'
|
||||||
]
|
]
|
||||||
@@ -2628,6 +2668,17 @@ class ChatService {
|
|||||||
return this.collectSessionExportStatsByCursorScan(sessionId, selfIdentitySet)
|
return this.collectSessionExportStatsByCursorScan(sessionId, selfIdentitySet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (preferAccurateSpecialTypes) {
|
||||||
|
try {
|
||||||
|
const preciseCounters = await this.collectSpecialMessageCountsByCursorScan(sessionId)
|
||||||
|
stats.transferMessages = preciseCounters.transferMessages
|
||||||
|
stats.redPacketMessages = preciseCounters.redPacketMessages
|
||||||
|
stats.callMessages = preciseCounters.callMessages
|
||||||
|
} catch {
|
||||||
|
// 保留聚合统计结果作为兜底
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isGroup) {
|
if (isGroup) {
|
||||||
stats.groupActiveSpeakers = senderIdentities.size
|
stats.groupActiveSpeakers = senderIdentities.size
|
||||||
if (Number.isFinite(stats.groupMyMessages)) {
|
if (Number.isFinite(stats.groupMyMessages)) {
|
||||||
@@ -2728,9 +2779,10 @@ class ChatService {
|
|||||||
private async computeSessionExportStats(
|
private async computeSessionExportStats(
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
selfIdentitySet: Set<string>,
|
selfIdentitySet: Set<string>,
|
||||||
includeRelations: boolean
|
includeRelations: boolean,
|
||||||
|
preferAccurateSpecialTypes: boolean = false
|
||||||
): Promise<ExportSessionStats> {
|
): Promise<ExportSessionStats> {
|
||||||
const stats = await this.collectSessionExportStats(sessionId, selfIdentitySet)
|
const stats = await this.collectSessionExportStats(sessionId, selfIdentitySet, preferAccurateSpecialTypes)
|
||||||
const isGroup = sessionId.endsWith('@chatroom')
|
const isGroup = sessionId.endsWith('@chatroom')
|
||||||
|
|
||||||
if (isGroup) {
|
if (isGroup) {
|
||||||
@@ -2768,7 +2820,8 @@ class ChatService {
|
|||||||
private async computeSessionExportStatsBatch(
|
private async computeSessionExportStatsBatch(
|
||||||
sessionIds: string[],
|
sessionIds: string[],
|
||||||
includeRelations: boolean,
|
includeRelations: boolean,
|
||||||
selfIdentitySet: Set<string>
|
selfIdentitySet: Set<string>,
|
||||||
|
preferAccurateSpecialTypes: boolean = false
|
||||||
): Promise<Record<string, ExportSessionStats>> {
|
): Promise<Record<string, ExportSessionStats>> {
|
||||||
const normalizedSessionIds = Array.from(
|
const normalizedSessionIds = Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
@@ -2824,7 +2877,7 @@ class ChatService {
|
|||||||
|
|
||||||
await this.forEachWithConcurrency(normalizedSessionIds, 3, async (sessionId) => {
|
await this.forEachWithConcurrency(normalizedSessionIds, 3, async (sessionId) => {
|
||||||
try {
|
try {
|
||||||
const stats = await this.collectSessionExportStats(sessionId, selfIdentitySet)
|
const stats = await this.collectSessionExportStats(sessionId, selfIdentitySet, preferAccurateSpecialTypes)
|
||||||
if (sessionId.endsWith('@chatroom')) {
|
if (sessionId.endsWith('@chatroom')) {
|
||||||
stats.groupMemberCount = typeof memberCountMap[sessionId] === 'number'
|
stats.groupMemberCount = typeof memberCountMap[sessionId] === 'number'
|
||||||
? Math.max(0, Math.floor(memberCountMap[sessionId]))
|
? Math.max(0, Math.floor(memberCountMap[sessionId]))
|
||||||
@@ -2851,8 +2904,13 @@ class ChatService {
|
|||||||
private async getOrComputeSessionExportStats(
|
private async getOrComputeSessionExportStats(
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
includeRelations: boolean,
|
includeRelations: boolean,
|
||||||
selfIdentitySet: Set<string>
|
selfIdentitySet: Set<string>,
|
||||||
|
preferAccurateSpecialTypes: boolean = false
|
||||||
): Promise<ExportSessionStats> {
|
): Promise<ExportSessionStats> {
|
||||||
|
if (preferAccurateSpecialTypes) {
|
||||||
|
return this.computeSessionExportStats(sessionId, selfIdentitySet, includeRelations, true)
|
||||||
|
}
|
||||||
|
|
||||||
const scopedKey = this.buildScopedSessionStatsKey(sessionId)
|
const scopedKey = this.buildScopedSessionStatsKey(sessionId)
|
||||||
|
|
||||||
if (!includeRelations) {
|
if (!includeRelations) {
|
||||||
@@ -2866,7 +2924,7 @@ class ChatService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const targetMap = includeRelations ? this.sessionStatsPendingFull : this.sessionStatsPendingBasic
|
const targetMap = includeRelations ? this.sessionStatsPendingFull : this.sessionStatsPendingBasic
|
||||||
const pending = this.computeSessionExportStats(sessionId, selfIdentitySet, includeRelations)
|
const pending = this.computeSessionExportStats(sessionId, selfIdentitySet, includeRelations, false)
|
||||||
targetMap.set(scopedKey, pending)
|
targetMap.set(scopedKey, pending)
|
||||||
try {
|
try {
|
||||||
return await pending
|
return await pending
|
||||||
@@ -5275,6 +5333,7 @@ class ChatService {
|
|||||||
const includeRelations = options.includeRelations ?? true
|
const includeRelations = options.includeRelations ?? true
|
||||||
const forceRefresh = options.forceRefresh === true
|
const forceRefresh = options.forceRefresh === true
|
||||||
const allowStaleCache = options.allowStaleCache === true
|
const allowStaleCache = options.allowStaleCache === true
|
||||||
|
const preferAccurateSpecialTypes = options.preferAccurateSpecialTypes === true
|
||||||
|
|
||||||
const normalizedSessionIds = Array.from(
|
const normalizedSessionIds = Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
@@ -5298,7 +5357,7 @@ class ChatService {
|
|||||||
? this.getGroupMyMessageCountHintEntry(sessionId)
|
? this.getGroupMyMessageCountHintEntry(sessionId)
|
||||||
: null
|
: null
|
||||||
const cachedResult = this.getSessionStatsCacheEntry(sessionId)
|
const cachedResult = this.getSessionStatsCacheEntry(sessionId)
|
||||||
if (!forceRefresh) {
|
if (!forceRefresh && !preferAccurateSpecialTypes) {
|
||||||
if (cachedResult && this.supportsRequestedRelation(cachedResult.entry, includeRelations)) {
|
if (cachedResult && this.supportsRequestedRelation(cachedResult.entry, includeRelations)) {
|
||||||
const stale = now - cachedResult.entry.updatedAt > this.sessionStatsCacheTtlMs
|
const stale = now - cachedResult.entry.updatedAt > this.sessionStatsCacheTtlMs
|
||||||
if (!stale || allowStaleCache) {
|
if (!stale || allowStaleCache) {
|
||||||
@@ -5334,7 +5393,7 @@ class ChatService {
|
|||||||
if (pendingSessionIds.length === 1) {
|
if (pendingSessionIds.length === 1) {
|
||||||
const sessionId = pendingSessionIds[0]
|
const sessionId = pendingSessionIds[0]
|
||||||
try {
|
try {
|
||||||
const stats = await this.getOrComputeSessionExportStats(sessionId, includeRelations, selfIdentitySet)
|
const stats = await this.getOrComputeSessionExportStats(sessionId, includeRelations, selfIdentitySet, preferAccurateSpecialTypes)
|
||||||
resultMap[sessionId] = stats
|
resultMap[sessionId] = stats
|
||||||
const updatedAt = this.setSessionStatsCacheEntry(sessionId, stats, includeRelations)
|
const updatedAt = this.setSessionStatsCacheEntry(sessionId, stats, includeRelations)
|
||||||
cacheMeta[sessionId] = {
|
cacheMeta[sessionId] = {
|
||||||
@@ -5352,7 +5411,8 @@ class ChatService {
|
|||||||
const batchedStatsMap = await this.computeSessionExportStatsBatch(
|
const batchedStatsMap = await this.computeSessionExportStatsBatch(
|
||||||
pendingSessionIds,
|
pendingSessionIds,
|
||||||
includeRelations,
|
includeRelations,
|
||||||
selfIdentitySet
|
selfIdentitySet,
|
||||||
|
preferAccurateSpecialTypes
|
||||||
)
|
)
|
||||||
for (const sessionId of pendingSessionIds) {
|
for (const sessionId of pendingSessionIds) {
|
||||||
const stats = batchedStatsMap[sessionId]
|
const stats = batchedStatsMap[sessionId]
|
||||||
@@ -5375,7 +5435,7 @@ class ChatService {
|
|||||||
if (!usedBatchedCompute) {
|
if (!usedBatchedCompute) {
|
||||||
await this.forEachWithConcurrency(pendingSessionIds, 3, async (sessionId) => {
|
await this.forEachWithConcurrency(pendingSessionIds, 3, async (sessionId) => {
|
||||||
try {
|
try {
|
||||||
const stats = await this.getOrComputeSessionExportStats(sessionId, includeRelations, selfIdentitySet)
|
const stats = await this.getOrComputeSessionExportStats(sessionId, includeRelations, selfIdentitySet, preferAccurateSpecialTypes)
|
||||||
resultMap[sessionId] = stats
|
resultMap[sessionId] = stats
|
||||||
const updatedAt = this.setSessionStatsCacheEntry(sessionId, stats, includeRelations)
|
const updatedAt = this.setSessionStatsCacheEntry(sessionId, stats, includeRelations)
|
||||||
cacheMeta[sessionId] = {
|
cacheMeta[sessionId] = {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { join, dirname } from 'path'
|
|||||||
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs'
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs'
|
||||||
import { ConfigService } from './config'
|
import { ConfigService } from './config'
|
||||||
|
|
||||||
const CACHE_VERSION = 1
|
const CACHE_VERSION = 2
|
||||||
const MAX_SESSION_ENTRIES_PER_SCOPE = 2000
|
const MAX_SESSION_ENTRIES_PER_SCOPE = 2000
|
||||||
const MAX_SCOPE_ENTRIES = 12
|
const MAX_SCOPE_ENTRIES = 12
|
||||||
|
|
||||||
@@ -53,16 +53,19 @@ function normalizeStats(raw: unknown): SessionStatsCacheStats | null {
|
|||||||
const imageMessages = toNonNegativeInt(source.imageMessages)
|
const imageMessages = toNonNegativeInt(source.imageMessages)
|
||||||
const videoMessages = toNonNegativeInt(source.videoMessages)
|
const videoMessages = toNonNegativeInt(source.videoMessages)
|
||||||
const emojiMessages = toNonNegativeInt(source.emojiMessages)
|
const emojiMessages = toNonNegativeInt(source.emojiMessages)
|
||||||
const transferMessages = toNonNegativeInt(source.transferMessages) ?? 0
|
const transferMessages = toNonNegativeInt(source.transferMessages)
|
||||||
const redPacketMessages = toNonNegativeInt(source.redPacketMessages) ?? 0
|
const redPacketMessages = toNonNegativeInt(source.redPacketMessages)
|
||||||
const callMessages = toNonNegativeInt(source.callMessages) ?? 0
|
const callMessages = toNonNegativeInt(source.callMessages)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
totalMessages === undefined ||
|
totalMessages === undefined ||
|
||||||
voiceMessages === undefined ||
|
voiceMessages === undefined ||
|
||||||
imageMessages === undefined ||
|
imageMessages === undefined ||
|
||||||
videoMessages === undefined ||
|
videoMessages === undefined ||
|
||||||
emojiMessages === undefined
|
emojiMessages === undefined ||
|
||||||
|
transferMessages === undefined ||
|
||||||
|
redPacketMessages === undefined ||
|
||||||
|
callMessages === undefined
|
||||||
) {
|
) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -154,6 +157,11 @@ export class SessionStatsCacheService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const payload = parsed as Record<string, unknown>
|
const payload = parsed as Record<string, unknown>
|
||||||
|
const version = Number(payload.version)
|
||||||
|
if (!Number.isFinite(version) || version !== CACHE_VERSION) {
|
||||||
|
this.store = { version: CACHE_VERSION, scopes: {} }
|
||||||
|
return
|
||||||
|
}
|
||||||
const scopesRaw = payload.scopes
|
const scopesRaw = payload.scopes
|
||||||
if (!scopesRaw || typeof scopesRaw !== 'object') {
|
if (!scopesRaw || typeof scopesRaw !== 'object') {
|
||||||
this.store = { version: CACHE_VERSION, scopes: {} }
|
this.store = { version: CACHE_VERSION, scopes: {} }
|
||||||
|
|||||||
@@ -925,7 +925,7 @@ function ChatPage(_props: ChatPageProps) {
|
|||||||
window.electronAPI.chat.getSessionDetailExtra(normalizedSessionId),
|
window.electronAPI.chat.getSessionDetailExtra(normalizedSessionId),
|
||||||
window.electronAPI.chat.getExportSessionStats(
|
window.electronAPI.chat.getExportSessionStats(
|
||||||
[normalizedSessionId],
|
[normalizedSessionId],
|
||||||
{ includeRelations: false, allowStaleCache: true }
|
{ includeRelations: false, forceRefresh: true, preferAccurateSpecialTypes: true }
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -983,7 +983,7 @@ function ChatPage(_props: ChatPageProps) {
|
|||||||
try {
|
try {
|
||||||
const relationResult = await window.electronAPI.chat.getExportSessionStats(
|
const relationResult = await window.electronAPI.chat.getExportSessionStats(
|
||||||
[normalizedSessionId],
|
[normalizedSessionId],
|
||||||
{ includeRelations: true, allowStaleCache: true }
|
{ includeRelations: true, forceRefresh: true, preferAccurateSpecialTypes: true }
|
||||||
)
|
)
|
||||||
if (requestSeq !== detailRequestSeqRef.current) return
|
if (requestSeq !== detailRequestSeqRef.current) return
|
||||||
|
|
||||||
@@ -1007,7 +1007,7 @@ function ChatPage(_props: ChatPageProps) {
|
|||||||
try {
|
try {
|
||||||
const freshResult = await window.electronAPI.chat.getExportSessionStats(
|
const freshResult = await window.electronAPI.chat.getExportSessionStats(
|
||||||
[normalizedSessionId],
|
[normalizedSessionId],
|
||||||
{ includeRelations: true, forceRefresh: true }
|
{ includeRelations: true, forceRefresh: true, preferAccurateSpecialTypes: true }
|
||||||
)
|
)
|
||||||
if (requestSeq !== detailRequestSeqRef.current) return
|
if (requestSeq !== detailRequestSeqRef.current) return
|
||||||
if (freshResult.success && freshResult.data) {
|
if (freshResult.success && freshResult.data) {
|
||||||
|
|||||||
@@ -3499,7 +3499,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, allowStaleCache: true }
|
{ includeRelations: false, forceRefresh: true, preferAccurateSpecialTypes: true }
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -3549,7 +3549,7 @@ function ExportPage() {
|
|||||||
try {
|
try {
|
||||||
const freshResult = await window.electronAPI.chat.getExportSessionStats(
|
const freshResult = await window.electronAPI.chat.getExportSessionStats(
|
||||||
[normalizedSessionId],
|
[normalizedSessionId],
|
||||||
{ includeRelations: refreshIncludeRelations, forceRefresh: true }
|
{ includeRelations: refreshIncludeRelations, forceRefresh: true, preferAccurateSpecialTypes: true }
|
||||||
)
|
)
|
||||||
if (requestSeq !== detailRequestSeqRef.current) return
|
if (requestSeq !== detailRequestSeqRef.current) return
|
||||||
if (freshResult.success && freshResult.data) {
|
if (freshResult.success && freshResult.data) {
|
||||||
@@ -3591,7 +3591,7 @@ function ExportPage() {
|
|||||||
try {
|
try {
|
||||||
const relationResult = await window.electronAPI.chat.getExportSessionStats(
|
const relationResult = await window.electronAPI.chat.getExportSessionStats(
|
||||||
[normalizedSessionId],
|
[normalizedSessionId],
|
||||||
{ includeRelations: true, allowStaleCache: true }
|
{ includeRelations: true, forceRefresh: true, preferAccurateSpecialTypes: true }
|
||||||
)
|
)
|
||||||
if (requestSeq !== detailRequestSeqRef.current) return
|
if (requestSeq !== detailRequestSeqRef.current) return
|
||||||
|
|
||||||
@@ -3615,7 +3615,7 @@ function ExportPage() {
|
|||||||
try {
|
try {
|
||||||
const freshResult = await window.electronAPI.chat.getExportSessionStats(
|
const freshResult = await window.electronAPI.chat.getExportSessionStats(
|
||||||
[normalizedSessionId],
|
[normalizedSessionId],
|
||||||
{ includeRelations: true, forceRefresh: true }
|
{ includeRelations: true, forceRefresh: true, preferAccurateSpecialTypes: true }
|
||||||
)
|
)
|
||||||
if (requestSeq !== detailRequestSeqRef.current) return
|
if (requestSeq !== detailRequestSeqRef.current) return
|
||||||
if (freshResult.success && freshResult.data) {
|
if (freshResult.success && freshResult.data) {
|
||||||
|
|||||||
2
src/types/electron.d.ts
vendored
2
src/types/electron.d.ts
vendored
@@ -242,7 +242,7 @@ export interface ElectronAPI {
|
|||||||
}>
|
}>
|
||||||
getExportSessionStats: (
|
getExportSessionStats: (
|
||||||
sessionIds: string[],
|
sessionIds: string[],
|
||||||
options?: { includeRelations?: boolean; forceRefresh?: boolean; allowStaleCache?: boolean }
|
options?: { includeRelations?: boolean; forceRefresh?: boolean; allowStaleCache?: boolean; preferAccurateSpecialTypes?: boolean }
|
||||||
) => Promise<{
|
) => Promise<{
|
||||||
success: boolean
|
success: boolean
|
||||||
data?: Record<string, {
|
data?: Record<string, {
|
||||||
|
|||||||
Reference in New Issue
Block a user