456 lines
11 KiB
Markdown
456 lines
11 KiB
Markdown
|
||
|
||
# Flutter 埋点 SDK 设计方案(独立 Dart 实现)
|
||
|
||
## 1. 概述
|
||
|
||
### 1.1 背景
|
||
|
||
为企业内部 OA / 教育等应用统一建设埋点能力,当前后端已提供日志上报接口:
|
||
|
||
- `GET /api/ExternalEventlogs/GetSystemAllDimInfo`
|
||
- `POST /api/ExternalEventlogs/AddEventListLog`
|
||
- `POST /api/ExternalEventlogs/AddEventLog`
|
||
|
||
需要基于上述接口,开发一个独立的 Flutter 埋点 SDK(Flutter 运行环境使用,非纯 Dart 运行时),并规划三阶段能力演进。
|
||
|
||
### 1.2 目标
|
||
|
||
- 开发 Flutter 独立埋点 SDK 包(例如:`company_analytics_sdk`)。
|
||
- 提供统一的初始化、事件上报、缓存与批量上报能力。
|
||
- 三阶段演进目标:
|
||
- Phase 1:可用性 + 安全稳定性。
|
||
- Phase 2:可维护性 + 配置化 + 调试。
|
||
- Phase 3:可观测性 + 动态策略 + 插件化。
|
||
|
||
---
|
||
|
||
## 2. 统一模型与对外 API
|
||
|
||
### 2.1 统一事件数据结构(对齐后端)
|
||
|
||
```jsonc
|
||
{
|
||
"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
|
||
|
||
```dart
|
||
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 统一规范
|
||
|
||
```dart
|
||
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 目录结构(建议)
|
||
|
||
```text
|
||
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:
|
||
|
||
```dart
|
||
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:
|
||
|
||
```dart
|
||
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:
|
||
|
||
```dart
|
||
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:
|
||
|
||
```dart
|
||
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)
|
||
|
||
表结构:
|
||
|
||
```sql
|
||
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`
|
||
|
||
```dart
|
||
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`
|
||
- 解析为:
|
||
- `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 动态策略控制
|
||
|
||
- 从配置中下发策略字段,例如:
|
||
|
||
```jsonc
|
||
{
|
||
"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 插件(拦截器)机制
|
||
|
||
接口示例:
|
||
|
||
```dart
|
||
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。
|