11 KiB
11 KiB
Flutter 埋点 SDK 设计方案(独立 Dart 实现)
1. 概述
1.1 背景
为企业内部 OA / 教育等应用统一建设埋点能力,当前后端已提供日志上报接口:
GET /api/ExternalEventlogs/GetSystemAllDimInfoPOST /api/ExternalEventlogs/AddEventListLogPOST /api/ExternalEventlogs/AddEventLog
需要基于上述接口,开发一个独立的 Flutter 埋点 SDK(纯 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": {
// 事件上下文
},
"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/api/ExternalEventlogs
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
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 任务。
- 校验
- 事件上报:
- 构造 Event(system_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
- 基于
dio或http实现:sendBatch(List<Event> events)调用/AddEventListLog。
- 超时控制:使用
config.connectTimeout/readTimeout。
4.4 核心逻辑 AnalyticsCore
职责:
- 保存配置、用户状态、设备信息。
track:构造 Event、写入存储、触发 flush。flush:从本地取批量事件,调用 ApiClient,上报异常处理和重试。
关键点:
- 使用
_isFlushing标志防止并发 flush; - 使用
Scheduler(Timer.periodic)实现周期 flush; - 所有 IO 调用 async/await,避免阻塞 UI。
4.5 Phase 1 验收标准(Flutter)
- 功能:
- Demo 中完成初始化、用户设置、事件上报、flush;
- 断网时事件写入本地,恢复网络后自动/手动 flush 成功。
- 稳定性:
- 高频
track(10 次/秒,5 分钟)无 crash、无明显卡顿。
- 高频
- 安全:
- 所有请求走 HTTPS;
- 默认不打印 userInfo 等敏感数据(除非 debug=true)。
- 文档:
- 接入文档 + 示例代码。
5. Phase 2:配置化 + 校验 + 调试
5.1 配置下发(GetSystemAllDimInfo)
- Flutter SDK 调用:
GET /GetSystemAllDimInfo?system_code=xxx
- 解析为:
SystemInfoEventDefinition列表(来自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。