数据库与缓存
MongoDB
MongoDB 保存历史热点、来源调度配置和每日简报:
MONGODB_URI=mongodb://localhost:27017/daily-hot-api目录约定:
src/database/
├── schemas/ # Mongoose Schema
└── repositories/ # 数据访问封装新增持久化模型时:
- 使用
@Schema()和SchemaFactory.createForClass()定义 Schema。 - 在
src/database/database.module.ts的MongooseModule.forFeature()中注册。 - 创建 Repository 集中封装查询与写入。
- 将 Repository 加入并导出
DatabaseModule。 - 为唯一约束、列表排序和常用过滤条件设计索引。
当前关键索引:
HotItem:source + url、source + timestamp、timestamp、标题/描述全文索引。SourceConfig:source唯一索引。DailyBrief:briefDate + period唯一索引、createdAt倒序索引。
去重说明
热点存储使用 {source, url} 唯一索引与 bulkWrite 幂等写入。现有数据库必须先执行唯一索引迁移,再部署依赖该约束的写入逻辑。
历史条目是首次抓取的快照
saveHotItems 的写入只用 $setOnInsert,因此同一 {source, url} 的记录只在首次抓取时写入,之后不再改动:
hot保持首次抓取时的热度,不随后续抓取刷新,因此hot_items不能用来做热度趋势。timestamp取上游时间戳,上游没有提供时取首次入库时刻,之后固定不变。这是回看窗口能正常工作的前提:若每次抓取都刷新该字段,上游不带时间戳的来源会让旧条目不断重新落入BRIEF_LOOKBACK_HOURS窗口和/api/history的时间范围查询。title、desc、cover等同样保持首次抓取时的内容,上游后来的编辑不会反映到历史记录里。savedCount(调度任务结果与GET /api/scheduler/runs/:runId)统计的是新插入条数,不是本次处理条数;重复抓取同一批数据时它会是 0。
简报的实时条目直接来自本次抓取结果,并且在合并时优先于历史条目(去重键为 source + url),所以仍在榜上的条目用的是新鲜热度;陈旧的 hot 只影响已经掉榜但仍在回看窗口内的条目。
如果后续需要热度趋势或"在榜时长",应新增 lastSeenAt 字段并只对 hot 追加 $set,而不是把整条记录改成每次抓取都覆盖。
Redis 与内存缓存
Redis 配置:
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
CACHE_TTL=3600Redis 客户端使用 lazyConnect,启动阶段不强制握手。CacheService 还组合了 Nest 内存缓存,在 Redis 不可用时可为部分业务提供进程内降级。
需要注意:
- 进程内缓存不会在多个实例间共享。
- 进程重启会清空内存缓存。
- 不同业务可以使用不同 TTL,例如股票历史排名由
BRIEF_STOCK_RANKING_CACHE_TTL控制。 - 写入或删除影响统计结果的数据后,应同步清理对应缓存。
上游请求缓存键
HttpClientService 把每次上游请求的缓存键算成 http:<sha256>,参与计算的是请求方法、不含查询串的地址、稳定查询、请求头、请求体和 responseType。URL 自带的查询串会和 params 合并后排序,因此参数顺序和写在 URL 还是 params 里都不影响命中。
以下字段不参与缓存身份,因为它们每次请求都变,算进键会让缓存永不命中并按请求数堆积 Redis 键:
- 查询参数和请求体顶层字段:
_、_t、_signature、msToken、nonce、sign、signature、timestamp、token、w_rid、wts(大小写不敏感)。 - 请求头:
Cookie、User-Agent、X-UDID。本项目的 Cookie 和 UA 只用于绕过反爬,不决定响应内容,UA 缺省时还会由HttpClientService随机生成。
新增来源时,如果上游要求的防缓存戳或签名字段不在上面的列表里,需要把字段名加进 src/http/http.service.ts 的 VOLATILE_REQUEST_KEYS / VOLATILE_HEADER_KEYS,否则该来源不会命中缓存。反过来,真正用于选择内容的参数(如 page、month、area)必须留在键里。
验证某个来源是否命中缓存:
curl -s 'http://localhost:6689/hot-lists/zhihu' | grep -o '"fromCache":[a-z]*'
curl -s 'http://localhost:6689/hot-lists/zhihu' | grep -o '"fromCache":[a-z]*'
redis-cli --scan --pattern 'http:*' | wc -l第二次请求应返回 "fromCache":true,并且键数量不随请求次数增长。
本地检查
curl http://localhost:6689/health
curl http://localhost:6689/ready
curl http://localhost:6689/api/history/statsDocker 环境下可以进入数据库检查集合:
docker compose exec mongodb mongosh daily-hot-apishow collections
db.hotitems.find().limit(5)
db.dailybriefs.find().sort({ createdAt: -1 }).limit(1)数据备份与恢复见数据卷与备份。