将缓存清除逻辑从 stats.js 路由移至 app.js 全局中间件, 确保 records.js 路由的写操作也能触发缓存清除。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
69 lines
1.6 KiB
JavaScript
69 lines
1.6 KiB
JavaScript
/**
|
|
* 统计数据缓存模块
|
|
* 基于 LRU 算法的内存缓存
|
|
*/
|
|
const config = require('../config');
|
|
const logger = require('./logger');
|
|
|
|
const statsCache = new Map();
|
|
const CACHE_TTL = config.cache.ttl;
|
|
const MAX_CACHE_SIZE = config.cache.maxSize;
|
|
|
|
/**
|
|
* 获取缓存数据,如果不存在则通过 fetchFn 获取并缓存
|
|
* @param {string} key 缓存键
|
|
* @param {Function} fetchFn 获取数据的函数
|
|
* @returns {Promise} 缓存数据
|
|
*/
|
|
function getCached(key, fetchFn) {
|
|
const cached = statsCache.get(key);
|
|
if (cached && Date.now() - cached.ts < CACHE_TTL) {
|
|
// LRU: move to end
|
|
statsCache.delete(key);
|
|
statsCache.set(key, cached);
|
|
return Promise.resolve(cached.data);
|
|
}
|
|
|
|
// 超出最大缓存数量时,删除最旧的条目
|
|
if (statsCache.size >= MAX_CACHE_SIZE) {
|
|
const firstKey = statsCache.keys().next().value;
|
|
statsCache.delete(firstKey);
|
|
}
|
|
|
|
return Promise.resolve(fetchFn()).then(data => {
|
|
statsCache.set(key, { data, ts: Date.now() });
|
|
logger.debug(`缓存更新: ${key}`);
|
|
return data;
|
|
}).catch(err => {
|
|
logger.error('缓存获取失败:', err.message);
|
|
throw err;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 清除指定用户的所有缓存
|
|
* @param {number} userId 用户ID
|
|
*/
|
|
function clearUserCache(userId) {
|
|
const prefix = `:${userId}:`;
|
|
for (const key of statsCache.keys()) {
|
|
if (key.includes(prefix)) {
|
|
statsCache.delete(key);
|
|
logger.debug(`缓存清除: ${key}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清除所有缓存
|
|
*/
|
|
function clearAllCache() {
|
|
statsCache.clear();
|
|
logger.debug('所有缓存已清除');
|
|
}
|
|
|
|
module.exports = {
|
|
getCached,
|
|
clearUserCache,
|
|
clearAllCache
|
|
}; |