chore: 后端首次基线提交

运行必须源码:app/(controller/model/service/middleware/lib/utils/validate/router)、config/、database/(migrations/seeders/sql)、启动脚本、app/public/templates 模板。
已忽略运行非必须:node_modules/、logs/、run/、app/public 运行时上传数据、.env*、构建/缓存产物。
This commit is contained in:
WorkBuddy
2026-08-17 12:25:42 +08:00
commit a303cf2d53
109 changed files with 25904 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
'use strict';
/**
* 下载前 file_path 短缓存(进程内,默认 30s)。
*
* 收益:高并发下载同一文件时,避免每次请求都回查 DB 取 file_path / file_name。
*
* 取舍(任务 #23 已确认):文件替换 / 删除后,≤ TTL 内可能命中旧路径。
* - 下游 _sendFile 仍按实际文件做 fs.existsSync 校验,旧路径不存在 → 404,不会吐错误内容;
* - 被替换文件的旧路径在 TTL 内至多吐出旧版本一小段时间(非安全敏感、非实时场景,可接受)。
* - 因此本缓存为「只读穿透 + TTL 自愈」,不强制接入上传/删除失效,
* 以最小化改动面与回归风险;如需更强一致可后续在上传/删除成功处调用 invalidate()。
*
* 键约定:调用方自行拼装唯一 key,如 `word:${group}:${id}` / `sub:${group}:${id}`。
*/
const TTL_MS = 30 * 1000;
const store = new Map(); // key -> { filePath, fileName, expire }
function _now() {
return Date.now();
}
function getFull(key) {
if (!key) return null;
const hit = store.get(key);
if (!hit) return null;
if (hit.expire < _now()) {
store.delete(key);
return null;
}
return { filePath: hit.filePath, fileName: hit.fileName };
}
function setFull(key, filePath, fileName) {
if (!key || filePath == null || filePath === '') return;
store.set(key, {
filePath: String(filePath),
fileName: fileName == null ? '' : String(fileName),
expire: _now() + TTL_MS,
});
}
function invalidate(key) {
if (key) store.delete(key);
}
module.exports = { getFull, setFull, invalidate, TTL_MS };