yx_tracking_flutter/2.Flutter 埋点 SDK 设计方案(独立 Da...

11 KiB
Raw Blame History

Flutter 埋点 SDK 设计方案(独立 Dart 实现)

1. 概述

1.1 背景

为企业内部 OA / 教育等应用统一建设埋点能力,当前后端已提供日志上报接口:

  • GET /api/ExternalEventlogs/GetSystemAllDimInfo
  • POST /api/ExternalEventlogs/AddEventListLog
  • POST /api/ExternalEventlogs/AddEventLog

需要基于上述接口,开发一个独立的 Flutter 埋点 SDKFlutter 运行环境使用,非纯 Dart 运行时),并规划三阶段能力演进。

1.2 目标

  • 开发 Flutter 独立埋点 SDK 包(例如:company_analytics_sdk)。
  • 提供统一的初始化、事件上报、缓存与批量上报能力。
  • 三阶段演进目标:
    • Phase 1可用性 + 安全稳定性。
    • Phase 2可维护性 + 配置化 + 调试。
    • Phase 3可观测性 + 动态策略 + 插件化。

2. 统一模型与对外 API

2.1 统一事件数据结构(对齐后端)

{
  "system_code": "string",
  "eventType": "string",
  "userInfo": {
    "userId": 0,
    "userName": "string",
    "account": "string"
  },
  "clientType": 1,
  "clientTimestamp": 0,
  "timestamp": "2026-01-26T08:29:44.734Z",
  "deviceInfo": {
    "os": "string",
    "model": "string",
    "screenResolution": "string"
  },
  "eventParams": {
    "Page": "string",
    "Url": "string",
    "ButtonId": "string"
  },
  "customTags": {
    // 业务维度
  }
}

2.2 Flutter SDK 对外 Facade API

class Analytics {
  static Future<void> init(AnalyticsConfig config);

  static Future<void> track(
    String eventType, {
    Map<String, dynamic>? eventParams,
    Map<String, dynamic>? customTags,
    int? timestamp, // ms
  });

  static Future<void> setUser(UserInfo? userInfo);

  static Future<void> setDeviceInfo(DeviceInfo deviceInfo);

  static Future<void> flush();
  
  // Phase 1: Lifecycle
  static void bindLifecycleObserver({bool flushOnBackground = true, bool flushOnDetached = true});
  static void unbindLifecycleObserver();

  static void setDebug(bool enabled);

  // Phase 3
  static void addInterceptor(AnalyticsInterceptor interceptor);
}

2.3 SDKConfig 统一规范

class AnalyticsConfig {
  final String systemCode;
  final String endpointBaseUrl;   // https://host仅基础 host
  final int clientType;           // 1=Android, 2=iOS, 3=Flutter
  final bool enableDebug;

  final int batchSize;            // 默认 20
  final int flushInterval;        // 秒,默认 15
  final int maxCacheSize;         // 默认 5000
  final int maxRetryCount;        // 默认 3

  final Duration connectTimeout;  // 默认 5s
  final Duration readTimeout;     // 默认 5s

  const AnalyticsConfig({
    required this.systemCode,
    required this.endpointBaseUrl,
    required this.clientType,
    this.enableDebug = false,
    this.batchSize = 20,
    this.flushInterval = 15,
    this.maxCacheSize = 5000,
    this.maxRetryCount = 3,
    this.connectTimeout = const Duration(seconds: 5),
    this.readTimeout = const Duration(seconds: 5),
    this.useIsolateStorage = true, // 默认开启 Isolate仅 Flutter 环境可用)
    this.maxEventAge = const Duration(days: 7), // 事件过期时间
  });
}

3. 包结构与模块划分

3.1 目录结构(建议)

