1. 埋点数据模型设计:事件类型定义、属性字段、用户标识、时间戳规范

埋点数据模型是数据采集的基石,决定了后续分析的维度、准确性与扩展性。本章定义统一的数据契约,涵盖事件类型、属性字段、用户标识与时间戳四大要素。

1.1 事件类型定义

事件类型用于区分用户行为的业务含义,采用枚举式分层结构:

  • 页面事件 — page_view(页面浏览)、page_leave(页面离开)
  • 交互事件 — click(点击)、long_press(长按)、swipe(滑动)
  • 业务事件 — purchase(购买)、login(登录)、share(分享)
  • 性能事件 — cold_start(冷启动耗时)、frame_drop(丢帧)

1.2 属性字段规范

每个事件携带公共属性与事件特有属性,采用扁平化 JSON 对象传输。

字段类型说明示例
event_nameString事件名称"click"
screen_nameString当前页面路由"home"
component_idStringComposable 组件唯一标识"btn_login"
extraMap<String,Any>自定义扩展属性{"item_id": "1001"}

1.3 用户标识策略

采用设备 ID + 登录用户 ID 双标识体系,保证设备级与账号级归因。

  • device_id:首次安装时通过 Android ID + UUID 生成,存储于 EncryptedSharedPreferences。
  • user_id:登录成功后写入,登出时清除;未登录时置空。
  • session_id:每次冷启动重新生成,关联事件序列。

1.4 时间戳规范

统一使用毫秒级 Unix 时间戳,由客户端在事件发生瞬间采集,避免服务器时间偏差。

// 时间戳生成示例(Kotlin)
val timestamp = System.currentTimeMillis()  // 毫秒
val event = Event(
    eventName = "click",
    componentId = "btn_pay",
    timestamp = timestamp,
    deviceId = deviceId,
    userId = userId
)

1.5 完整数据模型结构

注意:事件序列必须保证 timestamp 严格递增;同一会话内 session_id 保持不变;服务器端拒绝接收 timestamp 与接收时间差超过 10 分钟的事件。
埋点数据模型结构 事件类型(枚举) 属性字段(JSON) 用户标识 时间戳(毫秒) Event 对象(扁平 JSON) 设备侧采集 序列化 网络发送 关键约束 - 事件名称使用小写下划线命名法(snake_case) - 用户标识字段不可为 null(device_id 必须存在)

1.6 代码实现:Kotlin 数据类

data class Event(
    val eventName: String,
    val screenName: String?,
    val componentId: String?,
    val extra: Map<String, Any> = emptyMap(),
    val deviceId: String,
    val userId: String?,
    val sessionId: String,
    val timestamp: Long
)

// 使用示例
val loginClick = Event(
    eventName = "click",
    screenName = "login",
    componentId = "btn_login_confirm",
    extra = mapOf("click_source" to "button"),
    deviceId = deviceId,
    userId = null,
    sessionId = sessionId,
    timestamp = System.currentTimeMillis()
)

1.7 属性字段扩展原则

最佳实践:避免在 extra 中嵌套过深对象,保持扁平结构;对于频繁出现的自定义属性(如 item_id、category),建议提升为顶级字段,减少解析开销。

1.8 数据校验清单

  • 事件名称符合枚举范围,非空且长度 ≤ 64
  • device_id 不可为空,userId 可为空但需用 null 而非空字符串
  • timestamp 为 13 位毫秒级数字,范围在 1970-01-01 之后
  • extra 中键值对的键名使用 camelCase,值类型为 String / Number / Boolean