Files
weflow-new/electron/services/messageCacheService.ts
T
2026-06-30 00:18:31 +08:00

130 lines
3.7 KiB
TypeScript

import { join, dirname } from 'path'
import { existsSync, mkdirSync, readFileSync, rmSync, promises as fsPromises } from 'fs'
import { app } from 'electron'
import { ConfigService } from './config'
export interface SessionMessageCacheEntry {
version?: number
updatedAt: number
messages: any[]
}
export class MessageCacheService {
private static readonly CACHE_VERSION = 3
private readonly cacheFilePath: string
private cache: Record<string, SessionMessageCacheEntry> = {}
private readonly sessionLimit = 150
private readonly maxSessionEntries = 48
private persistTimer: ReturnType<typeof setTimeout> | null = null
private persistInFlight = false
private persistQueued = false
constructor(cacheBasePath?: string) {
const basePath = cacheBasePath && cacheBasePath.trim().length > 0
? cacheBasePath
: ConfigService.getInstance().getCacheBasePath()
this.cacheFilePath = join(basePath, 'session-messages.json')
this.ensureCacheDir()
this.loadCache()
}
private ensureCacheDir() {
const dir = dirname(this.cacheFilePath)
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true })
}
}
private loadCache() {
if (!existsSync(this.cacheFilePath)) return
try {
const raw = readFileSync(this.cacheFilePath, 'utf8')
const parsed = JSON.parse(raw)
if (parsed && typeof parsed === 'object') {
this.cache = Object.fromEntries(
Object.entries(parsed as Record<string, SessionMessageCacheEntry>)
.filter(([, entry]) => entry?.version === MessageCacheService.CACHE_VERSION)
)
this.pruneSessionEntries()
}
} catch (error) {
console.error('MessageCacheService: 载入缓存失败', error)
this.cache = {}
}
}
private pruneSessionEntries(): void {
const entries = Object.entries(this.cache || {})
if (entries.length <= this.maxSessionEntries) return
entries.sort((left, right) => {
const leftAt = Number(left[1]?.updatedAt || 0)
const rightAt = Number(right[1]?.updatedAt || 0)
return rightAt - leftAt
})
this.cache = Object.fromEntries(entries.slice(0, this.maxSessionEntries))
}
get(sessionId: string): SessionMessageCacheEntry | undefined {
return this.cache[sessionId]
}
set(sessionId: string, messages: any[]): void {
if (!sessionId) return
const trimmed = messages.length > this.sessionLimit
? messages.slice(-this.sessionLimit)
: messages.slice()
this.cache[sessionId] = {
version: MessageCacheService.CACHE_VERSION,
updatedAt: Date.now(),
messages: trimmed
}
this.pruneSessionEntries()
this.schedulePersist()
}
private schedulePersist(): void {
this.persistQueued = true
if (this.persistTimer) return
this.persistTimer = setTimeout(() => {
this.persistTimer = null
void this.persist()
}, 250)
}
private async persist() {
if (this.persistInFlight) {
this.schedulePersist()
return
}
if (!this.persistQueued) return
this.persistQueued = false
this.persistInFlight = true
try {
await fsPromises.writeFile(this.cacheFilePath, JSON.stringify(this.cache), 'utf8')
} catch (error) {
console.error('MessageCacheService: 保存缓存失败', error)
} finally {
this.persistInFlight = false
if (this.persistQueued) {
this.schedulePersist()
}
}
}
clear(): void {
this.cache = {}
this.persistQueued = false
if (this.persistTimer) {
clearTimeout(this.persistTimer)
this.persistTimer = null
}
try {
rmSync(this.cacheFilePath, { force: true })
} catch (error) {
console.error('MessageCacheService: 清理缓存失败', error)
}
}
}