From 6c2f742676eb06d53f8079cd7be2e71cf0b5ac38 Mon Sep 17 00:00:00 2001 From: wangdl Date: Thu, 18 Jun 2026 14:23:12 +0800 Subject: [PATCH] docs: add iOS reading info integration guide (IOS-INFO-026) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Architecture diagram: Swift UI → SessionManager → Collector → Queue → API - Core components: Context, SessionManager, Events, Position, UploadQueue - API protocol: batch upload, progress query, continue-learning, summary - Path resolution, marked-read store, app lifecycle, error handling Co-Authored-By: Claude Opus 4.7 --- docs/reading-info-integration.md | 231 +++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 docs/reading-info-integration.md diff --git a/docs/reading-info-integration.md b/docs/reading-info-integration.md new file mode 100644 index 0000000..c5d335e --- /dev/null +++ b/docs/reading-info-integration.md @@ -0,0 +1,231 @@ +# iOS 阅读信息采集接入文档 + +## 1. 概述 + +AIStudyApp 通过 Rust FFI(zx_document_core)和自有 Swift 层采集用户阅读行为数据,上传至 API Server,为 AI 分析和个性化推荐提供数据基础。 + +数据流:Swift UI → ReadingRuntimeSessionManager → ReadingEventCollector → ReadingEventUploadQueue → API Server + +--- + +## 2. 架构 + +``` +┌─────────────────────────────────────────────────────┐ +│ SwiftUI Layer │ +│ MaterialReaderView → open/close/heartbeat/position │ +└──────────────────┬──────────────────────────────────┘ + │ +┌──────────────────▼──────────────────────────────────┐ +│ ReadingRuntimeSessionManager (Singleton) │ +│ 职责:生命周期管理、状态机、适配器路由 │ +│ 状态:idle → opened → reading → closed │ +└──────────────────┬──────────────────────────────────┘ + │ +┌──────────────────▼──────────────────────────────────┐ +│ ReadingEventCollector (Singleton) │ +│ 职责:事件缓冲、去重、序列化 │ +│ 事件类型:opened / heartbeat / position_changed / │ +│ closed / marked_as_read / failed │ +└──────────────────┬──────────────────────────────────┘ + │ +┌──────────────────▼──────────────────────────────────┐ +│ ReadingEventUploadQueue (Singleton) │ +│ 职责:批量上传、重试、ACK 确认 │ +│ 上传间隔:30s(默认) │ +│ 最大批次:50 条 │ +└──────────────────┬──────────────────────────────────┘ + │ +┌──────────────────▼──────────────────────────────────┐ +│ ReadingAPIService (APIClient) │ +│ 端点:POST /api/reading-events/batch │ +│ GET /api/reading/progress/:id │ +└─────────────────────────────────────────────────────┘ +``` + +--- + +## 3. 核心组件 + +### 3.1 ReadingMaterialContext + +打开阅读器时需要提供的上下文: + +```swift +struct ReadingMaterialContext { + let readingTargetType: ReadingTargetType // .material, .knowledgeSource + let materialId: String + let knowledgeBaseId: String? + let userId: String? +} +``` + +### 3.2 ReadingRuntimeSessionManager + +| 方法 | 说明 | +|------|------| +| `openMaterial(_ ctx:)` | 打开阅读会话,触发 `opened` 事件 | +| `closeMaterial()` | 关闭会话,触发 `closed` 事件 | +| `updatePosition(_ pos:)` | 更新阅读位置,触发 `position_changed` 事件 | +| `sendHeartbeat()` | 心跳,更新活跃时间 | +| `markAsRead()` | 标记已读 | +| `adapter` | V1/V2 适配器切换 | + +### 3.3 ReadingEvent + +```swift +enum ReadingEvent { + case opened(materialId: String, timestamp: UInt64) + case heartbeat(materialId: String, position: ReadingPosition?, timestamp: UInt64) + case positionChanged(materialId: String, position: ReadingPosition, timestamp: UInt64) + case closed(materialId: String, totalActiveMs: UInt64, timestamp: UInt64) + case markedAsRead(materialId: String, timestamp: UInt64) + case failed(materialId: String, errorCode: String, timestamp: UInt64) +} +``` + +### 3.4 ReadingPosition + +```swift +enum ReadingPosition { + case markdown(blockId: String, scrollProgress: Float) + case text(lineNumber: UInt32, scrollProgress: Float) + case pdf(pageNumber: UInt32, pageProgress: Float, overallProgress: Float) + case image(zoomScale: Float, offsetX: Float, offsetY: Float) + case epub(chapterId: String, chapterProgress: Float, overallProgress: Float) + case unknown +} +``` + +--- + +## 4. 上传协议 + +### 4.1 批量上传 + +``` +POST /api/reading-events/batch +Authorization: Bearer + +{ + "events": [ + { + "eventType": "position_changed", + "materialId": "mat-001", + "position": { "type": "Markdown", "blockId": "b1", "scrollProgress": 0.5 }, + "timestampMs": 1700000000123, + "sessionId": "sess-abc" + } + ] +} +``` + +### 4.2 阅读进度查询 + +``` +GET /api/reading/progress/:materialId?targetType=knowledge_source +Authorization: Bearer + +Response 200: +{ + "materialId": "mat-001", + "targetType": "knowledge_source", + "status": "in_progress", + "totalActiveSeconds": 300, + "isMarkedRead": false, + "lastReadAt": "2026-06-18T12:00:00Z", + "lastPosition": { "type": "Markdown", "blockId": "b1", "scrollProgress": 0.5 } +} +``` + +--- + +## 5. 位置恢复 + +### 5.1 继续学习 + +``` +GET /api/reading/continue-learning +Authorization: Bearer + +Response 200: +{ + "type": "knowledge_source", + "materialId": "mat-001", + "title": "Machine Learning Basics", + "lastProgress": 0.5, + "totalActiveSeconds": 600, + "lastReadAt": "2026-06-18T10:00:00Z", + "lastPosition": { "type": "Markdown", "blockId": "b1", "scrollProgress": 0.5 } +} +``` + +### 5.2 学习摘要 + +``` +GET /api/reading/learning-summary +Authorization: Bearer + +Response 200: +{ + "todaySeconds": 3600, + "weekSeconds": 7200, + "totalSeconds": 50000, + "activeDays": 5, + "sessionsCount": 12, + "materialsReadCount": 3, + "markedReadCount": 1, + "dailyAverageSeconds": 1200 +} +``` + +--- + +## 6. 文件路径解析 + +`MaterialPathResolver` 负责将 API 返回的 materialId 解析为本地文件路径: + +| 优先级 | 策略 | 路径 | +|:--:|------|------| +| 1 | Knowledge Source | `Documents/knowledge_sources//` | +| 2 | Documents 目录 | `Documents/` | +| 3 | Caches 目录 | `Caches/` | + +--- + +## 7. 已读标记 + +`MarkedReadStore` 提供离线已读标记(UserDefaults 持久化): + +- `mark(materialId)` — 标记已读 +- `unmark(materialId)` — 取消标记 +- `contains(materialId)` — 查询是否已读 + +--- + +## 8. App 生命周期 + +| 事件 | 处理 | +|------|------| +| `sceneDidEnterBackground` | UploadScheduler.flush() 强制上传 | +| `sceneWillEnterForeground` | RuntimeRecoveryService 恢复未完成会话 | +| `applicationWillTerminate` | 关闭所有活跃 Session + flush | + +--- + +## 9. 错误处理 + +| 场景 | 策略 | +|------|------| +| 网络不可用 | 事件缓冲在本地队列,最多保留 1000 条 | +| 上传失败 | 指数退避重试(1s→2s→4s→8s→30s) | +| ACK 丢失 | 未 ACK 事件下次上传时重发 | +| Rust FFI 崩溃 | V1 fallback 适配器(NoopReadingRuntimeAdapter) | + +--- + +## 10. 相关文档 + +- [阅读事件 API 协议](./reading-event-api-protocol.md)(api-server 仓库) +- [V2 事件 Pipeline 架构](./v2-event-pipeline.md) +- [Rust FFI 绑定更新流程](./rust-ffi-update.md)