2.4 KiB
2.4 KiB
Reading Event Protocol
概述
Rust Core 生成阅读事件,App 负责收集和上传。这是一个单向数据流:Rust → App → Backend。
事件类型
enum ReadingEvent {
MaterialOpened { material_id, timestamp_ms },
MaterialClosed { material_id, timestamp_ms, active_seconds },
PositionChanged { material_id, position, timestamp_ms },
Heartbeat { material_id, active_seconds, position?, timestamp_ms },
MarkedAsRead { material_id, timestamp_ms },
}
事件说明
MaterialOpened
用户打开一份资料时触发。
{
"type": "material_opened",
"material_id": "abc123",
"timestamp_ms": 1717100000000
}
MaterialClosed
用户关闭资料时触发,active_seconds 为本次打开的累计活跃秒数。
{
"type": "material_closed",
"material_id": "abc123",
"timestamp_ms": 1717100300000,
"active_seconds": 285
}
PositionChanged
用户滚动/翻页/缩放导致阅读位置变化。频率由 App 控制(建议每 5 秒或停止交互后触发)。
{
"type": "position_changed",
"material_id": "abc123",
"timestamp_ms": 1717100100000,
"position": {
"type": "markdown",
"block_id": "heading-3",
"scroll_progress": 0.45
}
}
Heartbeat
定时心跳,用于计算阅读时长。即使位置未变化也应周期性触发(建议 10-15 秒)。
{
"type": "heartbeat",
"material_id": "abc123",
"timestamp_ms": 1717100150000,
"active_seconds": 15,
"position": null
}
MarkedAsRead
用户手动标记已读。
{
"type": "marked_as_read",
"material_id": "abc123",
"timestamp_ms": 1717100400000
}
事件收集流程
1. App 打开资料 → Rust 生成 MaterialOpened
2. App 启动定时器(~15s)
3. 每次定时器触发 → Rust 生成 Heartbeat
4. 用户交互(滚动/翻页)→ App 调用 update_position → Rust 生成 PositionChanged
5. App 关闭资料 → Rust 生成 MaterialClosed(含 active_seconds)
6. App 定期调用 export_pending_events() 获取所有未导出事件
7. App POST 事件到后端 /reading/events
8. App 调用 clear_exported_events() 清空缓冲区
App 侧实现要点
- 事件应在本地缓存(内存队列),不要一次传一个
- 批量上传(每次 5-20 条)或定时上传(每 30s)
- 网络失败时保留队列,下次重试
- 离线时事件不丢失,恢复网络后继续上传