# FairScan 功能细化规划(执行版) > 基于 `requirements/requirements.md` 的进一步落地拆解。 > 本文件用于实现阶段的任务规划、模块拆分、接口约定、风险控制与验收标准。 > > 📌 **实现状态**: > - P1(实时图传)✅ 已完成 > - P2(PDF 上传)+ P3(统一处理任务)✅ 已完成 > - 上传与处理已分离为独立接口,符合 pc-api-spec.md 规范 > - 导出页提供三个按钮:仅传输 / 传输+OCR PDF / 传输+Markdown > - PC 管理面板 ✅ 已完成(/dashboard) > - MinerU 真实接入 ✅ 已完成(markdown 处理 + ZIP 打包 + 任务管理面板) > - OCRmyPDF 🔥 **下一步** > - P0(局域网发现)📌 待排期 > > 重要说明: > > - 本文档**不是只写给当前对话中的执行者**。 > - 本文档应被视为一份**可交给任意工程执行者或 AI 编码代理**的实施说明。 > - 例如 Claude Code、其他 AI 编码工具、或人工开发者,都应能依据本文件理解目标、边界、优先级与接口契约。 > - 因此本文档尽量避免“只对当前上下文成立”的描述,改为更稳定的模块边界、任务拆分与接口说明。 ## 0. 核心结论 本项目后续新增能力,分成两条彼此解耦的主链路: 1. **实时图传链路** - 目标:低延迟、稳定预览 - 输入:手机相机预览帧 - 输出:PC 实时显示画面 - 特点:允许丢帧,不追求完整性 2. **文档处理链路** - 目标:正式文档处理与结果回到手机 - 输入:手机端本地生成的 PDF - 输出:Markdown 或 OCR 后的 PDF - 特点:追求正确性与完整性,可异步处理 同时明确以下原则: - **不在 App 内主动切换 Wi‑Fi** - **局域网相机功能的本质是实时图传,不是文档处理** - **文档处理只基于手机本地正式生成的 PDF,不基于图传流** - **PC 端后处理只需预留统一任务接口,MinerU 与 OCRmyPDF 复用同一套处理协议** - **处理结果回到手机,推荐通过“手机轮询状态 + 手机主动下载产物”实现** --- ## 1. 当前项目基础能力梳理 结合现有代码,当前 Android 端已经具备以下基础: - 文档扫描采集与页面处理 - 相机实时预览、分割、文档边缘检测:`app/src/main/java/org/fairscan/app/ui/screens/camera/CameraViewModel.kt:98` - 拍照后生成处理页:`app/src/main/java/org/fairscan/app/ui/screens/camera/CameraViewModel.kt:138` - 裁切/页面编辑/顺序调整入口:`app/src/main/java/org/fairscan/app/MainActivity.kt:190` - PDF/JPEG 导出 - 导出准备与结果管理:`app/src/main/java/org/fairscan/app/ui/screens/export/ExportViewModel.kt:66` - PDF 生成入口:`app/src/main/java/org/fairscan/app/ui/screens/export/ExportViewModel.kt:76` - PDF 写入封装:`app/src/main/java/org/fairscan/app/data/FileManager.kt:46` - 设置页基础结构 - 设置存储:`app/src/main/java/org/fairscan/app/ui/screens/settings/SettingsRepository.kt:31` - 设置界面:`app/src/main/java/org/fairscan/app/ui/screens/settings/SettingsScreen.kt:57` - 应用容器可扩展网络模块注入 - 容器入口:`app/src/main/java/org/fairscan/app/FairScanApp.kt:46` 这意味着后续新增能力不需要重做扫描器,而是围绕以下两条主链路扩展: 1. **实时图传链路**:手机相机预览 -> PC 实时显示 2. **文档处理链路**:手机扫描成 PDF -> PC 处理 -> 结果回到手机 --- 阅读的文档顺序 1. IMPLEMENTATION_COMPLETE.md - 了解已完成的工作 2. FIXES_SUMMARY.md - 理解技术决策和修复方案 3. NEXT_STEPS.md - 明确下一步的实现方向 你每次完成一个任务就需要更新这些文档 ## 2. 需求重述与产品化定义 ### 2.1 原有离线扫描功能 这部分以现有实现为基础,后续原则是: - 保持当前扫描主流程稳定 - 不让实时图传影响正式扫描质量 - 后续所有文档处理都复用现有扫描结果 建议统一定义“文档结果对象”: - 页面集合(已裁切、已旋转、已滤镜处理) - 本地导出的 PDF 文件 - 可选 JPEG 文件集合 - 文档元信息(文件名、页数、时间) ### 2.2 局域网相机功能 这里明确产品定位: - 该功能本质上是**局域网实时图传** - 主要目标是:**低延迟、稳定、可预览** - PC 端主要用途是: - 实时观察手机画面 - 辅助取景 - 远端监看 - **不把图传流直接作为 MinerU/OCRmyPDF 的输入** - **不从图传流直接生成正式 PDF** 也就是说: - 图传负责“看” - 扫描链路负责“生成正式文档” ### 2.3 离线扫描 PDF 发送到 PC 主机 主链路定义如下: - 手机端完成扫描与页面处理 - 手机端本地生成 PDF - 手机端把 PDF 发送到 PC 主机 - PC 主机接收 PDF 后再执行后处理任务 这条链路的好处是: - 延续现有扫描架构 - 手机端产物清晰、统一 - PC 端只负责重型处理任务 - 图传模块与文档处理模块不会互相拖累 ### 2.4 统一 PC 后处理能力 这里明确一个关键约束: - **PC 端后处理只需要预留统一接口,不需要为 MinerU 与 OCRmyPDF 设计两套完全不同的协议。** 建议抽象为一套统一任务模型: - 输入:手机上传的 PDF - 处理类型:`markdown` 或 `ocrpdf` - 输出: - `markdown` -> 返回 `.md` 及相关资源 - `ocrpdf` -> 返回处理后的 `.pdf` 也就是说: - **接口统一** - **任务状态统一** - **产物查询统一** - **差异只体现在处理类型和产物 MIME 类型** ### 2.5 结果回到手机 从产品描述上可以说: - PC 处理完毕后,把结果回传到手机 但从工程实现角度,推荐这样定义: - 手机端上传后拿到 `taskId` - 手机端轮询任务状态 - 任务完成后,手机端拉取产物列表 - 手机端主动下载产物到本地 这样做比“PC 主动推送到手机”更适合 Android: - 不需要手机长期监听端口 - 更适合前台/后台切换 - 更容易失败重试 - 也更适合未来交由不同执行者实现 --- ## 3. 建议的总体架构 建议按“手机端 + PC 端服务”的双端架构设计。 ### 3.1 两条主链路 #### A. 实时图传链路 ```text 手机相机预览帧 -> 抽帧/压缩 -> WebSocket 发送 -> PC 实时预览 ``` 特点: - 目标是低延迟 - 允许丢帧 - 不追求逐帧可靠到达 - 不参与正式 PDF 生成 #### B. 文档处理链路 ```text 手机扫描采集 -> 手机本地页面处理 -> 手机本地生成 PDF -> HTTP 上传到 PC -> 创建统一处理任务 -> PC 执行对应处理器 -> 手机查询任务状态 -> 手机下载处理结果 ``` 特点: - 目标是正确性与完整性 - 使用正式文档产物作为输入 - 允许异步处理 - 与图传链路解耦 ### 3.2 手机端职责(当前仓库) - 继续负责扫描采集、裁切、页面编辑、导出 PDF - 提供局域网发现与主机配置能力 - 提供实时图传能力 - 上传本地 PDF 到 PC - 发起统一处理任务 - 轮询任务状态并下载结果 - 在 UI 中展示连接状态、任务状态、结果入口 ### 3.3 PC 端职责(建议新建单独项目) - 广播局域网服务信息 - 接收图传帧并实时显示 - 接收手机上传的 PDF 文件 - 暴露统一处理任务接口 - 根据处理类型调用 MinerU 或 OCRmyPDF - 提供任务查询与产物下载接口 ### 3.4 推荐协议选型 为了降低复杂度,建议如下: - 局域网发现:**mDNS / NSD** - 实时图传:**WebSocket 二进制帧** - 文件上传:**HTTP multipart/form-data** - 任务创建:**HTTP POST** - 任务状态查询:**HTTP 轮询** - 结果下载:**HTTP GET** 第一版不建议直接做: - App 内切换 Wi‑Fi - 自定义复杂 UDP 视频协议 - PC 主动推送文件到手机 - 多主机自动同步 ### 3.5 关于实时性的技术取舍 由于实时性优先,建议这样分层: #### V1 方案 - 低频抽帧 - JPEG 压缩 - WebSocket 发送 - 明确丢帧策略 优点: - 实现快 - 调试简单 - 适合先验证局域网低延迟图传是否满足需求 #### V2 升级预案(仅在 V1 不满足时再评估) - 使用 MediaCodec 编码 H.264 - 再评估 WebRTC / RTP / 更底层的视频链路 不建议首版直接上 V2,因为会显著提高开发复杂度。 --- ## 4. 模块拆分规划 ## 4.1 模块 A:局域网连接与主机发现 ### 目标 让手机端可以: - 自动发现同一局域网中的 FairScan PC 服务 - 手动填写主机地址作为兜底 - 保存当前选中的 PC 主机配置 ### 手机端新增能力 - 新增网络设置项 - 主机 IP / 域名 - 端口 - 协议(第一期建议仅 http) - 显示当前手机 IP - 显示当前发现到的主机列表 - 支持一键选择已发现主机 - 测试连接按钮 - 可选:打开系统 Wi‑Fi 设置入口 ### 局域网发现机制 推荐采用: - **mDNS/NSD 自动发现 + 手动输入兜底** #### 推荐服务标识 - mDNS service type:`_fairscan._tcp` - service instance name:`FairScan-PC-{deviceName}` #### 推荐 TXT Record 字段 - `name`:设备显示名 - `features`:`upload,stream,process,download` - `version`:PC 服务版本 - `apiVersion`:接口版本 说明: - 这里建议把后处理能力统一成 `process`,而不是在发现层暴露过多具体工具细节。 - 是否支持 `markdown` / `ocrpdf`,可以通过健康检查响应或任务能力字段进一步细分。 #### Android 端发现状态模型 ```text DiscoveryState - Idle - Discovering - Success(list) - Empty - Error(message) ``` #### 发现结果建议结构 ```text DiscoveredHost - serviceName - displayName - host - port - features[] - version - lastSeenAt - isReachable ``` ### 建议新增数据项 在 `SettingsRepository` 中新增: - `serverHost` - `serverPort` - `serverDisplayName` - `lastSelectedServiceId` - `streamQuality` - `postProcessMode` - `autoDownloadProcessedResult` ### 建议涉及文件 - `app/src/main/java/org/fairscan/app/ui/screens/settings/SettingsRepository.kt` - `app/src/main/java/org/fairscan/app/ui/screens/settings/SettingsScreen.kt` - `app/src/main/java/org/fairscan/app/ui/screens/settings/SettingsViewModel.kt` - 建议新增: - `app/src/main/java/org/fairscan/app/network/NetworkInfoProvider.kt` - `app/src/main/java/org/fairscan/app/network/ServerEndpoint.kt` - `app/src/main/java/org/fairscan/app/network/discovery/LanServiceDiscovery.kt` - `app/src/main/java/org/fairscan/app/network/discovery/NsdLanServiceDiscovery.kt` - `app/src/main/java/org/fairscan/app/network/discovery/DiscoveryState.kt` - `app/src/main/java/org/fairscan/app/network/discovery/DiscoveredHost.kt` ### 验收标准 - 用户可以扫描到局域网中的 FairScan PC 服务 - 用户可以从发现列表中选择目标主机 - 自动发现失败时仍可手动输入地址 - 选择主机后可通过 `GET /health` 校验连通性 --- ## 4.2 模块 B:实时网络图传 ### 目标 把手机作为一个**低延迟局域网实时图传设备**,让 PC 端可以实时看到画面。 ### 功能定位 这一模块的核心不是文档处理,而是: - 实时预览 - 低延迟 - 稳定连接 - 图传状态清晰 ### 推荐实现路线 第一版建议: - 从 CameraX 预览/分析流中取图 - 按固定频率抽帧 - 压缩为 JPEG - 通过 WebSocket 发送二进制帧到 PC - PC 端显示实时画面 当前可接入入口: - `app/src/main/java/org/fairscan/app/ui/screens/camera/CameraViewModel.kt:98` ### 关键原则 - **必须允许丢帧**,不能把帧积压在队列里 - **必须以实时性优先**,而不是完整性优先 - **图传失败不能影响正式扫描** - **图传流不参与正式 PDF 生成** ### 压缩档位建议 | 档位 | 最长边 | JPEG质量 | 目标FPS | 用途 | |---|---:|---:|---:|---| | Low | 640 | 45 | 8~12 | 低延迟预览 | | Balanced | 960 | 60 | 6~10 | 默认 | | High | 1280 | 75 | 5~8 | 高清预览 | ### 手机端子任务 1. 设计图传状态模型 2. 新增帧压缩器 3. 新增图传客户端 4. 增加丢帧策略 5. 在相机界面增加图传开关和状态提示 6. 增加断线重连策略 7. 增加简单性能指标 ### 建议涉及文件 已有接入点: - `app/src/main/java/org/fairscan/app/ui/screens/camera/CameraViewModel.kt:98` - `app/src/main/java/org/fairscan/app/ui/screens/camera/CameraScreen.kt` - `app/src/main/java/org/fairscan/app/ui/screens/camera/CameraPreview.kt` 建议新增: - `app/src/main/java/org/fairscan/app/network/stream/StreamClient.kt` - `app/src/main/java/org/fairscan/app/network/stream/FrameCompressor.kt` - `app/src/main/java/org/fairscan/app/network/stream/StreamState.kt` - `app/src/main/java/org/fairscan/app/network/stream/StreamQualityPreset.kt` - `app/src/main/java/org/fairscan/app/network/stream/FrameDropController.kt` ### 验收标准 - 手机连接 PC 后可持续发送实时预览帧 - 在局域网环境下主观延迟明显低于文件传输式刷新 - 不同压缩档位的延迟和清晰度有可见差异 - 图传开启时,正式扫描/导出功能仍可正常使用 --- ## 4.3 模块 C:手机本地生成 PDF 并上传到 PC ### 目标 保持现有手机扫描方案不变,在手机端完成正式文档生成后,再把 PDF 上传给 PC。 ### 推荐实现路线 - 手机端按现有流程完成扫描 - 使用现有导出逻辑生成 PDF - 调用上传接口把 PDF 发给 PC 当前稳定入口: - `app/src/main/java/org/fairscan/app/ui/screens/export/ExportViewModel.kt:76` - `app/src/main/java/org/fairscan/app/ui/screens/export/ExportViewModel.kt:84` - `app/src/main/java/org/fairscan/app/data/FileManager.kt:46` ### 设计原则 - 正式文档输入只认手机本地生成的 PDF - 不使用实时图传流直接做正式文档处理 - 上传是文档处理链路的前半段,不与实时图传混合 ### 手机端子任务 1. 定义上传请求/响应模型 2. 导出页增加“发送到 PC”按钮 3. 支持发送后立即创建后处理任务,或只上传不处理 4. 展示上传进度与结果 5. 记录返回的文件标识或任务标识 ### 建议涉及文件 - `app/src/main/java/org/fairscan/app/ui/screens/export/ExportViewModel.kt:66` - `app/src/main/java/org/fairscan/app/MainActivity.kt:203` - 建议新增: - `app/src/main/java/org/fairscan/app/network/upload/PdfUploadClient.kt` - `app/src/main/java/org/fairscan/app/network/upload/UploadModels.kt` - `app/src/main/java/org/fairscan/app/ui/screens/export/NetworkExportState.kt` ### 验收标准 - 用户可以把当前扫描结果导出成 PDF 并发送给 PC - 上传进度可见 - 成功后能拿到后续处理所需的文件标识或任务标识 --- ## 4.4 模块 D:统一 PC 后处理接口 ### 目标 为 PC 端预留**一套统一的后处理任务接口**,由处理类型决定具体使用 MinerU 还是 OCRmyPDF。 ### 设计原则 - 统一任务创建接口 - 统一任务状态接口 - 统一产物列表接口 - 统一产物下载接口 - 差异只体现在: - `processType` - 输出文件类型 ### 推荐任务类型 ```text processType - markdown - ocrpdf ``` ### 输入输出约定 #### 输入 - 一个已上传或本次提交的 PDF - 一个 `processType` #### 输出 - 当 `processType=markdown` - 主要产物:`.md` - 可选附加产物:图片资源目录、JSON、日志 - 当 `processType=ocrpdf` - 主要产物:处理后的 `.pdf` - 可选附加产物:日志、识别报告 ### 推荐接口契约 #### 1. 创建任务 - `POST /tasks/process` 请求体建议: ```json { "fileId": "uploaded-file-id", "processType": "markdown", "options": {} } ``` #### 2. 查询任务状态 - `GET /tasks/{taskId}` 返回体建议: ```json { "taskId": "task-123", "status": "running", "processType": "markdown", "progress": 50, "message": "processing", "artifacts": [] } ``` #### 3. 查询产物列表 - `GET /tasks/{taskId}/artifacts` 返回体建议: ```json [ { "artifactId": "artifact-1", "fileName": "result.md", "mimeType": "text/markdown", "role": "primary" } ] ``` #### 4. 下载产物 - `GET /artifacts/{artifactId}/download` ### 手机端实现含义 手机端不需要关心 PC 内部到底调用了 MinerU 还是 OCRmyPDF,只需要关心: - 要处理哪个文件 - 处理类型是什么 - 当前任务状态是什么 - 可以下载哪些产物 ### 这样设计的好处 - PC 端工具实现可替换 - 后续新增其他处理器时接口不必大改 - 文档可交给不同 AI 或开发者实现而不易跑偏 - Android 端状态管理更统一 ### 建议新增文件 - `app/src/main/java/org/fairscan/app/network/tasks/TaskClient.kt` - `app/src/main/java/org/fairscan/app/network/tasks/TaskModels.kt` - `app/src/main/java/org/fairscan/app/network/tasks/ArtifactDownloadClient.kt` - `app/src/main/java/org/fairscan/app/ui/screens/export/ProcessingTaskState.kt` ### 验收标准 - 手机端可通过统一接口发起 `markdown` 任务 - 手机端可通过统一接口发起 `ocrpdf` 任务 - 任务状态查询逻辑对两类任务保持一致 - 产物下载逻辑对两类任务保持一致 --- ## 4.5 模块 E:处理结果回到手机端 ### 目标 让 PC 端的处理结果真正回到手机端,而不是停留在 PC 上。 ### 推荐实现方式 产品上: - 结果回传手机 技术上: - 手机端主动下载 ### 为什么不建议 PC 主动推送到手机 - Android 端长期开放监听端口不稳定 - 前后台切换复杂 - 手机网络权限与系统限制更多 - 局域网内由手机主动拉取更简单可靠 ### 手机端下载结果后的处理建议 - 下载到应用缓存目录 - 再提供: - 打开 - 分享 - 保存到用户指定目录 - 作为新文档导入应用 ### 验收标准 - 任务完成后手机端可获取产物列表 - 手机端可把产物下载回本地 - 下载失败时可重试 - 用户可明确区分“任务失败”和“下载失败” --- ## 5. 推荐开发阶段划分 ## 第一阶段:局域网发现与连接校验 目标:先把“发现谁、连接谁”做稳定。 ### 范围 - 设置页新增 PC 主机配置 - mDNS/NSD 发现机制 - 手动输入兜底 - `GET /health` 健康检查 ### 阶段验收 - 手机端可自动发现同局域网中的 FairScan PC 服务 - 发现失败时可手动输入地址 - 健康检查可明确提示成功/失败 --- ## 第二阶段:实时图传闭环 目标:优先完成低延迟实时图传。 ### 范围 - 图传客户端 - 相机页图传开关 - 图传压缩档位 - PC 端实时预览 - 复用第一阶段的主机发现结果 ### 阶段验收 - 手机端可以把实时预览帧发送到 PC - PC 端能低延迟显示画面 - 图传质量档位切换有效 - 图传失败不影响正常扫描 --- ## 第三阶段:手机本地 PDF 上传到 PC 目标:打通文档处理链路的前半段。 ### 范围 - 手机本地导出 PDF - 上传到 PC - 上传状态展示 ### 阶段验收 - 当前扫描文档可生成 PDF 并上传至 PC - 上传结果可见 - 可关联后续处理任务 --- ## 第四阶段:统一处理任务与结果下载 目标:打通文档处理链路的后半段。 ### 范围 - 统一处理任务接口 - `markdown` 处理类型 - `ocrpdf` 处理类型 - 手机查询任务状态 - 手机下载结果文件 ### 阶段验收 - 手机可用统一接口发起两种处理类型 - PC 可根据类型调用不同处理器 - 结果文件可从 PC 下载回手机 --- ## 第五阶段:体验优化 ### 范围 - 最近连接记录 - 自动下载开关 - 失败重试 - 后台通知 - 发现结果去重与缓存 - 可选:UDP/网段探测作为发现兜底 --- ## 5.1 局域网发现专项方案 ### 目标 在不切换 Wi‑Fi 的前提下,让手机端自动发现同一局域网中运行中的 FairScan PC 服务。 ### 主方案 - PC 端广播 `_fairscan._tcp` - Android 端 NSD 搜索 `_fairscan._tcp` - 用户确认选择目标主机 - 手机端调用 `GET /health` 校验 ### 发现流程 1. PC 启动 HTTP 服务 2. PC 注册 mDNS 服务 3. 手机点击“扫描局域网主机” 4. Android NSD 开始发现 5. 解析 host、port、features 6. 展示设备列表 7. 用户选择设备 8. 手机执行 `GET /health` 9. 校验成功后保存为当前目标主机 ### 备用方案触发条件 只有在以下情况长期存在时,才考虑第二通道: - 大量设备无法发现 mDNS - 企业网络场景较多 - Windows 防火墙导致广播不稳定 此时再评估: - 自定义 UDP 广播 - 指定网段 HTTP 探测 默认不建议首版同时做两套发现机制。 --- ## 6. Android 端建议修改点清单 ## 6.1 权限与清单 当前 Manifest 尚未为新网络能力补齐联网权限: - `app/src/main/AndroidManifest.xml:1` 后续大概率需要新增: - `android.permission.INTERNET` - `android.permission.ACCESS_WIFI_STATE` - 第一版不建议使用 `android.permission.CHANGE_WIFI_STATE` 如果使用明文 HTTP 局域网服务,还要评估: - network security config - 或仅在开发阶段允许局域网明文流量 ## 6.2 设置页 当前设置页很适合作为局域网协作入口: - `app/src/main/java/org/fairscan/app/ui/screens/settings/SettingsScreen.kt:57` - `app/src/main/java/org/fairscan/app/ui/screens/settings/SettingsRepository.kt:31` 建议新增“局域网协作”分组: - PC 主机地址 - 端口 - 当前手机 IP - 扫描局域网主机按钮 - 已发现主机列表 - 图传质量预设 - 默认处理类型 - 自动下载结果开关 - 测试连接按钮 - 打开系统 Wi‑Fi 设置快捷入口(可选) ## 6.3 相机页 当前相机实时分析入口适合接入图传: - `app/src/main/java/org/fairscan/app/ui/screens/camera/CameraViewModel.kt:98` 建议新增: - 图传开关 - 图传状态文本 - 当前目标主机提示 - 压缩档位快速切换 - 近似发送帧率或发送状态提示 ## 6.4 导出页 当前导出能力已较完整: - `app/src/main/java/org/fairscan/app/ui/screens/export/ExportViewModel.kt:66` 建议新增按钮: - 发送到 PC - 发送并处理(Markdown) - 发送并处理(OCR PDF) - 下载处理结果(当任务完成后显示) ## 6.5 任务与结果页面 建议后续增加一个轻量任务状态区域,至少包含: - 上传中 - 等待处理 - 处理中 - 处理成功 - 处理失败 - 下载中 - 下载成功 - 下载失败 ## 6.6 AppContainer 注入 当前容器适合新增网络模块注入: - `app/src/main/java/org/fairscan/app/FairScanApp.kt:46` 建议后续加入: - `lanServiceDiscovery` - `streamClient` - `uploadClient` - `taskClient` - `artifactDownloadClient` - `networkInfoProvider` --- ## 7. PC 端服务建议最小方案 ### 7.1 目标边界 PC 端在本阶段只需要提供: - 图传接收能力 - 文件接收能力 - **统一处理任务接口骨架** - 任务状态返回能力 - 产物下载能力 这里特别强调: - 对当前阶段来说,PC 端**只需预留好统一接口** - MinerU 与 OCRmyPDF 的内部执行器可以后续再逐步接入 - 但接口契约应先稳定下来,避免 Android 端后面反复改协议 ### 7.2 推荐技术路线 如果 MinerU 与 OCRmyPDF 最终都运行在 Python 环境,最自然的是: - PC 端统一使用 **Python FastAPI** ### 7.3 PC 端最小能力 - HTTP 服务 - WebSocket 图传接收 - mDNS 服务广播 - 上传文件保存 - 统一任务接口 - 任务状态查询 - 结果文件下载 ### 7.4 最小接口建议 - `GET /health` - 健康检查 - `WS /stream` - 实时图传接收 - `POST /upload/pdf` - 上传 PDF - `POST /tasks/process` - 发起统一处理任务 - `GET /tasks/{id}` - 查询任务状态 - `GET /tasks/{id}/artifacts` - 查询产物列表 - `GET /artifacts/{artifactId}/download` - 下载产物 ### 7.5 统一处理任务接口示例 #### 创建任务请求示例 ```json { "fileId": "uploaded-file-id", "processType": "ocrpdf", "options": {} } ``` #### 状态返回示例 ```json { "taskId": "task-123", "status": "completed", "processType": "ocrpdf", "progress": 100, "message": "done" } ``` #### 产物列表示例 ```json [ { "artifactId": "artifact-1", "fileName": "result.pdf", "mimeType": "application/pdf", "role": "primary" } ] ``` ### 7.6 目录规划建议 - `incoming/` 原始上传文件 - `tasks/` 任务工作目录 - `outputs/pdf/` OCR 输出 - `outputs/markdown/` Markdown 输出 - `logs/` 服务日志与任务日志 ### 7.7 广播能力建议 mDNS TXT Record 中建议广播: - `stream=1` - `upload=1` - `process=1` - `download=1` - `apiVersion=1` 如需更细粒度,也可通过 `GET /health` 返回: ```json { "name": "FairScan-PC-Office", "status": "ok", "features": ["stream", "upload", "process", "download"], "processTypes": ["markdown", "ocrpdf"] } ``` --- ## 8. 风险与难点 ## 8.1 实时图传的延迟与稳定性 风险: - CameraX 分析 + 压缩 + 网络发送会叠加延迟 - 发送队列积压会使画面越来越慢 应对: - 固定抽帧 - 只保留最新帧 - 上一帧未发完则直接丢弃当前帧 - 后台线程压缩与发送 - 优先保证低延迟而不是高保真 ## 8.2 Android 网络与 Wi‑Fi 限制 风险: - 获取 Wi‑Fi 名称和网络信息在新系统上限制较多 - App 内切换 Wi‑Fi 成本高且兼容性差 应对: - 不在 App 内切换 Wi‑Fi - 只显示网络信息 - 必要时提供“打开系统 Wi‑Fi 设置”入口 ## 8.3 局域网发现兼容性 风险: - 某些路由器可能屏蔽 mDNS 多播 - Windows 防火墙可能阻止服务广播 - 无线与有线终端可能被隔离 应对: - 使用 `NSD + 手动输入 + /health 校验` 三段式兜底 - 设置页提供简单排障提示 - 后续再评估 UDP/网段探测 ## 8.4 PC 后处理环境依赖 风险: - MinerU 与 OCRmyPDF 依赖较重 - Python 环境与外部工具安装复杂 - 内部执行器后续可能替换实现 应对: - Android 端只依赖统一处理接口,不依赖具体工具细节 - PC 端先稳定接口契约,再逐步补充内部执行器 - 通过 `processTypes` 或等价字段声明当前支持的处理类型 ## 8.5 结果回到手机的可靠性 风险: - 任务成功但下载失败 - 用户切到后台后任务状态丢失 - 下载到本地后存储路径不清晰 应对: - 区分“任务失败”和“下载失败” - 记录最近任务与产物信息 - 下载后提供打开、分享、另存为入口 --- ## 9. 建议的任务清单(可直接进入开发) ## P0:局域网发现与基础连接 ### Task P0-1:扩展设置仓库与设置页 - 增加 PC 主机地址和端口配置 - 增加发现结果展示区域 - 增加“扫描局域网主机”按钮 - 增加图传质量配置 - 增加默认处理类型配置 - 增加自动下载结果开关 - 显示手机当前 IP ### Task P0-2:实现局域网发现基础能力 - 接入 Android NSD - 定义 `DiscoveredHost` 与 `DiscoveryState` - 扫描 `_fairscan._tcp` - 发现后支持一键填充 host/port - 失败时保留手动输入兜底 ### Task P0-3:补充网络基础设施 - 增加网络权限 - 增加基础 HTTP 客户端 - 增加统一错误模型 - 增加 `GET /health` 校验 ### Task P0-4:实现 PC 最小服务骨架 - `GET /health` - mDNS 注册 `_fairscan._tcp` - 广播服务能力信息 ## P1:实时图传 ### Task P1-1:实现图传状态模型 - 未连接 - 连接中 - 已连接 - 发送中 - 出错 ### Task P1-2:实现帧压缩与抽帧策略 - 低/中/高三档 - 限帧率 - 丢帧策略 - 只保留最新帧 ### Task P1-3:相机页接入图传控制 - 开关 - 状态提示 - 主机信息展示 - 复用已发现或已保存的目标主机 ### Task P1-4:实现 PC 端实时预览 - `WS /stream` - 页面或桌面窗口显示 ### Task P1-5:图传与发现联动 - 直接选择发现到的主机作为图传目标 - 根据 `features` 判断主机是否支持 `stream` ## P2:手机本地 PDF 上传 ### Task P2-1:实现 PDF 上传客户端 - 导出页新增“发送到 PC”按钮 - 生成 PDF 后发给 PC 服务 - 展示上传进度和结果 ### Task P2-2:联调上传闭环 - 手机扫描文档 - 手机本地生成 PDF - 上传到 PC 成功 - 手动输入和自动发现两条路径都可工作 ## P3:统一处理任务与结果下载 ### Task P3-1:实现统一任务接口客户端 - 支持 `processType=markdown` - 支持 `processType=ocrpdf` - 统一查询任务状态 - 统一查询产物列表 ### Task P3-2:实现统一结果下载流程 - 下载 `.md` 结果 - 下载 `.pdf` 结果 - 基于 `mimeType` 决定本地处理方式 ### Task P3-3:统一任务状态 UI - 等待中 - 处理中 - 成功 - 失败 - 下载中 - 下载成功 - 下载失败 ### Task P3-4:PC 端统一接口占位实现 - `POST /tasks/process` - `GET /tasks/{id}` - `GET /tasks/{id}/artifacts` - `GET /artifacts/{artifactId}/download` - 先允许返回 mock 或占位结果,再逐步接入真实处理器 ## P4:体验优化 ### Task P4-1:发现结果去重与缓存 - 按 service name 或 host:port 去重 - 记住最近选择设备 - 显示最近发现时间 ### Task P4-2:后台任务与通知 - 上传后台继续 - 处理结果通知 - 下载结果通知 ### Task P4-3:高级兜底方案评估 - UDP 广播发现 - 网段 HTTP 探测 - 仅在主方案不稳定时启用 --- ## 10. 建议的验收用例 ### 用例 1:局域网发现 - PC 服务启动并广播 `_fairscan._tcp` - 手机点击“扫描局域网主机” - 手机看到可用设备列表 - 用户选择设备后自动填充 host/port - `GET /health` 验证通过 ### 用例 2:实时图传 - 手机开启图传 - PC 端能实时看到画面 - 切换压缩档位后画面质量与延迟明显变化 - 图传关闭后不影响正常扫描 ### 用例 3:手机本地 PDF 上传 - 手机按原方案扫描 3 页文档 - 手机本地生成 PDF - 上传到 PC 成功 - PC 成功保存原始 PDF ### 用例 4:统一处理接口返回 Markdown 结果 - 手机使用 `processType=markdown` 发起任务 - PC 返回任务状态 - PC 提供 `.md` 结果下载 - 手机成功下载 Markdown 结果 ### 用例 5:统一处理接口返回 OCR PDF 结果 - 手机使用 `processType=ocrpdf` 发起任务 - PC 返回任务状态 - PC 提供 `.pdf` 结果下载 - 手机成功下载 OCR 后 PDF ### 用例 6:异常处理 - 主机地址错误 - PC 服务未启动 - mDNS 发现失败 - 上传中断网 - 当前 `processType` 不受支持 - 下载结果失败 - UI 都能给出明确失败信息 --- ## 11. 建议的实现优先级 推荐开发顺序: 1. 主机发现 + 健康检查 2. 实时图传 3. 手机本地 PDF 上传到 PC 4. 统一处理任务接口与结果下载 5. 发现机制与任务体验优化 理由: - 你当前最关心的是实时图传,所以图传优先 - 图传与文档处理解耦,优先完成图传不会阻碍后续 PDF 链路 - PC 端后处理细节未来可替换,因此应先稳定统一接口契约 - 文档处理链路应尽量让 Android 端只依赖抽象接口,不依赖具体工具内部实现 一句话概括: - **先把“实时看画面”做好,再把“正式处理文档并回到手机”做好;PC 内部处理器可以后补,但统一接口要先定下来。** --- ## 12. 对执行者的说明 本节是写给任何执行这份文档的人或 AI 的。 ### 12.1 不要误解的点 - “局域网相机”不是文档处理功能,而是实时图传功能。 - 文档处理输入来自手机本地正式生成的 PDF,而不是图传流。 - PC 端现阶段最重要的是统一接口契约,不是先把 MinerU/OCRmyPDF 完整接好。 - Android 端应依赖统一处理接口,不要直接写死两套后处理协议。 ### 12.2 可以接受的实现策略 - PC 端统一处理接口可以先返回 mock 结果 - 只要契约稳定,内部执行器可以后续逐步替换成真实 MinerU / OCRmyPDF - Android 端先把: - 上传 - 创建任务 - 查询状态 - 下载产物 的全链路走通即可 ### 12.3 不建议的实现策略 - 不要把图传流直接接到 MinerU / OCRmyPDF - 不要在 App 内做 Wi‑Fi 切换 - 不要同时为 `markdown` 与 `ocrpdf` 设计两套完全独立的客户端协议 - 不要让 Android 端过早耦合 PC 内部处理器实现细节 ### 12.4 如果由 AI 编码代理执行 执行顺序建议严格按以下优先级推进: 1. 发现与连接 2. 实时图传 3. PDF 上传 4. 统一处理接口 5. 结果下载 6. 再考虑真实接入 MinerU/OCRmyPDF 如果 AI 只能做一部分任务,优先确保: - 接口稳定 - 状态模型稳定 - UI 路径清晰 - 占位实现可联调 --- ## 13. 与原始需求的映射关系 - “文件原有的离线扫描功能” - 对应当前现有扫描/导出链路 - “手机网络图传功能” - 对应模块 B,且定位为低延迟实时图传 - “局域网内压缩广播的实时网络摄像头” - 对应模块 B,第一版建议做点对点实时图传 - “压缩力度可选” - 对应图传质量预设 - “离线扫描 PDF 通过 Wi‑Fi 协议发送给 PC” - 对应模块 C - “显示自己的 IP 和端口” - 对应模块 A - “MinerU 转成 Markdown” - 对应统一处理接口中的 `processType=markdown` - “OCRmyPDF 转成双层 PDF” - 对应统一处理接口中的 `processType=ocrpdf` - “PC 处理完毕后再传回手机” - 对应模块 E,推荐技术实现为手机主动下载结果