Files
flutter-template/lib/core/data/freshness_policy.dart
T
SkyJourney 61017f1c39 chore: 初始化脚手架 — sunny_mochi 企业级 Flutter 模板
Core 基础设施:Flavor 三包体系、Drift+SQLCipher 加密数据库
7 拦截器 Dio 网络栈、CrashReporter 崩溃日志、Sealed Failure 错误体系
5 色板主题系统、Auth 认证骨架(Clean Architecture)、Dev Panel、Mock Adapter

通过验证:flutter analyze(0 errors)、flutter test(全绿)、staging APK 74.8MB
2026-05-14 12:51:05 +08:00

95 lines
3.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
/// Repository 层数据新鲜度策略 — 反馈文档 §"实时性分级的工程落地"。
///
/// **核心理念**:把"数据新鲜度"和"UI 可用性"解耦:
/// - 弱实时性场景(列表 / 档案)→ 优先返回缓存,后台刷新
/// - 强实时性场景(订单状态 / 支付结果)→ 优先实时,失败才降级
///
/// 在 Repository 方法签名里把策略当参数传入:
/// ```dart
/// Future<List<Order>> 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<T?> policyAwareFetch<T>({
required FreshnessPolicy policy,
required Future<T?> Function() readCache,
required Future<T> Function() fetchRemote,
required Future<void> 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;
}
}
}
}