zhixi-document-runtime/docs/app-rust-bridge.md
wangdl dd360c88e2 fix: RagChatModule 导入 AiModule,修复 AiGatewayService 未注入
AiGatewayService 使用了 @Optional() 导致不报错但始终为 null,
sendMessage 永远走 fallbackReply。现在导入 AiModule 正确注入。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 13:38:57 +08:00

201 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# App ↔ Rust 调用协议
## 概述
本文档定义宿主 AppiOS/Android与 Rust Core 之间的全部调用边界。
核心原则:
```text
Rust Core 是纯计算层。
不做网络请求、不保存 Token、不访问后端 API。
文件由 App 下载到本地后,把路径交给 Rust。
阅读事件由 Rust 生成App 负责上传。
```
---
## 调用方向
```text
App ──调用──→ Rust Core
App ←──返回── Rust Core
App ──上传──→ Backend API
```
---
## Rust 暴露给 App 的函数
所有函数通过 `#[uniffi::export]` proc-macro 标注,经 UDL bindgen 生成 Swift/Kotlin 绑定。
### 1. 文件类型识别
```rust
fn detect_material_type(file_path: String) -> Result<MaterialType, DocumentError>
```
**输出**MaterialType 枚举值
**用途**App 据此决定使用哪种 PreviewMode
### 2. 图片 Metadata
```rust
fn read_image_meta(file_path: String) -> Result<ImageMeta, DocumentError>
```
**输出**width, height, format, file_size
### 3. 文本统计
```rust
fn read_text_stats(file_path: String) -> Result<TextStats, DocumentError>
```
**输出**line_count, word_count
### 4. 解析 Markdown
```rust
fn parse_markdown(content: String) -> Result<Vec<DocumentBlock>, DocumentError>
```
**输出**DocumentBlock 列表8 种 block 类型)
### 5. 解析纯文本
```rust
fn parse_text(content: String) -> Result<Vec<DocumentBlock>, DocumentError>
```
**输出**:段落 block 列表
### 6. 搜索 Markdown Blocks
```rust
fn search_markdown_blocks(blocks: Vec<DocumentBlock>, query: String) -> Vec<SearchResult>
```
**输出**SearchResult 列表block_id, snippet, match range
### 7. 搜索纯文本
```rust
fn search_text_content(content: String, query: String) -> Vec<SearchResult>
```
**输出**SearchResult 列表line_number, snippet, match range
### 8. 创建笔记锚点
```rust
fn create_note_anchor(material_id: String, position: Option<ReadingPosition>) -> NoteAnchor
```
**输出**NoteAnchor从 ReadingPosition 自动映射)
### 9. 推送阅读事件
```rust
fn push_reading_event(event: ReadingEvent)
```
**用途**:将事件推入全局缓冲区
### 10. 更新阅读位置
```rust
fn update_reading_position(material_id: String, position: ReadingPosition)
```
**用途**:生成 PositionChanged 事件并推入缓冲区
### 11. 导出待上报事件
```rust
fn export_pending_events() -> Vec<ReadingEvent>
```
**输出**:所有未上报事件(不清空缓冲区)
### 12. 清空已上报事件
```rust
fn clear_exported_events(count: u32)
```
**用途**:确认前 count 条已成功上传,从缓冲区移除
---
## Rust 不提供的函数(由 App 负责)
以下函数**不存在于 Rust Core**,完全由宿主 App 实现:
| App 职责 | 说明 |
|---------|------|
| `download_file(url)` | COS 下载 → 本地沙盒 |
| `upload_reading_events(events)` | POST 到 /reading/events |
| `save_note_to_backend(note)` | POST 到 /notes |
| `call_ai_chat(prompt)` | 调 AI API |
| `share_file(material_id)` | 系统分享面板 |
| `delete_material(material_id)` | 调后端删除接口 |
| `get_user_token()` | Token 管理 |
| `open_system_preview(file_path)` | QuickLook / 系统预览 |
---
## 调用时序
### 打开并阅读 Markdown
```text
1. App: download_file(cos_url) → /tmp/doc.md
2. App: detect_material_type("/tmp/doc.md") → Markdown
3. App: open_document("/tmp/doc.md", "abc123") → handle
4. App: get_document_info(handle) → DocumentInfo
5. App: get_markdown_blocks(handle) → [Block]
6. App: 渲染 blocks
7. 用户滚动 → App: update_reading_position("abc123", pos)
8. 定时器 → App: export_pending_events() → [events]
9. App: POST /reading/events
10. App: clear_exported_events(n)
11. 用户关闭 → App: export_pending_events() → last events
12. App: POST /reading/events
13. App: clear_exported_events(n)
```
### 打开 PDF平台预览
```text
1. App: download_file(cos_url) → /tmp/doc.pdf
2. App: detect_material_type("/tmp/doc.pdf") → Pdf
3. App: 判断 PreviewMode::PlatformPreview
4. App: open_system_preview("/tmp/doc.pdf") // QuickLook
5. App: 监听页码变化 → 转换为 PdfReadingPosition
6. App: update_reading_position("abc123", pos)
7. 同上导出事件流程
```
---
## 错误处理约定
所有函数返回 `Result<T, DocumentError>`
| 错误 | 场景 | App 处理 |
|------|------|---------|
| FileNotFound | 文件路径不存在 | 提示用户重新下载 |
| UnsupportedFormat | 无法识别的格式 | 显示"暂不支持" |
| ParseError | 文件内容损坏 | 显示"文件异常" |
| InvalidEncoding | 非 UTF-8 | 尝试其他编码或提示 |
| IoError | 磁盘错误 | 重试或提示 |
---
## 线程安全
- Rust Core 的函数应在**后台线程**调用
- App 主线程只做 UI 渲染
- `export_pending_events``clear_exported_events` 内部需要 Mutex 保护