457 lines
13 KiB
Markdown
457 lines
13 KiB
Markdown
# 一、总体目标与边界
|
||
|
||
## 1.1 目标
|
||
|
||
基于现有后端接口,建设一套统一的埋点 SDK,覆盖:
|
||
|
||
- 技术栈:
|
||
- Flutter:**独立纯 Dart SDK**
|
||
- Android:原生 Kotlin SDK
|
||
- iOS:原生 Swift SDK
|
||
- 对齐后端接口:
|
||
- `GET /api/ExternalEventlogs/GetSystemAllDimInfo`
|
||
- `POST /api/ExternalEventlogs/AddEventListLog`
|
||
- `POST /api/ExternalEventlogs/AddEventLog`
|
||
- 三阶段演进:
|
||
- Phase 1:可用性 + 安全稳定性
|
||
- Phase 2:可维护性 + 配置化 + 调试
|
||
- Phase 3:可观测性 + 动态策略 + 插件化
|
||
|
||
不绑定具体业务逻辑,但要能支持 OA、教育等各类场景。
|
||
|
||
## 1.2 统一事件数据模型
|
||
|
||
### 请求结构(与后端对齐)
|
||
|
||
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、buttonId、url…
|
||
},
|
||
"customTags": {
|
||
// 扩展维度,业务可自定义
|
||
}
|
||
}
|
||
```
|
||
|
||
### 统一对外 API 设计(跨三端)
|
||
|
||
(伪接口,三端风格尽量保持一致)
|
||
|
||
ts
|
||
|
||
```
|
||
class Analytics {
|
||
static init(config: SDKConfig): void | Future<void>
|
||
|
||
static track(
|
||
eventType: string,
|
||
options?: {
|
||
eventParams?: Map<string, any>
|
||
customTags?: Map<string, any>
|
||
timestamp?: number // 毫秒级 client 时间,可不传
|
||
}
|
||
): void | Future<void>
|
||
|
||
static setUser(userInfo: UserInfo | null): void | Future<void>
|
||
|
||
static setDeviceInfo(deviceInfo: DeviceInfo): void | Future<void>
|
||
|
||
static flush(): void | Future<void>
|
||
|
||
static setDebug(enabled: boolean): void
|
||
}
|
||
```
|
||
|
||
`SDKConfig` 关键字段(统一):
|
||
|
||
- `systemCode: string`
|
||
- `endpointBaseUrl: string`(如 `https://host/api/ExternalEventlogs`)
|
||
- `clientType: int`(统一约定:1=Android,2=iOS,3=Flutter)
|
||
- `enableDebug: boolean`
|
||
- `batchSize: int`(默认 20)
|
||
- `flushInterval: int`(秒,默认 15)
|
||
- `maxCacheSize: int`(默认 5000)
|
||
- `maxRetryCount: int`(默认 3)
|
||
- `connectTimeout`, `readTimeout`
|
||
|
||
------
|
||
|
||
# 二、整体架构设计(通用视角)
|
||
|
||
SDK 内部统一分层:
|
||
|
||
1. **API 层(Facade)**
|
||
- 暴露 `init / track / flush / setUser / setDebug`
|
||
- 做参数校验、线程切换(原生)。
|
||
2. **EventManager(事件管理层)**
|
||
- 构造标准事件对象(填充 system_code、userInfo、deviceInfo、时间)。
|
||
- 写入本地队列;触发上传调度。
|
||
3. **Storage(存储层)**
|
||
- 事件持久化(SQLite / 本地 KV)。
|
||
- 控制队列长度、过期清理。
|
||
4. **NetworkClient(网络层)**
|
||
- 封装调用:`AddEventListLog`(优先)、`AddEventLog`(可选降级)。
|
||
- 统一超时、重试、错误处理。
|
||
5. **Config / DimensionManager(配置 & 维度)**
|
||
- 管理 SDKConfig。
|
||
- Phase 2+:拉取 `GetSystemAllDimInfo`,存本地。
|
||
6. **Validator / Debug(校验 & 调试)**
|
||
- Phase 2+:基于配置做事件参数校验。
|
||
- 统一 debug 日志输出。
|
||
7. **Interceptors / Plugins(插件层)**
|
||
- Phase 3:对事件发送前后提供 Hook。
|
||
|
||
------
|
||
|
||
# 三、三端实现策略
|
||
|
||
## 3.1 Flutter 独立 SDK(纯 Dart)
|
||
|
||
- 语言:Dart
|
||
- 存储:推荐 `sqflite`(SQLite)或 `hive`(KV):
|
||
- `events(id, payload_json, retry_count, create_time)`
|
||
- 网络:`dio` 或 `http` 包
|
||
- 调度:
|
||
- `Timer.periodic` 实现定时 flush
|
||
- 控制并发:用一个「上传中」标志避免并发多次 flush
|
||
- 生命周期:
|
||
- SDK 已实现 `Analytics.bindLifecycleObserver()`,自动在后台/销毁时 flush;
|
||
- 应用也可手动调用 `flush()`。
|
||
|
||
## 3.2 Android SDK
|
||
|
||
- 语言:Kotlin(对 Java 兼容)
|
||
- 存储:Room 或 SQLite 封装
|
||
- 网络:OkHttp + Retrofit
|
||
- 线程:Kotlin Coroutines + 单一上传协程
|
||
- 生命周期:`ProcessLifecycleOwner` 监听前后台 → 后台前触发 `flush`
|
||
|
||
## 3.3 iOS SDK
|
||
|
||
- 语言:Swift(暴露 ObjC 兼容 API)
|
||
- 存储:SQLite 或 Core Data 轻量封装
|
||
- 网络:URLSession
|
||
- 生命周期:监听 `willResignActive` / `didEnterBackground` 调用 `flush`
|
||
|
||
------
|
||
|
||
# 四、三阶段规划(含架构与验收)
|
||
|
||
------
|
||
|
||
## Phase 1:可用性 + 安全稳定性
|
||
|
||
### 4.1 目标
|
||
|
||
- 提供稳定、可用的基础事件采集 & 批量上报能力。
|
||
- 不影响业务性能和体验。
|
||
- 基本安全(HTTPS,预留签名扩展)。
|
||
|
||
### 4.2 能力范围
|
||
|
||
1. 初始化
|
||
- 校验并保存 `SDKConfig`;
|
||
- 自动采集 `DeviceInfo`;
|
||
- 初始化本地存储;
|
||
- 启动定时 flush 调度(根据 flushInterval)。
|
||
2. 事件写入(track)
|
||
- 检查 SDK 是否已 init;
|
||
- 构造 Event:
|
||
- `system_code` from config
|
||
- `eventType` from 调用
|
||
- `userInfo` from `setUser`
|
||
- `clientType` from config
|
||
- `clientTimestamp` = 当前毫秒时间戳
|
||
- `timestamp` = ISO8601 字符串
|
||
- `deviceInfo` from 初始化采集
|
||
- `eventParams`、`customTags` from 调用
|
||
- 事件入内存队列 + 异步持久化保存。
|
||
3. 缓存与发送策略
|
||
- 存储:
|
||
- SQLite / KV + 索引;
|
||
- 最大缓存条数:`maxCacheSize`,超限则删除最旧事件。
|
||
- 发送:
|
||
- 定时器触发 flush;
|
||
- 队列长度 >= `batchSize` 可立即 flush;
|
||
- 调用 `AddEventListLog` 批量上传;
|
||
- 如后端明确不支持,可选降级 `AddEventLog`。
|
||
- 重试:
|
||
- 网络错误或 5xx 重试,最多 `maxRetryCount`,指数退避;
|
||
- 4xx 视为业务失败,不重试,直接丢弃该批,并记录错误(日志)。
|
||
4. 安全与稳定
|
||
- endpoint 必须为 HTTPS;
|
||
- 预留请求 header 扩展(签名 / 时间戳 / nonce)。
|
||
- 所有 IO 和网络在后台线程 / async,不阻塞 UI;
|
||
- 所有异常在 SDK 内部捕获,不向上抛出导致崩溃。
|
||
|
||
### 4.3 开发要求
|
||
|
||
通用:
|
||
|
||
- 核心逻辑单元测试覆盖率 ≥ 80%(事件构造、缓存、上传、重试)。
|
||
- 使用统一的日志前缀(例如 `[AnalyticsSDK]`)。
|
||
- API 文档齐全,含参数说明和示例代码。
|
||
|
||
Flutter:
|
||
|
||
- 不引入过重依赖,确保兼容常见 Flutter stable 版本;
|
||
- 高频 track 时(例如 10 次/秒)主 Isolate 无明显卡顿。
|
||
|
||
Android:
|
||
|
||
- 支持主流 Android 版本(按照公司基线,例如 API 21+);
|
||
- 在 debug 版开启详细日志,release 版关闭。
|
||
|
||
iOS:
|
||
|
||
- 支持主流 iOS 版本(例如 iOS 12+);
|
||
- 保证在 App 退后台 / 杀进程前能尽可能 flush。
|
||
|
||
### 4.4 Phase 1 验收标准
|
||
|
||
功能:
|
||
|
||
- 三端均可:
|
||
`init → setUser → track → flush` 全流程成功;
|
||
- 离线:
|
||
- 断网时事件成功写入本地;
|
||
- 恢复网络后自动/手动 flush,可在后端查看到补发数据;
|
||
- 批量:
|
||
- 实际调用 `AddEventListLog`;
|
||
- 后端数据结构与约定一致(字段正确)。
|
||
|
||
稳定:
|
||
|
||
- 连续调用 track 1 万次以内无崩溃;
|
||
- 事件缓存满时按预期丢弃最旧数据,不影响业务。
|
||
|
||
安全:
|
||
|
||
- 所有请求通过 HTTPS;
|
||
- 默认不输出包含 userInfo 的敏感日志(除非显式 enableDebug)。
|
||
|
||
文档:
|
||
|
||
- 各端接入指南(集成、初始化、上报示例);
|
||
- 一份基础 FAQ(常见错误、排查方式)。
|
||
|
||
------
|
||
|
||
## Phase 2:可维护性 + 配置化 + 调试能力
|
||
|
||
### 5.1 目标
|
||
|
||
- 降低埋点维护成本;
|
||
- 支持通过后端配置管理事件和维度;
|
||
- 提供调试和校验工具,提高埋点正确率。
|
||
|
||
### 5.2 能力范围
|
||
|
||
1. 利用 `GetSystemAllDimInfo` 下发配置
|
||
|
||
- SDK 在 init 后或定期调用:
|
||
- 获取 `systemInfo`、`systemEventTypes`、`systemCustonTas`;
|
||
- 转换成本地配置对象,缓存到本地(带 version / lastModified):
|
||
|
||
ts
|
||
|
||
```
|
||
type EventDefinition = {
|
||
eventCode: string
|
||
eventName: string
|
||
description?: string
|
||
}
|
||
|
||
type TagDefinition = {
|
||
tagName: string
|
||
tagType: string // string/int/bool/enum
|
||
isRequired: boolean
|
||
description?: string
|
||
}
|
||
```
|
||
|
||
- 失败不影响事件发送(降级为无配置模式)。
|
||
|
||
1. 事件校验
|
||
|
||
- 在 `track` 时(主要在 debug 模式启用):
|
||
- 校验 `eventType` 是否存在于 `systemEventTypes`;
|
||
- 使用 `systemCustonTas` 检查:
|
||
- 必填 tag 是否存在;
|
||
- 类型是否匹配(尝试容错转换)。
|
||
- 错误处理:
|
||
- Debug 模式:
|
||
- 在日志中详细打印:未注册事件、缺失字段、类型错误;
|
||
- 可配置:是否禁止发送这类事件。
|
||
- Release 模式:
|
||
- 仍发送,但在 `customTags` 中附加:
|
||
- `_sdk_invalid_event`
|
||
- `_sdk_missing_tags`
|
||
- `_sdk_type_error_fields`
|
||
|
||
1. 调试工具
|
||
|
||
- 日志级:
|
||
- `setDebug(true)` 时:
|
||
- 打印每条事件的 JSON;
|
||
- 打印发送结果(状态码、错误内容)。
|
||
- Demo 内调试页面(建议):
|
||
- 支持:
|
||
- 查看缓存事件数量 / 最近 N 条事件摘要;
|
||
- 按钮触发 flush。
|
||
|
||
1. 队列与策略优化(基本)
|
||
|
||
- 事件过期时间(例如默认 7 天);
|
||
- 网络状态感知(原生):
|
||
- Wi-Fi 下更高频 / 大批次;
|
||
- 蜂窝网络下减少频率 / 批量。
|
||
|
||
### 5.3 开发要求
|
||
|
||
- 新增模块:
|
||
- `ConfigManager`:拉取与缓存后端维度配置;
|
||
- `Validator`:统一事件校验逻辑。
|
||
- 兼容 Phase 1:
|
||
- 未取到配置时,一切行为回退到 Phase 1 模式;
|
||
- 增加测试:
|
||
- 事件存在 / 不存在;
|
||
- 必填字段缺失;
|
||
- 类型错误时的行为。
|
||
|
||
### 5.4 Phase 2 验收标准
|
||
|
||
功能:
|
||
|
||
- 初始化后,可以成功调用 `GetSystemAllDimInfo` 并本地缓存;
|
||
- 在 debug 模式下:
|
||
- 未配置的 `eventType` 调用时有明确日志提示;
|
||
- 缺失必填 tag / 类型错误能被检测并打印提示。
|
||
|
||
调试体验:
|
||
|
||
- 开发可以在本地:
|
||
- 看到完整事件 JSON;
|
||
- 根据提示迅速发现埋点错漏。
|
||
|
||
可维护性:
|
||
|
||
- 后端新增事件配置(systemEventTypes 中新增)后,不发版即可在客户端调用新事件;
|
||
- 对关键事件,可以通过配置 + SDK 校验,显著减少埋点错误。
|
||
|
||
------
|
||
|
||
## Phase 3:可观测性 + 动态策略 + 插件化
|
||
|
||
### 6.1 目标
|
||
|
||
- 让 SDK 自身「可被监控、可被远程控制」;
|
||
- 支持高级使用场景:采样、动态开关、插件扩展。
|
||
|
||
### 6.2 能力范围
|
||
|
||
1. SDK 自身监控埋点
|
||
|
||
- 内部采集 SDK 运行指标(作为特殊事件上报):
|
||
- 发送成功计数 / 失败计数 / 重试次数;
|
||
- 平均延迟;
|
||
- 队列当前长度 / 溢出丢弃数;
|
||
- 周期性上报(如每 N 分钟一次)。
|
||
|
||
1. 动态策略控制
|
||
|
||
- 通过配置(可在 `GetSystemAllDimInfo` 扩展,或单独接口)下发策略,如:
|
||
|
||
jsonc
|
||
|
||
```
|
||
{
|
||
"enabled": true,
|
||
"eventSettings": {
|
||
"EVENT_A": { "enabled": true, "sampleRate": 1.0 },
|
||
"EVENT_B": { "enabled": false, "sampleRate": 0.0 },
|
||
"EVENT_C": { "enabled": true, "sampleRate": 0.1 }
|
||
}
|
||
}
|
||
```
|
||
|
||
- SDK 在 `track` 时:
|
||
- 若事件被标记 `enabled=false` → 直接丢弃;
|
||
- 若 `sampleRate < 1` → 按采样率丢弃部分事件(哈希或随机)。
|
||
|
||
1. 插件 / 拦截器机制
|
||
|
||
- 接口示例(通用设计):
|
||
|
||
ts
|
||
|
||
```
|
||
interface EventInterceptor {
|
||
beforeSend(event: Event): Event | null | Promise<Event | null>
|
||
afterSend(event: Event, result: SendResult): void | Promise<void>
|
||
}
|
||
```
|
||
|
||
- SDK 提供:
|
||
- `registerInterceptor(interceptor)` / `addInterceptor` 方法;
|
||
- 内置拦截器:debug 打印、公共字段填充等。
|
||
|
||
典型用途:
|
||
|
||
- 业务统一追加自定义 `customTags`(如租户 ID、业务线 ID);
|
||
- 将部分发送失败事件写入本地日志,便于问题排查。
|
||
|
||
1. 版本管理
|
||
|
||
- 事件级 schemaVersion 预留:
|
||
- 在 `customTags` 或 `eventParams` 中加入 `_schema_version`。
|
||
- SDK 版本上报:
|
||
- 每条事件默认携带 `_sdk_version` 和 `_platform`,便于后端统计版本分布。
|
||
|
||
### 6.3 开发要求
|
||
|
||
- 性能:
|
||
- 插件链路运行开销可控(单条事件处理在毫秒级以内);
|
||
- 隔离:
|
||
- 单个插件抛出的异常不会影响主流程和其他插件;
|
||
- 文档:
|
||
- 清晰说明策略配置字段及含义;
|
||
- 插件开发 & 使用指南。
|
||
|
||
### 6.4 Phase 3 验收标准
|
||
|
||
- 自监控:
|
||
- 后端可以查看 SDK 的发送成功率、失败率、队列长度等;
|
||
- 动态控制:
|
||
- 通过配置关闭某个 eventType 后,客户端不再上报该事件;
|
||
- 调整 sampleRate 后,事件量按预期变化;
|
||
- 插件:
|
||
- 样例插件能实现:
|
||
- 自动附加业务 tag;
|
||
- 记录失败事件到本地日志。
|
||
|
||
------
|
||
|
||
这版方案已经:
|
||
|
||
- 明确:Flutter 为独立纯 Dart SDK,与 Android/iOS 平行;
|
||
- 统一:三端对外 API、数据结构、阶段目标和验收标准;
|
||
- 预留:维度配置、校验、动态策略、插件机制的扩展空间。 |