feat: 添加缓存作用域支持多用户数据隔离
This commit is contained in:
Vendored
+28
@@ -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';
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+3
-1
@@ -3,6 +3,8 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
import 'cache_scope.dart';
|
||||||
|
|
||||||
class CacheEntry<T> {
|
class CacheEntry<T> {
|
||||||
const CacheEntry({required this.value, required this.fetchedAt});
|
const CacheEntry({required this.value, required this.fetchedAt});
|
||||||
|
|
||||||
@@ -196,7 +198,7 @@ class CacheInvalidator {
|
|||||||
void invalidate(String key) {
|
void invalidate(String key) {
|
||||||
final store = _store;
|
final store = _store;
|
||||||
if (store != null) {
|
if (store != null) {
|
||||||
unawaited(store.remove(key));
|
unawaited(store.remove(CacheScope.scopedKey(key)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-13
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'cache_scope.dart';
|
||||||
import 'cache_policy.dart';
|
import 'cache_policy.dart';
|
||||||
import 'cache_store.dart';
|
import 'cache_store.dart';
|
||||||
|
|
||||||
@@ -31,18 +32,21 @@ abstract class CachedRepository<T> {
|
|||||||
bool Function(T loaded)? shouldWriteLoaded,
|
bool Function(T loaded)? shouldWriteLoaded,
|
||||||
bool forceRefresh = false,
|
bool forceRefresh = false,
|
||||||
}) async {
|
}) async {
|
||||||
|
final scopeToken = CacheScope.token();
|
||||||
|
final scopedKey = CacheScope.scopedKey(key, scopeToken: scopeToken);
|
||||||
|
|
||||||
if (forceRefresh) {
|
if (forceRefresh) {
|
||||||
return _refreshAndWrite(
|
return _refreshAndWrite(
|
||||||
key,
|
scopedKey,
|
||||||
loadFromRemote,
|
loadFromRemote,
|
||||||
shouldWriteLoaded: shouldWriteLoaded,
|
shouldWriteLoaded: shouldWriteLoaded,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final cached = await readCacheEntry(key);
|
final cached = await _readDecodedEntry(scopedKey);
|
||||||
if (cached == null) {
|
if (cached == null) {
|
||||||
return _refreshAndWrite(
|
return _refreshAndWrite(
|
||||||
key,
|
scopedKey,
|
||||||
loadFromRemote,
|
loadFromRemote,
|
||||||
shouldWriteLoaded: shouldWriteLoaded,
|
shouldWriteLoaded: shouldWriteLoaded,
|
||||||
);
|
);
|
||||||
@@ -51,14 +55,14 @@ abstract class CachedRepository<T> {
|
|||||||
final decision = policy.evaluate(now: now(), fetchedAt: cached.fetchedAt);
|
final decision = policy.evaluate(now: now(), fetchedAt: cached.fetchedAt);
|
||||||
if (decision.shouldRefreshInBackground) {
|
if (decision.shouldRefreshInBackground) {
|
||||||
refreshInBackground(
|
refreshInBackground(
|
||||||
key: key,
|
key: scopedKey,
|
||||||
loadFromRemote: loadFromRemote,
|
loadFromRemote: loadFromRemote,
|
||||||
shouldWriteLoaded: shouldWriteLoaded,
|
shouldWriteLoaded: shouldWriteLoaded,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (decision.mustBlockForNetwork || !decision.canUseCached) {
|
if (decision.mustBlockForNetwork || !decision.canUseCached) {
|
||||||
return _refreshAndWrite(
|
return _refreshAndWrite(
|
||||||
key,
|
scopedKey,
|
||||||
loadFromRemote,
|
loadFromRemote,
|
||||||
shouldWriteLoaded: shouldWriteLoaded,
|
shouldWriteLoaded: shouldWriteLoaded,
|
||||||
);
|
);
|
||||||
@@ -66,13 +70,13 @@ abstract class CachedRepository<T> {
|
|||||||
return cached.value;
|
return cached.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<CacheEntry<T>?> readCacheEntry(String key) {
|
Future<CacheEntry<T>?> readCacheEntry(String key, {String? scopeToken}) {
|
||||||
return _readDecodedEntry(key);
|
return _readDecodedEntry(CacheScope.scopedKey(key, scopeToken: scopeToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> writeCacheEntry(String key, T value) {
|
Future<void> writeCacheEntry(String key, T value, {String? scopeToken}) {
|
||||||
return store.write<CacheEntry<Object?>>(
|
return store.write<CacheEntry<Object?>>(
|
||||||
key,
|
_scopedKey(key, scopeToken: scopeToken),
|
||||||
CacheEntry<Object?>(value: encodeValue(value), fetchedAt: now()),
|
CacheEntry<Object?>(value: encodeValue(value), fetchedAt: now()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -93,8 +97,8 @@ abstract class CachedRepository<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeCacheKey(String key) {
|
Future<void> removeCacheKey(String key, {String? scopeToken}) {
|
||||||
return store.remove(key);
|
return store.remove(CacheScope.scopedKey(key, scopeToken: scopeToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
void refreshInBackground({
|
void refreshInBackground({
|
||||||
@@ -118,7 +122,7 @@ abstract class CachedRepository<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<T> _refreshAndWrite(
|
Future<T> _refreshAndWrite(
|
||||||
String key,
|
String scopedKey,
|
||||||
Future<T> Function() loadFromRemote, {
|
Future<T> Function() loadFromRemote, {
|
||||||
bool Function(T loaded)? shouldWriteLoaded,
|
bool Function(T loaded)? shouldWriteLoaded,
|
||||||
}) async {
|
}) async {
|
||||||
@@ -126,7 +130,14 @@ abstract class CachedRepository<T> {
|
|||||||
if (shouldWriteLoaded != null && !shouldWriteLoaded(remote)) {
|
if (shouldWriteLoaded != null && !shouldWriteLoaded(remote)) {
|
||||||
return remote;
|
return remote;
|
||||||
}
|
}
|
||||||
await writeCacheEntry(key, remote);
|
await store.write<CacheEntry<Object?>>(
|
||||||
|
scopedKey,
|
||||||
|
CacheEntry<Object?>(value: encodeValue(remote), fetchedAt: now()),
|
||||||
|
);
|
||||||
return remote;
|
return remote;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _scopedKey(String key, {String? scopeToken}) {
|
||||||
|
return CacheScope.scopedKey(key, scopeToken: scopeToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>[DialCode('+86')];
|
||||||
Reference in New Issue
Block a user