docs: add iOS reading info integration guide (IOS-INFO-026)
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
de384dc027
commit
6c2f742676
231
docs/reading-info-integration.md
Normal file
231
docs/reading-info-integration.md
Normal file
@ -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 <USER_JWT>
|
||||||
|
|
||||||
|
{
|
||||||
|
"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 <USER_JWT>
|
||||||
|
|
||||||
|
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 <USER_JWT>
|
||||||
|
|
||||||
|
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 <USER_JWT>
|
||||||
|
|
||||||
|
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/<id>/<filename>` |
|
||||||
|
| 2 | Documents 目录 | `Documents/<materialId>` |
|
||||||
|
| 3 | Caches 目录 | `Caches/<materialId>` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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)
|
||||||
Loading…
x
Reference in New Issue
Block a user