import 'dart:async'; /// Repository 层数据新鲜度策略 — 反馈文档 §"实时性分级的工程落地"。 /// /// **核心理念**:把"数据新鲜度"和"UI 可用性"解耦: /// - 弱实时性场景(列表 / 档案)→ 优先返回缓存,后台刷新 /// - 强实时性场景(订单状态 / 支付结果)→ 优先实时,失败才降级 /// /// 在 Repository 方法签名里把策略当参数传入: /// ```dart /// Future> fetchOrders({ /// FreshnessPolicy policy = FreshnessPolicy.cacheFirst, /// }); /// ``` /// UseCase / Notifier 决定使用哪种策略,Repository 内部用 [policyAwareFetch] /// 实现统一的"读缓存 / 拉网络 / 写缓存"流程。 enum FreshnessPolicy { /// 弱实时:先缓存,后台刷新。 /// 网络失败不影响 UI 显示(缓存兜底)。**列表页、档案展示首选**。 cacheFirst, /// 强实时:先尝试网络,失败时才降级到缓存。 /// 适合订单状态、库存、消息计数等"过期数据会误导用户"的场景。 networkFirst, /// 纯离线:只读缓存,不发起网络请求。 /// 适合电梯/地铁等已知无网场景,或用户主动选择离线模式。 cacheOnly, /// 强制实时:只走网络,不读不写缓存。 /// 适合一次性操作(提交表单、确认下单)—— 缓存反而是污染源。 networkOnly, } /// 按 [FreshnessPolicy] 编排"读缓存 + 拉网络 + 写缓存"的统一流程。 /// /// **接入约定**: /// - [readCache]:从 Drift / Hive 读最近一份数据,**无缓存返回 null**(不要抛异常) /// - [fetchRemote]:调 datasource 拉网络,失败抛 [Exception](被本函数 catch) /// - [writeCache]:把网络结果写回缓存(cacheOnly / networkOnly 不会触发) /// /// **行为矩阵**: /// /// | 策略 | 读缓存 | 网络 | 网络失败 | /// |------|------|------|---------| /// | cacheFirst | 立即返回(如有)+ 后台刷新写回 | 后台 | 静默忽略(缓存已显示) | /// | networkFirst | — | 前台 | 降级返回缓存(如有),无缓存则抛 | /// | cacheOnly | 立即返回(如无返回 null) | 不发 | — | /// | networkOnly | 不读 | 前台 | 抛 | /// /// 返回 [Stream] 而非 Future 的原因:cacheFirst 场景 UI 需要先看到缓存、 /// 后台刷新后再看到最新值,自然是两次 emit。Drift 本身就支持 Stream, /// 让 UI 通过 StreamProvider 订阅即可。 Stream policyAwareFetch({ required FreshnessPolicy policy, required Future Function() readCache, required Future Function() fetchRemote, required Future Function(T value) writeCache, }) async* { switch (policy) { case FreshnessPolicy.cacheOnly: yield await readCache(); case FreshnessPolicy.networkOnly: yield await fetchRemote(); case FreshnessPolicy.cacheFirst: final cached = await readCache(); if (cached != null) yield cached; try { final fresh = await fetchRemote(); await writeCache(fresh); yield fresh; } on Object { // 静默:UI 已显示缓存值,网络失败不打扰用户 // 业务侧若需提示"刷新失败",应在 Notifier 层用 ref.listen 单独监听网络层 Failure if (cached == null) rethrow; } case FreshnessPolicy.networkFirst: try { final fresh = await fetchRemote(); await writeCache(fresh); yield fresh; } on Object catch (_) { final cached = await readCache(); if (cached != null) { yield cached; } else { rethrow; } } } }