139 lines
4.1 KiB
Dart
139 lines
4.1 KiB
Dart
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;
|
||
}
|
||
}
|