lib/
  company_analytics.dart           // 对外 Facade唯一入口

  src/
    config/
      analytics_config.dart        // SDKConfig
      config_manager.dart          // (Phase 2+) 维度 & 策略配置

    model/
      event.dart                   // Event, StoredEvent
      user_info.dart
      device_info.dart
      system_dim_info.dart         // (Phase 2+) SystemInfo, EventDefinition, TagDefinition

    storage/
      event_storage.dart           // 抽象接口
      sqflite_event_storage.dart   // SQLite 实现
      isolate_event_storage.dart   // (New) Isolate 异步存储包装

    network/
      api_client.dart              // 与后端 API 通信
      http_client.dart             // 封装 Dio / http

    core/
      analytics_core.dart          // 主逻辑init/track/flush
      scheduler.dart               // 定时任务 & 并发控制
      validator.dart               // (Phase 2+) 事件校验
      interceptors.dart            // (Phase 3) 插件接口 & 管理

    util/
      logger.dart                  // 日志 & debug 控制
      time_util.dart               // 时间格式化
      device_util.dart             // 获取 os/model/screenResolution
      id_generator.dart            // 本地 eventId 生成

3.2 核心模型

UserInfo

class UserInfo {
  final int? userId;
  final String? userName;
  final String? account;

  const UserInfo({this.userId, this.userName, this.account});

  Map<String, dynamic> toJson() => {
        'userId': userId,
        'userName': userName,
        'account': account,
      }..removeWhere((k, v) => v == null);
}

DeviceInfo

class DeviceInfo {
  final String os;
  final String model;
  final String screenResolution;

  const DeviceInfo({
    required this.os,
    required this.model,
    required this.screenResolution,
  });

  Map<String, dynamic> toJson() => {
        'os': os,
        'model': model,
        'screenResolution': screenResolution,
      };
}

Event

class Event {
  final String systemCode;
  final String eventType;
  final UserInfo? userInfo;
  final int clientType;
  final int clientTimestamp;
  final String timestamp;
  final DeviceInfo deviceInfo;
  final Map<String, dynamic>? eventParams;
  final Map<String, dynamic>? customTags;
  final DateTime createTime;
  final int retryCount;

  Event({
    required this.systemCode,
    required this.eventType,
    required this.userInfo,
    required this.clientType,
    required this.clientTimestamp,
    required this.timestamp,
    required this.deviceInfo,
    required this.eventParams,
    required this.customTags,
    required this.createTime,
    this.retryCount = 0,
  });

  Map<String, dynamic> toJson() => {
        'system_code': systemCode,
        'eventType': eventType,
        'userInfo': userInfo?.toJson(),
        'clientType': clientType,
        'clientTimestamp': clientTimestamp,
        'timestamp': timestamp,
        'deviceInfo': deviceInfo.toJson(),
        'eventParams': eventParams,
        'customTags': customTags,
      };
}

StoredEvent

class StoredEvent {
  final int id;
  final Event event;
  final int retryCount;
  final DateTime createTime;

  StoredEvent({
    required this.id,
    required this.event,
    required this.retryCount,
    required this.createTime,
  });
}

4. Phase 1可用性 + 安全稳定性

4.1 功能范围

  • SDK 初始化:
    • 校验 AnalyticsConfig
    • 自动采集 DeviceInfo
    • 初始化本地 SQLite 存储;
    • 启动定时 flush 任务。
  • 事件上报:
    • 构造 Eventsystem_code、userInfo、deviceInfo、时间戳等
    • 异步写入本地队列SQLite
  • 批量上传:
    • 定时 & 条数触发 flush
    • 调用 POST /AddEventListLog
    • 处理重试 / 失败 / 删除等逻辑。
  • 安全:
    • endpoint 强制 HTTPS
    • 预留 header 扩展(签名等)。

4.2 本地存储设计Sqflite

表结构:

CREATE TABLE events (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  payload TEXT NOT NULL,
  retry_count INTEGER NOT NULL DEFAULT 0,
  create_time INTEGER NOT NULL
);

CREATE INDEX idx_events_create_time ON events(create_time);

接口:EventStorage

abstract class EventStorage {
  Future<void> init();
  Future<int> insert(Event event);
  Future<List<StoredEvent>> fetchBatch(int limit);
  Future<void> deleteByIds(List<int> ids);
  Future<int> count();
  Future<void> trimToMaxSize(int maxSize);
  Future<void> updateRetryCount(int id, int retryCount);
}

