152 lines
3.3 KiB
TypeScript
152 lines
3.3 KiB
TypeScript
import { logger } from "../utils/logger";
|
|
|
|
export interface CacheOptions {
|
|
ttl?: number; // 生存时间(毫秒)
|
|
maxSize?: number; // 最大缓存项数
|
|
}
|
|
|
|
export interface CacheEntry<T> {
|
|
value: T;
|
|
timestamp: number;
|
|
ttl: number;
|
|
}
|
|
|
|
class Cache {
|
|
private cache = new Map<string, CacheEntry<unknown>>();
|
|
private maxSize: number;
|
|
private defaultTtl: number;
|
|
|
|
constructor(options: CacheOptions = {}) {
|
|
this.maxSize = options.maxSize || 1000;
|
|
this.defaultTtl = options.ttl || 5 * 60 * 1000; // 默认5分钟
|
|
}
|
|
|
|
set<T>(key: string, value: T, ttl?: number): void {
|
|
// 如果缓存已满,删除最旧的项
|
|
if (this.cache.size >= this.maxSize) {
|
|
this.evictOldest();
|
|
}
|
|
|
|
const entry: CacheEntry<T> = {
|
|
value,
|
|
timestamp: Date.now(),
|
|
ttl: ttl || this.defaultTtl,
|
|
};
|
|
|
|
this.cache.set(key, entry);
|
|
logger.debug("Cache set", { key, ttl: entry.ttl });
|
|
}
|
|
|
|
get<T>(key: string): T | null {
|
|
const entry = this.cache.get(key) as CacheEntry<T> | undefined;
|
|
|
|
if (!entry) {
|
|
logger.debug("Cache miss", { key });
|
|
return null;
|
|
}
|
|
|
|
// 检查是否过期
|
|
if (Date.now() - entry.timestamp > entry.ttl) {
|
|
this.cache.delete(key);
|
|
logger.debug("Cache expired", { key });
|
|
return null;
|
|
}
|
|
|
|
logger.debug("Cache hit", { key });
|
|
return entry.value;
|
|
}
|
|
|
|
has(key: string): boolean {
|
|
const entry = this.cache.get(key);
|
|
if (!entry) return false;
|
|
|
|
// 检查是否过期
|
|
if (Date.now() - entry.timestamp > entry.ttl) {
|
|
this.cache.delete(key);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
delete(key: string): boolean {
|
|
const deleted = this.cache.delete(key);
|
|
if (deleted) {
|
|
logger.debug("Cache deleted", { key });
|
|
}
|
|
return deleted;
|
|
}
|
|
|
|
clear(): void {
|
|
this.cache.clear();
|
|
logger.info("Cache cleared");
|
|
}
|
|
|
|
size(): number {
|
|
return this.cache.size;
|
|
}
|
|
|
|
private evictOldest(): void {
|
|
let oldestKey: string | null = null;
|
|
let oldestTime = Date.now();
|
|
|
|
for (const [key, entry] of this.cache.entries()) {
|
|
if (entry.timestamp < oldestTime) {
|
|
oldestTime = entry.timestamp;
|
|
oldestKey = key;
|
|
}
|
|
}
|
|
|
|
if (oldestKey) {
|
|
this.cache.delete(oldestKey);
|
|
logger.debug("Cache evicted oldest", { key: oldestKey });
|
|
}
|
|
}
|
|
|
|
// 清理过期项
|
|
cleanup(): void {
|
|
const now = Date.now();
|
|
let cleanedCount = 0;
|
|
|
|
for (const [key, entry] of this.cache.entries()) {
|
|
if (now - entry.timestamp > entry.ttl) {
|
|
this.cache.delete(key);
|
|
cleanedCount++;
|
|
}
|
|
}
|
|
|
|
if (cleanedCount > 0) {
|
|
logger.info("Cache cleanup completed", { cleanedCount });
|
|
}
|
|
}
|
|
|
|
// 获取缓存统计信息
|
|
getStats(): {
|
|
size: number;
|
|
maxSize: number;
|
|
hitRate: number;
|
|
missRate: number;
|
|
} {
|
|
return {
|
|
size: this.cache.size,
|
|
maxSize: this.maxSize,
|
|
hitRate: 0, // 需要实现命中率统计
|
|
missRate: 0, // 需要实现未命中率统计
|
|
};
|
|
}
|
|
}
|
|
|
|
// 创建全局缓存实例
|
|
export const cache = new Cache({
|
|
maxSize: 1000,
|
|
ttl: 5 * 60 * 1000, // 5分钟
|
|
});
|
|
|
|
// 定期清理过期项
|
|
if (typeof window === "undefined") {
|
|
// 仅在服务器端运行
|
|
setInterval(() => {
|
|
cache.cleanup();
|
|
}, 60 * 1000); // 每分钟清理一次
|
|
}
|