import { hash } from 'ohash'
|
const CacheInvalidationStrategy = Object.freeze({
|
CLEAR_ALL: 'clear_all',
|
CLEAR_CURRENT: 'clear_current',
|
CLEAR_PAGINATION: 'clear_pagination',
|
KEEP_ALL: 'keep_all'
|
})
|
|
class TableCache {
|
constructor(cacheTime = 5 * 60 * 1e3, maxSize = 50, enableLog = false) {
|
this.cache = new Map()
|
this.cacheTime = cacheTime
|
this.maxSize = maxSize
|
this.enableLog = enableLog
|
}
|
// 内部日志工具
|
log(message, ...args) {
|
if (this.enableLog) {
|
console.log(`[TableCache] ${message}`, ...args)
|
}
|
}
|
// 生成稳定的缓存键
|
generateKey(params) {
|
return hash(params)
|
}
|
// 🔧 优化:增强类型安全性
|
generateTags(params) {
|
const tags = /* @__PURE__ */ new Set()
|
const searchKeys = Object.keys(params).filter(
|
(key) =>
|
!['current', 'size', 'total'].includes(key) &&
|
params[key] !== void 0 &&
|
params[key] !== '' &&
|
params[key] !== null
|
)
|
if (searchKeys.length > 0) {
|
const searchTag = searchKeys.map((key) => `${key}:${String(params[key])}`).join('|')
|
tags.add(`search:${searchTag}`)
|
} else {
|
tags.add('search:default')
|
}
|
tags.add(`pagination:${params.size || 10}`)
|
tags.add('pagination')
|
return tags
|
}
|
// 🔧 优化:LRU 缓存清理
|
evictLRU() {
|
if (this.cache.size <= this.maxSize) return
|
let lruKey = ''
|
let minAccessCount = Infinity
|
let oldestTime = Infinity
|
for (const [key, item] of this.cache.entries()) {
|
if (
|
item.accessCount < minAccessCount ||
|
(item.accessCount === minAccessCount && item.lastAccessTime < oldestTime)
|
) {
|
lruKey = key
|
minAccessCount = item.accessCount
|
oldestTime = item.lastAccessTime
|
}
|
}
|
if (lruKey) {
|
this.cache.delete(lruKey)
|
this.log(`LRU 清理缓存: ${lruKey}`)
|
}
|
}
|
// 设置缓存
|
set(params, data, response) {
|
const key = this.generateKey(params)
|
const tags = this.generateTags(params)
|
const now = Date.now()
|
this.evictLRU()
|
this.cache.set(key, {
|
data,
|
response,
|
timestamp: now,
|
params: key,
|
tags,
|
accessCount: 1,
|
lastAccessTime: now
|
})
|
}
|
// 获取缓存
|
get(params) {
|
const key = this.generateKey(params)
|
const item = this.cache.get(key)
|
if (!item) return null
|
if (Date.now() - item.timestamp > this.cacheTime) {
|
this.cache.delete(key)
|
return null
|
}
|
item.accessCount++
|
item.lastAccessTime = Date.now()
|
return item
|
}
|
// 根据标签清除缓存
|
clearByTags(tags) {
|
let clearedCount = 0
|
for (const [key, item] of this.cache.entries()) {
|
const hasMatchingTag = tags.some((tag) =>
|
Array.from(item.tags).some((itemTag) => itemTag.includes(tag))
|
)
|
if (hasMatchingTag) {
|
this.cache.delete(key)
|
clearedCount++
|
}
|
}
|
return clearedCount
|
}
|
// 清除当前搜索条件的缓存
|
clearCurrentSearch(params) {
|
const key = this.generateKey(params)
|
const deleted = this.cache.delete(key)
|
return deleted ? 1 : 0
|
}
|
// 清除分页缓存
|
clearPagination() {
|
return this.clearByTags(['pagination'])
|
}
|
// 清空所有缓存
|
clear() {
|
this.cache.clear()
|
}
|
// 获取缓存统计信息
|
getStats() {
|
const total = this.cache.size
|
let totalSize = 0
|
let totalAccess = 0
|
for (const item of this.cache.values()) {
|
totalSize += JSON.stringify(item.data).length
|
totalAccess += item.accessCount
|
}
|
const sizeInKB = (totalSize / 1024).toFixed(2)
|
const avgHits = total > 0 ? (totalAccess / total).toFixed(1) : '0'
|
return {
|
total,
|
size: `${sizeInKB}KB`,
|
hitRate: `${avgHits} avg hits`
|
}
|
}
|
// 清理过期缓存
|
cleanupExpired() {
|
let cleanedCount = 0
|
const now = Date.now()
|
for (const [key, item] of this.cache.entries()) {
|
if (now - item.timestamp > this.cacheTime) {
|
this.cache.delete(key)
|
cleanedCount++
|
}
|
}
|
return cleanedCount
|
}
|
}
|
export { CacheInvalidationStrategy, TableCache }
|