/** * 缓存数据源实现 * 基于内存和 AsyncStorage 的混合缓存实现 */ import AsyncStorage from '@react-native-async-storage/async-storage'; import { ICacheDataSource, DataSourceError } from './interfaces'; interface CacheEntry { value: T; expiry: number | null; // null 表示永不过期 } export class CacheDataSource implements ICacheDataSource { private memoryCache: Map> = new Map(); private maxSize: number; private defaultTtl: number; // 默认过期时间(毫秒) private persistPrefix: string; constructor(config?: { maxSize?: number; defaultTtl?: number; // 默认5分钟 persistPrefix?: string; }) { this.maxSize = config?.maxSize || 100; this.defaultTtl = config?.defaultTtl || 5 * 60 * 1000; this.persistPrefix = config?.persistPrefix || 'cache_'; } /** * 检查 key 是否过期 */ private isExpired(entry: CacheEntry): boolean { if (entry.expiry === null) return false; return Date.now() > entry.expiry; } /** * 清理过期缓存 */ private cleanup(): void { const now = Date.now(); for (const [key, entry] of this.memoryCache.entries()) { if (entry.expiry !== null && now > entry.expiry) { this.memoryCache.delete(key); } } // 如果仍然超过最大大小,删除最旧的条目 while (this.memoryCache.size > this.maxSize) { const firstKey = this.memoryCache.keys().next().value; if (firstKey !== undefined) { this.memoryCache.delete(firstKey); } } } /** * 生成持久化存储 key */ private getPersistKey(key: string): string { return `${this.persistPrefix}${key}`; } // ==================== ICacheDataSource 实现 ==================== async get(key: string): Promise { try { // 先检查内存缓存 const memoryEntry = this.memoryCache.get(key); if (memoryEntry) { if (!this.isExpired(memoryEntry)) { return memoryEntry.value as T; } // 过期了,从内存中删除 this.memoryCache.delete(key); } // 尝试从持久化存储读取 const persistKey = this.getPersistKey(key); const stored = await AsyncStorage.getItem(persistKey); if (stored) { const entry: CacheEntry = JSON.parse(stored); if (!this.isExpired(entry)) { // 重新加载到内存缓存 this.memoryCache.set(key, entry); this.cleanup(); return entry.value; } // 过期了,删除 await AsyncStorage.removeItem(persistKey); } return null; } catch (error) { console.warn(`[CacheDataSource] Failed to get ${key}:`, error); return null; } } async set(key: string, value: T, ttl?: number): Promise { try { const expiry = ttl === undefined ? (this.defaultTtl > 0 ? Date.now() + this.defaultTtl : null) : (ttl > 0 ? Date.now() + ttl : null); const entry: CacheEntry = { value, expiry }; // 存入内存 this.memoryCache.set(key, entry); this.cleanup(); // 持久化存储(重要数据) if (ttl === undefined || ttl > 0) { const persistKey = this.getPersistKey(key); await AsyncStorage.setItem(persistKey, JSON.stringify(entry)); } } catch (error) { throw new DataSourceError( `Failed to set cache ${key}`, 'CACHE_SET_ERROR', 'CacheDataSource', error instanceof Error ? error : undefined ); } } async delete(key: string): Promise { try { // 从内存删除 this.memoryCache.delete(key); // 从持久化存储删除 const persistKey = this.getPersistKey(key); await AsyncStorage.removeItem(persistKey); } catch (error) { throw new DataSourceError( `Failed to delete cache ${key}`, 'CACHE_DELETE_ERROR', 'CacheDataSource', error instanceof Error ? error : undefined ); } } async clear(): Promise { try { // 清空内存 this.memoryCache.clear(); // 清空持久化存储中的所有缓存项 const keys = await AsyncStorage.getAllKeys(); const cacheKeys = keys.filter(k => k.startsWith(this.persistPrefix)); if (cacheKeys.length > 0) { await AsyncStorage.multiRemove(cacheKeys); } } catch (error) { throw new DataSourceError( 'Failed to clear cache', 'CACHE_CLEAR_ERROR', 'CacheDataSource', error instanceof Error ? error : undefined ); } } async has(key: string): Promise { try { // 检查内存 const memoryEntry = this.memoryCache.get(key); if (memoryEntry && !this.isExpired(memoryEntry)) { return true; } // 检查持久化存储 const persistKey = this.getPersistKey(key); const stored = await AsyncStorage.getItem(persistKey); if (stored) { const entry: CacheEntry = JSON.parse(stored); return !this.isExpired(entry); } return false; } catch (error) { return false; } } async getMultiple(keys: string[]): Promise<(T | null)[]> { return Promise.all(keys.map(key => this.get(key))); } async setMultiple(entries: { key: string; value: T; ttl?: number }[]): Promise { await Promise.all(entries.map(entry => this.set(entry.key, entry.value, entry.ttl))); } } // 导出单例实例 export const cacheDataSource = new CacheDataSource();