多文件分析流程
OneDocs 的多文件分析流程允许用户同时管理多个 PDF,并在各文件完成分析后,将分析结果按文件顺序合并为一份 Markdown 文档。
Purpose and Scope
本文档聚焦于分析页面中的多文件工作流:文件选择与排序、单文件分析结果的状态展示、合并前置条件、合并结果的组织方式,以及复制和导出边界。页面入口由 App 渲染的 Analysis 组件承载。
本文不展开具体分析模型、API 请求协议、持久化归档或设置页面的实现;这些属于分析执行、归档和设置等相邻能力。当前源码证据能够确认多文件能力的用户规则和分析页面的状态入口,但未能在限定的源码范围内定位上传组件、合并动作以及分析 Hook 的具体实现细节,因此相关部分只记录已验证行为,不推断未读实现。
Overview
多文件流程的核心是把一组 PDF 文件作为有序集合管理:
- 文件格式限定为 PDF,单文件不超过 30 MB;数量没有硬性上限,但文档建议控制在 10 个以内。
- 文件列表顺序不是纯 UI 顺序,它同时决定分析执行顺序和合并结果顺序。
- 每个文件可以处于已分析或未分析状态;未分析文件不会进入合并结果。
- 只有至少两个已分析文件时,
一键合并才具备执行条件。 - 合并结果按文件顺序拼接,每个文件的完整分析内容以独立 Markdown 标题区块分隔。
- 合并结果仍可复制 Markdown 源码或导出为单个
.md文件,导出文件名包含“合并”标识。
分析页面本身通过全局 useAppStore 读取 files、currentFile、analysisResult 和 multiFileAnalysisResults。这使页面能够同时判断“是否已有输入”和“是否已有单文件或多文件结果”,并据此切换上传界面与结果界面。
Architecture
Sources:
App 将 currentPage === "analysis" 映射到 Analysis,并根据窗口宽度传入 isMobile。Analysis 再组合功能选择器、文件上传、进度条和结果展示组件。多文件状态并不在页面组件内重新建模,而是从 useAppStore 中读取,因此页面只负责状态派生和视图分支。
页面入口与状态分支
Analysis 的输入属性只有可选的 isMobile。组件从 Store 读取以下与多文件流程直接相关的状态:
| 状态 | 用途 |
|---|---|
files | 判断当前是否存在上传文件,并向上传/文件列表区域提供状态来源 |
currentFile | 兼容当前文件模型;与 files 一起构成输入存在性判断 |
isAnalyzing | 阻止分析按钮在分析进行中再次执行 |
analysisResult | 表示单个分析结果是否存在 |
multiFileAnalysisResults | 表示是否存在多文件分析结果 |
getCurrentSettings() | 获取当前功能设置,页面使用其中的 apiKey 判断是否允许开始分析 |
showFormatNotice | 控制格式提示的显示 |
resetAll() | 清除结果并重新开始 |
页面首先计算输入和执行条件:
1const hasFiles = files.length > 0 || currentFile !== null;
2const canAnalyze = hasFiles && settings.apiKey && !isAnalyzing;
3
4const hasAnalysisResults =
5 analysisResult !== null || Object.keys(multiFileAnalysisResults).length > 0;Source: Analysis.tsx
这里的设计意图是将“输入存在”“API Key 已配置”“当前未执行”三个条件集中为 canAnalyze,避免上传区自行重复判断。结果判断同时覆盖单文件和多文件,因而多文件结果可以复用同一结果展示分支,而无需另建页面入口。
当已有结果时,页面保留 FunctionSelector,并在右侧渲染 ResultDisplay;同时显示一个新建分析按钮,点击后调用 resetAll。没有结果时,页面显示空结果卡片、可关闭的格式提示、FileUpload 和 ProgressBar。
与应用导航的关系
App 的页面类型包含 analysis、archive、about、discover 和 settings,初始页面为 analysis。桌面端使用 TitleBar,移动端使用 MobileNav。当移动端切换到分析页时,子标签同步到 Store 中的 selectedFunction;因此多文件分析仍属于分析页的当前功能上下文,而不是独立顶层页面。
{currentPage === "analysis" && <Analysis isMobile={isMobile} />}Source: App.tsx
核心流程
Sources:
上图中上传组件到 Store 的具体写入函数、analyzeDocument 的请求细节以及 ResultDisplay 的导出实现,当前读取范围未包含;这些属于实现待补充区域。已确认的页面控制流是:主按钮通过 handleMainButtonClick 在“已有结果”和“尚无结果”之间分支,前者重置流程,后者调用 analyzeDocument。
多文件管理规则
上传与排序
用户可以通过文件选择对话框按住 Windows 的 Ctrl 或 macOS 的 Cmd 多选文件,也可以将多个 PDF 拖入上传区域。当前文档规则只允许 PDF,单文件大小上限为 30 MB;数量没有硬性限制,但建议不超过 10 个,以降低分析质量下降和结果过长的风险。
上传后可通过拖拽文件标签调整顺序。这个顺序会影响两个实际结果:文件的分析执行顺序,以及最终合并文档中各部分的排列顺序。因此,课程课件、新闻报道或季度报告等有明显逻辑顺序的资料,应在分析前按内容顺序排列。
文件状态与删除
文件标签区分已分析和未分析状态。已分析文件显示文件名,未分析文件显示 文件名 (未分析)。关闭文件标签即可移除文件。根据当前规则,未分析文件不会被纳入合并结果;如果需要只合并部分文件,应先移除不需要的文件,再执行合并。
合并前置条件
合并至少需要两个已分析文件。单个已分析文件、尚未完成分析的文件,或者只剩一个已分析文件时,都不满足“一键合并”的业务条件。合并动作会包含当前列表中的全部已分析文件,而不是提供一个独立的逐项选择器。
合并结果的数据组织
合并文档使用 Markdown 标题和水平分隔线保持文件边界,逻辑结构如下:
1# 文件1的分析结果
2(文件1的完整分析内容)
3
4---
5
6# 文件2的分析结果
7(文件2的完整分析内容)Source: Multi-File.md
这种组织方式保留了每个源文件的完整分析内容,同时用标题和 --- 提供清晰边界。由于合并结果包含所有入选文件的完整内容,文件数量越多,输出就越长;文档建议按需合并,必要时再将 Markdown 复制到编辑器中精简。
使用示例
分析页的主按钮分支
以下是页面如何将“重新开始”和“开始分析”统一到一个按钮处理器中的实际代码:
1const handleMainButtonClick = () => {
2 if (hasAnalysisResults) {
3 resetAll();
4 } else {
5 analyzeDocument();
6 }
7};Source: Analysis.tsx
当多文件结果已经写入 multiFileAnalysisResults 后,hasAnalysisResults 为真,主操作不再启动新的分析,而是清空当前流程。这样可以避免结果页面与新一轮上传状态混杂。
结果存在时的展示分支
1if (hasAnalysisResults) {
2 return (
3 <div className={`tools-container ${isMobile ? 'mobile' : ''}`}>
4 <FunctionSelector isMobile={isMobile} />
5 <section className="tools-content" style={{ position: "relative" }}>
6 <ResultDisplay />
7 <button
8 className="analysis-fab-new"
9 onClick={resetAll}
10 title={t("upload.analyze.new")}
11 >
12 <span className="analysis-fab-icon">+</span>
13 <span className="analysis-fab-text">{t("upload.analyze.new")}</span>
14 </button>
15 </section>
16 </div>
17 );
18}Source: Analysis.tsx
这里的 ResultDisplay 同时承接单文件和多文件结果;页面只根据 Store 中是否存在结果决定进入结果视图。新建按钮直接调用 resetAll,用户无需返回其他页面即可开始下一批文件。
空结果状态下的上传区
1<FileUpload
2 onAnalyze={handleMainButtonClick}
3 canAnalyze={!!canAnalyze}
4 isAnalyzing={isAnalyzing}
5 hasAnalysisResults={hasAnalysisResults}
6/>
7
8<ProgressBar />Source: Analysis.tsx
FileUpload 接收分析回调、可执行标志、执行中标志和结果标志;ProgressBar 独立渲染进度。源码未在当前读取范围内公开 FileUpload 的内部状态更新或合并按钮实现,因此不能进一步断言具体请求、队列或并发策略。
配置与约束
多文件规则目前以用户文档形式给出,已确认的约束如下:
| 选项/约束 | 类型 | 默认值或上限 | 说明 |
|---|---|---|---|
| 文件格式 | 仅 PDF | 不支持其他格式 | |
| 单文件大小 | 文件大小 | 不超过 30 MB | 每个文件分别受限 |
| 文件数量 | 数量 | 无硬性限制 | 建议不超过 10 个 |
| 合并最小数量 | 已分析文件数 | 至少 2 个 | 未分析文件不计入合并 |
| 合并顺序 | 文件列表顺序 | 用户可拖拽调整 | 影响分析顺序和输出排列顺序 |
Analysis 还要求当前设置中存在 apiKey,并且没有正在分析,才将 canAnalyze 判定为真。API Key 的来源和设置页面的保存方式不在本页范围内。
API 与组件契约
Analysis({ isMobile?: boolean }): React.ReactElement
- 参数:
isMobile为可选布尔值,默认值为false。 - 返回值:React 元素;无结果时返回上传/进度布局,有结果时返回结果展示布局。
- 状态依赖:通过
useAppStore()获取文件、分析状态、结果和重置操作;通过useAnalysis()获取analyzeDocument。 - 错误行为:该组件本身没有声明
try/catch或异常转换逻辑;分析请求和错误处理应在 Hook 或下层组件中查找。
1export const Analysis: React.FC<AnalysisProps> = ({ isMobile = false }) => {
2 const { t } = useTranslation();
3 const {
4 files,
5 currentFile,
6 isAnalyzing,
7 analysisResult,
8 multiFileAnalysisResults,
9 getCurrentSettings,
10 showFormatNotice,
11 setShowFormatNotice,
12 resetAll,
13 } = useAppStore();
14
15 const { analyzeDocument } = useAnalysis();
16 const settings = getCurrentSettings();Source: Analysis.tsx
失败模式、边界与并发注意事项
已确认的业务边界
- 文件不是 PDF 时不符合上传规则。
- 单文件超过 30 MB 时不符合上传规则。
- 少于两个已分析文件时不能合并。
- 未分析文件会保留在文件管理列表中,但不会进入合并结果。
- 合并所有已分析文件可能产生过长文档;这是输出规模问题,不是合并失败。
- 如果只想合并部分文件,必须先从列表中移除其他文件。
分析中的重复操作
页面使用 !isAnalyzing 参与 canAnalyze 计算,并把 isAnalyzing 传给 FileUpload。这表明 UI 层会在分析进行时限制开始分析操作,以避免重复提交。源码没有显示锁、取消请求、任务队列或多线程同步实现,因此不能将此 UI 保护扩展解释为后端级幂等保证。
未在当前范围确认的失败处理
上传组件的文件读取错误、分析 API 错误、单个文件失败后是否继续分析其他文件、合并过程的异常回滚以及导出失败提示,均未在已读取源码中出现。Implementation details not found in source;这些情况应在 FileUpload、useAnalysis、Store 和 ResultDisplay 的实现页中继续核对。
性能与运维注意事项
多文件流程的主要成本来自文件数量、单文件大小和完整结果拼接。当前文档建议不超过 10 个文件,原因是分析质量和最终文档长度都会受到影响。合并结果不做摘要或裁剪,而是保留每个入选文件的完整分析内容,因此使用者应把“一次合并多少文件”视为输出规模控制手段。
源码证据只显示组件级的 isAnalyzing 状态和进度条入口,未发现重试、超时、缓存、并发上限或后台任务配置。不要将页面进度显示误解为已经存在可观测性或可恢复任务机制。
扩展点
当前可见的扩展点主要是组件边界和 Store 状态边界:
- 在
FileUpload中扩展文件筛选、排序或合并按钮交互时,应继续通过onAnalyze、canAnalyze、isAnalyzing和hasAnalysisResults与页面协作。 - 在
useAppStore中扩展多文件状态时,应保持analysisResult与multiFileAnalysisResults的结果判断语义一致,否则结果页可能无法正确切换。 - 在
ResultDisplay中扩展复制或导出时,应保留当前已确认的 Markdown 分隔结构和“合并”文件名标识。 - 如果引入部分选择合并,需要改变当前“包含所有已分析文件”的规则,并同步更新文件列表状态与合并前置条件。
这些是基于当前组件契约的安全扩展方向;具体函数签名和数据结构需以相应实现文件为准。