26 KiB
01 文档阅读 — 跨项目完整能力分析
试点能力一:从 C 端业务能力"文档阅读"出发,跨项目完整整理。 项目角色分类:主链路(iOS、Rust Document Runtime、主 API、数据库/COS)、上游支撑(Python RAG)、下游消费(Heavy Runtime)、运维可观测(Admin)。
1. 能力目标
用户能够在知习 App 中流畅地阅读上传到知识库的学习资料,支持多种文件格式,自动记录阅读位置,标记已读状态,并在下次打开时从上次中断处继续阅读。
2. C 端入口
用户可以从以下 iOS 页面进入文档阅读:
| 入口页面 | 触发方式 | 进入场景 |
|---|---|---|
| 学习主页 | 点击主操作卡片("继续上次学习") | 继续上次未读完的资料 |
| 知识库详情页·来源 Tab | 点击来源文件条目 | 阅读知识库中的资料 |
| 知识点详情页 | 点击"阅读原文件"或自动渲染 | 阅读知识点关联的原始资料 |
| 学习分析页 | 点击"继续阅读"卡片 | 从分析页继续阅读 |
| AI 反馈页 | "加入待巩固" → 学习主页 → 资料阅读 | 间接进入 |
在资料阅读页中,用户可以执行以下操作:
- 阅读内容(滚动浏览,支持 Markdown/纯文本/图片/EPUB 的原生渲染)
- 系统预览 PDF、Word、Excel(通过 QuickLook)
- 外部打开 PowerPoint(通过 ShareLink)
- 标记已读(本地即时生效,marked_as_read 事件异步上报)
- 取消标记已读(本地即时生效,服务端同步方式暂未确认)
- 打开 AI 对话(资料范围,需 RAG 已完成向量化才能获得语义检索结果)
- 查看资料详情(元数据、AI 处理状态、阅读进度)
- 开始专注学习会话(主动计时)
- 添加笔记(自动记录锚点位置)
3. 用户主流程
以下使用纯业务语言描述用户从进入资料到退出阅读的完整流程:
-
进入资料: 用户从知识库来源文件列表、继续学习推荐或知识点详情中点击资料,进入资料阅读页。
-
资料加载: App 检查设备上是否已有该文件。
- 如果有本地文件:直接打开。
- 如果没有:从云端 COS 下载文件到本地缓存目录。
- 只要资料记录存在、用户有访问权限且 COS/本地文件可用,用户即可阅读原文件,不需要等待 Python RAG 的文本解析、分块、向量化和索引完成。
-
类型检测与渲染: 系统检测文件类型,根据类型选择渲染方式。
- Markdown/文本/图片/EPUB:由 Rust Document Runtime 解析并渲染为可读内容。
- PDF/Word/Excel:提供"系统预览"按钮,用户点击后使用 iOS QuickLook 预览。
- PowerPoint:提供"外部打开"按钮,用户点击后使用系统分享功能在外部应用打开。
- 不支持的格式:显示"暂不支持此格式"提示。
-
自动阅读会话启动: 用户进入资料阅读页时,系统自动创建阅读会话(无需用户手动操作)。iOS 调用 Rust Document Runtime 开启 V2 ReadingSession,生成唯一的 client_session_id。该会话自动推送 material_opened 事件。
-
阅读位置恢复: 系统按优先级恢复上次阅读位置。
- 优先:从服务端 API 查询该资料的阅读进度(上次位置)。
- 其次:从本地缓存读取上次位置。
- 最后:从当前 Rust 阅读会话的 lastPosition 恢复。
-
阅读中: 用户滚动浏览内容。
- 系统自动记录阅读位置(2 秒防抖,iOS 调用 Rust push_position_changed)。
- iOS 创建并管理 15 秒心跳定时器,仅在阅读会话为 active 且 App 位于前台时触发。每次 Tick 调用 Rust heartbeat 接口,Rust 根据 ActiveTimeTracker 计算 activeSecondsDelta 后创建 heartbeat 事件并写入缓冲区。
- Rust 不创建也不管理 iOS 的系统定时器。
-
前后台切换: 用户将 App 切到后台时,iOS 暂停心跳定时器,阅读会话自动暂停,时间跟踪停止。切回前台时自动恢复。
-
标记已读: 用户点击工具栏"标记已读"按钮。该操作在本地 UserDefaults 立即更新,同时推送 marked_as_read 事件到 Rust 缓冲区,后续通过上传链路同步至服务端。取消标记已读可在本地即时生效,但服务端同步方式暂未确认(当前事件模型只有 marked_as_read,没有 unmarked_as_read)。
-
添加笔记: 用户点击工具栏笔记按钮,弹出快速笔记 Sheet。系统自动捕获当前阅读位置作为笔记锚点。用户输入笔记文字并保存(本地 UserDefaults 存储)。
-
AI 对话(资料范围): 用户点击工具栏 AI 按钮,跳转到 AI 对话页。对话自动限定在当前资料范围内。如果 Python RAG 已完成该资料的向量化索引,AI 可通过语义检索引用资料内容回答用户问题;如果 RAG 未完成,AI 范围对话、语义检索和知识点候选提取可能暂不可用。
-
开始专注学习会话: 用户点击工具栏"开始学习"按钮,启动专注学习计时器。计时器以 1 秒精度运行,默认目标 30 分钟。该学习会话是用户主动发起的专注时段,与进入资料时自动创建的阅读会话是不同的业务对象。
-
退出阅读: 用户返回上一页或切换到其他 Tab。
- iOS 调用 Rust close_reading_session,Rust 计算最终活跃时长增量,推送 material_closed 事件。
- 阅读位置保存到本地缓存。
- iOS 周期性地从 Rust 导出已缓冲事件,写入本地持久化上传队列,后续由队列负责 API 上传。
4. 项目参与关系
iOS(AIStudyApp)— 主链路参与者
职责:
- 提供资料阅读页的用户界面
- 调用 Rust Document Runtime(通过 UniFFI FFI)进行文件解析
- 管理 COS 文件下载和本地文件缓存
- 驱动自动阅读会话生命周期(调用 Rust V2 Session API)
- 创建并管理 15 秒心跳定时器(仅在 session active 且前台时触发)
- 驱动阅读事件采集(调用 Rust push 函数 + heartbeat 接口)
- 管理本地上传队列:将 Rust 导出事件映射为 UploadItem、写入持久化队列、确认 Rust 交接完成
- 管理阅读位置本地缓存
- 提供系统预览和外部打开能力
- 提供笔记创建界面和本地存储
- 提供标记已读本地缓存
- 前台/后台切换时管理会话暂停/恢复和心跳定时器启停
- App 被杀死后重启时执行崩溃恢复
Rust Document Runtime(zhixi-document-runtime)— 主链路参与者
职责:
- 检测文件类型(根据扩展名和文件内容推断 MaterialType)
- 解析 Markdown 为结构化文档块树
- 解析纯文本为段落块
- 提取 EPUB 章节结构和元数据
- 提取 PDF 页数和基本文本(用于搜索)
- 提取图片元数据
- 管理自动阅读会话(V2 ReadingSession:start、pause、resume、close、interrupt)
- 管理阅读事件缓冲区(push、export、ack、reload)
- 根据 ActiveTimeTracker 计算 activeSecondsDelta(由 iOS heartbeat tick 触发)
- 提供阅读位置模型
- 提供文件搜索能力
- 提供笔记锚点模型
- 提供活跃时间跟踪器
- 提供崩溃恢复机制(stale event reload、stale session cleanup)
不负责: 网络请求、COS 下载、API 调用、用户认证、UI 渲染、Office 文件高清预览、PDF 完整渲染、iOS 系统定时器创建和管理。
主 API(api-server)— 主链路参与者
职责:
- 提供资料信息查询
- 提供文件下载 URL(COS 预签名 URL)
- 提供资料阅读进度查询
- 接收并处理阅读事件批量上传
- 将原始阅读事件聚合为:自动阅读会话(LearningSession)、资料阅读进度、每日学习活动、学习记录
- 提供文件上传 URL
- 管理知识源的三个处理状态(parseStatus、indexStatus、learningStatus)
- 提供继续学习推荐
- 提供学习摘要
- 提供阅读趋势
Python RAG Worker — 上游支撑
不直接参与文档阅读。 只要资料记录、文件访问权限和 COS/本地文件可用,用户即可阅读原文件。Python RAG 负责文档导入后的异步处理(下载上传的文件 → 解析文本内容 → 分块 → 向量化嵌入 → 存入 Qdrant → AI 提取候选知识点)。RAG 未完成时,阅读原文件不受影响,但资料范围 AI 问答的语义检索和知识点候选提取可能暂不可用。
Heavy Runtime — 下游数据消费者
不直接参与文档阅读。 消费已聚合的阅读数据,生成 AI 分析结果(掌握度评估、薄弱点识别、学习建议、测验题目、闪卡)。位于阅读数据采集链的下游,不属于用户阅读时的同步调用链。
Admin(admin-projects)— 运维与可观测
不属于用户阅读时的同步调用链。 职责包括:
- 知识库来源文件列表查看
- 来源文件详情查看(含解析状态、索引状态)
- 文件管理(查看、删除上传文件)
- 资料阅读进度查看
- 资料诊断
- 文档导入监控与重试
5. 项目间调用关系
5.1 iOS → Rust Document Runtime
| 调用目的 | 调用方向 | 业务结果 |
|---|---|---|
| 检测文件类型 | iOS → Rust | 返回 MaterialType 和 PreviewMode 建议 |
| 解析 Markdown/文本文件 | iOS → Rust | 返回 DocumentBlock 数组 |
| 提取 EPUB/PDF/图片信息 | iOS → Rust | 返回对应元数据 |
| 搜索文档内容 | iOS → Rust | 返回 SearchResult 列表 |
| 开启自动阅读会话 | iOS → Rust | 创建 V2 ReadingSession,返回 client_session_id |
| 推送阅读事件 | iOS → Rust | 事件进入内存缓冲区(Pending 状态) |
| 心跳 Tick | iOS → Rust | Rust 计算 activeSecondsDelta,创建 heartbeat 事件写入缓冲区 |
| 导出待交接事件 | iOS → Rust | 返回 Exported 状态的事件列表,交接给 iOS |
| 确认事件交接完成 | iOS → Rust | 已 Ack 的事件从 Rust 缓冲区移除(交接给 iOS 本地队列后立即调用) |
| 崩溃恢复 | iOS → Rust | Exported 未 Ack 事件重置为 Pending |
| 清理过期会话 | iOS → Rust | 超时会话标记为 Interrupted 并从内存移除 |
5.2 iOS → 主 API
| 调用目的 | 调用方向 | 业务结果 |
|---|---|---|
| 获取文件下载 URL | iOS → API | 返回 COS 预签名下载链接 |
| 查询阅读进度 | iOS → API | 返回上次阅读位置、累计阅读时长、是否已读 |
| 上传阅读事件批次 | iOS → API | 从 iOS 本地持久化队列发起,返回 processed/duplicate/failed 计数 |
| 查询继续学习 | iOS → API | 返回最近阅读的资料信息 |
| 查询学习摘要/趋势 | iOS → API | 返回聚合数据 |
| 查询资料信息 | iOS → API | 返回知识源详情 |
5.3 主 API 内部调用
| 调用目的 | 调用方向 | 业务结果 |
|---|---|---|
| 阅读事件处理 | API 内部(同步事务) | 事件入库 → 聚合自动阅读会话 → 聚合阅读进度 → 聚合每日活动 → 创建学习记录 |
| 资料解析触发 | API → Redis 队列 | 将资料加入 document-import 队列,由 Python RAG Worker 异步处理(不阻塞阅读) |
5.4 Admin → 主 API(运维可观测,非同步调用链)
| 调用目的 | 调用方向 | 业务结果 |
|---|---|---|
| 查看知识库来源文件 | Admin → API | 返回来源文件列表(含解析和索引状态) |
| 查看/删除文件 | Admin → API | 文件管理 |
| 查看资料阅读进度 | Admin → API | 阅读进度详情 |
| 资料诊断 | Admin → API | 全部阅读数据和关联会话 |
| 文档导入监控/重试 | Admin → API | 导入任务列表、重试失败任务 |
6. 核心业务数据
6.1 资料(Material / KnowledgeSource)
- 谁创建: iOS 用户在导入资料页或添加知识点页上传文件后,API 创建 KnowledgeSource 记录。
- 谁更新: Python RAG Worker 更新解析状态和索引状态(异步,不阻塞阅读);API 在处理阅读事件时更新关联的阅读状态。
- 谁查询: iOS 在知识库详情页来源 Tab 查询列表、在资料详情页查询详情;Admin 在来源文件页查询列表。
- 用于什么功能: 知识库内容组织、文档阅读入口、资料处理状态展示。
6.2 阅读位置(ReadingPosition)
- 谁创建: Rust Document Runtime 在 iOS 推送 position_changed 事件时创建。
- 谁更新: 每次用户滚动时更新(2 秒防抖),由 Rust Document Runtime 维护 last_position。
- 谁查询: iOS 在进入阅读页时恢复上次位置(优先 API、其次本地缓存、最后会话位置);API 在阅读进度查询时返回。
- 用于什么功能: 跨会话的阅读位置恢复、"继续学习"推荐。
6.3 自动阅读会话(ReadingSession,Rust V2)
- 谁创建: Rust Document Runtime 在 iOS 调用 start_reading_session 时创建(用户进入资料阅读页时自动触发,无需用户手动操作)。
- 谁更新: Rust 在每次事件推送时更新内存中的会话状态(lastEventAt、totalActiveSeconds、lastPosition)。
- 谁查询: iOS 在恢复会话时查询 lastPosition 用于位置恢复。
- 用于什么功能: 自动追踪单次阅读行为的持续时长和事件序号,管理阅读会话生命周期(active→paused→closed)。产生 opened、closed、position_changed、heartbeat、marked_as_read 事件。
6.4 专注学习会话(LearningSessionView 计时)
- 谁创建: 用户主动点击资料阅读页工具栏"开始学习"按钮后,iOS 创建本地计时器。
- 谁更新: 用户操作暂停/继续/结束(含确认弹窗),计时器以 1 秒精度运行,默认目标 30 分钟。
- 谁查询: iOS 学习会话页展示当前进度。
- 用于什么功能: 用户主动发起的专注学习时段计时。与自动阅读会话(ReadingSession)是不同的业务对象。 当前 iOS 是否调用专注学习会话 API(LearningSessionService)持久化仍标记为"暂未确认"。
6.5 阅读事件(ReadingEvent)
- 谁创建: Rust Document Runtime 创建(5 种事件类型:material_opened、material_closed、position_changed、heartbeat、marked_as_read)。
- 谁更新: 事件创建后不可修改。API 更新其处理状态(processing → processed/failed/duplicate)。
- 谁查询: API 在批处理时查重;Admin 在阅读事件列表和异常检测页查询。
- 用于什么功能: 所有学习分析的原始数据源。API 根据事件聚合自动阅读会话、阅读进度、每日活动、学习记录。
6.6 资料阅读进度(MaterialReadingProgress)
- 谁创建: API 在收到该资料的第一个阅读事件时自动创建。
- 谁更新: API 在每次事件处理事务中 upsert(递增总时长、更新最后位置、更新会话数、标记已读状态)。
- 谁查询: iOS 在进入阅读页时查询(恢复位置);Admin 在阅读进度页查询。
- 用于什么功能: 资料级别的阅读进度追踪、"继续学习"推荐的数据源、阅读统计。
6.7 笔记(QuickNote)
- 谁创建: iOS 用户在快速笔记 Sheet 中输入并保存时创建。
- 谁更新: 当前不支持修改(仅创建和删除)。
- 谁查询: iOS 在资料阅读页和知识点详情页查询(本地 UserDefaults)。
- 用于什么功能: 阅读过程中的快速记录。当前仅本地存储,未同步到服务端。
7. 查询入口
用户在哪里查看
- 阅读内容: iOS 资料阅读页直接渲染文档内容。
- 阅读进度: iOS 资料详情页的阅读进度卡片(累计时长、阅读状态、上次阅读时间)。
- 资料信息: iOS 资料详情页(文件名、格式、大小、上传日期、AI 处理状态)。
- 来源文件列表: iOS 知识库详情页的来源 Tab。
- 继续阅读: iOS 学习主页的主操作卡片(继续上次学习)。
Admin 在哪里查看(运维可观测)
- 来源文件列表: Admin 知识库→来源文件页(含解析状态、索引状态)。
- 文件管理: Admin 文件与 COS 管理页(按类型筛选、查看详情、删除)。
- 阅读进度: Admin 学习数据→阅读进度页(按用户/资料查看进度)。
- 资料诊断: Admin 学习数据→资料诊断页。
- 导入监控: Admin 知识库→文档导入监控页(任务状态、步骤日志、重试)。
系统内部在哪里使用
- 继续学习推荐: API GET /learning/continue → iOS 学习主页优先级算法。
- 学习摘要: API GET /learning/summary → iOS 分析页 + 学习主页每周摘要。
- 阅读趋势: API GET /learning/trend → iOS 分析页阅读时长趋势图。
- 学习记录: API GET /learning/records → iOS 分析页最近学习记录。
- 资料范围 AI 问答: API 在 RAG 对话中根据资料范围检索相关 Chunk(依赖 Python RAG 的向量化结果;RAG 未完成时不可用)。
8. 修改入口
用户主动修改
- 标记已读: iOS 资料阅读页工具栏按钮。本地 MarkedReadStore 即时生效,marked_as_read 事件通过事件采集链路上报。服务端同步已完成。
- 取消标记已读: 本地 MarkedReadStore 即时生效。服务端同步方式暂未确认(当前事件模型无 unmarked_as_read 事件类型)。
- 添加笔记: iOS 快速笔记 Sheet(本地 UserDefaults 存储)。
- 删除来源文件: iOS 知识库详情页来源 Tab 滑动删除(API 软删除)。
- 开始/结束专注学习会话: iOS 资料阅读页工具栏学习按钮 / 学习会话页结束按钮。
- 上传新资料: iOS 导入资料页(文件/相册/链接)。
Admin 人工修改(运维可观测)
- 删除文件: Admin 文件管理页删除上传文件。
- 重试导入: Admin 文档导入监控页重试失败任务。
- 重新解析: Admin 可触发知识源的重新解析。
系统自动修改
- 阅读位置更新: iOS 2 秒防抖自动调用 Rust push_position_changed。
- 心跳: iOS 15 秒定时器 Tick → 调用 Rust heartbeat 接口 → Rust 计算 activeSecondsDelta 并创建事件。
- 阅读会话状态切换: iOS 前后台切换时自动暂停/恢复 Rust ReadingSession,同步控制心跳定时器。
- 阅读进度聚合: API 在每次事件处理事务中自动更新 MaterialReadingProgress。
- 会话中断检测: API 在事件处理前自动清理 30 分钟无新事件的活跃会话(标记为 interrupted)。
- 解析状态更新: Python RAG Worker 异步更新 KnowledgeSource 的 parseStatus(不阻塞阅读)。
- 文件过期处理: API 自动检测临时资料是否过期。
系统配置修改
- 文件大小限制: API 配置(待确认具体配置入口)。
- COS 预签名 URL 有效期: API 文件上传服务配置。
- 解析触发轮询间隔: iOS 端硬编码(60×2s),非配置化。
9. 异常与恢复
9.1 文件加载失败
- 场景: 本地文件不存在或 COS 下载失败。
- iOS 处理: 资料阅读页进入错误状态,显示错误提示和重试按钮。自动尝试从 COS 重新下载。
- API 处理: 文件下载 URL 返回 COS 预签名链接,含过期时间。过期后 iOS 需重新请求。
9.2 解析失败
- 场景: 文件内容损坏、格式不支持、或解析器报错。
- iOS 处理: 资料阅读页显示"暂不支持此格式"或错误提示。
- API 处理: Python RAG Worker 解析失败时更新 DocumentImport 状态为 FAILED,记录错误步骤。Admin 可查看步骤日志并重试。解析失败不影响用户阅读原文件。
9.3 网络中断
- 场景: 阅读过程中网络断开。
- iOS 处理: Rust Document Runtime 继续在本地采集事件,事件缓冲区正常运行。iOS 本地持久化队列中的事件暂停上传,NetworkMonitor 检测到网络恢复后自动触发队列 flush。阅读会话继续,心跳和位置事件照常写入 Rust 缓冲区。
9.4 App 被关闭(强制杀死或系统回收)
- 场景: 用户在阅读过程中 App 被系统杀死或用户强制关闭。
- iOS 处理(App 重新启动时):
- RuntimeRecoveryService.run() 执行崩溃恢复
- Rust reload_stale_events_v2:所有 Exported 但未 Ack 的事件重置为 Pending(处理 export 后、本地写入前崩溃的场景)
- Rust cleanup_stale_sessions_v2:清理超过 24 小时的非活跃会话
- iOS 本地队列 reloadOnLaunch:重新从 Rust 导出事件、映射为 UploadItem、写入持久化队列、立即 Ack。已持久化但未上传的队列条目继续上传。
- 服务器端:API 在下次处理事件前,清理超过 30 分钟的活跃会话(标记为 interrupted)
9.5 Export/Ack 链路中的三种崩溃场景
- export 后、本地写入前崩溃: Rust reload_stale_events_v2 将 Exported 事件重置为 Pending,下次启动重新导出。
- 本地写入成功、Ack 前崩溃: 重新启动后重新导出,API 端通过 eventId 去重(已在队列中的重复事件不会产生重复数据),iOS 再调用 Ack 完成交接。
- Ack 后、API 上传前崩溃: 事件已从 Rust 缓冲区安全交接至 iOS 本地持久化队列。重启后本地队列继续上传,Rust 不需要再参与。
9.6 阅读位置恢复失败
- 场景: API 未返回阅读进度(首次打开)或本地缓存丢失。
- iOS 处理: 三级回退——API 位置 → 本地缓存位置 → Rust 会话位置 → 从头开始。
9.7 事件上传失败
- 场景: 上传请求返回错误或超时。上传完全由 iOS 本地持久化队列负责,失败时不再调用 Rust markFailed。
- iOS 本地队列处理:
- 401 错误:暂停队列(保留 pending 状态),等待 Token 刷新
- 403/404 错误:标记条目为永久失败(dead)
- 其他错误:标记条目为 failed,进入指数退避重试(5s × 2^n,最多 3 次,最长 5 分钟)
- 重试次数耗尽:条目进入 dead 状态,不再重试
- API 处理:
- 重复事件(相同 userId + eventId):标记为 duplicate,返回已处理
- 验证失败(无效类型/超限/无权限):标记为 failed,返回错误码
- 材料不存在:标记为 failed
9.8 资料被删除或失去权限
- 场景: 用户正在阅读的资料被管理员删除或用户失去访问权限。
- API 处理: 事件处理时校验材料存在性、归属性和状态。不存在的材料标记事件为 failed;无权限的标记为 failed;已过期的临时材料标记为 failed。
- iOS 处理: 当前的阅读会话仍可继续(本地文件未被删除),但后续事件上传将被拒绝。
10. 当前完成情况
统一使用以下口径:
- 已完成: 跨项目链路已接通,用户可以获得最终结果。
- 部分完成: 只有部分项目、部分页面或部分场景完成。
- 占位实现: 存在结构,但使用 Mock、硬编码或静态数据。
- 暂未确认: 无法确认完整调用链。
| 组件 | 状态 | 说明 |
|---|---|---|
| iOS 多格式渲染(Markdown/文本/图片/EPUB) | 已完成 | Rust FFI 解析 + SwiftUI 渲染 |
| iOS 系统预览(PDF/Word/Excel/PPT) | 已完成 | QuickLook + ShareLink |
| iOS 阅读位置记录与恢复 | 已完成 | 三级优先级(API→本地→会话) |
| iOS 标记已读(服务端同步) | 已完成 | 本地 UserDefaults + marked_as_read 事件上报链路已接通 |
| iOS 取消标记已读(服务端同步) | 暂未确认 | 本地即时生效,但当前事件模型无 unmarked_as_read,API 是否支持待确认 |
| iOS 自动阅读会话管理 | 已完成 | Rust V2 ReadingSession(含前后台自动切换) |
| iOS 专注学习会话计时 | 暂未确认 | 本地计时已完成,API 持久化调用待确认 |
| iOS 快速笔记 | 已完成 | 锚点记录,本地 UserDefaults 存储 |
| iOS 15 秒心跳定时器 | 已完成 | iOS 创建 Timer,仅在 session active 且前台时触发 |
| Rust Document Runtime 文件解析 | 已完成 | 9 种 MaterialType 检测 + Markdown/文本/EPUB/图片/PDF 基础解析 |
| Rust Document Runtime V2 自动阅读会话 | 已完成 | 完整生命周期 + 5 种事件类型 + 缓冲区管理 |
| Rust 活跃时间跟踪器 | 已完成 | ActiveTimeTracker,由 iOS heartbeat tick 驱动 |
| Rust 崩溃恢复 | 已完成 | stale event reload + stale session cleanup |
| Rust → iOS 事件交接链路 | 已完成 | export → 本地队列写入 → Ack(交接在本地完成,不依赖网络) |
| iOS 本地上传队列 | 已完成 | JSON 持久化,独立于 Rust 缓冲区的网络上传队列 |
| iOS 上传重试与网络恢复 | 已完成 | 指数退避重试 + NetworkMonitor 自动恢复 |
| API 文件上传与下载 | 已完成 | COS 预签名 URL 流程 |
| API 阅读事件处理管线 | 已完成 | 去重→验证→事件入库→自动阅读会话聚合→进度聚合→每日活动→学习记录 |
| API 阅读进度查询 | 已完成 | GET /materials/:id/reading-progress |
| API 继续学习推荐 | 已完成 | 最近的未读完资料 |
| Admin 来源文件查看 | 已完成 | 列表+筛选+解析状态(运维可观测) |
| Admin 资料诊断 | 已完成 | material-diagnose 端点(运维可观测) |
| Admin 阅读进度查看 | 已完成 | 列表+详情(运维可观测) |
| 原文件阅读(不依赖 RAG) | 已完成 | 只要资料记录+权限+COS/本地文件可用即可阅读 |
| 资料范围 AI 问答(依赖 RAG) | 部分完成 | 需 RAG 完成向量化后才可用;RAG 未完成时语义检索不可用 |
| iOS 解析触发轮询 | 部分完成 | 使用硬编码 60×2s 轮询,非事件驱动 |
| 笔记服务端同步 | 未完成 | 当前仅本地 UserDefaults 存储 |
| Office 文件高清预览 | 暂未确认 | Word/Excel/PPT 依赖系统预览或外部打开,无内置高清渲染 |