zhixi-document-runtime/docs/event-protocol.md

2.4 KiB
Raw Permalink Blame History

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
  • 网络失败时保留队列,下次重试
  • 离线时事件不丢失,恢复网络后继续上传