feat: DOC-FULL-000 完整架构文档 v2
7 设计决策、V2 核心模型、V1→V2 迁移、API 协议映射、项目结构、验收链路 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
dd360c88e2
commit
22276bd44e
265
docs/document-runtime-architecture.md
Normal file
265
docs/document-runtime-architecture.md
Normal file
@ -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<ReadingPosition>,
|
||||||
|
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<ReadingPosition>,
|
||||||
|
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<i64>,
|
||||||
|
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<i64>,
|
||||||
|
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
|
||||||
|
```
|
||||||
Loading…
x
Reference in New Issue
Block a user