import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:puzzleweave/models/api_helper.dart'; import 'package:puzzleweave/utils/utils.dart'; import 'package:logging/logging.dart'; final Logger _log = Logger('cached_request.dart'); typedef TransformFunction = Future Function(dynamic json); class CachedRequest with WidgetsBindingObserver { /// ✅ 优化:网络状态跟踪 bool _hasRecentSuccessfulFetch = false; bool get hasRecentSuccessfulFetch => _hasRecentSuccessfulFetch; /// ✅ 优化:防抖机制 Timer? _refreshDebouncer; static const _refreshDebounceDelay = Duration(seconds: 2); /// ✅ 优化:重试机制 int _retryCount = 0; static const _maxRetries = 3; static const _retryDelay = Duration(seconds: 5); static final Map _cache = {}; final String url; TransformFunction? transformFunction; final StreamController _streamController = StreamController.broadcast(); dynamic _transformed; dynamic get cachedData => _transformed; /// ✅ 优化:请求状态跟踪 bool _isLoading = false; bool get isLoading => _isLoading; CachedRequest._internal(this.url, this.transformFunction) { _log.info('New cached request: $url'); WidgetsBinding.instance.addObserver(this); _init(); } factory CachedRequest.fromUrl(String url, {TransformFunction? transformFunction}) { if (!_cache.containsKey(url)) { _cache[url] = CachedRequest._internal(url, transformFunction); } return _cache[url]!; } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { _log.info('App Resumed from background. Scheduling refresh for $url'); /// ✅ 优化:使用防抖刷新而不是立即刷新 _debouncedRefresh(); } } _init() { _cacheLoad(); _remoteLoad(); _streamController.onListen = () { _log.info('Stream listener added for $url. Current data available? ${_transformed != null}'); if (_transformed != null) { Future.microtask(() => _streamController.add(_transformed)); } }; } /// ✅ 优化:防抖刷新 void _debouncedRefresh() { _refreshDebouncer?.cancel(); _refreshDebouncer = Timer(_refreshDebounceDelay, () { if (!_isLoading) { refresh(); } }); } Future refresh() async { /// ✅ 优化:防止重复请求 if (_isLoading) { _log.info('Refresh already in progress for $url, skipping'); return; } _retryCount = 0; await _remoteLoad(); } Future reload() async { await _cacheLoad(); } /// ✅ 优化:带重试机制的远程加载 _remoteLoad() async { if (_isLoading) return; _isLoading = true; try { _log.info('Starting remote load for $url (attempt ${_retryCount + 1})'); /// ✅ 优化:添加超时控制 final response = await http.get(Uri.parse(url)).timeout( const Duration(seconds: 30), onTimeout: () => throw TimeoutException('Request timeout for $url', const Duration(seconds: 30)), ); if (response.statusCode != 200) { _hasRecentSuccessfulFetch = false; throw Exception('Invalid status code: ${response.statusCode} when fetching: $url'); } _hasRecentSuccessfulFetch = true; _retryCount = 0; // 重置重试计数 _log.info('${response.statusCode}, $url, Network Success: true'); final data = jsonDecode(response.body); await _emit(data); await saveString(cachePath, response.body); } catch (error) { _hasRecentSuccessfulFetch = false; _log.severe('Remote load failed for $url (attempt ${_retryCount + 1}): $error'); /// ✅ 优化:智能重试机制 if (_retryCount < _maxRetries && _shouldRetry(error)) { _retryCount++; _log.info('Scheduling retry ${_retryCount}/$_maxRetries for $url in ${_retryDelay.inSeconds}s'); Timer(_retryDelay, () { if (!_isLoading) { // 确保没有其他请求在进行 _remoteLoad(); } }); } else { _streamController.addError(error); } } finally { _isLoading = false; } } /// ✅ 优化:判断是否应该重试 bool _shouldRetry(dynamic error) { if (error is TimeoutException) return true; if (error is Exception) { final message = error.toString().toLowerCase(); // 网络相关错误可以重试 if (message.contains('timeout') || message.contains('connection') || message.contains('network') || message.contains('socket')) { return true; } } return false; } /// ✅ 优化:异步数据发射 Future _emit(dynamic data) async { _log.info('Emitting data for $url'); try { if (transformFunction != null) { data = await transformFunction?.call(data); } if (_streamController.hasListener) { _streamController.add(data); } _transformed = data; } catch (error) { _streamController.addError(error); _log.severe('Data transformation or emission failed: $error'); } } /// ✅ 优化:缓存加载错误处理 _cacheLoad() async { try { final file = await localFile(cachePath); if (await file.exists()) { _log.info('Loading from cache: $cachePath'); final data = await loadJson(cachePath); await _emit(data); return; } /// ✅ 优化:内置资源加载 if (url == ApiHelper.latestUri) { _log.info('Loading builtin latest asset data...'); final data = await loadJSONFromAsset('assets/builtin/latest.json'); await _emit(data); } else if (url == ApiHelper.collectionUri) { _log.info('Loading builtin collection asset data...'); final data = await loadJSONFromAsset('assets/builtin/collection.json'); await _emit(data); } } catch (error) { _log.warning('Cache load failed for $url: $error'); // 缓存加载失败不应该阻止远程加载,只记录警告 } } /// ✅ 优化:清理资源 void dispose() { _refreshDebouncer?.cancel(); WidgetsBinding.instance.removeObserver(this); _streamController.close(); } String get hash => md5Hash(url); String get cachePath => 'api_cache/$hash'; Stream get stream => _streamController.stream; @override String toString() => 'CachedRequest(url=$url, loading=$_isLoading, hasRecentFetch=$_hasRecentSuccessfulFetch)'; }