yx_tracking_flutter/lib/src/config/config_manager.dart

139 lines
4.1 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<void> 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<void> fetchAndCacheConfig({bool force = false}) async {
if (!force && _shouldSkipFetch()) {
final last = _current?.lastFetchedAt;
Logger.info('配置未过期,跳过拉取: lastFetchedAt=$last');
return;
}
try {
final response = await _httpClient.get<dynamic>(
config.getSystemAllDimInfoUri.toString(),
queryParameters: <String, dynamic>{'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<void> forceRefresh() => fetchAndCacheConfig(force: true);
/// 释放资源。
Future<void> dispose() => _storage.dispose();
bool _shouldSkipFetch() {
final current = _current;
if (current == null) {
return false;
}
final age = DateTime.now().difference(current.lastFetchedAt);
return age < refreshInterval;
}
Map<String, dynamic>? _extractPayloadMap(dynamic data) {
final root = _asStringKeyMap(data);
if (root == null) {
return null;
}
if (_looksLikeDimInfo(root)) {
return root;
}
for (final key in const <String>['data', 'result', 'payload']) {
final nested = _asStringKeyMap(root[key]);
if (nested != null && _looksLikeDimInfo(nested)) {
return nested;
}
}
Logger.warn('配置响应结构无法识别,已忽略');
return null;
}
bool _looksLikeDimInfo(Map<String, dynamic> map) {
return map.containsKey('systemEventTypes') ||
map.containsKey('systemCustonTas') ||
map.containsKey('systemCustomTags');
}
Map<String, dynamic>? _asStringKeyMap(dynamic value) {
if (value is Map<String, dynamic>) {
return value;
}
if (value is Map) {
return value.map((k, v) => MapEntry(k.toString(), v));
}
return null;
}
}