使用 SqfliteEventStorage 具体实现。

4.3 网络层 ApiClient

  • 基于 diohttp 实现:
    • sendBatch(List<Event> events) 调用 /AddEventListLog
  • 超时控制:使用 config.connectTimeout / readTimeout

4.4 核心逻辑 AnalyticsCore

职责:

  • 保存配置、用户状态、设备信息。
  • track:构造 Event、写入存储、触发 flush。
  • flush:从本地取批量事件,调用 ApiClient上报异常处理和重试。

关键点:

  • 使用 _isFlushing 标志防止并发 flush
  • 使用 SchedulerTimer.periodic实现周期 flush
  • 所有 IO 调用 async/await避免阻塞 UI。

4.5 Phase 1 验收标准Flutter

  • 功能:
    • Demo 中完成初始化、用户设置、事件上报、flush
    • 断网时事件写入本地,恢复网络后自动/手动 flush 成功。
  • 稳定性:
    • 高频 track10 次/秒5 分钟)无 crash、无明显卡顿。
  • 安全:
    • 所有请求走 HTTPS
    • 默认不打印 userInfo 等敏感数据(除非 debug=true
  • 文档:
    • 接入文档 + 示例代码。

5. Phase 2配置化 + 校验 + 调试

5.1 配置下发GetSystemAllDimInfo

  • Flutter SDK 调用:
    • GET /GetSystemAllDimInfo?system_code=xxx
  • 解析为:
    • SystemInfo
    • EventDefinition 列表(来自 systemEventTypes
    • TagDefinition 列表(来自 systemCustonTas

本地缓存为 SystemDimInfo(可存 SQLite / hive / SharedPreferences

5.2 ConfigManager

  • 负责:
    • 首次 init 后拉取配置;
    • 定期刷新;
    • 提供当前配置给 Validator 使用。

5.3 Validator 事件校验

  • 校验点:
    • eventType 是否在配置中存在;
    • 必填 tag来自 TagDefinition.isRequired)是否存在于 customTags
    • 简单类型检查int/bool/string

策略:

  • Debug 模式:
    • 日志详细提示未知事件、缺失字段、类型错误;
    • 可配置是否阻断发送。
  • Release 模式:
    • 不阻断,错误信息写入 customTags._sdk_validation

5.4 调试能力

  • 日志:
    • Logger 控制 debug 开关;
    • 打印事件 JSON、发送结果、校验结果。
  • Demo 调试界面(推荐):
    • 展示缓存事件数量 / 最近 N 条;
    • 提供“立即 flush”按钮。

6. Phase 3可观测性 + 策略 + 插件

6.1 SDK 自监控

  • 内部统计:
    • 发送成功次数 / 失败次数;
    • 重试次数;
    • 队列长度 / 溢出丢弃个数。
  • 周期上报为特殊事件(如 SDK_METRICS_SEND/SDK_METRICS_QUEUE)。

6.2 动态策略控制

  • 从配置中下发策略字段,例如:
{
  "sdkStrategy": {
    "enabled": true,
    "defaultSampleRate": 1.0,
    "eventSettings": {
      "PAGE_VIEW": { "enabled": true, "sampleRate": 0.2 },
      "DEBUG_EVENT": { "enabled": false, "sampleRate": 0.0 }
    }
  }
}
  • track 时根据策略:
    • 全局关停:直接丢弃全部事件;
    • 单事件关闭:丢弃该 eventType
    • 按 sampleRate 采样丢弃部分事件。

6.3 插件(拦截器)机制

接口示例:

abstract class AnalyticsInterceptor {
  Future<Event?> beforeSend(Event event) async => event;
  Future<void> afterSend(Event event, bool success) async {}
}
  • AnalyticsCore 中注册多个拦截器;
  • flush 时在实际发送前后调用;
  • 捕获拦截器内部异常,不影响主流程。

内置拦截器示例:

  • DebugInterceptor:打印事件详细信息;
  • CommonTagsInterceptor:为所有事件追加 _sdk_version_platform 等 tag。