fix: 修复添加记录后首页数据不刷新的问题

将缓存清除逻辑从 stats.js 路由移至 app.js 全局中间件,
确保 records.js 路由的写操作也能触发缓存清除。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
lizhilun
2026-03-31 16:59:32 +08:00
co-authored by Claude Opus 4.6
parent d551841035
commit ac2d21a5a0
3 changed files with 82 additions and 41 deletions
+11
View File
@@ -5,6 +5,7 @@ const compression = require('compression');
const config = require('./config');
const logger = require('./utils/logger');
const { clearUserCache } = require('./utils/cache');
const db = require('./db');
const { router: authRouter, auth } = require('./routes/auth');
const statsRouter = require('./routes/stats');
@@ -30,6 +31,16 @@ app.use('/api', (req, res, next) => {
next();
});
// 全局缓存清除中间件:在写操作完成后清除用户缓存
app.use('/api', (req, res, next) => {
res.once('finish', () => {
if (['POST', 'DELETE', 'PUT', 'PATCH'].includes(req.method) && req.user?.id) {
clearUserCache(req.user.id);
}
});
next();
});
app.get('/api/contacts', auth, (req, res) => {
const field = req.query.field;
const allowed = ['boss', 'partner', 'source', 'destination'];
+2 -41
View File
@@ -3,50 +3,11 @@ const db = require('../db');
const config = require('../config');
const logger = require('../utils/logger');
const { getMonthRange, AppError, validateRequired, validateNumber, validateInList } = require('../utils/helpers');
const { getCached } = require('../utils/cache');
const router = express.Router();
// 内存缓存(限制大小,基于LRU
const statsCache = new Map();
const CACHE_TTL = config.cache.ttl;
const MAX_CACHE_SIZE = config.cache.maxSize;
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) {
// Delete oldest entry
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;
});
}
// POST/DELETE/PUT/PATCH 后清除当前用户的缓存
router.use((req, res, next) => {
res.once('finish', () => {
if (['POST', 'DELETE', 'PUT', 'PATCH'].includes(req.method) && req.user?.id) {
for (const key of statsCache.keys()) {
if (key.includes(`:${req.user.id}:`)) {
statsCache.delete(key);
}
}
}
});
next();
});
// 缓存模块已移至 utils/cache.js
// 格式化本地日期为 YYYY-MM-DD
function formatLocalDate(date) {
+69
View File
@@ -0,0 +1,69 @@
/**
* 统计数据缓存模块
* 基于 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
};