docs(architecture): 同步缓存基础设施 + classes 标杆迁移 + 缓存策略规则

- 004: 补全 shared/lib/cache 9 文件说明(types/memory-store/redis-store/store-factory/cache-fn/invalidation-map/client-invalidation-map/invalidate/index)+ shared/lib/redis-client + shared/lib/query-keys

- 005: shared.lib.exports.functions 补 5 个节点(MemoryCacheStore / RedisCacheStore / CLIENT_INVALIDATION_MAP / getRedisClient / queryKeys)

- known-issues.md 新增「二十四、缓存策略规则」章节,含 14 条规则表与涉及文件清单
This commit is contained in:
SpecialX
2026-07-05 19:04:03 +08:00
parent a3dc22cb9e
commit 99f15ee37a
3 changed files with 118 additions and 4 deletions

View File

@@ -1236,3 +1236,38 @@ if (announcement.type === "school") {
**涉及文件**`src/shared/lib/cache/invalidate.ts`、`src/shared/lib/cache/invalidate.test.ts`、`src/shared/lib/cache/cache-fn.ts`、`src/shared/lib/cache/cache-fn.test.ts`
---
## 二十四、缓存策略规则2026-07-05 缓存策略重构)
> 标杆模块:`classes`。后续模块按此模式扩展。底层基础设施见 `src/shared/lib/cache/`。
| 规则 | 正确写法 | 错误写法 |
|------|---------|---------|
| data-access 查询函数包装 | `export const fn = cacheFn(fnRaw, { tags, ttl, keyParts })` | `cache(fn)` 或不包装 |
| 双导出 raw 版本 | `export const fnRaw = ...; export const fn = cacheFn(fnRaw, ...)` | 仅导出缓存版本(单测无法 mock |
| Server Action 写后失效 | `await invalidateFor("module.action", { id })` | `revalidatePath("/path")` 直接调用 |
| 未知 actionId | 抛错(必须先登记 INVALIDATION_MAP | 静默忽略 |
| 客户端 useQuery queryKey | `queryKeys.classes.detail(id)` 工厂调用 | 手写字符串数组 `["classes", "detail", id]` |
| useActionQuery 传 queryKey | `useActionQuery(action, { queryKey: queryKeys.classes.detail(id) })` | 不传 queryKey旧模式不跨页共享 |
| useActionMutation 自动 invalidate | `useActionMutation({ mutationFn, actionId: "classes.update", params: { id } })` | onSuccess 中手动 invalidateQueries |
| Tag 命名 | `classes:detail:{id}` 模块:资源:ID | `class_detail` 或 `ClassesDetail` |
| TTL 选择 | 列表 300s / 学生 60s / 课表 600s / 统计 60s | 全局统一 TTL |
| CLIENT_INVALIDATION_MAP 与 INVALIDATION_MAP 同步 | 服务端 invalidation-map.ts 增项后,同步在 client-invalidation-map.ts 增 queryKeys 子项 | 只改服务端,客户端 useActionMutation 自动 invalidate 失效 |
| client-invalidation-map 仅含 queryKeys | `CLIENT_INVALIDATION_MAP["x.y"] = { queryKeys: [["x", "y"]] }` | 在客户端文件中引入 tags/paths拉入 server-only |
| raw 函数命名 | `getXxxRaw`(内部实现)+ `getXxx`cacheFn 包装后的对外 API | `getXxx` 与 `getXxxCached` 命名混用 |
| cacheFn options.tags 必填 | `{ tags: ["classes:detail:{id}"], ttl: 60, keyParts: [id] }` | `{ ttl: 60 }` 缺 tags无法失效 |
| invalidateFor 调用位置 | mutation Action 成功分支末尾 `await invalidateFor(...)` | 在 catch 分支调用(失败也失效缓存) |
| queryKey 工厂扩展 | 在 `query-keys.ts` 按模块命名空间扩展 `xxx: { all, lists, list, detail, ... }` | 在组件内就地定义 queryKey 字面量 |
### 缓存策略重构涉及文件清单
- `src/shared/lib/cache/{types,memory-store,redis-store,store-factory,cache-fn,invalidation-map,client-invalidation-map,invalidate,index}.ts` — 缓存基础设施9 个文件)
- `src/shared/lib/redis-client.ts` — 共享 Redis 客户端单例
- `src/shared/lib/query-keys.ts` — 客户端 queryKey 工厂
- `src/shared/hooks/use-action-query.ts` — 新增 queryKey 入参走 QueryClient
- `src/shared/hooks/use-action-mutation.ts` — 新增 actionId 自动 invalidate
- `src/modules/classes/data-access-*.ts` — 5+ 文件全部 cacheFn 包装 + 双导出 raw
- `src/modules/classes/actions-*.ts` — 5 文件全部 invalidateFor 替换 revalidatePath
- `src/modules/classes/components/*` — useActionQuery/useActionMutation 适配新版 API