新增:埋点队列三项完善——30 秒定时冲刷、失败指数退避、anonymousId 持久化(T3-19)
CI / flutter-gates (push) Successful in 2m15s

- 30 秒定时冲刷:AnalyticsService.startPeriodicFlush/stopPeriodicFlush,
  前台期间 Timer.periodic 周期冲刷;SessionTracker 新增 onEnterForeground
  回调,退后台停(并保留既有 flushNow 触发)、回前台恢复,App dispose 收尾。
- 失败退避:网络错误/5xx 后 30s→60s→120s 指数退避封顶 5 分钟,退避窗口
  只挡定时冲刷(flushNow/满 20/冷启动显式触发不受限),上传成功即重置;
  429 改按网络错误同路径保段重试(Retry-After 分支待后端限流落地)。
- anonymousId 持久化:restore 时从 shared_preferences 采用/落盘
  pb.analytics.anonymousId,首次生成后跨冷启动稳定;读取失败降级
  进程内临时 id 不崩溃。
- 测试 272 → 286(+14):fakeAsync 定时/退避 8 个、anonymousId 4 个、
  429 保段 1 个、前后台回调成对 1 个;analyze 0 问题、format 无 diff。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-08 16:12:19 +08:00
parent 720865bcb9
commit 4d40c38f06
9 changed files with 445 additions and 14 deletions
+10 -2
View File
@@ -15,6 +15,7 @@ class SessionTracker with WidgetsBindingObserver {
SessionTracker({
this.timeout = const Duration(minutes: 30),
this.onLeaveForeground,
this.onEnterForeground,
DateTime Function()? now,
}) : _now = now ?? DateTime.now,
_sessionId = const Uuid().v7();
@@ -25,6 +26,10 @@ class SessionTracker with WidgetsBindingObserver {
/// 首次离开前台时回调(app 装配层用于触发埋点队列冲刷)。
final VoidCallback? onLeaveForeground;
/// 回到前台时回调(app 装配层用于恢复 30 秒定时冲刷);
/// 冷启动首个 resumed 不触发(此前未离开过前台)。
final VoidCallback? onEnterForeground;
/// 时钟注入口,测试免真实等待。
final DateTime Function() _now;
@@ -39,8 +44,11 @@ class SessionTracker with WidgetsBindingObserver {
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
final leftAt = _leftForegroundAt;
if (leftAt != null && _now().difference(leftAt) > timeout) {
_sessionId = const Uuid().v7();
if (leftAt != null) {
if (_now().difference(leftAt) > timeout) {
_sessionId = const Uuid().v7();
}
onEnterForeground?.call();
}
_leftForegroundAt = null;
} else if (_lastState == AppLifecycleState.resumed) {