# 前端导航解耦与统一缓存重构设计 ## 1. 背景与问题定义 当前 `apps` 端在日历(日/月)与待办页面中存在以下系统性问题: 1. 页面切换语义错误:将业务 tab 切换实现为 `push/go` 混用,导致页面重建与路由栈膨胀。 2. 数据刷新触发错误:页面通过路由监听触发 `load`,频繁重复请求后端。 3. 状态职责耦合:导航状态、页面状态、数据状态边界不清,导致“切换逻辑改动会牵出数据 bug”。 4. 回主页语义不一致:Dock 首页按钮被 `canPop -> pop` 策略污染,行为变成“返回上页”。 5. 缓存能力分散:仅存在局部的个人信息缓存(`SettingsUserCache`),缺少统一可复用缓存模块。 目标是完成一次结构化重构,建立「解耦的导航切换 + 统一缓存 + 可控一致性」体系。 ## 2. 目标与非目标 ### 2.1 目标 1. Home/Calendar/Todo 切换不重建主页面(保持页面实例与滚动状态)。 2. 日/月视图切换不触发整页重建和无必要网络请求。 3. 建立统一缓存模块,合并个人信息缓存并覆盖 Calendar/Todo 数据读取。 4. 启动体验采用「本地优先 + 后台静默刷新」策略,减少进入 App 的重复请求。 5. 数据只在必要时刷新:手动下拉、写操作失效、生命周期关键点、缓存策略命中。 6. 主页按钮语义固定为“回主页”,不再变成“返回上一页”。 ### 2.2 非目标 1. 本次不改后端协议与接口契约。 2. 本次不引入复杂离线同步冲突解决(如多端 CRDT)。 3. 本次不引入全量本地数据库迁移(先基于 SharedPreferences 持久化层)。 ## 3. 复杂度与风险分级 - Complexity: `S3` - 跨 router、calendar、todo、settings、DI 的架构级调整。 - Risk Tier: `L1` - 不触及鉴权协议和支付等高风险域,但涉及导航返回栈与数据一致性高回归区。 ## 4. 架构总览 ### 4.1 导航分层 采用两级导航: 1. 一级(主容器):`StatefulShellRoute.indexedStack` - 分支:Home / Calendar / Todo - 作用:保活分支页面,避免 tab 切换重建。 2. 二级(分支内部) - Calendar 分支:管理 month/day 主视图切换 + event detail/edit/share 子路由。 - Todo 分支:管理 list/detail/edit 子路由。 ### 4.2 状态与数据边界 1. 导航状态:Shell 当前分支 index、Calendar 内部视图类型。 2. UI 状态:选中日期、滚动位置、拖拽态、loading/error。 3. 数据状态:统一缓存模块管理(内存 + 持久化 + 网络回写)。 结论:页面只发“意图”,不直接承担缓存与路由策略。 ## 5. 统一缓存模块设计 ## 5.1 模块结构 新增 `apps/lib/core/cache/`: 1. `cache_key.dart` - 统一 key 命名规范。 2. `cache_policy.dart` - TTL、软/硬过期、最小刷新间隔、刷新原因枚举。 3. `cache_entry.dart` - 标准缓存实体(data/fetchedAt/expiresAt/version/dirty)。 4. `cache_store.dart` - 抽象接口(get/set/remove/invalidateNamespace)。 5. `memory_cache_store.dart` - 会话级热缓存。 6. `persistent_cache_store.dart` - 本地冷缓存(SharedPreferences JSON)。 7. `hybrid_cache_store.dart` - 两级缓存协调与 singleflight 去重。 8. `cache_invalidator.dart` - 统一精准失效入口。 ### 5.2 key 设计(首版) 1. 用户信息 - `user:profile:me` 2. 日历 - `calendar:day:YYYY-MM-DD` - `calendar:month:YYYY-MM` 3. 待办 - `todo:list:pending` - `todo:list:priority:`(按需) - `todo:detail:`(按需) ### 5.3 策略设计(平衡型) 读取顺序:`memory -> persistent -> network`。 刷新策略: 1. 软过期(stale-while-revalidate) - 先展示缓存,后台静默刷新。 2. 硬过期 - 超过硬过期后必须请求网络或提示数据过旧。 3. 最小刷新间隔 - 避免频繁切换/回前台引发抖动请求。 建议默认值: 1. `user:profile`:软过期 30min,硬过期 24h。 2. `calendar:day`:软过期 2min,硬过期 30min。 3. `calendar:month`:软过期 5min,硬过期 60min。 4. `todo:list:pending`:软过期 2min,硬过期 30min。 ### 5.4 个人信息缓存合并方案 现有 `SettingsUserCache` 并入统一缓存模块: 1. 新建 `UserProfileRepository`(或在现有 settings service 中引入统一缓存)。 2. `getProfile()` 通过 hybrid cache 获取 `user:profile:me`。 3. 更新 profile 成功后立即写回缓存并同步持久化。 4. 登出/会话失效时统一调用 `invalidateNamespace('user')`。 ## 6. 一致性风险与解决方案 平衡型缓存会存在“短暂陈旧窗口”。本设计通过以下机制将体验风险降到可接受范围。 ### 6.1 触发刷新矩阵 1. 手动下拉刷新:强制网络刷新。 2. 写操作成功:精准失效受影响 key 并触发回填。 3. App 回前台:若超过最小刷新间隔,触发静默刷新。 4. 网络离线 -> 在线:触发静默刷新。 5. 进入关键详情页:按策略进行 freshness check。 ### 6.2 写后一致性 1. 乐观更新:本地先更新 UI 与缓存,避免“我刚改完却没变”。 2. 失败回滚:API 失败时恢复旧值并 Toast 提示。 3. 精准失效:不做全局清空,只失效关联 key,兼顾一致性与性能。 ### 6.3 并发安全 1. singleflight:同 key 同时只允许一个网络请求。 2. 版本保护:缓存写入比较 `updatedAt/version`,拒绝旧响应覆盖新状态。 3. 失败兜底:请求失败不清空旧缓存,保持可读并允许重试。 ### 6.4 可见性保障 1. 页面可显示“上次同步时间”(轻提示)。 2. 硬过期数据需可见提醒(弱提示,不阻断基础浏览)。 3. 提供稳定手动刷新入口。 ## 7. 导航与页面职责重构 ### 7.1 路由重构 1. `app_router` 引入 shell 分支,不再平铺所有主页面。 2. Dock 切换改为 branch index 切换,不再 `push` 主页面。 3. Calendar 内部 month/day 切换改为视图切换,不新增栈层。 4. 事件详情/编辑等保留 `push`(细节页合理叠栈)。 ### 7.2 回主页逻辑修正 1. Dock Home 统一执行“切到 Home 分支/`go('/home')`”。 2. `returnToHomePreserveState` 仅用于非 Dock 的返回策略场景。 3. 消除 `canPop -> pop` 对主页按钮语义的影响。 ### 7.3 页面职责收敛 1. Calendar/Todo 页面移除路由监听触发 `load`。 2. 页面只调用 repository: - `get(policy)` - `refresh(force: true)` - `mutate(...) + invalidate(...)` 3. 页面不直接感知“缓存在哪一层”。 ## 8. 分阶段实施计划(里程碑) ### M1 导航壳层与切换语义 1. 引入 shell + 分支保活。 2. Dock 接口改造与主 tab 切换实现。 3. Home 按钮语义修正。 ### M2 统一缓存骨架 1. 新增 core cache 模块。 2. 接入 user profile(替换 `SettingsUserCache`)。 3. DI 注入 cache store 与 invalidator。 ### M3 Calendar 接入 1. 引入 `CalendarRepository` 与 day/month key。 2. 移除 route listener 自动刷新。 3. 切换 month/day 时默认走缓存,不触发无必要请求。 ### M4 Todo 接入 1. 引入 `TodoRepository` 与 list/detail key。 2. 拖拽、完成、编辑后的精准失效。 3. 下拉刷新走强制网络。 ### M5 清理与验证 1. 清理旧缓存与重复加载逻辑。 2. 补齐测试与性能观测。 3. 评估参数并收敛默认策略。 ## 9. 验收标准 ### 9.1 体验验收 1. Home/Calendar/Todo 切换无明显重建卡顿。 2. 日/月切换响应明显变快。 3. 首次冷启动可先看到本地缓存内容。 4. Dock Home 始终回主页。 ### 9.2 网络验收 1. 切换页面时网络请求显著减少。 2. 写操作后关联数据可及时更新。 3. 手动刷新可强制拉取并回写缓存。 ### 9.3 一致性验收 1. 不出现旧响应覆盖新数据。 2. 离线后恢复在线可自动静默同步。 3. 软过期/硬过期行为符合策略定义。 ## 10. 测试与验证计划 ### 10.1 单元测试 1. `hybrid_cache_store`:命中链路、singleflight、软硬过期判定。 2. `cache_invalidator`:写操作触发的 key 精准失效。 3. repository:读缓存、后台刷新、失败兜底、版本保护。 ### 10.2 组件/页面测试(高回归) 1. Dock 切换不重建分支主页面。 2. 日/月切换不重复触发全量加载。 3. Home 按钮行为稳定。 ### 10.3 集成回归 1. Calendar -> Todo -> Calendar 多轮切换请求计数。 2. Todo 完成后列表更新与缓存一致性。 3. profile 更新后设置页/其他依赖页可见一致。 ## 11. 风险与回滚 ### 11.1 主要风险 1. 导航壳层改造可能引发深链与返回栈回归。 2. 缓存策略参数不当可能造成陈旧感。 3. 早期失效 key 设计不完整可能出现局部不刷新。 ### 11.2 控制策略 1. 按里程碑逐步落地,每个里程碑可单独回滚。 2. 默认保留手动刷新兜底。 3. 增加请求计数与缓存命中日志(开发态)。 ### 11.3 回滚策略 1. 若 M1 不稳定,可先回退 shell 改造并保留缓存模块。 2. 若缓存接入问题集中,可按域回退(user/calendar/todo 分域开关)。 ## 12. 最终落地参数(2026-03-20) 1. 导航分级 - 一级页面唯一为 `Home`。 - 二级页面(日/月、待办、设置)侧滑返回统一回 `Home`,不允许直接退出 App。 - App 退出入口仅保留在 `Home`。 2. 缓存默认策略 - `user:profile`:软过期 30min,硬过期 24h。 - `calendar:day`:软过期 2min,硬过期 30min。 - `calendar:month`:软过期 5min,硬过期 60min。 - `todo:list:pending`:软过期 2min,硬过期 30min。 3. 生命周期刷新 - App 回前台时启用最小间隔 5min 的静默刷新协调器。 4. 提醒归档策略 - App 活跃态点击取消:立即请求后端归档。 - 延迟归档(pending/outbox)仅用于 App 不可用场景兜底。