// server/utils/cache.ts - 简单内存缓存,按题目、题型和选项生成缓存键 import { createHash } from "node:crypto"; import { serverEnv } from "~~/server/utils/env"; /** * 旧 Python 服务使用进程内字典缓存答案 * * Nuxt 版保持同样的“内存缓存”语义:服务重启后缓存清空,不跨实例共享; * 这对个人题库服务足够简单,也避免为了缓存引入数据库或 Redis */ export class SimpleCache { /** Map key 是题目、题型和选项计算出的 md5;value 保存写入时间和答案 */ private readonly cache = new Map< string, { timestamp: number; value: string; } >(); constructor(private readonly expirationSeconds: number) {} /** * 根据题目、题型和选项生成缓存键 * * 同一道题如果选项不同,不能复用旧答案,所以三个字段都参与计算 */ private generateKey(question: string, questionType = "", options = "") { return createHash("md5") .update(`${question}|${questionType}|${options}`, "utf8") .digest("hex"); } /** * 读取缓存答案 * * 命中过期项时会顺手删除,避免长时间运行后堆积无效缓存 */ get(question: string, questionType = "", options = "") { const key = this.generateKey(question, questionType, options); const item = this.cache.get(key); if (!item) return undefined; if (Date.now() - item.timestamp < this.expirationSeconds * 1000) { return item.value; } this.cache.delete(key); return undefined; } /** 写入答案缓存,时间戳使用当前进程时间即可 */ set(question: string, answer: string, questionType = "", options = "") { const key = this.generateKey(question, questionType, options); this.cache.set(key, { timestamp: Date.now(), value: answer }); } /** 清空全部缓存,对应 `/api/cache/clear` */ clear() { this.cache.clear(); } /** * 批量移除过期项 * * 目前在统计缓存大小时调用,避免 stats 返回已经过期的数量 */ removeExpired() { const now = Date.now(); let removedCount = 0; for (const [key, item] of this.cache.entries()) { if (now - item.timestamp >= this.expirationSeconds * 1000) { this.cache.delete(key); removedCount += 1; } } return removedCount; } /** 获取当前有效缓存数量 */ size() { this.removeExpired(); return this.cache.size; } } /** * 全局答案缓存实例 * * 如果 `ENABLE_CACHE=false`,导出 undefined,调用处用可选链即可保持逻辑简洁 */ export const answerCache = serverEnv.enableCache ? new SimpleCache(serverEnv.cacheExpiration) : undefined;