import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:uuid/uuid.dart'; import 'events/events.dart'; import 'queue/event_queue.dart'; import 'sender.dart'; class AnalyticsTracker { static AnalyticsTracker? _instance; late final AnalyticsSender _sender; late final EventQueue _queue; late final String _deviceId; late final String _sessionId; late final String _platform; late final String _appVersion; late final String? _appBuild; late final String _env; String? _userId; AnalyticsTracker._(); static AnalyticsTracker get instance { if (_instance == null) { throw StateError('AnalyticsTracker not initialized. Call init() first.'); } return _instance!; } static Future init({ required String endpoint, required String deviceId, }) async { if (_instance != null) return; final packageInfo = await PackageInfo.fromPlatform(); final sessionId = await _getOrCreateSessionId(); final platform = Platform.isAndroid ? 'android' : 'ios'; final env = kDebugMode ? 'dev' : 'prod'; final tracker = AnalyticsTracker._(); tracker._sender = AnalyticsSender(endpoint: endpoint); tracker._queue = EventQueue( maxSize: 50, flushInterval: const Duration(seconds: 30), onFlush: tracker._handleFlush, ); tracker._deviceId = deviceId; tracker._sessionId = sessionId; tracker._platform = platform; tracker._appVersion = packageInfo.version; tracker._appBuild = packageInfo.buildNumber.isNotEmpty ? packageInfo.buildNumber : null; tracker._env = env; tracker._queue.start(); _instance = tracker; } static Future _getOrCreateSessionId() async { const uuid = Uuid(); final prefs = await SharedPreferences.getInstance(); var sessionId = prefs.getString('_analytics_session_id'); if (sessionId == null) { sessionId = 'sess_${uuid.v4()}'; await prefs.setString('_analytics_session_id', sessionId); } return sessionId; } void setUserId(String? userId) { _userId = userId; } String get userId => _userId ?? 'anonymous'; String get sessionId => _sessionId; void track(BaseAnalyticsEvent event) { _queue.add(event); } void trackLogin({ required String method, String? traceId, String? requestId, AnalyticsContext? context, }) { track( SessionLoginEvent( eventId: _generateEventId(), timestamp: DateTime.now(), userId: userId, deviceId: _deviceId, sessionId: _sessionId, platform: _platform, appVersion: _appVersion, appBuild: _appBuild, env: _env, pageName: 'login', traceId: traceId, requestId: requestId, method: method, context: context, ), ); } void trackLogout({ String? reason, int? sessionDurationS, String? pageName, String? traceId, AnalyticsContext? context, }) { track( SessionLogoutEvent( eventId: _generateEventId(), timestamp: DateTime.now(), userId: userId, deviceId: _deviceId, sessionId: _sessionId, platform: _platform, appVersion: _appVersion, appBuild: _appBuild, env: _env, pageName: pageName, traceId: traceId, reason: reason, sessionDurationS: sessionDurationS, context: context, ), ); } void trackAgentChatCompleted({ required String conversationId, String? scenario, int? messageCount, int? responseTimeMs, String? traceId, String? requestId, AnalyticsContext? context, }) { track( AgentChatCompletedEvent( eventId: _generateEventId(), timestamp: DateTime.now(), userId: userId, deviceId: _deviceId, sessionId: _sessionId, platform: _platform, appVersion: _appVersion, appBuild: _appBuild, env: _env, pageName: 'chat', traceId: traceId, requestId: requestId, conversationId: conversationId, scenario: scenario, messageCount: messageCount, responseTimeMs: responseTimeMs, context: context, ), ); } void trackPageView({ required String pageName, String? pageFrom, int? stayDurationMs, int? clickCount, String? traceId, AnalyticsContext? context, }) { track( PageViewEvent( eventId: _generateEventId(), timestamp: DateTime.now(), userId: userId, deviceId: _deviceId, sessionId: _sessionId, platform: _platform, appVersion: _appVersion, appBuild: _appBuild, env: _env, pageName: pageName, pageFrom: pageFrom, stayDurationMs: stayDurationMs, clickCount: clickCount, traceId: traceId, context: context, ), ); } void trackClick({ required String pageName, required String elementId, String? elementType, String? traceId, AnalyticsContext? context, }) { track( UiClickEvent( eventId: _generateEventId(), timestamp: DateTime.now(), userId: userId, deviceId: _deviceId, sessionId: _sessionId, platform: _platform, appVersion: _appVersion, appBuild: _appBuild, env: _env, pageName: pageName, elementId: elementId, elementType: elementType, traceId: traceId, context: context, ), ); } String _generateEventId() { return const Uuid().v4(); } Future _handleFlush(List events) async { try { await _sender.send(events); } catch (e) { // TODO: 失败时落盘本地,下次启动重试 debugPrint('Analytics send failed: $e'); } } void dispose() { _queue.stop(); } }