mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-24 23:06:51 +00:00
fix:修复了清除缓存功能的缺失
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -56,3 +56,4 @@ Thumbs.db
|
|||||||
*.aps
|
*.aps
|
||||||
|
|
||||||
wcdb/
|
wcdb/
|
||||||
|
*info
|
||||||
|
|||||||
@@ -482,6 +482,40 @@ function registerIpcHandlers() {
|
|||||||
return analyticsService.getTimeDistribution()
|
return analyticsService.getTimeDistribution()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 缓存管理
|
||||||
|
ipcMain.handle('cache:clearAnalytics', async () => {
|
||||||
|
return analyticsService.clearCache()
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('cache:clearImages', async () => {
|
||||||
|
const imageResult = await imageDecryptService.clearCache()
|
||||||
|
const emojiResult = chatService.clearCaches({ includeMessages: false, includeContacts: false, includeEmojis: true })
|
||||||
|
const errors = [imageResult, emojiResult]
|
||||||
|
.filter((result) => !result.success)
|
||||||
|
.map((result) => result.error)
|
||||||
|
.filter(Boolean) as string[]
|
||||||
|
if (errors.length > 0) {
|
||||||
|
return { success: false, error: errors.join('; ') }
|
||||||
|
}
|
||||||
|
return { success: true }
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('cache:clearAll', async () => {
|
||||||
|
const [analyticsResult, imageResult] = await Promise.all([
|
||||||
|
analyticsService.clearCache(),
|
||||||
|
imageDecryptService.clearCache()
|
||||||
|
])
|
||||||
|
const chatResult = chatService.clearCaches()
|
||||||
|
const errors = [analyticsResult, imageResult, chatResult]
|
||||||
|
.filter((result) => !result.success)
|
||||||
|
.map((result) => result.error)
|
||||||
|
.filter(Boolean) as string[]
|
||||||
|
if (errors.length > 0) {
|
||||||
|
return { success: false, error: errors.join('; ') }
|
||||||
|
}
|
||||||
|
return { success: true }
|
||||||
|
})
|
||||||
|
|
||||||
// 群聊分析相关
|
// 群聊分析相关
|
||||||
ipcMain.handle('groupAnalytics:getGroupChats', async () => {
|
ipcMain.handle('groupAnalytics:getGroupChats', async () => {
|
||||||
return groupAnalyticsService.getGroupChats()
|
return groupAnalyticsService.getGroupChats()
|
||||||
|
|||||||
@@ -140,6 +140,13 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 缓存管理
|
||||||
|
cache: {
|
||||||
|
clearAnalytics: () => ipcRenderer.invoke('cache:clearAnalytics'),
|
||||||
|
clearImages: () => ipcRenderer.invoke('cache:clearImages'),
|
||||||
|
clearAll: () => ipcRenderer.invoke('cache:clearAll')
|
||||||
|
},
|
||||||
|
|
||||||
// 群聊分析
|
// 群聊分析
|
||||||
groupAnalytics: {
|
groupAnalytics: {
|
||||||
getGroupChats: () => ipcRenderer.invoke('groupAnalytics:getGroupChats'),
|
getGroupChats: () => ipcRenderer.invoke('groupAnalytics:getGroupChats'),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ConfigService } from './config'
|
import { ConfigService } from './config'
|
||||||
import { wcdbService } from './wcdbService'
|
import { wcdbService } from './wcdbService'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { readFile, writeFile } from 'fs/promises'
|
import { readFile, writeFile, rm } from 'fs/promises'
|
||||||
import { app } from 'electron'
|
import { app } from 'electron'
|
||||||
|
|
||||||
export interface ChatStatistics {
|
export interface ChatStatistics {
|
||||||
@@ -528,6 +528,18 @@ class AnalyticsService {
|
|||||||
return { success: false, error: String(e) }
|
return { success: false, error: String(e) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async clearCache(): Promise<{ success: boolean; error?: string }> {
|
||||||
|
this.aggregateCache = null
|
||||||
|
this.fallbackAggregateCache = null
|
||||||
|
this.aggregatePromise = null
|
||||||
|
try {
|
||||||
|
await rm(this.getCacheFilePath(), { force: true })
|
||||||
|
return { success: true }
|
||||||
|
} catch (e) {
|
||||||
|
return { success: false, error: String(e) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const analyticsService = new AnalyticsService()
|
export const analyticsService = new AnalyticsService()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { join, dirname } from 'path'
|
import { join, dirname } from 'path'
|
||||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs'
|
||||||
import { app } from 'electron'
|
import { app } from 'electron'
|
||||||
|
|
||||||
export interface ContactCacheEntry {
|
export interface ContactCacheEntry {
|
||||||
@@ -72,4 +72,13 @@ export class ContactCacheService {
|
|||||||
console.error('ContactCacheService: 保存缓存失败', error)
|
console.error('ContactCacheService: 保存缓存失败', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear(): void {
|
||||||
|
this.cache = {}
|
||||||
|
try {
|
||||||
|
rmSync(this.cacheFilePath, { force: true })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('ContactCacheService: 清理缓存失败', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { app, BrowserWindow } from 'electron'
|
|||||||
import { basename, dirname, extname, join } from 'path'
|
import { basename, dirname, extname, join } from 'path'
|
||||||
import { pathToFileURL } from 'url'
|
import { pathToFileURL } from 'url'
|
||||||
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, appendFileSync } from 'fs'
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, appendFileSync } from 'fs'
|
||||||
import { writeFile } from 'fs/promises'
|
import { writeFile, rm, readdir } from 'fs/promises'
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
import { Worker } from 'worker_threads'
|
import { Worker } from 'worker_threads'
|
||||||
import { ConfigService } from './config'
|
import { ConfigService } from './config'
|
||||||
@@ -1646,6 +1646,71 @@ export class ImageDecryptService {
|
|||||||
|
|
||||||
await writeFile(outputPath, decrypted)
|
await writeFile(outputPath, decrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async clearCache(): Promise<{ success: boolean; error?: string }> {
|
||||||
|
this.resolvedCache.clear()
|
||||||
|
this.hardlinkCache.clear()
|
||||||
|
this.pending.clear()
|
||||||
|
this.updateFlags.clear()
|
||||||
|
this.cacheIndexed = false
|
||||||
|
this.cacheIndexing = null
|
||||||
|
|
||||||
|
const configured = this.configService.get('cachePath')
|
||||||
|
const root = configured
|
||||||
|
? join(configured, 'Images')
|
||||||
|
: join(app.getPath('documents'), 'WeFlow', 'Images')
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!existsSync(root)) {
|
||||||
|
return { success: true }
|
||||||
|
}
|
||||||
|
const monthPattern = /^\d{4}-\d{2}$/
|
||||||
|
const clearFilesInDir = async (dirPath: string): Promise<void> => {
|
||||||
|
let entries: Array<{ name: string; isDirectory: () => boolean }>
|
||||||
|
try {
|
||||||
|
entries = await readdir(dirPath, { withFileTypes: true })
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = join(dirPath, entry.name)
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
await clearFilesInDir(fullPath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await rm(fullPath, { force: true })
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const traverse = async (dirPath: string): Promise<void> => {
|
||||||
|
let entries: Array<{ name: string; isDirectory: () => boolean }>
|
||||||
|
try {
|
||||||
|
entries = await readdir(dirPath, { withFileTypes: true })
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = join(dirPath, entry.name)
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
if (monthPattern.test(entry.name)) {
|
||||||
|
await clearFilesInDir(fullPath)
|
||||||
|
} else {
|
||||||
|
await traverse(fullPath)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await rm(fullPath, { force: true })
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await traverse(root)
|
||||||
|
return { success: true }
|
||||||
|
} catch (e) {
|
||||||
|
return { success: false, error: String(e) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const imageDecryptService = new ImageDecryptService()
|
export const imageDecryptService = new ImageDecryptService()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { join, dirname } from 'path'
|
import { join, dirname } from 'path'
|
||||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs'
|
||||||
import { app } from 'electron'
|
import { app } from 'electron'
|
||||||
|
|
||||||
export interface SessionMessageCacheEntry {
|
export interface SessionMessageCacheEntry {
|
||||||
@@ -65,4 +65,13 @@ export class MessageCacheService {
|
|||||||
console.error('MessageCacheService: 保存缓存失败', error)
|
console.error('MessageCacheService: 保存缓存失败', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear(): void {
|
||||||
|
this.cache = {}
|
||||||
|
try {
|
||||||
|
rmSync(this.cacheFilePath, { force: true })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('MessageCacheService: 清理缓存失败', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef } from 'react'
|
||||||
import { useAppStore } from '../stores/appStore'
|
import { useAppStore } from '../stores/appStore'
|
||||||
import { useThemeStore, themes } from '../stores/themeStore'
|
import { useThemeStore, themes } from '../stores/themeStore'
|
||||||
|
import { useAnalyticsStore } from '../stores/analyticsStore'
|
||||||
import { dialog } from '../services/ipc'
|
import { dialog } from '../services/ipc'
|
||||||
import * as configService from '../services/config'
|
import * as configService from '../services/config'
|
||||||
import {
|
import {
|
||||||
@@ -27,6 +28,7 @@ interface WxidOption {
|
|||||||
function SettingsPage() {
|
function SettingsPage() {
|
||||||
const { setDbConnected, setLoading, reset } = useAppStore()
|
const { setDbConnected, setLoading, reset } = useAppStore()
|
||||||
const { currentTheme, themeMode, setTheme, setThemeMode } = useThemeStore()
|
const { currentTheme, themeMode, setTheme, setThemeMode } = useThemeStore()
|
||||||
|
const clearAnalyticsStoreCache = useAnalyticsStore((state) => state.clearCache)
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<SettingsTab>('appearance')
|
const [activeTab, setActiveTab] = useState<SettingsTab>('appearance')
|
||||||
const [decryptKey, setDecryptKey] = useState('')
|
const [decryptKey, setDecryptKey] = useState('')
|
||||||
@@ -55,6 +57,11 @@ function SettingsPage() {
|
|||||||
const [dbKeyStatus, setDbKeyStatus] = useState('')
|
const [dbKeyStatus, setDbKeyStatus] = useState('')
|
||||||
const [imageKeyStatus, setImageKeyStatus] = useState('')
|
const [imageKeyStatus, setImageKeyStatus] = useState('')
|
||||||
const [isManualStartPrompt, setIsManualStartPrompt] = useState(false)
|
const [isManualStartPrompt, setIsManualStartPrompt] = useState(false)
|
||||||
|
const [isClearingAnalyticsCache, setIsClearingAnalyticsCache] = useState(false)
|
||||||
|
const [isClearingImageCache, setIsClearingImageCache] = useState(false)
|
||||||
|
const [isClearingAllCache, setIsClearingAllCache] = useState(false)
|
||||||
|
|
||||||
|
const isClearingCache = isClearingAnalyticsCache || isClearingImageCache || isClearingAllCache
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadConfig()
|
loadConfig()
|
||||||
@@ -428,6 +435,59 @@ function SettingsPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleClearAnalyticsCache = async () => {
|
||||||
|
if (isClearingCache) return
|
||||||
|
setIsClearingAnalyticsCache(true)
|
||||||
|
try {
|
||||||
|
const result = await window.electronAPI.cache.clearAnalytics()
|
||||||
|
if (result.success) {
|
||||||
|
clearAnalyticsStoreCache()
|
||||||
|
showMessage('已清除分析缓存', true)
|
||||||
|
} else {
|
||||||
|
showMessage(`清除分析缓存失败: ${result.error || '未知错误'}`, false)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showMessage(`清除分析缓存失败: ${e}`, false)
|
||||||
|
} finally {
|
||||||
|
setIsClearingAnalyticsCache(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClearImageCache = async () => {
|
||||||
|
if (isClearingCache) return
|
||||||
|
setIsClearingImageCache(true)
|
||||||
|
try {
|
||||||
|
const result = await window.electronAPI.cache.clearImages()
|
||||||
|
if (result.success) {
|
||||||
|
showMessage('已清除图片缓存', true)
|
||||||
|
} else {
|
||||||
|
showMessage(`清除图片缓存失败: ${result.error || '未知错误'}`, false)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showMessage(`清除图片缓存失败: ${e}`, false)
|
||||||
|
} finally {
|
||||||
|
setIsClearingImageCache(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClearAllCache = async () => {
|
||||||
|
if (isClearingCache) return
|
||||||
|
setIsClearingAllCache(true)
|
||||||
|
try {
|
||||||
|
const result = await window.electronAPI.cache.clearAll()
|
||||||
|
if (result.success) {
|
||||||
|
clearAnalyticsStoreCache()
|
||||||
|
showMessage('已清除所有缓存', true)
|
||||||
|
} else {
|
||||||
|
showMessage(`清除所有缓存失败: ${result.error || '未知错误'}`, false)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showMessage(`清除所有缓存失败: ${e}`, false)
|
||||||
|
} finally {
|
||||||
|
setIsClearingAllCache(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const renderAppearanceTab = () => (
|
const renderAppearanceTab = () => (
|
||||||
<div className="tab-content">
|
<div className="tab-content">
|
||||||
<div className="theme-mode-toggle">
|
<div className="theme-mode-toggle">
|
||||||
@@ -597,9 +657,15 @@ function SettingsPage() {
|
|||||||
<div className="tab-content">
|
<div className="tab-content">
|
||||||
<p className="section-desc">管理应用缓存数据</p>
|
<p className="section-desc">管理应用缓存数据</p>
|
||||||
<div className="btn-row">
|
<div className="btn-row">
|
||||||
<button className="btn btn-secondary"><Trash2 size={16} /> 清除分析缓存</button>
|
<button className="btn btn-secondary" onClick={handleClearAnalyticsCache} disabled={isClearingCache}>
|
||||||
<button className="btn btn-secondary"><Trash2 size={16} /> 清除图片缓存</button>
|
<Trash2 size={16} /> 清除分析缓存
|
||||||
<button className="btn btn-danger"><Trash2 size={16} /> 清除所有缓存</button>
|
</button>
|
||||||
|
<button className="btn btn-secondary" onClick={handleClearImageCache} disabled={isClearingCache}>
|
||||||
|
<Trash2 size={16} /> 清除图片缓存
|
||||||
|
</button>
|
||||||
|
<button className="btn btn-danger" onClick={handleClearAllCache} disabled={isClearingCache}>
|
||||||
|
<Trash2 size={16} /> 清除所有缓存
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="divider" />
|
<div className="divider" />
|
||||||
<p className="section-desc">清除当前配置并重新开始首次引导</p>
|
<p className="section-desc">清除当前配置并重新开始首次引导</p>
|
||||||
|
|||||||
5
src/types/electron.d.ts
vendored
5
src/types/electron.d.ts
vendored
@@ -148,6 +148,11 @@ export interface ElectronAPI {
|
|||||||
}>
|
}>
|
||||||
onProgress: (callback: (payload: { status: string; progress: number }) => void) => () => void
|
onProgress: (callback: (payload: { status: string; progress: number }) => void) => () => void
|
||||||
}
|
}
|
||||||
|
cache: {
|
||||||
|
clearAnalytics: () => Promise<{ success: boolean; error?: string }>
|
||||||
|
clearImages: () => Promise<{ success: boolean; error?: string }>
|
||||||
|
clearAll: () => Promise<{ success: boolean; error?: string }>
|
||||||
|
}
|
||||||
groupAnalytics: {
|
groupAnalytics: {
|
||||||
getGroupChats: () => Promise<{
|
getGroupChats: () => Promise<{
|
||||||
success: boolean
|
success: boolean
|
||||||
|
|||||||
Reference in New Issue
Block a user