# 客户端缓存键作用域规范(Cache Key Scoping) ## 目标 防止同一设备多账号切换时出现缓存串读(A 账号数据在 B 账号展示)。 本规范适用于 `apps/lib/data/cache/**` 以及所有通过 `CachedRepository` 读写的业务缓存。 --- ## 作用域模型 ### 1) 作用域级别 当前实现仅启用以下作用域: - `user:`:已登录用户态 未登录态实现说明: - `apps/lib/app/services/session_scope_manager.dart` 在登出后会调用 `CacheScope.resetProvider()`。 - 当 provider 未配置时,`apps/lib/data/cache/cached_repository.dart` 会跳过 scoped cache 读写,而不是写入 `anonymous` 命名空间。 ### 2) 键格式 客户端持久缓存最终键必须按以下格式生成: `cache::` 当前实现未使用会话代际后缀(epoch/version token),最终键格式为: - `cache:user::` 示例: - `cache:user:8ef4...:chat:history:first:default` - `cache:user:8ef4...:calendar:day:2026-03-29` --- ## 责任边界 ### 基础层(必须) - `CachedRepository` 负责统一附加 `` 前缀。 - Feature Repository 只声明业务键(``),不得手工拼接 userId 前缀。 ### 应用层(必须) - 在认证状态变化时更新当前缓存作用域: - 登录成功 -> `user:` - 登出/失效 -> 清空当前用户作用域并重置 provider(不进入 `anonymous` 作用域) --- ## 并发与切号安全 - 切号后,旧账号异步请求结果不得回写到新账号 UI 状态。 - 当前实现依赖 `user:` 级别缓存分区 + 认证切换时清理上一用户前缀: - - 登录切换:清理上一用户 `cache:user::` 前缀 - - 登出:清理当前用户前缀并关闭 scoped cache 读写 - 当前实现未引入 epoch/token 代际保护;若未来出现“切号后旧请求晚到并覆盖新 UI”问题,需要另行升级协议与实现。 --- ## 兼容与迁移策略 ### 向后兼容 - 旧无作用域键允许保留在本地存储中,不参与新读取路径。 - 新版本只读取带 `cache:user::` 前缀的键。 ### 迁移方式 - 采用增量迁移(additive),不执行强制删除旧键。 - 如需清理旧键,必须通过统一维护任务处理,不在功能逻辑中零散实现。 --- ## 验收标准 1. 同设备 A/B 账号来回切换,不出现跨账号历史/列表串读。 2. 登录后首次读取命中当前用户作用域键;登出后 scoped cache 不再参与读取。 3. Feature 仓库不再自行实现 userId 拼 key 逻辑。 --- ## Compatibility Strategy - 策略:`backward-compatible` - 本次为文档对齐更新,收敛未落地的 `anonymous`/epoch 设计,不改变现有应用行为。