yx_tracking_flutter/1.统一模型 & 规范(细化).md

530 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 一、统一模型 & 规范(细化)
### 1.1 SDKConfig 统一规范
所有端保持字段一致,命名按各端风格适配:
jsonc
```
{
"systemCode": "OA_APP",
"endpointBaseUrl": "https://host/api/ExternalEventlogs",
"clientType": 1, // 1 Android, 2 iOS, 3 Flutter
"enableDebug": false,
"batchSize": 20, // 单次最多上传条数
"flushInterval": 15, // 秒,定时 flush 间隔
"maxCacheSize": 5000, // 本地缓存最大事件条数
"maxRetryCount": 3, // 最大重试次数
"connectTimeout": 5000, // ms
"readTimeout": 5000 // ms
// 预留,将来可扩:
// "globalTags": { "appVersion": "1.0.0", "channel": "official" }
}
```
### 1.2 Event 内部字段规范
统一内部 Event 类字段(各端自行建模):
ts
```
class Event {
String id; // 本地ID/主键
String systemCode;
String eventType;
UserInfo? userInfo;
int clientType;
int clientTimestamp; // ms
String timestamp; // ISO8601
DeviceInfo deviceInfo;
Map<String, dynamic>? eventParams;
Map<String, dynamic>? customTags;
int retryCount; // 已重试次数
DateTime createTime;
}
```
------
## 二、Phase 1可用 + 稳定(细化到类 & 表)
### 2.1 通用逻辑(跨端统一)
#### 2.1.1 发送流程(时序)
1. App 调用 `Analytics.init(config)`
2. SDK
- 保存 config
- 采集 DeviceInfo
- 初始化 Storage
- 启动定时任务(每 flushInterval 秒执行 flush
3. App 调用 `Analytics.setUser(...)`(可多次)
4. App 调用 `Analytics.track(eventType, {...})`
- 构造 Event
- 持久化入库
- 如果缓存条数 ≥ batchSize启动一次 flush
5. flush 流程:
- 从存储取前 `batchSize` 条事件;
- 调用 `AddEventListLog`
- 成功:删除这些事件;
- 网络错/5xx更新 retryCount重排队按指数退避调度
- 4xx记录日志删除这些事件视为「业务不可重试」
#### 2.1.2 本地表结构(推荐统一)
SQLite 表 `events`
sql
```
CREATE TABLE events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
payload TEXT NOT NULL, -- 事件完整 JSON
retry_count INTEGER NOT NULL DEFAULT 0,
create_time INTEGER NOT NULL -- ms since epoch
);
```
索引:
sql
```
CREATE INDEX idx_events_create_time ON events(create_time);
```
> Flutter 可用 sqflite 按此结构实现。
------
### 2.2 Flutter SDKDart 独立实现)
#### 2.2.1 目录结构建议
text
```
lib/
analytics.dart // 对外 Facade
src/
config/
analytics_config.dart
model/
event.dart
user_info.dart
device_info.dart
storage/
event_storage.dart // 接口
sqflite_event_storage.dart
network/
api_client.dart
core/
analytics_core.dart // EventManager + Scheduler
scheduler.dart
util/
logger.dart
time_util.dart
```
#### 2.2.2 关键类职责
- `Analytics`facade
- 静态方法:`init / track / setUser / setDeviceInfo / flush / setDebug`
- 内部持有单例 `AnalyticsCore`
- `AnalyticsCore`
- 字段:
- `AnalyticsConfig _config`
- `UserInfo? _user`
- `DeviceInfo _device`
- `EventStorage _storage`
- `ApiClient _apiClient`
- `Timer? _timer`
- `bool _isFlushing`
- 方法:
- `init(config)`
- `setUser`
- `setDeviceInfo`
- `track(...)`
- `flush({bool force = false})`
- `EventStorage` 接口 + `SqfliteEventStorage` 实现
- `Future<void> insert(Event event)`
- `Future<List<StoredEvent>> fetchBatch(int limit)`
- `Future<void> deleteByIds(List<int> ids)`
- `Future<int> count()`
- `Future<void> trimToMaxSize(int maxSize)`
- `ApiClient`
- `Future<bool> sendBatch(List<Event> events)`
- POST `/AddEventListLog`Body 为数组;
- 成功返回 true失败 false 或抛异常。
- (可选)单条 `sendSingle(Event)` 作降级方案。
- `Scheduler`
- 内含 `Timer.periodic`
- 每次 tick 调用 `core.flush()`
- 通过 `_isFlushing` 防并发。
#### 2.2.3 技术要求Flutter
- 使用 `async/await`,所有 IO 非阻塞;
- `Analytics` 所有对外 API 均返回 `Future`init / track / flush便于业务 await
- 避免 `Timer` 仍在运行却对象已销毁:在必要时提供 `dispose`(可选)。
------
### 2.3 Android SDKKotlin
#### 2.3.1 目录结构建议
text
```
com.company.analytics/
Analytics.kt // Facade
config/
AnalyticsConfig.kt
model/
Event.kt
UserInfo.kt
DeviceInfo.kt
storage/
EventDao.kt
EventDatabase.kt
network/
ApiService.kt
NetworkClient.kt
core/
AnalyticsCore.kt
FlushScheduler.kt
util/
Logger.kt
TimeUtil.kt
```
#### 2.3.2 关键实现要点
- Room 表结构:
kotlin
```
@Entity(tableName = "events")
data class EventEntity(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
@ColumnInfo(name = "payload") val payload: String,
@ColumnInfo(name = "retry_count") val retryCount: Int = 0,
@ColumnInfo(name = "create_time") val createTime: Long = System.currentTimeMillis()
)
```
- DAO:
kotlin
```
@Dao
interface EventDao {
@Insert
suspend fun insert(entity: EventEntity)
@Query("SELECT * FROM events ORDER BY create_time ASC LIMIT :limit")
suspend fun fetchBatch(limit: Int): List<EventEntity>
@Query("DELETE FROM events WHERE id IN (:ids)")
suspend fun deleteByIds(ids: List<Long>)
@Query("SELECT COUNT(*) FROM events")
suspend fun count(): Int
@Query("""
DELETE FROM events WHERE id IN (
SELECT id FROM events ORDER BY create_time ASC LIMIT
(SELECT MAX(0, COUNT(*) - :maxSize) FROM events)
)
""")
suspend fun trimToMaxSize(maxSize: Int)
}
```
- 调度:
- 使用 `CoroutineScope(SupervisorJob() + Dispatchers.IO)`
- `FlushScheduler``delay``TickerChannel` 实现周期任务;
- 使用 `Mutex` 或 flag 控制“单一 flush”。
------
### 2.4 iOS SDKSwift
#### 2.4.1 目录结构建议
text
```
AnalyticsSDK/
Analytics.swift // Facade
Config/
AnalyticsConfig.swift
Model/
Event.swift
UserInfo.swift
DeviceInfo.swift
Storage/
EventStorage.swift
SQLiteEventStorage.swift
Network/
ApiClient.swift
Core/
AnalyticsCore.swift
FlushScheduler.swift
Util/
Logger.swift
TimeUtil.swift
```
#### 2.4.2 关键实现要点
- 存储:
- 使用 SQLite用 FMDB/GRDB 等封装,或自己写简单 wrapper
- 网络:
- URLSession + Codable 序列化;
- 定时:
- `Timer.scheduledTimer``DispatchSourceTimer`
- 生命周期:
- 订阅 `UIApplication` 通知,在进入后台前调用 `flush()`
------
### 2.5 Phase 1 验收 checklist落地用
- 三端提供初始化、用户设置、事件上报、flush API
- 本地缓存可持久化,多次启动数据不丢;
- 断网 → 上报 → 恢复,事件能补发到后端;
- 压测 10K 条事件,上报成功率可接受(> 99%,无 crash
- 业务方集成 Demo App 并通过功能测试。
------
## 三、Phase 2配置化 + 校验 + 调试(细化)
### 3.1 新增数据结构
#### 3.1.1 维度配置缓存(本地)
以通用视角描述,三端类似:
ts
```
class SystemDimInfo {
SystemInfo systemInfo;
List<EventDefinition> eventDefinitions;
List<TagDefinition> tagDefinitions;
DateTime lastFetchedAt;
String? version; // 从后端 header 或 body 获得
}
```
- `EventDefinition` 来自 `systemEventTypes`
- `eventCode`, `eventName`, `description`
- `TagDefinition` 来自 `systemCustonTas`
- `tag_name`, `tag_type`, `is_required`, `description`
本地可存储在:
- Fluttersqflite/hive 单独表/box
- AndroidRoom 新表 `config`;
- iOSUserDefaults / SQLite。
### 3.2 ConfigManager / DimensionManager
职责:
- 拉取 `GetSystemAllDimInfo(system_code)`
- 解析为 `SystemDimInfo`
- 序列化到本地;
- 提供只读访问 API 给 Validator。
调用时机:
- init 成功后异步拉取;
- 间隔 N 小时(如 12 / 24h自动刷新
- 提供 `forceRefresh()` 接口(仅 debug 使用)。
### 3.3 Validator 详细逻辑
以伪代码描述:
ts
```
validateEvent(event: Event): ValidationResult {
let result = new ValidationResult()
// 1. eventType 是否在配置中
if (!config.hasEvent(event.eventType)) {
result.addError("UNKNOWN_EVENT_TYPE")
}
// 2. 校验必填 tagcustomTags
let requiredTags = config.getRequiredTags()
for (tag in requiredTags) {
if (!event.customTags?.containsKey(tag.name)) {
result.addWarning("MISSING_REQUIRED_TAG", tag.name)
}
else {
// 3. 简单类型校验
if (!typeMatch(event.customTags[tag.name], tag.type)) {
result.addWarning("TYPE_MISMATCH", tag.name)
}
}
}
return result
}
```
- Debug 模式:
- 所有 `errors` + `warnings` 打到日志;
- 可选策略:有 error 的事件不发送。
- Release 模式:
- 仍发送,但将错误信息 encode 到 `customTags._sdk_validation` 中。
### 3.4 调试支持细化
#### 3.4.1 日志规范
统一日志前缀与级别:
- `[AnalyticsSDK][DEBUG] ...`
- `[AnalyticsSDK][ERROR] ...`
在 debug 打开时:
- 打印:
- 初始化配置;
- 事件构造后的 JSON
- 每次发送的批次大小、结果状态码;
- 校验结果(仅 debug
#### 3.4.2 Demo 调试面板(可选但推荐)
Flutter / 原生各自 Demo App 中:
- 提供一个 Debug 页面:
- 显示当前缓存条数;
- 最近 20 条事件简要信息id、eventType、createTime、是否校验通过
- 按钮:`Flush Now`,调用 `Analytics.flush()`
------
## 四、Phase 3监控 + 动态策略 + 插件(细化)
### 4.1 SDK 自监控事件设计
定义一批内部保留事件(只你们自己分析用):
- `SDK_METRICS_SEND`
- 字段:
- `sentCount`
- `failedCount`
- `retryCount`
- `avgLatencyMs`
- `SDK_METRICS_QUEUE`
- 字段:
- `queueSize`
- `droppedEvents`
实现:
- 在 SDK 内部维护计数器;
- 每隔 M 分钟(如 10 分钟)封装成 Event正常通过日志接口上报。
### 4.2 策略配置结构
可通过扩展 `GetSystemAllDimInfo` 或另建接口下发 JSON例如
jsonc
```
{
"sdkStrategy": {
"enabled": true,
"defaultSampleRate": 1.0,
"eventSettings": {
"PAGE_VIEW": { "enabled": true, "sampleRate": 0.2 },
"DEBUG_EVENT": { "enabled": false, "sampleRate": 0.0 }
}
}
}
```
SDK 行为:
- 加载策略后:
- `if !strategy.enabled``track` 直接返回,不做任何事情(紧急开关)。
- 查找具体 eventType 的策略:
- `enabled=false` → 直接丢;
- `sampleRate<1` → 按 sampleRate 随机采样(或使用 eventId hash
### 4.3 插件机制设计
通用接口(以伪代码):
ts
```
interface EventInterceptor {
beforeSend(event: Event): Event | null | Promise<Event | null>
afterSend(event: Event, result: SendResult): void | Promise<void>
}
```
- SDK 内部维护 `List<EventInterceptor>`
- 在真正上传前,按顺序调用 `beforeSend`
- 任一返回 null → 事件被拦截,标记为丢弃;
- 上传结束后,按顺序调用 `afterSend`
错误隔离:
ts
```
for interceptor in interceptors:
try {
event = interceptor.beforeSend(event)
} catch (e) {
log.warn("interceptor error: ", e)
}
```
示例内置插件:
- DebugInterceptor
- beforeSend 打印事件 JSON受 debug 开关控制);
- CommonTagsInterceptor
- 自动在 customTags 注入 `_sdk_version`、`_platform` 等。
------
## 五、落地实施建议(简短)
### 5.1 Phase 1 任务拆分(多端并行)
- 架构公共约定 & 协议定义12 天)
- Flutter SDK Phase 1 实现(约 510 个工作日)
- Android SDK Phase 1 实现(约 510 天)
- iOS SDK Phase 1 实现(约 510 天)
- 后端联调 & Demo App 验证35 天)
### 5.2 后续 Phase 2/3 可以按版本迭代
- v1.x只做 Phase 1快速在一个业务 App 落地;
- v2.x引入配置拉取 + 校验 + Debug 面板;
- v3.x按需要逐步加上策略控制 & 插件。