# ReadingEvent V2 → API 上传协议映射 ## 字段映射 | Rust ReadingEventV2 | API ReadingEventUploadItem | 来源 | |---------------------|---------------------------|------| | event_id | eventId | Rust 生成 UUID | | client_session_id | clientSessionId | Rust 生成 UUID(start_reading_session_v2) | | material_id | materialId | Rust 保存,iOS 传入 | | event_type | eventType | Rust→iOS 转 snake_case | | position | position | Rust(camelCase JSON,clamped) | | active_seconds_delta | activeSecondsDelta | Rust ActiveTimeTracker 计算 | | timestamp_ms | clientTimestampMs | Rust | | sequence | sequence | Rust(session 内递增) | | — | readingTargetType | **iOS 补充** | | — | platform | **iOS 补充**(= "ios") | | — | appVersion | **iOS 补充** | | — | clientTimezoneOffsetMinutes | **iOS 补充** | ## iOS 上传适配流程 ``` 1. Rust export_pending_events_v2(limit, ts) 2. iOS 获得 Vec 3. iOS 遍历每个 event,补充 readingTargetType/platform/appVersion/timezone 4. iOS 写入本地上传队列 5. 成功后调用 Rust ack_events_v2(eventIds) 6. 失败后调用 Rust mark_events_failed_v2(eventIds) 7. App 启动时调用 reload_stale_events_v2() 恢复未 ack 的事件 ``` ## eventType 映射 | Rust | API | |------|-----| | MaterialOpened | material_opened | | MaterialClosed | material_closed | | PositionChanged | position_changed | | Heartbeat | heartbeat | | MarkedAsRead | marked_as_read | ## delta 规则 | 事件 | delta | |------|-------| | MaterialOpened | 0 | | PositionChanged | 0 | | MarkedAsRead | 0 | | Heartbeat | tracker.tick() 返回值 | | MaterialClosed | tracker.close() 返回值 |