Template
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
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user