From 41f35d6e22c04208b66b4a753de829a622ff38a3 Mon Sep 17 00:00:00 2001 From: zl-q Date: Mon, 30 Mar 2026 09:06:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E4=BD=9C=E7=94=A8=E5=9F=9F=E6=94=AF=E6=8C=81=E5=A4=9A=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=95=B0=E6=8D=AE=E9=9A=94=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/lib/data/cache/cache_scope.dart | 28 ++++++++++++++++ apps/lib/data/cache/cache_store.dart | 4 ++- apps/lib/data/cache/cached_repository.dart | 37 ++++++++++++++-------- apps/lib/data/models/dial_codes.dart | 17 ++++++++++ 4 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 apps/lib/data/cache/cache_scope.dart create mode 100644 apps/lib/data/models/dial_codes.dart diff --git a/apps/lib/data/cache/cache_scope.dart b/apps/lib/data/cache/cache_scope.dart new file mode 100644 index 0000000..6547fb8 --- /dev/null +++ b/apps/lib/data/cache/cache_scope.dart @@ -0,0 +1,28 @@ +typedef CacheScopeProvider = String? Function(); + +class CacheScope { + CacheScope._(); + + static CacheScopeProvider? _provider; + + static void configureProvider(CacheScopeProvider provider) { + _provider = provider; + } + + static void resetProvider() { + _provider = null; + } + + static String token() { + final raw = _provider?.call()?.trim(); + if (raw == null || raw.isEmpty) { + return 'anonymous'; + } + return raw; + } + + static String scopedKey(String key, {String? scopeToken}) { + final scope = scopeToken ?? token(); + return 'cache:$scope:$key'; + } +} diff --git a/apps/lib/data/cache/cache_store.dart b/apps/lib/data/cache/cache_store.dart index a6351e9..d92c823 100644 --- a/apps/lib/data/cache/cache_store.dart +++ b/apps/lib/data/cache/cache_store.dart @@ -3,6 +3,8 @@ import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; +import 'cache_scope.dart'; + class CacheEntry { const CacheEntry({required this.value, required this.fetchedAt}); @@ -196,7 +198,7 @@ class CacheInvalidator { void invalidate(String key) { final store = _store; if (store != null) { - unawaited(store.remove(key)); + unawaited(store.remove(CacheScope.scopedKey(key))); } } diff --git a/apps/lib/data/cache/cached_repository.dart b/apps/lib/data/cache/cached_repository.dart index ffeb8e4..79ef4e8 100644 --- a/apps/lib/data/cache/cached_repository.dart +++ b/apps/lib/data/cache/cached_repository.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'cache_scope.dart'; import 'cache_policy.dart'; import 'cache_store.dart'; @@ -31,18 +32,21 @@ abstract class CachedRepository { bool Function(T loaded)? shouldWriteLoaded, bool forceRefresh = false, }) async { + final scopeToken = CacheScope.token(); + final scopedKey = CacheScope.scopedKey(key, scopeToken: scopeToken); + if (forceRefresh) { return _refreshAndWrite( - key, + scopedKey, loadFromRemote, shouldWriteLoaded: shouldWriteLoaded, ); } - final cached = await readCacheEntry(key); + final cached = await _readDecodedEntry(scopedKey); if (cached == null) { return _refreshAndWrite( - key, + scopedKey, loadFromRemote, shouldWriteLoaded: shouldWriteLoaded, ); @@ -51,14 +55,14 @@ abstract class CachedRepository { final decision = policy.evaluate(now: now(), fetchedAt: cached.fetchedAt); if (decision.shouldRefreshInBackground) { refreshInBackground( - key: key, + key: scopedKey, loadFromRemote: loadFromRemote, shouldWriteLoaded: shouldWriteLoaded, ); } if (decision.mustBlockForNetwork || !decision.canUseCached) { return _refreshAndWrite( - key, + scopedKey, loadFromRemote, shouldWriteLoaded: shouldWriteLoaded, ); @@ -66,13 +70,13 @@ abstract class CachedRepository { return cached.value; } - Future?> readCacheEntry(String key) { - return _readDecodedEntry(key); + Future?> readCacheEntry(String key, {String? scopeToken}) { + return _readDecodedEntry(CacheScope.scopedKey(key, scopeToken: scopeToken)); } - Future writeCacheEntry(String key, T value) { + Future writeCacheEntry(String key, T value, {String? scopeToken}) { return store.write>( - key, + _scopedKey(key, scopeToken: scopeToken), CacheEntry(value: encodeValue(value), fetchedAt: now()), ); } @@ -93,8 +97,8 @@ abstract class CachedRepository { } } - Future removeCacheKey(String key) { - return store.remove(key); + Future removeCacheKey(String key, {String? scopeToken}) { + return store.remove(CacheScope.scopedKey(key, scopeToken: scopeToken)); } void refreshInBackground({ @@ -118,7 +122,7 @@ abstract class CachedRepository { } Future _refreshAndWrite( - String key, + String scopedKey, Future Function() loadFromRemote, { bool Function(T loaded)? shouldWriteLoaded, }) async { @@ -126,7 +130,14 @@ abstract class CachedRepository { if (shouldWriteLoaded != null && !shouldWriteLoaded(remote)) { return remote; } - await writeCacheEntry(key, remote); + await store.write>( + scopedKey, + CacheEntry(value: encodeValue(remote), fetchedAt: now()), + ); return remote; } + + String _scopedKey(String key, {String? scopeToken}) { + return CacheScope.scopedKey(key, scopeToken: scopeToken); + } } diff --git a/apps/lib/data/models/dial_codes.dart b/apps/lib/data/models/dial_codes.dart new file mode 100644 index 0000000..14efe79 --- /dev/null +++ b/apps/lib/data/models/dial_codes.dart @@ -0,0 +1,17 @@ +class DialCode { + const DialCode(this.value); + + final String value; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is DialCode && + runtimeType == other.runtimeType && + value == other.value; + + @override + int get hashCode => value.hashCode; +} + +const kDialCodes = [DialCode('+86')];