12 KiB
12 KiB
一、统一模型 & 规范(细化)
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 发送流程(时序)
- App 调用
Analytics.init(config) - SDK:
- 保存 config
- 采集 DeviceInfo
- 初始化 Storage
- 启动定时任务(每 flushInterval 秒执行 flush)
- App 调用
Analytics.setUser(...)(可多次) - App 调用
Analytics.track(eventType, {...}):- 构造 Event
- 持久化入库
- 如果缓存条数 ≥ batchSize:启动一次 flush
- 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 SDK(Dart 独立实现)
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 _configUserInfo? _userDeviceInfo _deviceEventStorage _storageApiClient _apiClientTimer? _timerbool _isFlushing
- 方法:
init(config)setUsersetDeviceInfotrack(...)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)
ApiClientFuture<bool> sendBatch(List<Event> events)- POST
/AddEventListLog,Body 为数组; - 成功返回 true,失败 false 或抛异常。
- POST
- (可选)单条
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 SDK(Kotlin)
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 SDK(Swift)
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
本地可存储在:
- Flutter:sqflite/hive 单独表/box;
- Android:Room 新表
config; - iOS:UserDefaults / 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. 校验必填 tag(customTags)
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中。
- 仍发送,但将错误信息 encode 到
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:- 字段:
sentCountfailedCountretryCountavgLatencyMs
- 字段:
SDK_METRICS_QUEUE:- 字段:
queueSizedroppedEvents
- 字段:
实现:
- 在 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等。
- 自动在 customTags 注入
五、落地实施建议(简短)
5.1 Phase 1 任务拆分(多端并行)
- 架构公共约定 & 协议定义(1~2 天)
- Flutter SDK Phase 1 实现(约 5~10 个工作日)
- Android SDK Phase 1 实现(约 5~10 天)
- iOS SDK Phase 1 实现(约 5~10 天)
- 后端联调 & Demo App 验证(3~5 天)
5.2 后续 Phase 2/3 可以按版本迭代
- v1.x:只做 Phase 1,快速在一个业务 App 落地;
- v2.x:引入配置拉取 + 校验 + Debug 面板;
- v3.x:按需要逐步加上策略控制 & 插件。