import 'package:dio/dio.dart'; import 'package:yx_tracking_flutter/src/config/analytics_config.dart'; import 'package:yx_tracking_flutter/src/model/system_dim_info.dart'; import 'package:yx_tracking_flutter/src/network/http_client.dart'; import 'package:yx_tracking_flutter/src/storage/config_storage.dart'; import 'package:yx_tracking_flutter/src/storage/sqflite_config_storage.dart'; import 'package:yx_tracking_flutter/src/util/logger.dart'; /// Phase 2:配置拉取与缓存管理。 class ConfigManager { /// 创建配置管理器。 ConfigManager({ required this.config, this.refreshInterval = const Duration(hours: 12), ConfigStorage? storage, HttpClient? httpClient, }) : _httpClient = httpClient ?? HttpClient(config), _storage = storage ?? SqfliteConfigStorage(); /// SDK 配置。 final AnalyticsConfig config; /// 配置刷新间隔。 final Duration refreshInterval; final HttpClient _httpClient; final ConfigStorage _storage; SystemDimInfo? _current; /// 当前缓存的配置(可能为空)。 SystemDimInfo? get currentConfig => _current; /// 初始化本地缓存。 Future init() async { await _storage.init(); _current = await _storage.loadSystemDimInfo(); if (_current != null) { final eventCount = _current!.eventDefinitions.length; final tagCount = _current!.tagDefinitions.length; Logger.info( '已加载本地配置缓存: events=$eventCount, tags=$tagCount', ); } } /// 拉取并缓存配置。 Future fetchAndCacheConfig({bool force = false}) async { if (!force && _shouldSkipFetch()) { final last = _current?.lastFetchedAt; Logger.info('配置未过期,跳过拉取: lastFetchedAt=$last'); return; } try { final response = await _httpClient.get( config.getSystemAllDimInfoUri.toString(), queryParameters: {'systemCode': config.systemCode}, ); final payload = _extractPayloadMap(response.data); if (payload == null) { Logger.warn('配置拉取成功但响应结构无法解析,已忽略'); return; } final version = response.headers.value('etag') ?? response.headers.value('x-config-version'); final info = SystemDimInfo.fromResponse(payload, version: version); await _storage.saveSystemDimInfo(info); _current = info; final eventCount = info.eventDefinitions.length; final tagCount = info.tagDefinitions.length; Logger.info( '配置拉取并缓存成功: events=$eventCount, tags=$tagCount', ); } on DioException catch (e, st) { Logger.error('配置拉取失败(DioException)', e, st); } on Object catch (e, st) { Logger.error('配置拉取失败(未知异常)', e, st); } } /// 强制刷新配置。 Future forceRefresh() => fetchAndCacheConfig(force: true); /// 释放资源。 Future dispose() => _storage.dispose(); bool _shouldSkipFetch() { final current = _current; if (current == null) { return false; } final age = DateTime.now().difference(current.lastFetchedAt); return age < refreshInterval; } Map? _extractPayloadMap(dynamic data) { final root = _asStringKeyMap(data); if (root == null) { return null; } if (_looksLikeDimInfo(root)) { return root; } for (final key in const ['data', 'result', 'payload']) { final nested = _asStringKeyMap(root[key]); if (nested != null && _looksLikeDimInfo(nested)) { return nested; } } Logger.warn('配置响应结构无法识别,已忽略'); return null; } bool _looksLikeDimInfo(Map map) { return map.containsKey('systemEventTypes') || map.containsKey('systemCustonTas') || map.containsKey('systemCustomTags'); } Map? _asStringKeyMap(dynamic value) { if (value is Map) { return value; } if (value is Map) { return value.map((k, v) => MapEntry(k.toString(), v)); } return null; } }