From 22276bd44ecf90c9c5cfc6c88501c0415d7b3497 Mon Sep 17 00:00:00 2001 From: wangdl Date: Sun, 7 Jun 2026 19:23:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20DOC-FULL-000=20=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E6=9E=B6=E6=9E=84=E6=96=87=E6=A1=A3=20v2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 7 设计决策、V2 核心模型、V1→V2 迁移、API 协议映射、项目结构、验收链路 Co-Authored-By: Claude Opus 4.7 --- docs/document-runtime-architecture.md | 265 ++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 docs/document-runtime-architecture.md diff --git a/docs/document-runtime-architecture.md b/docs/document-runtime-architecture.md new file mode 100644 index 0000000..4e4fc45 --- /dev/null +++ b/docs/document-runtime-architecture.md @@ -0,0 +1,265 @@ +# Document Runtime 完整架构 v2 + +> DOC-FULL-000 | 2026-06-07 + +## 1. 职责边界 + +### Rust (zx_document_core + zx_document_ffi) 负责 + +```text +文件类型识别 → 预览模式 → 文档信息提取 +Markdown / Text / Image / PDF / EPUB / Office 能力模型 +ReadingPosition → ReadingSessionV2 → ReadingEventV2 +ActiveTimeTracker → activeSecondsDelta 计算 +EventBuffer → export / ack / failed / clear +SearchResult → NoteAnchor → 阅读位置恢复 +UniFFI DTO → Swift/Kotlin binding → XCFramework 构建 +``` + +### Rust 不负责 + +```text +userId / token / knowledgeBaseId +readingTargetType (knowledge_source / temporary_file) +后端 API 请求 / COS 下载 / 本地持久化上传队列 +iOS UI / Android UI / AI / RAG / 向量化 +``` + +### iOS 负责补充 + +```text +readingTargetType / platform / appVersion / clientTimezoneOffsetMinutes +本地上传队列 / 调用 API batch upload / 离线重试 +阅读页 UI / 首页继续学习定位 / 分析展示 +``` + +### API 负责 + +```text +接收 ReadingEventUploadItem → 去重入库 → 聚合 +LearningSession / MaterialReadingProgress / DailyLearningActivity / LearningRecord +提供 continue / summary / trend / progress 查询接口 +``` + +--- + +## 2. 关键设计决策 + +### D1:Rust 只保存 materialId + +```rust +pub struct ReadingMaterialRef { + pub material_id: String, +} +``` + +Rust 不知道 `material_id` 是 `KnowledgeSource.id` 还是 `TemporaryReadingMaterial.id`。`readingTargetType` 由 iOS 上传适配层补充。 + +### D2:V1 保留 deprecated,V2 新增独立模块 + +``` +events.rs ← V1, 保留 deprecated +events_v2.rs ← V2, 新模块 +``` + +V1 不删除,iOS 已有接入。新代码走 V2。 + +### D3:clientSessionId 由 Rust 生成 UUID + +一次阅读页生命周期 = 一个 `clientSessionId`。Rust 生成保证跨平台一致。`sequence` 从 1 递增。 + +### D4:iOS 控制 tick 节奏,Rust 计算 delta + +``` +iOS Timer 每 15s → Rust pushHeartbeatV2 +App 后台 → iOS 调用 pause +App 前台 → iOS 调用 resume +退出页面 → iOS 调用 close +``` + +Rust 不创建 timer。`ActiveTimeTracker` 根据 timestamp 计算增量,时间倒退不产生负数。 + +### D5:Position JSON 使用 camelCase + +```rust +#[serde(tag = "type", rename_all = "camelCase")] +``` + +输出:`{"type":"pdf","pageNumber":12,"pageProgress":0.4,"overallProgress":0.32}` + +### D6:progress clamp 到 0~1 + +所有 `scrollProgress` / `pageProgress` / `overallProgress` / `chapterProgress` 字段:NaN→0, Infinity→1, 负数→0, >1→1。 + +### D7:ack 按 eventId 删除 + +``` +Rust exportPendingEventsV2 → iOS 写本地队列 → iOS ackEventsV2(eventIds) → Rust 删除 +``` + +`ack` 按 `eventId` 精确删除,不按数量。重复 ack 不崩溃。Buffer 最大 1000 条。 + +--- + +## 3. 核心 V2 模型 + +### 3.1 ReadingMaterialRef + +```rust +pub struct ReadingMaterialRef { + pub material_id: String, +} +``` + +### 3.2 ReadingSessionV2 + +```rust +pub struct ReadingSessionV2 { + pub client_session_id: String, // UUID, Rust 生成 + pub material: ReadingMaterialRef, + pub started_at_ms: i64, + pub last_event_at_ms: i64, + pub next_sequence: u64, // 从 1 递增 + pub total_active_seconds: u32, + pub last_position: Option, + pub status: ReadingSessionStatus, // Active | Paused | Closed +} +``` + +### 3.3 ReadingEventV2 + +```rust +pub struct ReadingEventV2 { + pub event_id: String, // UUID, Rust 生成 + pub client_session_id: String, // 来自 ReadingSessionV2 + pub material_id: String, // 来自 ReadingMaterialRef + pub event_type: ReadingEventTypeV2, + pub position: Option, + pub active_seconds_delta: u32, // 增量,非累计 + pub timestamp_ms: i64, + pub sequence: u64, +} +``` + +delta 规则: +| 事件 | delta | +|------|-------| +| MaterialOpened | 0 | +| PositionChanged | 0 | +| MarkedAsRead | 0 | +| Heartbeat | tick 产生的增量 | +| MaterialClosed | close 残余增量 | + +### 3.4 ActiveTimeTracker + +```rust +pub struct ActiveTimeTracker { + pub last_tick_ms: Option, + pub is_active: bool, + pub accumulated_remainder_ms: i64, +} +``` + +方法:`start / pause / resume / tick / close` + +### 3.5 EventBuffer V2 + +```rust +pub struct BufferedReadingEventV2 { + pub event: ReadingEventV2, + pub state: BufferedEventState, // Pending | Exported | Failed + pub exported_at_ms: Option, + pub retry_count: u32, +} +``` + +方法:`push_event_v2 / export_pending_events_v2 / ack_events_v2 / mark_events_failed_v2` + +容量:最大 1000 条,超出时 Failed→Exported→Pending 顺序清理。 + +--- + +## 4. V1 → V2 迁移策略 + +``` +1. 新增 events_v2.rs 模块(V2 核心) +2. V1 events.rs 保留,标记 #[deprecated] +3. V1 FFI 方法保留,不破坏 iOS 编译 +4. 新功能走 V2 FFI +5. iOS ReadingRuntimeAdapter 优先 V2,V1 为 fallback +``` + +### V1 vs V2 差异 + +| 字段 | V1 | V2 | +|------|----|----| +| event_id | 无 | UUID | +| client_session_id | 无 | UUID | +| active_seconds | 累计值/语义模糊 | active_seconds_delta 增量 | +| sequence | 无 | 递增 | +| target | material_id 裸字符串 | ReadingMaterialRef | + +--- + +## 5. 与 API 协议映射 + +| Rust ReadingEventV2 | API ReadingEventUploadItem | 来源 | +|---------------------|---------------------------|------| +| event_id | eventId | Rust | +| client_session_id | clientSessionId | Rust | +| material_id | materialId | Rust | +| event_type | eventType | Rust→iOS 转 snake_case | +| position | position | Rust (camelCase JSON) | +| active_seconds_delta | activeSecondsDelta | Rust | +| timestamp_ms | clientTimestampMs | Rust | +| sequence | sequence | Rust | +| — | readingTargetType | iOS 补充 | +| — | platform | iOS 补充 | +| — | appVersion | iOS 补充 | +| — | clientTimezoneOffsetMinutes | iOS 补充 | + +--- + +## 6. 项目结构 + +``` +zhixi-document-runtime +├── zx_document_core/src +│ ├── events.rs ← V1 (deprecated) +│ ├── events_v2.rs ← V2 (新增) +│ ├── session_v2.rs ← ReadingSessionV2 (新增) +│ ├── time_tracker.rs ← ActiveTimeTracker (新增) +│ ├── progress.rs ← ReadingPosition (改 camelCase+clamp) +│ ├── material_type.rs ← ✅ 完成 +│ ├── markdown.rs ← ✅ 完成 +│ ├── text.rs ← ✅ 完成 +│ ├── image_meta.rs ← ✅ 完成 +│ ├── search.rs ← ⚠️ 扩展 PDF/EPUB +│ ├── anchors.rs ← ⚠️ 补 from_search_result +│ ├── document.rs ← ⚠️ 扩展 DocumentInfo +│ ├── pdf.rs ← ❌ stub +│ ├── epub.rs ← ❌ stub +│ └── blocks.rs ← ✅ 完成 +├── zx_document_ffi/src +│ └── lib.rs ← 新增 V2 exports +├── docs/ +├── fixtures/ +├── bindings/ios/ +└── scripts/ +``` + +--- + +## 7. 最终验收链路 + +``` +iOS 创建 materialId +→ Rust startReadingSessionV2 +→ Rust 生成 clientSessionId +→ push MaterialOpened / PositionChanged / Heartbeat / MaterialClosed +→ exportPendingEventsV2 +→ iOS 写入本地队列 +→ ackEventsV2 +→ iOS 补 readingTargetType / platform / appVersion / timezone +→ API batch upload +```