From 92bfc76e50f4c709c6697a2f557f0683ab3ee22d Mon Sep 17 00:00:00 2001 From: Zhou Weidong <502333560@qq.com> Date: Sat, 17 Oct 2026 02:55:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20C3=20=E5=AE=A2=E6=88=B7=E7=AB=AF?= =?UTF-8?q?=E5=9F=8B=E7=82=B9=20SDK=EF=BC=88EventTracker=20=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E4=B8=8A=E6=8A=A5=20+=20=E6=A0=B8=E5=BF=83=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/app.dart | 2 + lib/core/data/services/event_tracker.dart | 64 +++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 lib/core/data/services/event_tracker.dart diff --git a/lib/app.dart b/lib/app.dart index 3f353e5..3a6393f 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -41,6 +41,7 @@ import 'core/data/services/download_controller.dart'; import 'core/data/services/favorite_controller.dart'; import 'core/data/services/interaction_controller.dart'; import 'core/data/services/locale_controller.dart'; +import 'core/data/services/event_tracker.dart'; import 'core/data/services/playback_session.dart'; import 'core/data/services/video_player_port.dart'; import 'core/data/services/wallet_controller.dart'; @@ -235,6 +236,7 @@ class _LumaDramaAppState extends State { return MultiProvider( providers: [ Provider(create: (_) => PlaybackCoordinator()), + Provider(create: (_) => EventTracker(_apiClient)), Provider(create: (_) => widget.playerFactory), Provider.value(value: _catalogRepository), Provider.value(value: _playback), diff --git a/lib/core/data/services/event_tracker.dart b/lib/core/data/services/event_tracker.dart new file mode 100644 index 0000000..d60cec4 --- /dev/null +++ b/lib/core/data/services/event_tracker.dart @@ -0,0 +1,64 @@ +// Author: fruit.com.cn + +import 'dart:async'; + +import '../remote/api_client.dart'; + +/// 核心埋点事件名(第一阶段口径,后续与运营对齐后可扩展)。 +class AppEvents { + AppEvents._(); + + static const String playbackStart = 'playback_start'; + static const String playbackEnd = 'playback_end'; + static const String paySuccess = 'pay_success'; + static const String register = 'register'; + static const String click = 'click'; +} + +/// 埋点上报:本地缓冲、批量上报,失败静默丢弃不影响主流程。 +class EventTracker { + EventTracker(this._api); + + final ApiClient _api; + + static const int _flushSize = 10; + static const Duration _flushInterval = Duration(seconds: 10); + + final List> _buffer = []; + Timer? _timer; + + void track( + String eventName, { + String? page, + Map? properties, + }) { + _buffer.add({ + 'eventName': eventName, + 'eventTime': DateTime.now().toIso8601String(), + 'page': page, + 'properties': properties, + }); + if (_buffer.length >= _flushSize) { + flush(); + } else { + _timer ??= Timer(_flushInterval, flush); + } + } + + Future flush() async { + if (_buffer.isEmpty) return; + final events = List>.from(_buffer); + _buffer.clear(); + _timer?.cancel(); + _timer = null; + try { + await _api.post('/app/events', body: { + 'deviceId': 'device-0001', + 'sessionId': 'session-0001', + 'events': events, + }); + } catch (_) { + // 埋点失败不影响主流程,静默丢弃。 + } + } +}