feat: 添加用户行为分析功能
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
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<void> 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<String> _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<void> _handleFlush(List<BaseAnalyticsEvent> events) async {
|
||||
try {
|
||||
await _sender.send(events);
|
||||
} catch (e) {
|
||||
// TODO: 失败时落盘本地,下次启动重试
|
||||
debugPrint('Analytics send failed: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_queue.stop();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user