docs(file-summary): 完善自动汇总流程设计与开发计划
This commit is contained in:
597
docs/功能设计/1.自动汇总.md
Normal file
597
docs/功能设计/1.自动汇总.md
Normal file
@@ -0,0 +1,597 @@
|
||||
# 自动汇总文件夹文件目录与页数流程功能设计
|
||||
|
||||
## 文档信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 需求分析文档 | docs/需求分析/1.自动汇总.md |
|
||||
| 功能名称 | 自动汇总文件夹文件目录与页数 |
|
||||
| 所属模块 | 审核智能体 review_agent |
|
||||
| 设计日期 | 2026-06-05 |
|
||||
| 设计版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 一、设计目标
|
||||
|
||||
本功能面向试剂盒 NMPA 注册申报资料审核场景,支持用户在 AI 对话页上传压缩包或多个文件,由后台异步执行文件汇总工作流,自动完成解压、文件清单扫描、页数统计、产品名识别、Markdown 报告生成和 Excel 导出。
|
||||
|
||||
前台 AI 对话页需要展示一个工作流卡片,实时呈现“上传中、解压中、扫描中、解析中、识别中、输出中、完成/失败”等节点状态;工作流完成后,AI 对话框展示 Markdown 简表,并提供 Markdown 报告和 Excel 明细下载链接。上传文件、批次、节点状态、文件明细和导出结果均需要与当前对话绑定,一个对话对应一套文件,不能跨对话串用。
|
||||
|
||||
---
|
||||
|
||||
## 二、总体架构
|
||||
|
||||
### 2.1 架构原则
|
||||
|
||||
| 原则 | 说明 |
|
||||
| --- | --- |
|
||||
| 对话绑定 | 上传文件、处理批次、结果文件均绑定当前 Conversation |
|
||||
| 按需加载 | 将文件处理流程拆分为多个可单独执行的 Skill,按工作流节点调用 |
|
||||
| 后台异步 | 用户提交后后台执行工作流,前台通过 SSE 接收状态事件 |
|
||||
| 失败隔离 | 解压失败导致批次失败;单文件解析失败最多重试 3 次后记录异常并继续 |
|
||||
| 可迁移 MCP | Demo 阶段使用项目内 Skill 注册与调用,后续可迁移为 MCP Tool |
|
||||
| 可追溯 | 每个节点状态、文件统计结果、导出文件均持久化入库 |
|
||||
|
||||
### 2.2 逻辑架构
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["AI 对话页"] --> B["上传接收接口"]
|
||||
B --> C["工作流任务表"]
|
||||
C --> D["后台工作流执行器"]
|
||||
D --> E["Skill 注册表"]
|
||||
E --> F1["上传接收 Skill"]
|
||||
E --> F2["压缩包解压 Skill"]
|
||||
E --> F3["文件清单扫描 Skill"]
|
||||
E --> F4["文档页数统计 Skill"]
|
||||
E --> F5["产品信息识别 Skill"]
|
||||
E --> F6["汇总报告生成 Skill"]
|
||||
E --> F7["Excel 导出 Skill"]
|
||||
D --> G["数据库存档"]
|
||||
D --> H["导出文件存储"]
|
||||
D --> I["SSE 状态事件"]
|
||||
I --> A
|
||||
```
|
||||
|
||||
### 2.3 技术选型
|
||||
|
||||
| 设计项 | Demo 方案 | 后续演进 |
|
||||
| --- | --- | --- |
|
||||
| 工作流编排 | 项目内 LangGraph 风格节点图执行器 | 接入 LangGraph |
|
||||
| Skill 形态 | Python 类或函数注册表,按节点名称动态调用 | 封装为 MCP Tool |
|
||||
| 后台任务 | Django 后台线程 + 数据库状态 | Celery/RQ + Redis |
|
||||
| 实时更新 | 沿用现有 SSE 流式能力,新增 workflow 事件 | 独立任务事件通道 |
|
||||
| 文件存储 | 本地 media 目录 | 对象存储或加密文件服务 |
|
||||
| Markdown 渲染 | 前端引入安全 Markdown 渲染 | 统一富文本渲染组件 |
|
||||
|
||||
---
|
||||
|
||||
## 三、工作流设计
|
||||
|
||||
### 3.1 节点图
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
N1["上传中"] --> N2{"是否压缩包"}
|
||||
N2 -->|"是"| N3["解压中"]
|
||||
N2 -->|"否"| N4["扫描中"]
|
||||
N3 --> N4
|
||||
N4 --> N5["解析页数中"]
|
||||
N5 --> N6["识别产品名中"]
|
||||
N6 --> N7["输出中"]
|
||||
N7 --> N8["完成"]
|
||||
N3 -->|"解压失败"| F["失败"]
|
||||
N7 -->|"导出失败"| F
|
||||
```
|
||||
|
||||
### 3.2 节点定义
|
||||
|
||||
| 节点编码 | 节点名称 | 触发 Skill | 成功条件 | 失败处理 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| upload | 上传中 | 上传接收 Skill | 原始文件保存成功,批次创建成功 | 批次失败 |
|
||||
| extract | 解压中 | 压缩包解压 Skill | zip/rar/7z 等压缩包解压成功 | 批次失败 |
|
||||
| inventory | 扫描中 | 文件清单扫描 Skill | 生成文件清单 | 批次失败或空文件提示 |
|
||||
| page_count | 解析页数中 | 文档页数统计 Skill | 支持类型完成页数统计或异常记录 | 单文件失败不阻断 |
|
||||
| product_detect | 识别产品名中 | 产品信息识别 Skill | 识别到产品名或返回空 | 不阻断 |
|
||||
| report | 输出中 | 汇总报告生成 Skill | Markdown 报告与对话简表生成成功 | 批次失败 |
|
||||
| export | 输出中 | Excel 导出 Skill | Excel 明细生成成功 | 批次失败或记录导出异常 |
|
||||
| completed | 完成 | 工作流执行器 | 所有必需产物完成 | 写入完成状态 |
|
||||
|
||||
### 3.3 状态机
|
||||
|
||||
| 状态 | 含义 |
|
||||
| --- | --- |
|
||||
| pending | 已创建,等待执行 |
|
||||
| running | 执行中 |
|
||||
| retrying | 单文件解析失败,正在重试 |
|
||||
| success | 节点执行成功 |
|
||||
| failed | 节点或批次失败 |
|
||||
| skipped | 当前节点不需要执行,例如非压缩包跳过解压 |
|
||||
|
||||
---
|
||||
|
||||
## 四、Skill 设计
|
||||
|
||||
### 4.1 Skill 注册与调用
|
||||
|
||||
Demo 阶段在项目内定义 Skill 注册表,工作流执行器根据节点编码按需加载并执行对应 Skill。
|
||||
|
||||
```text
|
||||
WorkflowExecutor
|
||||
-> 根据当前节点读取 Skill 名称
|
||||
-> 从 SkillRegistry 获取 Skill
|
||||
-> 执行 skill.run(context)
|
||||
-> 写入节点状态与结果
|
||||
-> 发送 SSE 状态事件
|
||||
-> 进入下一节点
|
||||
```
|
||||
|
||||
后续 MCP 化时,每个 Skill 可映射为独立 MCP Tool,输入输出保持稳定 JSON 契约。
|
||||
|
||||
### 4.2 上传接收 Skill
|
||||
|
||||
| 项目 | 说明 |
|
||||
| --- | --- |
|
||||
| 中文名称 | 上传接收 Skill |
|
||||
| 职责 | 接收对话页上传的压缩包或多个文件,保存原始文件,创建上传批次 |
|
||||
| 输入 | conversation_id、user_id、uploaded_files |
|
||||
| 输出 | batch_id、upload_file_ids、upload_type、original_storage_paths |
|
||||
| 数据写入 | FileSummaryBatch、UploadedSourceFile |
|
||||
| 关键规则 | 文件必须绑定当前 Conversation;同一对话只使用本对话上传的文件 |
|
||||
|
||||
### 4.3 压缩包解压 Skill
|
||||
|
||||
| 项目 | 说明 |
|
||||
| --- | --- |
|
||||
| 中文名称 | 压缩包解压 Skill |
|
||||
| 职责 | 识别并解压 zip、rar、7z 等常见压缩包,保留目录结构 |
|
||||
| 输入 | batch_id、source_file_path |
|
||||
| 输出 | extract_root、extracted_file_count |
|
||||
| 数据写入 | WorkflowNodeRun、批次工作目录 |
|
||||
| 关键规则 | 防止路径穿越;解压目录必须限定在批次工作目录内;解压失败批次失败 |
|
||||
|
||||
### 4.4 文件清单扫描 Skill
|
||||
|
||||
| 项目 | 说明 |
|
||||
| --- | --- |
|
||||
| 中文名称 | 文件清单扫描 Skill |
|
||||
| 职责 | 遍历解压目录或散装文件目录,生成文件清单 |
|
||||
| 输入 | batch_id、scan_root |
|
||||
| 输出 | inventory_items |
|
||||
| 数据写入 | FileSummaryItem |
|
||||
| 关键规则 | 保留目录层级;散装文件归入同一批次根目录;隐藏文件和空文件可标记跳过 |
|
||||
|
||||
### 4.5 文档页数统计 Skill
|
||||
|
||||
| 项目 | 说明 |
|
||||
| --- | --- |
|
||||
| 中文名称 | 文档页数统计 Skill |
|
||||
| 职责 | 对支持类型统计页数或数量 |
|
||||
| 输入 | batch_id、FileSummaryItem 列表 |
|
||||
| 输出 | page_count、statistics_status、error_message |
|
||||
| 数据写入 | FileSummaryItem |
|
||||
| 关键规则 | 支持 pdf、doc、docx、xls、xlsx、ppt、pptx;单文件失败最多重试 3 次,仍失败则记录异常并继续 |
|
||||
|
||||
页数统计口径:
|
||||
|
||||
| 文件类型 | 统计口径 |
|
||||
| --- | --- |
|
||||
| pdf | PDF 页面数量 |
|
||||
| doc/docx | 优先转 PDF 后统计页面数量 |
|
||||
| xls/xlsx | Demo 阶段按工作表数量统计 |
|
||||
| ppt/pptx | 按幻灯片数量统计 |
|
||||
|
||||
### 4.6 产品信息识别 Skill
|
||||
|
||||
| 项目 | 说明 |
|
||||
| --- | --- |
|
||||
| 中文名称 | 产品信息识别 Skill |
|
||||
| 职责 | 尝试识别产品名称,并用于更新对话标题 |
|
||||
| 输入 | batch_id、文件名、目录名、可读取文本片段 |
|
||||
| 输出 | product_name、confidence、evidence |
|
||||
| 数据写入 | FileSummaryBatch.product_name、Conversation.title |
|
||||
| 关键规则 | 优先从文件名和目录名识别;其次读取文档首页或关键文本;识别失败不阻断流程 |
|
||||
|
||||
会话标题规则:
|
||||
|
||||
| 场景 | 标题处理 |
|
||||
| --- | --- |
|
||||
| 识别到产品名 | 更新为“产品名-文件汇总” |
|
||||
| 未识别产品名 | 保持原对话标题 |
|
||||
| 用户已手动命名 | 可保留用户标题,产品名写入批次信息 |
|
||||
|
||||
### 4.7 汇总报告生成 Skill
|
||||
|
||||
| 项目 | 说明 |
|
||||
| --- | --- |
|
||||
| 中文名称 | 汇总报告生成 Skill |
|
||||
| 职责 | 生成完整 Markdown 报告和对话框展示简表 |
|
||||
| 输入 | batch_id、统计摘要、文件明细、异常清单 |
|
||||
| 输出 | markdown_report_path、assistant_markdown_summary |
|
||||
| 数据写入 | ExportedSummaryFile、Message |
|
||||
| 关键规则 | Markdown 简表需要适合前端对话框渲染;完整报告包含全部文件明细 |
|
||||
|
||||
### 4.8 Excel 导出 Skill
|
||||
|
||||
| 项目 | 说明 |
|
||||
| --- | --- |
|
||||
| 中文名称 | Excel 导出 Skill |
|
||||
| 职责 | 生成 Excel 汇总文件 |
|
||||
| 输入 | batch_id、统计摘要、文件明细 |
|
||||
| 输出 | excel_path、download_url |
|
||||
| 数据写入 | ExportedSummaryFile |
|
||||
| 关键规则 | 至少包含“汇总信息”“文件明细”两个 Sheet |
|
||||
|
||||
---
|
||||
|
||||
## 五、数据模型设计
|
||||
|
||||
### 5.1 FileSummaryBatch
|
||||
|
||||
文件汇总批次,表示一次对话内的文件汇总任务。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| id | BigAutoField | 主键 |
|
||||
| conversation | ForeignKey(Conversation) | 绑定对话 |
|
||||
| user | ForeignKey(User) | 上传用户 |
|
||||
| batch_no | CharField | 批次编号 |
|
||||
| product_name | CharField | 识别出的产品名,可为空 |
|
||||
| upload_type | CharField | archive、multi_file |
|
||||
| status | CharField | pending、running、success、failed |
|
||||
| total_files | Integer | 文件总数 |
|
||||
| supported_files | Integer | 支持统计的文件数 |
|
||||
| success_files | Integer | 统计成功数 |
|
||||
| failed_files | Integer | 统计失败数 |
|
||||
| unsupported_files | Integer | 不支持文件数 |
|
||||
| total_pages | Integer | 总页数 |
|
||||
| work_dir | CharField | 批次工作目录 |
|
||||
| error_message | TextField | 批次异常说明 |
|
||||
| created_at | DateTimeField | 创建时间 |
|
||||
| started_at | DateTimeField | 开始时间 |
|
||||
| finished_at | DateTimeField | 完成时间 |
|
||||
|
||||
### 5.2 UploadedSourceFile
|
||||
|
||||
上传原始文件记录。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| id | BigAutoField | 主键 |
|
||||
| batch | ForeignKey(FileSummaryBatch) | 所属批次 |
|
||||
| original_name | CharField | 原始文件名 |
|
||||
| storage_path | CharField | 保存路径 |
|
||||
| file_size | BigInteger | 文件大小 |
|
||||
| content_type | CharField | MIME 类型 |
|
||||
| created_at | DateTimeField | 上传时间 |
|
||||
|
||||
### 5.3 FileSummaryItem
|
||||
|
||||
文件明细记录。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| id | BigAutoField | 主键 |
|
||||
| batch | ForeignKey(FileSummaryBatch) | 所属批次 |
|
||||
| file_index | Integer | 文件序号 |
|
||||
| directory_level | CharField | 目录层级 |
|
||||
| file_name | CharField | 文件名 |
|
||||
| file_type | CharField | 文件类型 |
|
||||
| relative_path | CharField | 相对路径 |
|
||||
| storage_path | CharField | 实际处理路径 |
|
||||
| page_count | Integer | 页数,可为空 |
|
||||
| statistics_status | CharField | success、failed、unsupported、skipped |
|
||||
| retry_count | Integer | 页数统计重试次数 |
|
||||
| error_message | TextField | 异常说明 |
|
||||
| created_at | DateTimeField | 创建时间 |
|
||||
| updated_at | DateTimeField | 更新时间 |
|
||||
|
||||
### 5.4 WorkflowNodeRun
|
||||
|
||||
工作流节点运行记录。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| id | BigAutoField | 主键 |
|
||||
| batch | ForeignKey(FileSummaryBatch) | 所属批次 |
|
||||
| node_code | CharField | 节点编码 |
|
||||
| node_name | CharField | 节点名称 |
|
||||
| status | CharField | pending、running、retrying、success、failed、skipped |
|
||||
| progress | Integer | 进度百分比 |
|
||||
| message | TextField | 节点提示 |
|
||||
| started_at | DateTimeField | 开始时间 |
|
||||
| finished_at | DateTimeField | 完成时间 |
|
||||
|
||||
### 5.5 ExportedSummaryFile
|
||||
|
||||
导出文件记录。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| id | BigAutoField | 主键 |
|
||||
| batch | ForeignKey(FileSummaryBatch) | 所属批次 |
|
||||
| export_type | CharField | markdown、excel |
|
||||
| file_name | CharField | 文件名 |
|
||||
| storage_path | CharField | 保存路径 |
|
||||
| download_url | CharField | 下载链接 |
|
||||
| status | CharField | success、failed |
|
||||
| error_message | TextField | 导出异常说明 |
|
||||
| created_at | DateTimeField | 生成时间 |
|
||||
|
||||
---
|
||||
|
||||
## 六、后端接口设计
|
||||
|
||||
### 6.1 上传并启动工作流
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| URL | POST /api/review-agent/conversations/{conversation_id}/file-summary/start/ |
|
||||
| 认证 | 登录用户 |
|
||||
| 请求类型 | multipart/form-data |
|
||||
| 请求参数 | files[]、prompt |
|
||||
| 响应 | batch_id、status、workflow_nodes |
|
||||
|
||||
处理逻辑:
|
||||
|
||||
```text
|
||||
校验 conversation 属于当前用户
|
||||
-> 保存上传文件
|
||||
-> 创建 FileSummaryBatch
|
||||
-> 创建 WorkflowNodeRun 初始节点
|
||||
-> 启动后台线程执行工作流
|
||||
-> 返回 batch_id 和初始节点状态
|
||||
```
|
||||
|
||||
### 6.2 工作流 SSE 事件流
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| URL | GET /api/review-agent/file-summary/{batch_id}/events/ |
|
||||
| 认证 | 登录用户 |
|
||||
| 响应类型 | text/event-stream |
|
||||
|
||||
事件类型:
|
||||
|
||||
| 事件 | 说明 |
|
||||
| --- | --- |
|
||||
| workflow_started | 工作流开始 |
|
||||
| node_started | 节点开始 |
|
||||
| node_progress | 节点进度更新 |
|
||||
| node_retrying | 单文件解析重试 |
|
||||
| node_completed | 节点完成 |
|
||||
| node_failed | 节点失败 |
|
||||
| workflow_completed | 工作流完成 |
|
||||
| workflow_failed | 工作流失败 |
|
||||
|
||||
示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"batch_id": 12,
|
||||
"node_code": "page_count",
|
||||
"node_name": "解析页数中",
|
||||
"status": "retrying",
|
||||
"progress": 42,
|
||||
"message": "检测报告.pdf 解析失败,正在第 2 次重试"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 查询批次状态
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| URL | GET /api/review-agent/file-summary/{batch_id}/ |
|
||||
| 认证 | 登录用户 |
|
||||
| 响应 | 批次摘要、节点状态、文件简表、导出文件链接 |
|
||||
|
||||
用途:
|
||||
|
||||
| 场景 | 说明 |
|
||||
| --- | --- |
|
||||
| 页面刷新恢复 | 前端重新加载后恢复工作流卡片状态 |
|
||||
| 历史记录查看 | 从会话历史进入后展示已完成汇总结果 |
|
||||
|
||||
### 6.4 下载导出文件
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| URL | GET /api/review-agent/file-summary/exports/{export_id}/download/ |
|
||||
| 认证 | 登录用户 |
|
||||
| 响应 | 文件流 |
|
||||
|
||||
权限规则:
|
||||
|
||||
```text
|
||||
export_id -> batch -> conversation -> user
|
||||
必须等于当前登录用户,才允许下载。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、前端设计
|
||||
|
||||
### 7.1 AI 对话页改造
|
||||
|
||||
现有 `templates/home.html` 和 `static/js/app.js` 需要增强:
|
||||
|
||||
| 改造点 | 说明 |
|
||||
| --- | --- |
|
||||
| 附件选择 | 在输入框旁增加文件上传按钮,支持压缩包和多个文件 |
|
||||
| 工作流卡片 | 用户提交后在对话流中插入工作流状态卡片 |
|
||||
| SSE 监听 | 监听后台节点事件,实时更新卡片节点状态 |
|
||||
| Markdown 渲染 | AI 回复支持 Markdown 表格和下载链接渲染 |
|
||||
| 状态恢复 | 页面刷新后查询批次状态,恢复工作流卡片 |
|
||||
|
||||
### 7.2 工作流卡片
|
||||
|
||||
卡片包含节点列表:
|
||||
|
||||
| 节点 | 前台展示文案 |
|
||||
| --- | --- |
|
||||
| upload | 上传中 |
|
||||
| extract | 解压中 |
|
||||
| inventory | 扫描中 |
|
||||
| page_count | 解析页数中 |
|
||||
| product_detect | 识别产品名中 |
|
||||
| report/export | 输出中 |
|
||||
| completed | 已完成 |
|
||||
|
||||
节点状态样式:
|
||||
|
||||
| 状态 | 展示 |
|
||||
| --- | --- |
|
||||
| pending | 灰色等待 |
|
||||
| running | 高亮进行中 |
|
||||
| retrying | 黄色重试中 |
|
||||
| success | 绿色完成 |
|
||||
| failed | 红色失败 |
|
||||
| skipped | 灰色跳过 |
|
||||
|
||||
### 7.3 对话框结果展示
|
||||
|
||||
工作流完成后,AI 对话框新增助手消息,内容为 Markdown:
|
||||
|
||||
```markdown
|
||||
已完成文件目录与页数汇总。
|
||||
|
||||
| 指标 | 数量 |
|
||||
| --- | --- |
|
||||
| 文件总数 | 24 |
|
||||
| 统计成功 | 21 |
|
||||
| 统计失败 | 2 |
|
||||
| 不支持 | 1 |
|
||||
| 总页数 | 386 |
|
||||
|
||||
| 序号 | 目录层级 | 文件名 | 类型 | 页数 | 状态 | 异常说明 |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| 1 | 注册资料/说明书 | 说明书.docx | docx | 12 | 成功 | |
|
||||
| 2 | 注册资料/检测报告 | 检测报告.pdf | pdf | 38 | 成功 | |
|
||||
|
||||
[下载 Markdown 报告](download-url)
|
||||
[下载 Excel 明细](download-url)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、后台服务设计
|
||||
|
||||
### 8.1 WorkflowExecutor
|
||||
|
||||
负责批次级工作流编排。
|
||||
|
||||
| 方法 | 说明 |
|
||||
| --- | --- |
|
||||
| start(batch_id) | 启动后台任务 |
|
||||
| run(batch_id) | 串行执行节点图 |
|
||||
| run_node(node_code, context) | 执行单个节点 |
|
||||
| emit_event(batch_id, event_type, payload) | 写入并推送事件 |
|
||||
| complete(batch_id) | 完成批次 |
|
||||
| fail(batch_id, error) | 标记批次失败 |
|
||||
|
||||
### 8.2 SkillRegistry
|
||||
|
||||
负责 Skill 注册与按需加载。
|
||||
|
||||
| 方法 | 说明 |
|
||||
| --- | --- |
|
||||
| register(name, skill_cls) | 注册 Skill |
|
||||
| get(name) | 获取 Skill |
|
||||
| run(name, context) | 执行 Skill |
|
||||
|
||||
### 8.3 PageCountService
|
||||
|
||||
负责具体文件页数统计。
|
||||
|
||||
| 方法 | 说明 |
|
||||
| --- | --- |
|
||||
| count_pdf(path) | 统计 PDF 页面数 |
|
||||
| count_word(path) | doc/docx 转 PDF 后统计页面数 |
|
||||
| count_excel(path) | 统计工作表数量 |
|
||||
| count_ppt(path) | 统计幻灯片数量 |
|
||||
| count_with_retry(item, max_retry=3) | 单文件重试统计 |
|
||||
|
||||
### 8.4 ExportService
|
||||
|
||||
负责 Markdown 和 Excel 导出。
|
||||
|
||||
| 方法 | 说明 |
|
||||
| --- | --- |
|
||||
| build_markdown_report(batch) | 生成完整 Markdown 报告 |
|
||||
| build_chat_summary(batch) | 生成对话简表 |
|
||||
| build_excel(batch) | 生成 Excel 明细 |
|
||||
| create_download_record(batch, path, type) | 创建下载记录 |
|
||||
|
||||
---
|
||||
|
||||
## 九、异常与重试设计
|
||||
|
||||
### 9.1 批次级失败
|
||||
|
||||
| 场景 | 处理 |
|
||||
| --- | --- |
|
||||
| 上传保存失败 | 批次不创建或标记失败 |
|
||||
| 压缩包无法解压 | 批次失败,工作流终止 |
|
||||
| 文件清单为空 | 批次失败,提示未检测到可处理文件 |
|
||||
| 报告导出失败 | 批次失败或标记导出异常 |
|
||||
|
||||
### 9.2 文件级失败
|
||||
|
||||
| 场景 | 处理 |
|
||||
| --- | --- |
|
||||
| 单文件页数解析失败 | 最多重试 3 次 |
|
||||
| 重试仍失败 | statistics_status=failed,记录异常说明,继续处理其他文件 |
|
||||
| 不支持类型 | statistics_status=unsupported,不重试 |
|
||||
| 加密或损坏文件 | statistics_status=failed,记录“文件加密或损坏” |
|
||||
|
||||
---
|
||||
|
||||
## 十、安全设计
|
||||
|
||||
| 设计点 | 说明 |
|
||||
| --- | --- |
|
||||
| 对话隔离 | 所有批次查询和下载必须校验 conversation.user |
|
||||
| 防串文件 | 工作流只能读取当前 batch 绑定的 UploadedSourceFile |
|
||||
| 解压安全 | 禁止压缩包内路径跳出批次工作目录 |
|
||||
| 文件执行安全 | 不执行上传文件中的脚本、宏或外部链接 |
|
||||
| 下载权限 | 下载接口必须验证当前用户拥有批次所属对话 |
|
||||
| 存储隔离 | 按 user_id/conversation_id/batch_id 建立存储目录 |
|
||||
|
||||
---
|
||||
|
||||
## 十一、验收设计
|
||||
|
||||
| 序号 | 验收项 | 验收标准 |
|
||||
| --- | --- | --- |
|
||||
| 1 | 对话绑定 | A 对话上传的文件不会出现在 B 对话的汇总结果中 |
|
||||
| 2 | 压缩包处理 | 支持 zip、rar、7z 常见压缩包解压并保留目录结构 |
|
||||
| 3 | 多文件处理 | 支持一次上传多个散装文件并生成同一批次结果 |
|
||||
| 4 | 工作流卡片 | 前台能实时展示上传中、解压中、扫描中、解析中、输出中、完成状态 |
|
||||
| 5 | 解析重试 | 单文件解析失败最多重试 3 次,失败后记录异常并继续 |
|
||||
| 6 | Markdown 展示 | 对话框能正确渲染 Markdown 表格和下载链接 |
|
||||
| 7 | 导出下载 | Markdown 报告和 Excel 明细可通过对话框链接下载 |
|
||||
| 8 | 数据存档 | 数据库保留批次、上传文件、节点状态、文件明细、导出文件记录 |
|
||||
| 9 | 标题更新 | 识别到产品名后,可将会话标题更新为“产品名-文件汇总” |
|
||||
|
||||
---
|
||||
|
||||
## 十二、待确认事项
|
||||
|
||||
| 序号 | 问题 | 当前建议 | 状态 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | 是否接入真实 LangGraph 依赖 | Demo 先按 LangGraph 节点图思想自实现轻量编排器 | 待确认 |
|
||||
| 2 | rar/7z 解压依赖 | 可选 py7zr、rarfile、系统 7z 命令 | 待技术验证 |
|
||||
| 3 | doc/docx 转 PDF 依赖 | 建议使用 LibreOffice headless | 待技术验证 |
|
||||
| 4 | 用户手动命名对话时是否允许覆盖 | 建议不覆盖,仅写入产品名字段 | 待确认 |
|
||||
| 5 | 后台任务是否需要取消能力 | Demo 可不做,正式版建议支持取消 | 待确认 |
|
||||
|
||||
---
|
||||
|
||||
## 十三、实施建议
|
||||
|
||||
1. 先补充数据模型和迁移,建立批次、文件明细、节点状态和导出文件表。
|
||||
2. 增加上传并启动工作流接口,确保文件和当前对话强绑定。
|
||||
3. 实现轻量 WorkflowExecutor 和 SkillRegistry,先完成 zip、pdf、xlsx、pptx 的主链路。
|
||||
4. 改造前端对话框,增加附件上传、工作流卡片和 Markdown 渲染。
|
||||
5. 补齐 doc/docx、rar、7z 等依赖能力,再完善异常重试和下载权限测试。
|
||||
634
docs/开发计划/1.自动汇总.md
Normal file
634
docs/开发计划/1.自动汇总.md
Normal file
@@ -0,0 +1,634 @@
|
||||
# 自动汇总文件夹文件目录与页数流程开发计划
|
||||
|
||||
## 文档信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 需求分析文档 | docs/需求分析/1.自动汇总.md |
|
||||
| 功能设计文档 | docs/功能设计/1.自动汇总.md |
|
||||
| 详细设计文档 | docs/详细设计/1.自动汇总.md |
|
||||
| 数据库设计文档 | docs/数据库设计/1.自动汇总.md |
|
||||
| 功能名称 | 自动汇总文件夹文件目录与页数 |
|
||||
| 所属模块 | 审核智能体 review_agent |
|
||||
| 执行方式 | 单人开发 + Codex 流水线自动化执行 |
|
||||
| 计划日期 | 2026-06-05 |
|
||||
| 计划版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 一、开发计划目标
|
||||
|
||||
本开发计划用于指导 Codex 按阶段自动完成“自动汇总文件夹文件目录与页数”功能开发。任务拆分按可交付阶段组织,每个任务都需要具备明确目标、涉及文件、前置依赖、开发步骤、验收标准、验证命令和 Codex 执行提示。
|
||||
|
||||
本功能不按 MVP 缩减范围,必须按需求分析、功能设计、详细设计、数据库设计中的全部范围完成。
|
||||
|
||||
---
|
||||
|
||||
## 二、已确认开发规则
|
||||
|
||||
| 规则项 | 内容 |
|
||||
| --- | --- |
|
||||
| 拆分方式 | 按可交付阶段拆分 |
|
||||
| 任务粒度 | 每个任务写到可直接交给 Codex 执行 |
|
||||
| 执行对象 | 一个开发者使用 Codex 流水线自动化执行 |
|
||||
| 单任务范围 | 尽量控制在 1 到 3 类文件 |
|
||||
| Codex 提示 | 每个任务都提供“Codex 执行提示” |
|
||||
| 功能范围 | 必须完成全部需求,不允许降级为最小闭环 |
|
||||
| 前端验证 | 使用 Playwright 做真实浏览器端到端测试 |
|
||||
| 测试数据 | 测试代码中可动态创建登录用户和临时文件 |
|
||||
| Git 提交 | 每个阶段完成并验证通过后提交一次 |
|
||||
| 提交摘要 | 使用执行机器上的 `git-commit-summary` skill |
|
||||
| 分支规则 | 从 `V2` 创建日期 + 中文功能名分支,完成后合并回 `V2` |
|
||||
|
||||
---
|
||||
|
||||
## 三、总体验收标准
|
||||
|
||||
| 类别 | 完成标准 |
|
||||
| --- | --- |
|
||||
| 数据库 | 7 张 `ra_` 表全部通过 Django migration 落库,约束、索引、枚举齐全 |
|
||||
| 上传 | 当前对话右侧上传区支持多文件和压缩包上传,上传即存储,附件不跨对话 |
|
||||
| 触发 | 用户发送命中提示词后才启动自动汇总工作流,普通对话不误触发 |
|
||||
| 工作流 | 后台异步执行,节点状态可实时更新,事件可持久化和恢复 |
|
||||
| 解压 | 支持 zip、7z、rar,解压安全检查必须完成 |
|
||||
| 统计 | 支持 pdf、doc、docx、xls、xlsx、ppt、pptx,失败重试 3 次,失败不阻断批次 |
|
||||
| 输出 | 生成 Markdown 报告、Excel 明细,对话框展示 Markdown 简表和下载链接 |
|
||||
| 前端 | 三栏布局、上方拖拽上传、下方工作流卡片、Markdown 表格渲染正常 |
|
||||
| 存档 | 批次、附件、文件明细、节点、事件、导出文件全部入库 |
|
||||
| 标题 | 识别到产品名后按规则更新对话标题 |
|
||||
| 权限 | 上传、查询、下载都校验当前用户和当前对话 |
|
||||
| 测试 | 单元、接口、集成、Playwright 端到端测试全部覆盖 |
|
||||
| 部署 | requirements 可安装,Docker 部署说明包含 7z/p7zip,rar/7z 解压验证通过 |
|
||||
|
||||
---
|
||||
|
||||
## 四、阶段总览
|
||||
|
||||
| 阶段 | 名称 | 目标 | 阶段验收 |
|
||||
| --- | --- | --- | --- |
|
||||
| P0 | 流水线准备 | 建立开发分支,确认依赖、规范和现状 | 分支创建完成,开发前检查通过 |
|
||||
| P1 | 数据模型与迁移 | 完成 7 张 ra_ 表 ORM 与 migration | SQLite 可建表,模型约束正确 |
|
||||
| P2 | 上传与对话绑定 | 实现上传即存储、同名版本和附件权限 | 上传接口可用,附件不跨对话 |
|
||||
| P3 | 工作流触发与后台执行 | 实现提示词触发、批次创建、后台节点执行和事件持久化 | 命中提示词可启动工作流,状态可查询 |
|
||||
| P4 | Skill 与文件处理能力 | 实现解压、扫描、页数统计、重试和产品名识别 | 支持格式全部进入处理流程 |
|
||||
| P5 | 报告生成与下载 | 实现 Markdown 报告、Excel 导出、下载权限和助手消息 | 可下载报告,数据库留痕完整 |
|
||||
| P6 | 前端三栏与工作流卡片 | 实现右侧上传区、工作流卡片、SSE 更新和 Markdown 渲染 | Playwright 验证前端主流程 |
|
||||
| P7 | 测试、部署与总体验收 | 补齐自动化测试、端到端测试、Docker 说明和最终合并 | 全部测试通过,合并回 V2 |
|
||||
|
||||
---
|
||||
|
||||
## 五、P0 流水线准备
|
||||
|
||||
### FS-P0-001 创建开发分支并检查现状
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | Git / 准备 |
|
||||
| 前置任务 | 无 |
|
||||
| 涉及文件 | 无固定文件 |
|
||||
| 目标 | 从 `V2` 分支创建日期 + 中文功能名开发分支,并确认工作区状态 |
|
||||
| 开发步骤 | 1. 切换到 `V2`;2. 拉取或确认本地最新状态;3. 创建 `codex/YYYYMMDD-自动汇总文件目录页数` 分支;4. 检查 `git status`;5. 确认已有设计文档存在 |
|
||||
| 验收标准 | 开发分支创建成功;工作区变更来源清楚;不会覆盖用户已有未提交改动 |
|
||||
| 验证命令 | `git branch --show-current`; `git status --short` |
|
||||
| Codex 执行提示 | 请从 `V2` 创建 `codex/YYYYMMDD-自动汇总文件目录页数` 开发分支,检查当前工作区状态,不要回滚用户已有变更。 |
|
||||
|
||||
### FS-P0-002 补充依赖清单与部署前置说明
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 依赖 / 部署准备 |
|
||||
| 前置任务 | FS-P0-001 |
|
||||
| 涉及文件 | `requirements.txt`、部署说明文档、前端静态资源引入位置 |
|
||||
| 目标 | 增加文件解析与导出所需 Python 依赖,并说明 rar/7z 的系统依赖 |
|
||||
| 开发步骤 | 1. 在 `requirements.txt` 增加 `pypdf`、`python-docx`、`python-pptx`、`openpyxl`、`xlrd`、`olefile`、`py7zr`;2. 在前端任务中明确 `marked + DOMPurify` 通过模板或静态资源引入;3. 在部署说明中写明 Docker 需要安装 7z/p7zip;4. 明确不强制依赖 LibreOffice |
|
||||
| 验收标准 | Python 依赖可安装;部署说明明确 rar 依赖系统 7z/p7zip;未引入 LibreOffice 强依赖 |
|
||||
| 验证命令 | `pip install -r requirements.txt` |
|
||||
| Codex 执行提示 | 请按详细设计补充轻量依赖,并在部署说明中写清 Docker 需安装 7z/p7zip 支持 rar/7z,禁止把 LibreOffice 作为必需依赖。 |
|
||||
|
||||
---
|
||||
|
||||
## 六、P1 数据模型与迁移
|
||||
|
||||
### FS-P1-001 新增文件汇总 ORM 模型
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 数据库 / 后端 |
|
||||
| 前置任务 | P0 |
|
||||
| 涉及文件 | `review_agent/models.py` |
|
||||
| 目标 | 新增文件汇总相关 7 个模型和状态枚举 |
|
||||
| 开发步骤 | 1. 定义 `FileAttachment`;2. 定义 `FileSummaryBatch`;3. 定义 `FileSummaryBatchAttachment`;4. 定义 `FileSummaryItem`;5. 定义 `WorkflowNodeRun`;6. 定义 `WorkflowEvent`;7. 定义 `ExportedSummaryFile`;8. 使用 Django `TextChoices` 管理枚举 |
|
||||
| 验收标准 | 模型字段、关联、默认值、`db_table`、`indexes`、`constraints` 与数据库设计一致 |
|
||||
| 验证命令 | `python manage.py check` |
|
||||
| Codex 执行提示 | 请按 `docs/数据库设计/1.自动汇总.md` 在 `review_agent/models.py` 新增 7 个 `ra_` 表模型,使用 Django ORM、TextChoices、短表名、索引和唯一约束。 |
|
||||
|
||||
### FS-P1-002 生成并验证数据库迁移
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 数据库 / 迁移 |
|
||||
| 前置任务 | FS-P1-001 |
|
||||
| 涉及文件 | `review_agent/migrations/` |
|
||||
| 目标 | 生成 migration 并验证 SQLite 可落表 |
|
||||
| 开发步骤 | 1. 执行 `makemigrations`;2. 检查 migration 是否只包含本功能相关模型;3. 执行 `migrate`;4. 检查表结构和索引 |
|
||||
| 验收标准 | migration 可执行;SQLite 中生成 7 张 `ra_` 表;约束和索引生效 |
|
||||
| 验证命令 | `python manage.py makemigrations review_agent`; `python manage.py migrate`; `python manage.py check` |
|
||||
| Codex 执行提示 | 请为文件汇总模型生成 Django migration 并执行迁移验证,确保 SQLite 下 7 张 `ra_` 表均可创建。 |
|
||||
|
||||
### FS-P1-003 增加模型级测试
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 测试 / 数据库 |
|
||||
| 前置任务 | FS-P1-002 |
|
||||
| 涉及文件 | `tests/test_file_summary_models.py` |
|
||||
| 目标 | 覆盖附件版本、批次绑定、唯一约束和权限查询基础逻辑 |
|
||||
| 开发步骤 | 1. 测试同一对话同名附件版本号递增;2. 测试 active 版本切换;3. 测试批次绑定附件唯一;4. 测试同批次 relative_path 唯一;5. 测试导出文件能追溯到用户和对话 |
|
||||
| 验收标准 | 模型测试全部通过,关键约束失败时能暴露错误 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_models.py` |
|
||||
| Codex 执行提示 | 请新增模型级测试,覆盖文件汇总表的版本、绑定、唯一约束和对话隔离规则。 |
|
||||
|
||||
### P1 阶段提交规则
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 阶段验证 | `python manage.py check`; `pytest tests/test_file_summary_models.py` |
|
||||
| 提交动作 | 调用 `git-commit-summary` 生成提交摘要并提交 |
|
||||
| Codex 执行提示 | P1 验证通过后,请调用 `git-commit-summary` 分析本阶段变更,并按其输出提交到当前开发分支。 |
|
||||
|
||||
---
|
||||
|
||||
## 七、P2 上传与对话绑定
|
||||
|
||||
### FS-P2-001 实现附件存储服务
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 存储 |
|
||||
| 前置任务 | P1 |
|
||||
| 涉及文件 | `review_agent/file_summary/storage.py`、`review_agent/file_summary/constants.py` |
|
||||
| 目标 | 实现上传文件保存、版本号生成、存储目录生成和逻辑删除基础能力 |
|
||||
| 开发步骤 | 1. 创建 `file_summary` 目录;2. 实现按 `user/conversation/attachments` 保存文件;3. 实现同名附件版本递增;4. 新版本设为 active 并关闭旧 active;5. 实现路径安全处理 |
|
||||
| 验收标准 | 上传文件保存到受控目录;附件记录绑定当前用户和对话;同名多版本不覆盖 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_storage.py` |
|
||||
| Codex 执行提示 | 请实现文件汇总附件存储服务,保证上传即存储、同名多版本、当前对话绑定和路径安全。 |
|
||||
|
||||
### FS-P2-002 实现附件上传接口
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 接口 |
|
||||
| 前置任务 | FS-P2-001 |
|
||||
| 涉及文件 | `review_agent/file_summary/views.py`、`review_agent/file_summary/urls.py`、`config/urls.py` |
|
||||
| 目标 | 新增对话附件上传接口,支持多文件和压缩包上传 |
|
||||
| 开发步骤 | 1. 新增 `POST /api/review-agent/conversations/{conversation_id}/attachments/`;2. 校验 conversation 属于 request.user;3. 保存多个文件;4. 返回 attachment 列表;5. 接入 URL |
|
||||
| 验收标准 | 当前用户只能向自己的对话上传;接口返回附件 ID、文件名、大小、版本和状态 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_views.py -k upload` |
|
||||
| Codex 执行提示 | 请新增对话附件上传 API,支持一次上传多个文件,所有附件必须绑定当前 Conversation,禁止跨用户上传。 |
|
||||
|
||||
### FS-P2-003 实现附件列表和删除接口
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 接口 |
|
||||
| 前置任务 | FS-P2-002 |
|
||||
| 涉及文件 | `review_agent/file_summary/views.py`、`review_agent/file_summary/urls.py` |
|
||||
| 目标 | 支持前端右侧上传区展示当前对话附件,并允许逻辑删除 |
|
||||
| 开发步骤 | 1. 新增当前对话附件列表接口;2. 返回 active 和历史版本信息;3. 新增附件逻辑删除接口;4. 删除时设置 `upload_status=deleted`、`is_active=false` |
|
||||
| 验收标准 | 附件列表只返回当前对话文件;逻辑删除不影响历史批次追溯 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_views.py -k attachment` |
|
||||
| Codex 执行提示 | 请实现当前对话附件列表和逻辑删除接口,支持同名版本展示,删除不得物理移除历史批次需要的文件。 |
|
||||
|
||||
### P2 阶段提交规则
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 阶段验证 | `pytest tests/test_file_summary_storage.py tests/test_file_summary_views.py -k "upload or attachment"` |
|
||||
| 提交动作 | 调用 `git-commit-summary` 生成提交摘要并提交 |
|
||||
| Codex 执行提示 | P2 验证通过后,请调用 `git-commit-summary` 分析本阶段变更,并按其输出提交到当前开发分支。 |
|
||||
|
||||
---
|
||||
|
||||
## 八、P3 工作流触发与后台执行
|
||||
|
||||
### FS-P3-001 实现提示词触发判断
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 意图识别 |
|
||||
| 前置任务 | P2 |
|
||||
| 涉及文件 | `review_agent/file_summary/services/workflow_trigger.py`、`review_agent/services.py` |
|
||||
| 目标 | 根据提示词决定是否启动自动汇总工作流 |
|
||||
| 开发步骤 | 1. 定义触发关键词;2. 判断当前对话是否存在可用 active 附件;3. 命中时返回 workflow 类型;4. 未命中走普通 LLM;5. 命中但无附件时返回提示 |
|
||||
| 验收标准 | “自动汇总”“文件目录”“页数”等关键词可触发;普通对话不误触发 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_trigger.py` |
|
||||
| Codex 执行提示 | 请实现自动汇总工作流触发判断,只有当前对话存在可用附件且提示词命中关键词时才启动工作流。 |
|
||||
|
||||
### FS-P3-002 实现批次创建与附件固化
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 工作流 |
|
||||
| 前置任务 | FS-P3-001 |
|
||||
| 涉及文件 | `review_agent/file_summary/workflow.py`、`review_agent/file_summary/storage.py` |
|
||||
| 目标 | 用户消息触发时创建 FileSummaryBatch,并固化本次使用的附件版本 |
|
||||
| 开发步骤 | 1. 创建批次编号;2. 创建 `FileSummaryBatch`;3. 绑定 active 附件到中间表;4. 标记附件为 bound;5. 创建初始节点记录 |
|
||||
| 验收标准 | 同一对话可多次汇总;历史批次绑定历史附件版本;不会读取其他对话文件 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_workflow.py -k batch` |
|
||||
| Codex 执行提示 | 请实现批次创建和附件版本固化,确保每次汇总只读取本批次绑定的附件。 |
|
||||
|
||||
### FS-P3-003 实现 WorkflowEvent 与 SSE 事件查询
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / SSE |
|
||||
| 前置任务 | FS-P3-002 |
|
||||
| 涉及文件 | `review_agent/file_summary/events.py`、`review_agent/file_summary/views.py` |
|
||||
| 目标 | 持久化工作流事件,并支持前端按 batch 监听和断点续传 |
|
||||
| 开发步骤 | 1. 实现事件写入;2. 实现 SSE 格式化;3. 新增 `GET /api/review-agent/file-summary/{batch_id}/events/?after=`;4. 新增批次状态查询接口;5. 校验用户权限 |
|
||||
| 验收标准 | 节点事件可入库;SSE 可返回事件流;页面刷新可通过状态接口恢复 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_views.py -k "event or status"` |
|
||||
| Codex 执行提示 | 请实现工作流事件持久化、事件 SSE 接口和批次状态查询接口,所有查询必须校验当前用户权限。 |
|
||||
|
||||
### FS-P3-004 实现轻量后台工作流执行器
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 工作流 |
|
||||
| 前置任务 | FS-P3-003 |
|
||||
| 涉及文件 | `review_agent/file_summary/workflow.py`、`review_agent/file_summary/skills/` |
|
||||
| 目标 | 实现串行节点图执行器,后台异步执行并更新节点状态 |
|
||||
| 开发步骤 | 1. 定义节点顺序;2. 实现后台线程启动;3. 实现节点开始、成功、失败、跳过状态;4. 每个节点写入 WorkflowNodeRun;5. 每个节点发送 WorkflowEvent |
|
||||
| 验收标准 | 命中提示词后可后台创建并推进节点;节点状态可查询;异常能标记批次失败 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_workflow.py -k executor` |
|
||||
| Codex 执行提示 | 请实现轻量 WorkflowExecutor,按节点图异步执行文件汇总流程,实时写入节点状态和事件。 |
|
||||
|
||||
### FS-P3-005 接入现有流式聊天接口
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 对话 |
|
||||
| 前置任务 | FS-P3-004 |
|
||||
| 涉及文件 | `review_agent/services.py`、`review_agent/views.py` |
|
||||
| 目标 | 在现有 `stream_chat` 流程中按需启动自动汇总工作流 |
|
||||
| 开发步骤 | 1. 用户消息入库后判断触发;2. 命中时创建批次并启动后台;3. SSE meta 返回 workflow 信息;4. 对话中返回“已启动工作流”类助手消息或后续由报告生成写入结果;5. 未命中时保持原 LLM 流式逻辑 |
|
||||
| 验收标准 | 普通聊天不受影响;自动汇总触发后前端可拿到 batch_id;无附件时提示用户先上传 |
|
||||
| 验证命令 | `pytest tests/test_chat.py tests/test_file_summary_workflow.py -k trigger` |
|
||||
| Codex 执行提示 | 请把自动汇总触发接入现有流式聊天接口,保证普通 LLM 对话兼容,命中工作流时返回 workflow meta。 |
|
||||
|
||||
### P3 阶段提交规则
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 阶段验证 | `pytest tests/test_file_summary_trigger.py tests/test_file_summary_workflow.py tests/test_file_summary_views.py` |
|
||||
| 提交动作 | 调用 `git-commit-summary` 生成提交摘要并提交 |
|
||||
| Codex 执行提示 | P3 验证通过后,请调用 `git-commit-summary` 分析本阶段变更,并按其输出提交到当前开发分支。 |
|
||||
|
||||
---
|
||||
|
||||
## 九、P4 Skill 与文件处理能力
|
||||
|
||||
### FS-P4-001 实现 Skill 基类与注册表
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / Skill |
|
||||
| 前置任务 | P3 |
|
||||
| 涉及文件 | `review_agent/file_summary/skills/base.py`、`review_agent/file_summary/skills/registry.py`、`review_agent/file_summary/schemas.py` |
|
||||
| 目标 | 建立项目内 Skill 注册与调用机制,后续可迁移 MCP |
|
||||
| 开发步骤 | 1. 定义 `WorkflowContext`;2. 定义 `SkillResult`;3. 定义 `BaseSkill`;4. 实现 `SkillRegistry`;5. 支持按名称获取和执行 Skill |
|
||||
| 验收标准 | 工作流执行器通过注册表调用 Skill;Skill 输入输出保持 JSON 友好 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_skills.py -k registry` |
|
||||
| Codex 执行提示 | 请实现文件汇总 Skill 基类、上下文、统一返回结构和注册表,使工作流节点能按需加载 Skill。 |
|
||||
|
||||
### FS-P4-002 实现压缩包解压 Skill
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 文件处理 |
|
||||
| 前置任务 | FS-P4-001 |
|
||||
| 涉及文件 | `review_agent/file_summary/services/archive.py`、`review_agent/file_summary/skills/archive_extract.py` |
|
||||
| 目标 | 支持 zip、7z、rar 解压,并完成路径穿越防护 |
|
||||
| 开发步骤 | 1. 实现压缩包识别;2. 使用 `zipfile` 解压 zip;3. 使用 `py7zr` 解压 7z;4. 使用系统 `7z` 解压 rar;5. 检查解压目标路径必须在批次工作目录内;6. 解压失败标记批次失败 |
|
||||
| 验收标准 | zip、7z、rar 均进入解压流程;恶意路径压缩包被拒绝;解压目录保留层级 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_archive.py` |
|
||||
| Codex 执行提示 | 请实现压缩包解压服务和 Skill,必须支持 zip、7z、rar,并对所有解压路径做 target_dir 内部校验。 |
|
||||
|
||||
### FS-P4-003 实现文件清单扫描 Skill
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 文件处理 |
|
||||
| 前置任务 | FS-P4-002 |
|
||||
| 涉及文件 | `review_agent/file_summary/services/inventory.py`、`review_agent/file_summary/skills/file_inventory.py` |
|
||||
| 目标 | 扫描解压目录或散装文件,生成 FileSummaryItem 明细 |
|
||||
| 开发步骤 | 1. 识别扫描根目录;2. 递归遍历文件;3. 生成相对路径;4. 生成目录层级;5. 标记支持、不支持、空文件或跳过状态;6. 按目录顺序生成 file_index |
|
||||
| 验收标准 | 文件明细保留目录层级;散装文件进入同一批次根;relative_path 唯一 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_inventory.py` |
|
||||
| Codex 执行提示 | 请实现文件清单扫描服务和 Skill,保留目录层级,生成文件序号、相对路径、文件类型和初始统计状态。 |
|
||||
|
||||
### FS-P4-004 实现页数统计服务
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 文件解析 |
|
||||
| 前置任务 | FS-P4-003 |
|
||||
| 涉及文件 | `review_agent/file_summary/services/page_count.py` |
|
||||
| 目标 | 支持 pdf、doc、docx、xls、xlsx、ppt、pptx 页数或数量统计 |
|
||||
| 开发步骤 | 1. pdf 使用 `pypdf` 统计页面;2. docx 使用 `python-docx` 读取内置页数属性;3. doc 使用 `olefile` 读取 OLE 元数据;4. xlsx 使用 `openpyxl` 统计工作表;5. xls 使用 `xlrd` 统计工作表;6. pptx 使用 `python-pptx` 统计幻灯片;7. ppt 使用 `olefile` 读取元数据;8. 无可靠页数时标记 uncertain |
|
||||
| 验收标准 | 7 类格式全部有处理分支;读不到页数不崩溃;状态区分 success、failed、unsupported、uncertain |
|
||||
| 验证命令 | `pytest tests/test_file_summary_page_count.py` |
|
||||
| Codex 执行提示 | 请实现页数统计服务,覆盖 pdf/doc/docx/xls/xlsx/ppt/pptx,老格式读不到可靠页数时标记 uncertain,不允许中断批次。 |
|
||||
|
||||
### FS-P4-005 实现页数统计 Skill 与三次重试
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / Skill |
|
||||
| 前置任务 | FS-P4-004 |
|
||||
| 涉及文件 | `review_agent/file_summary/skills/document_page_count.py`、`review_agent/file_summary/services/page_count.py` |
|
||||
| 目标 | 对每个支持文件执行页数统计,失败最多重试 3 次 |
|
||||
| 开发步骤 | 1. 遍历 FileSummaryItem;2. 支持类型调用 page_count 服务;3. 失败重试 3 次;4. 更新 retry_count、statistics_status、page_count、error_message;5. 更新节点进度事件;6. 汇总批次数量 |
|
||||
| 验收标准 | 单文件失败不阻断其他文件;重试事件可记录;批次统计字段更新正确 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_page_count.py -k retry` |
|
||||
| Codex 执行提示 | 请实现文档页数统计 Skill,对单文件解析失败最多重试 3 次,仍失败则记录异常并继续处理其他文件。 |
|
||||
|
||||
### FS-P4-006 实现产品名识别 Skill 与会话标题更新
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 识别 |
|
||||
| 前置任务 | FS-P4-005 |
|
||||
| 涉及文件 | `review_agent/file_summary/services/product_detect.py`、`review_agent/file_summary/skills/product_detect.py` |
|
||||
| 目标 | 从目录名、文件名和少量元数据中识别产品名,并按规则更新对话标题 |
|
||||
| 开发步骤 | 1. 优先使用顶层目录名;2. 从含“产品”“试剂盒”“说明书”等关键词的文件名提取;3. 尝试读取 docx/PDF 元数据 title;4. 写入 batch.product_name;5. 默认标题时更新 Conversation.title;6. 用户自定义标题不覆盖 |
|
||||
| 验收标准 | 识别失败不阻断;识别成功后批次记录产品名;默认对话标题可更新为“产品名-文件汇总” |
|
||||
| 验证命令 | `pytest tests/test_file_summary_product_detect.py` |
|
||||
| Codex 执行提示 | 请实现产品名识别 Skill,从目录名、文件名和轻量元数据识别产品名,识别成功后按规则更新批次和对话标题。 |
|
||||
|
||||
### P4 阶段提交规则
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 阶段验证 | `pytest tests/test_file_summary_skills.py tests/test_file_summary_archive.py tests/test_file_summary_inventory.py tests/test_file_summary_page_count.py tests/test_file_summary_product_detect.py` |
|
||||
| 提交动作 | 调用 `git-commit-summary` 生成提交摘要并提交 |
|
||||
| Codex 执行提示 | P4 验证通过后,请调用 `git-commit-summary` 分析本阶段变更,并按其输出提交到当前开发分支。 |
|
||||
|
||||
---
|
||||
|
||||
## 十、P5 报告生成与下载
|
||||
|
||||
### FS-P5-001 实现 Markdown 报告生成
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 报告 |
|
||||
| 前置任务 | P4 |
|
||||
| 涉及文件 | `review_agent/file_summary/services/report.py`、`review_agent/file_summary/skills/summary_report.py` |
|
||||
| 目标 | 生成完整 Markdown 报告和对话框展示简表 |
|
||||
| 开发步骤 | 1. 构建统计摘要;2. 构建对话简表;3. 构建完整 Markdown 报告;4. 保存到批次 exports 目录;5. 创建 ExportedSummaryFile;6. 生成助手消息内容 |
|
||||
| 验收标准 | Markdown 包含汇总信息、统计摘要、文件明细、异常清单、处理说明和下载链接占位 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_report.py -k markdown` |
|
||||
| Codex 执行提示 | 请实现 Markdown 报告生成 Skill,完整报告和对话简表必须包含文件序号、目录层级、文件名、类型、页数、路径、状态、异常说明。 |
|
||||
|
||||
### FS-P5-002 实现 Excel 导出
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 导出 |
|
||||
| 前置任务 | FS-P5-001 |
|
||||
| 涉及文件 | `review_agent/file_summary/services/export_excel.py`、`review_agent/file_summary/skills/excel_export.py` |
|
||||
| 目标 | 生成 Excel 明细文件 |
|
||||
| 开发步骤 | 1. 使用 `openpyxl` 创建 Workbook;2. 创建“汇总信息”Sheet;3. 创建“文件明细”Sheet;4. 写入状态、重试次数和异常说明;5. 保存到 exports 目录;6. 创建 ExportedSummaryFile |
|
||||
| 验收标准 | Excel 可打开;至少包含两个工作表;字段与需求一致 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_report.py -k excel` |
|
||||
| Codex 执行提示 | 请实现 Excel 导出 Skill,生成包含“汇总信息”和“文件明细”两个 Sheet 的汇总文件。 |
|
||||
|
||||
### FS-P5-003 实现导出下载接口
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 下载 |
|
||||
| 前置任务 | FS-P5-002 |
|
||||
| 涉及文件 | `review_agent/file_summary/views.py`、`review_agent/file_summary/urls.py` |
|
||||
| 目标 | 提供 Markdown 和 Excel 文件下载,并校验权限 |
|
||||
| 开发步骤 | 1. 新增 `GET /api/review-agent/file-summary/exports/{export_id}/download/`;2. 校验 export -> batch -> conversation -> user;3. 返回文件流;4. 设置合适文件名;5. 文件不存在时返回错误 |
|
||||
| 验收标准 | 当前用户可下载自己的导出文件;不能下载其他用户文件;下载链接可用于 Markdown |
|
||||
| 验证命令 | `pytest tests/test_file_summary_views.py -k download` |
|
||||
| Codex 执行提示 | 请实现导出文件下载接口,下载权限必须沿 export -> batch -> conversation -> user 校验。 |
|
||||
|
||||
### FS-P5-004 完成报告 Skill 与工作流衔接
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 后端 / 工作流 |
|
||||
| 前置任务 | FS-P5-003 |
|
||||
| 涉及文件 | `review_agent/file_summary/workflow.py`、`review_agent/file_summary/services/report.py` |
|
||||
| 目标 | 工作流完成后写入助手消息,展示 Markdown 简表和真实下载链接 |
|
||||
| 开发步骤 | 1. 报告和 Excel 导出完成后生成下载 URL;2. 替换对话简表中的下载链接;3. 创建 assistant Message;4. 标记 batch success;5. 发送 workflow_completed 事件 |
|
||||
| 验收标准 | 工作流完成后对话中出现 Markdown 简表;下载链接可点击;批次状态成功 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_workflow.py -k report` |
|
||||
| Codex 执行提示 | 请把 Markdown 报告、Excel 导出和工作流完成逻辑串起来,完成后向当前对话写入助手消息。 |
|
||||
|
||||
### P5 阶段提交规则
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 阶段验证 | `pytest tests/test_file_summary_report.py tests/test_file_summary_views.py -k download tests/test_file_summary_workflow.py -k report` |
|
||||
| 提交动作 | 调用 `git-commit-summary` 生成提交摘要并提交 |
|
||||
| Codex 执行提示 | P5 验证通过后,请调用 `git-commit-summary` 分析本阶段变更,并按其输出提交到当前开发分支。 |
|
||||
|
||||
---
|
||||
|
||||
## 十一、P6 前端三栏与工作流卡片
|
||||
|
||||
### FS-P6-001 改造页面为三栏布局
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 前端 / 布局 |
|
||||
| 前置任务 | P5 |
|
||||
| 涉及文件 | `templates/home.html`、实际静态 CSS 文件 |
|
||||
| 目标 | 在现有对话页增加右侧第三栏,上半部分上传区,下半部分工作流卡片 |
|
||||
| 开发步骤 | 1. 确认真实静态样式文件路径;2. 调整 workspace 结构;3. 增加 `workflow-panel`;4. 增加 `upload-dropzone`;5. 增加 `workflow-card-list`;6. 保证桌面和移动端不遮挡 |
|
||||
| 验收标准 | 页面显示左侧会话、中间聊天、右侧上传/工作流三栏;移动端布局可用 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_e2e.py -k layout` 或 Playwright 对应命令 |
|
||||
| Codex 执行提示 | 请把审核智能体页面改造成三栏布局,右侧上半部分为拖拽上传区,下半部分为工作流卡片列表,并保持现有聊天能力可用。 |
|
||||
|
||||
### FS-P6-002 实现前端附件上传交互
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 前端 / 上传 |
|
||||
| 前置任务 | FS-P6-001 |
|
||||
| 涉及文件 | `static/js/app.js`、`templates/home.html`、实际静态 CSS 文件 |
|
||||
| 目标 | 支持拖拽或选择多个文件上传,上传成功后展示附件列表 |
|
||||
| 开发步骤 | 1. 绑定 dropzone;2. 支持点击选择文件;3. 调用附件上传 API;4. 展示文件名、版本、大小和状态;5. 上传失败展示错误 |
|
||||
| 验收标准 | 上传即存储;前端展示当前对话附件;切换对话不串附件 |
|
||||
| 验证命令 | Playwright 上传测试 |
|
||||
| Codex 执行提示 | 请实现右侧上传区前端交互,支持拖拽和选择多个文件,调用附件上传接口并展示当前对话附件列表。 |
|
||||
|
||||
### FS-P6-003 实现工作流卡片与 SSE 更新
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 前端 / 工作流 |
|
||||
| 前置任务 | FS-P6-002 |
|
||||
| 涉及文件 | `static/js/app.js`、实际静态 CSS 文件 |
|
||||
| 目标 | 在发送提示词触发工作流后创建卡片,并根据 SSE 更新节点状态 |
|
||||
| 开发步骤 | 1. 解析 chat stream 中的 workflow meta;2. 创建 workflow card;3. 连接 batch events SSE;4. 更新节点 pending/running/retrying/success/failed/skipped;5. workflow_completed 后更新完成状态;6. 页面刷新后通过状态接口恢复 |
|
||||
| 验收标准 | 工作流节点实时更新;刷新页面可恢复;失败状态可见 |
|
||||
| 验证命令 | Playwright 工作流卡片测试 |
|
||||
| Codex 执行提示 | 请实现工作流卡片前端逻辑,接收 workflow meta 后连接事件流,实时更新上传、解压、扫描、解析、识别、输出、完成等节点状态。 |
|
||||
|
||||
### FS-P6-004 实现 Markdown 安全渲染
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 前端 / 渲染 |
|
||||
| 前置任务 | FS-P6-003 |
|
||||
| 涉及文件 | `templates/home.html`、`static/js/app.js`、静态依赖文件或 CDN 引入 |
|
||||
| 目标 | 让助手消息支持 Markdown 表格和下载链接渲染 |
|
||||
| 开发步骤 | 1. 引入 `marked + DOMPurify`;2. 普通用户消息保持 escape;3. 助手消息使用安全 Markdown 渲染;4. 历史消息渲染兼容;5. 下载链接可点击 |
|
||||
| 验收标准 | Markdown 表格渲染为 HTML table;链接渲染为 a 标签;无明显 XSS 风险 |
|
||||
| 验证命令 | Playwright Markdown 渲染测试 |
|
||||
| Codex 执行提示 | 请引入 marked + DOMPurify 实现助手消息安全 Markdown 渲染,确保文件汇总结果表格和下载链接正常显示。 |
|
||||
|
||||
### FS-P6-005 实现 Playwright 端到端测试
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 前端 / E2E |
|
||||
| 前置任务 | FS-P6-004 |
|
||||
| 涉及文件 | Playwright 测试文件、测试配置 |
|
||||
| 目标 | 使用真实浏览器覆盖上传、触发、卡片、渲染、下载和恢复 |
|
||||
| 开发步骤 | 1. 创建测试用户;2. 登录系统;3. 打开审核智能体页面;4. 上传动态生成的测试文件;5. 发送“自动汇总文件目录与页数”;6. 等待工作流卡片完成;7. 验证 Markdown table 和下载链接;8. 刷新后验证卡片恢复;9. 验证越权访问失败 |
|
||||
| 验收标准 | Playwright 端到端测试通过;关键页面截图可生成;失败时能定位到具体断言 |
|
||||
| 验证命令 | Playwright 对应执行命令 |
|
||||
| Codex 执行提示 | 请使用 Playwright 增加真实浏览器端到端测试,从登录、上传、发送提示词一直验证到报告渲染、下载和刷新恢复。 |
|
||||
|
||||
### P6 阶段提交规则
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 阶段验证 | Playwright 端到端测试 + 相关后端接口测试 |
|
||||
| 提交动作 | 调用 `git-commit-summary` 生成提交摘要并提交 |
|
||||
| Codex 执行提示 | P6 验证通过后,请调用 `git-commit-summary` 分析本阶段变更,并按其输出提交到当前开发分支。 |
|
||||
|
||||
---
|
||||
|
||||
## 十二、P7 测试、部署与总体验收
|
||||
|
||||
### FS-P7-001 补齐后端测试矩阵
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 测试 / 后端 |
|
||||
| 前置任务 | P6 |
|
||||
| 涉及文件 | `tests/test_file_summary_*.py` |
|
||||
| 目标 | 覆盖单元、接口、工作流集成和权限隔离 |
|
||||
| 开发步骤 | 1. 覆盖触发词;2. 覆盖附件版本;3. 覆盖解压安全;4. 覆盖文件扫描;5. 覆盖页数统计;6. 覆盖报告导出;7. 覆盖下载权限;8. 覆盖完整工作流 |
|
||||
| 验收标准 | 后端文件汇总测试全部通过;失败场景覆盖充分 |
|
||||
| 验证命令 | `pytest tests/test_file_summary_*.py` |
|
||||
| Codex 执行提示 | 请补齐文件汇总后端测试矩阵,覆盖单元、接口、工作流集成和权限隔离。 |
|
||||
|
||||
### FS-P7-002 补充部署与 Docker 说明
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 部署 / 文档 |
|
||||
| 前置任务 | FS-P7-001 |
|
||||
| 涉及文件 | README 或部署说明文档 |
|
||||
| 目标 | 写明生产或 Docker 部署时的依赖安装和验证方式 |
|
||||
| 开发步骤 | 1. 写明 Python 依赖安装;2. 写明 7z/p7zip 安装;3. 写明 rar/7z 验证命令;4. 写明 LibreOffice 非必需、仅未来增强使用;5. 写明 media 文件存储目录 |
|
||||
| 验收标准 | 部署说明可指导在 Docker 中启用 rar/7z 解压;依赖边界清楚 |
|
||||
| 验证命令 | `python manage.py check` |
|
||||
| Codex 执行提示 | 请补充部署说明,明确 Docker 环境需要安装 7z/p7zip 支持 rar/7z,LibreOffice 不是必需依赖。 |
|
||||
|
||||
### FS-P7-003 执行总体验收
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | 验收 / 流水线 |
|
||||
| 前置任务 | FS-P7-002 |
|
||||
| 涉及文件 | 无固定文件 |
|
||||
| 目标 | 运行全部测试和端到端验证,确认功能完整 |
|
||||
| 开发步骤 | 1. 运行 Django check;2. 运行全量 pytest;3. 运行 Playwright E2E;4. 手工或自动验证下载文件可打开;5. 检查数据库记录;6. 检查 git status |
|
||||
| 验收标准 | 总体验收标准全部满足;没有未解释的失败测试;没有意外文件变更 |
|
||||
| 验证命令 | `python manage.py check`; `pytest`; Playwright 对应命令 |
|
||||
| Codex 执行提示 | 请执行文件汇总功能总体验收,运行后端全量测试和 Playwright 端到端测试,确认所有验收标准已满足。 |
|
||||
|
||||
### FS-P7-004 合并回 V2 分支
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 任务类型 | Git / 收尾 |
|
||||
| 前置任务 | FS-P7-003 |
|
||||
| 涉及文件 | 无固定文件 |
|
||||
| 目标 | 将开发分支合并回 `V2`,并在合并后再次运行总体验收 |
|
||||
| 开发步骤 | 1. P7 通过后调用 `git-commit-summary` 提交阶段变更;2. 切换到 `V2`;3. 合并开发分支;4. 解决冲突但不得覆盖用户变更;5. 合并后运行总体验收;6. 保留最终 git status |
|
||||
| 验收标准 | 开发分支成功合并到 `V2`;合并后测试通过;本地 Git 历史包含阶段提交 |
|
||||
| 验证命令 | `git branch --show-current`; `git status --short`; `python manage.py check`; `pytest`; Playwright 对应命令 |
|
||||
| Codex 执行提示 | 请在全部阶段完成后提交 P7 变更,切回 `V2` 并合并开发分支,合并后重新运行总体验收。 |
|
||||
|
||||
### P7 阶段提交规则
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 阶段验证 | `python manage.py check`; `pytest`; Playwright 端到端测试 |
|
||||
| 提交动作 | 调用 `git-commit-summary` 生成提交摘要并提交 |
|
||||
| 合并动作 | 所有阶段提交完成后合并回 `V2` |
|
||||
| Codex 执行提示 | P7 验证通过后,请调用 `git-commit-summary` 提交本阶段变更,然后合并回 `V2` 并再次总体验收。 |
|
||||
|
||||
---
|
||||
|
||||
## 十三、测试分层要求
|
||||
|
||||
| 层级 | 验证内容 | 建议文件 |
|
||||
| --- | --- | --- |
|
||||
| 单元测试 | 触发词、附件版本、解压安全、文件扫描、页数统计、报告生成 | `tests/test_file_summary_*.py` |
|
||||
| 接口测试 | 上传接口、批次状态接口、事件接口、下载接口、权限隔离 | `tests/test_file_summary_views.py` |
|
||||
| 工作流集成测试 | 上传附件后发送提示词,完整执行到生成 Markdown/Excel | `tests/test_file_summary_workflow.py` |
|
||||
| Playwright E2E | 登录、上传、触发、卡片更新、Markdown 渲染、下载、刷新恢复 | Playwright 测试文件 |
|
||||
| 部署验证 | requirements 安装成功,Docker 中 7z/p7zip 可用,rar/7z 解压可跑通 | 部署说明和验证命令 |
|
||||
|
||||
说明:测试样例文件不单独拆任务,可在测试代码中动态生成临时 pdf、docx、xlsx、pptx、zip、7z、rar、损坏文件或不可读文件。
|
||||
|
||||
---
|
||||
|
||||
## 十四、Codex 自动化执行规则
|
||||
|
||||
| 规则 | 内容 |
|
||||
| --- | --- |
|
||||
| 顺序执行 | 必须从 P0 到 P7 顺序执行,不得跳阶段 |
|
||||
| 当前阶段优先 | 某阶段测试失败时,必须先修复当前阶段,不得继续后续阶段 |
|
||||
| 连续失败处理 | 同一阶段连续 3 次失败时,记录阻塞原因、已尝试方案和下一步建议 |
|
||||
| 每任务验证 | 每个任务完成后运行对应验证命令或说明无法运行原因 |
|
||||
| 每阶段提交 | 每个阶段全部任务完成并验证通过后,调用 `git-commit-summary` 后本地提交 |
|
||||
| 前端强验证 | P6 完成后必须运行 Playwright 端到端测试和截图/断言验证 |
|
||||
| 不覆盖变更 | 不得回滚或覆盖用户已有未提交变更 |
|
||||
| 合并收尾 | 全部完成后必须合并回 `V2` 并再次总体验收 |
|
||||
|
||||
---
|
||||
|
||||
## 十五、推荐一键执行提示词
|
||||
|
||||
后续可直接对 Codex 输入:
|
||||
|
||||
```text
|
||||
请按 docs/开发计划/1.自动汇总.md 执行,从 V2 创建 codex/YYYYMMDD-自动汇总文件目录页数 分支,按 P0 到 P7 顺序开发、验证和阶段提交。每个阶段完成后调用 git-commit-summary 生成提交摘要并本地提交。全部完成后合并回 V2,并重新运行总体验收。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十六、待执行前检查清单
|
||||
|
||||
| 检查项 | 状态 |
|
||||
| --- | --- |
|
||||
| 需求分析、功能设计、详细设计、数据库设计均已存在 | 待执行时确认 |
|
||||
| 当前分支是否为 `V2` | 待执行时确认 |
|
||||
| 是否存在用户未提交变更 | 待执行时确认 |
|
||||
| Python 依赖是否可安装 | 待执行时确认 |
|
||||
| Playwright 或对应 MCP/Skill 是否可用 | 待执行时确认 |
|
||||
| 执行机器是否提供 `git-commit-summary` skill | 待执行时确认 |
|
||||
| Docker 环境是否可安装 7z/p7zip | 待执行时确认 |
|
||||
651
docs/数据库设计/1.自动汇总.md
Normal file
651
docs/数据库设计/1.自动汇总.md
Normal file
@@ -0,0 +1,651 @@
|
||||
# 自动汇总文件夹文件目录与页数流程数据库设计
|
||||
|
||||
## 文档信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 需求分析文档 | docs/需求分析/1.自动汇总.md |
|
||||
| 功能设计文档 | docs/功能设计/1.自动汇总.md |
|
||||
| 详细设计文档 | docs/详细设计/1.自动汇总.md |
|
||||
| 数据库类型 | SQLite / Django ORM |
|
||||
| 表名前缀 | ra_ |
|
||||
| 设计日期 | 2026-06-05 |
|
||||
| 设计版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 一、设计原则
|
||||
|
||||
| 原则 | 说明 |
|
||||
| --- | --- |
|
||||
| ORM 优先 | 当前项目使用 Django,实际落地以 Django Model 与 migration 为准 |
|
||||
| SQLite 兼容 | 字段类型、索引和约束优先保证 SQLite 可运行 |
|
||||
| 短表名前缀 | 使用 `ra_` 作为审核智能体文件汇总相关表前缀 |
|
||||
| 不建枚举表 | 状态枚举使用 Django `TextChoices`,数据库存储字符串 |
|
||||
| 对话隔离 | 所有附件、批次、导出文件均可追溯到 Conversation 和 User |
|
||||
| 多版本附件 | 同一对话同名附件允许多次上传,以版本号区分 |
|
||||
| 批次固化 | 每次汇总批次通过中间表绑定本次使用的附件版本,防止串文件 |
|
||||
| 事件留痕 | 保留 WorkflowEvent,用于 SSE 断线续传、页面刷新恢复和排查问题 |
|
||||
|
||||
---
|
||||
|
||||
## 二、ER 图
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
AUTH_USER ||--o{ CONVERSATION : owns
|
||||
CONVERSATION ||--o{ MESSAGE : contains
|
||||
CONVERSATION ||--o{ RA_FILE_ATTACHMENT : has
|
||||
CONVERSATION ||--o{ RA_FILE_SUMMARY_BATCH : has
|
||||
AUTH_USER ||--o{ RA_FILE_ATTACHMENT : uploads
|
||||
AUTH_USER ||--o{ RA_FILE_SUMMARY_BATCH : runs
|
||||
MESSAGE ||--o{ RA_FILE_SUMMARY_BATCH : triggers
|
||||
RA_FILE_SUMMARY_BATCH ||--o{ RA_FILE_SUMMARY_BATCH_ATTACHMENT : binds
|
||||
RA_FILE_ATTACHMENT ||--o{ RA_FILE_SUMMARY_BATCH_ATTACHMENT : selected
|
||||
RA_FILE_SUMMARY_BATCH ||--o{ RA_FILE_SUMMARY_ITEM : produces
|
||||
RA_FILE_SUMMARY_BATCH ||--o{ RA_WORKFLOW_NODE_RUN : tracks
|
||||
RA_FILE_SUMMARY_BATCH ||--o{ RA_WORKFLOW_EVENT : emits
|
||||
RA_FILE_SUMMARY_BATCH ||--o{ RA_EXPORTED_SUMMARY_FILE : exports
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、表结构设计
|
||||
|
||||
### 3.1 ra_file_attachment
|
||||
|
||||
用户在对话右侧上传区上传后的附件记录。上传即存储,不代表已启动工作流。
|
||||
|
||||
| 字段名 | Django 类型 | SQLite 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| id | BigAutoField | integer | 是 | 主键 |
|
||||
| conversation_id | ForeignKey | bigint | 是 | 绑定对话 |
|
||||
| user_id | ForeignKey | bigint | 是 | 上传用户 |
|
||||
| original_name | CharField(255) | varchar(255) | 是 | 原始文件名 |
|
||||
| version_no | PositiveIntegerField | integer | 是 | 同一对话同名文件版本号,从 1 递增 |
|
||||
| is_active | BooleanField | bool | 是 | 是否当前默认版本 |
|
||||
| storage_path | CharField(500) | varchar(500) | 是 | 文件存储路径 |
|
||||
| file_size | BigIntegerField | bigint | 是 | 文件大小 |
|
||||
| content_type | CharField(120) | varchar(120) | 否 | MIME 类型 |
|
||||
| upload_status | CharField(20) | varchar(20) | 是 | uploaded、bound、deleted |
|
||||
| created_at | DateTimeField | datetime | 是 | 上传时间 |
|
||||
|
||||
唯一约束:
|
||||
|
||||
| 约束名 | 字段 |
|
||||
| --- | --- |
|
||||
| uq_ra_attachment_conv_name_version | conversation_id, original_name, version_no |
|
||||
|
||||
索引:
|
||||
|
||||
| 索引名 | 字段 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| idx_ra_attachment_conv_created | conversation_id, created_at | 查询对话附件列表 |
|
||||
| idx_ra_attachment_user_created | user_id, created_at | 查询用户上传记录 |
|
||||
| idx_ra_attachment_active | conversation_id, original_name, is_active | 查询当前默认版本 |
|
||||
|
||||
---
|
||||
|
||||
### 3.2 ra_file_summary_batch
|
||||
|
||||
一次文件目录与页数汇总工作流批次。
|
||||
|
||||
| 字段名 | Django 类型 | SQLite 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| id | BigAutoField | integer | 是 | 主键 |
|
||||
| conversation_id | ForeignKey | bigint | 是 | 绑定对话 |
|
||||
| user_id | ForeignKey | bigint | 是 | 执行用户 |
|
||||
| trigger_message_id | ForeignKey | bigint | 否 | 触发工作流的用户消息 |
|
||||
| batch_no | CharField(64) | varchar(64) | 是 | 批次编号,唯一 |
|
||||
| product_name | CharField(200) | varchar(200) | 否 | 识别出的产品名称 |
|
||||
| status | CharField(20) | varchar(20) | 是 | pending、running、success、failed |
|
||||
| total_files | IntegerField | integer | 是 | 文件总数 |
|
||||
| supported_files | IntegerField | integer | 是 | 支持统计文件数 |
|
||||
| success_files | IntegerField | integer | 是 | 统计成功文件数 |
|
||||
| failed_files | IntegerField | integer | 是 | 统计失败文件数 |
|
||||
| unsupported_files | IntegerField | integer | 是 | 不支持文件数 |
|
||||
| uncertain_files | IntegerField | integer | 是 | 页数不可确定文件数 |
|
||||
| total_pages | IntegerField | integer | 是 | 总页数 |
|
||||
| work_dir | CharField(500) | varchar(500) | 否 | 批次工作目录 |
|
||||
| error_message | TextField | text | 否 | 批次异常说明 |
|
||||
| created_at | DateTimeField | datetime | 是 | 创建时间 |
|
||||
| started_at | DateTimeField | datetime | 否 | 开始时间 |
|
||||
| finished_at | DateTimeField | datetime | 否 | 完成时间 |
|
||||
|
||||
唯一约束:
|
||||
|
||||
| 约束名 | 字段 |
|
||||
| --- | --- |
|
||||
| uq_ra_batch_no | batch_no |
|
||||
|
||||
索引:
|
||||
|
||||
| 索引名 | 字段 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| idx_ra_batch_conv_created | conversation_id, created_at | 查询对话下批次 |
|
||||
| idx_ra_batch_user_created | user_id, created_at | 查询用户批次 |
|
||||
| idx_ra_batch_status | status, created_at | 查询执行中或失败批次 |
|
||||
|
||||
---
|
||||
|
||||
### 3.3 ra_file_summary_batch_attachment
|
||||
|
||||
批次与附件版本绑定表。一个对话可多次上传同名附件,批次必须固化本次使用的附件版本。
|
||||
|
||||
| 字段名 | Django 类型 | SQLite 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| id | BigAutoField | integer | 是 | 主键 |
|
||||
| batch_id | ForeignKey | bigint | 是 | 汇总批次 |
|
||||
| attachment_id | ForeignKey | bigint | 是 | 本次使用的附件版本 |
|
||||
| source_role | CharField(20) | varchar(20) | 是 | archive、multi_file |
|
||||
| created_at | DateTimeField | datetime | 是 | 绑定时间 |
|
||||
|
||||
唯一约束:
|
||||
|
||||
| 约束名 | 字段 |
|
||||
| --- | --- |
|
||||
| uq_ra_batch_attachment | batch_id, attachment_id |
|
||||
|
||||
索引:
|
||||
|
||||
| 索引名 | 字段 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| idx_ra_batch_attachment_batch | batch_id, created_at | 查询批次附件 |
|
||||
| idx_ra_batch_attachment_attachment | attachment_id | 查询附件被哪些批次使用 |
|
||||
|
||||
---
|
||||
|
||||
### 3.4 ra_file_summary_item
|
||||
|
||||
文件明细表,记录扫描到的每个文件及页数统计结果。
|
||||
|
||||
| 字段名 | Django 类型 | SQLite 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| id | BigAutoField | integer | 是 | 主键 |
|
||||
| batch_id | ForeignKey | bigint | 是 | 所属批次 |
|
||||
| file_index | PositiveIntegerField | integer | 是 | 文件序号 |
|
||||
| directory_level | CharField(300) | varchar(300) | 否 | 目录层级 |
|
||||
| file_name | CharField(255) | varchar(255) | 是 | 文件名 |
|
||||
| file_type | CharField(20) | varchar(20) | 是 | 文件类型 |
|
||||
| relative_path | CharField(500) | varchar(500) | 是 | 相对路径,用于展示和导出 |
|
||||
| storage_path | CharField(500) | varchar(500) | 是 | 实际处理路径 |
|
||||
| page_count | IntegerField | integer | 否 | 页数,失败或不可确定时为空 |
|
||||
| statistics_status | CharField(20) | varchar(20) | 是 | success、failed、unsupported、uncertain、skipped |
|
||||
| retry_count | PositiveIntegerField | integer | 是 | 页数统计重试次数 |
|
||||
| error_message | TextField | text | 否 | 异常说明 |
|
||||
| created_at | DateTimeField | datetime | 是 | 创建时间 |
|
||||
| updated_at | DateTimeField | datetime | 是 | 更新时间 |
|
||||
|
||||
唯一约束:
|
||||
|
||||
| 约束名 | 字段 |
|
||||
| --- | --- |
|
||||
| uq_ra_item_batch_relative_path | batch_id, relative_path |
|
||||
|
||||
索引:
|
||||
|
||||
| 索引名 | 字段 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| idx_ra_item_batch_index | batch_id, file_index | 按序展示文件明细 |
|
||||
| idx_ra_item_batch_status | batch_id, statistics_status | 查询失败/不可确定文件 |
|
||||
| idx_ra_item_batch_type | batch_id, file_type | 按类型统计 |
|
||||
|
||||
---
|
||||
|
||||
### 3.5 ra_workflow_node_run
|
||||
|
||||
工作流节点运行状态表,用于右侧工作流卡片状态恢复。
|
||||
|
||||
| 字段名 | Django 类型 | SQLite 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| id | BigAutoField | integer | 是 | 主键 |
|
||||
| batch_id | ForeignKey | bigint | 是 | 所属批次 |
|
||||
| node_code | CharField(40) | varchar(40) | 是 | 节点编码 |
|
||||
| node_name | CharField(80) | varchar(80) | 是 | 节点名称 |
|
||||
| status | CharField(20) | varchar(20) | 是 | pending、running、retrying、success、failed、skipped |
|
||||
| progress | PositiveIntegerField | integer | 是 | 进度百分比,0-100 |
|
||||
| message | TextField | text | 否 | 节点提示 |
|
||||
| started_at | DateTimeField | datetime | 否 | 开始时间 |
|
||||
| finished_at | DateTimeField | datetime | 否 | 完成时间 |
|
||||
|
||||
唯一约束:
|
||||
|
||||
| 约束名 | 字段 |
|
||||
| --- | --- |
|
||||
| uq_ra_node_batch_code | batch_id, node_code |
|
||||
|
||||
索引:
|
||||
|
||||
| 索引名 | 字段 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| idx_ra_node_batch_status | batch_id, status | 查询批次节点状态 |
|
||||
|
||||
---
|
||||
|
||||
### 3.6 ra_workflow_event
|
||||
|
||||
工作流事件表,用于 SSE 事件持久化、断线续传和调试。
|
||||
|
||||
| 字段名 | Django 类型 | SQLite 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| id | BigAutoField | integer | 是 | 主键,同时可作为 event_id |
|
||||
| batch_id | ForeignKey | bigint | 是 | 所属批次 |
|
||||
| event_type | CharField(40) | varchar(40) | 是 | workflow_started、node_progress 等 |
|
||||
| payload | JSONField | text/json | 是 | 事件载荷 |
|
||||
| created_at | DateTimeField | datetime | 是 | 事件时间 |
|
||||
|
||||
索引:
|
||||
|
||||
| 索引名 | 字段 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| idx_ra_event_batch_id | batch_id, id | SSE after 续传 |
|
||||
| idx_ra_event_batch_created | batch_id, created_at | 按时间查询事件 |
|
||||
|
||||
---
|
||||
|
||||
### 3.7 ra_exported_summary_file
|
||||
|
||||
导出文件记录表。下载链接运行时根据 export_id 生成。
|
||||
|
||||
| 字段名 | Django 类型 | SQLite 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| id | BigAutoField | integer | 是 | 主键 |
|
||||
| batch_id | ForeignKey | bigint | 是 | 所属批次 |
|
||||
| export_type | CharField(20) | varchar(20) | 是 | markdown、excel |
|
||||
| file_name | CharField(255) | varchar(255) | 是 | 导出文件名 |
|
||||
| storage_path | CharField(500) | varchar(500) | 是 | 保存路径 |
|
||||
| status | CharField(20) | varchar(20) | 是 | success、failed |
|
||||
| error_message | TextField | text | 否 | 导出异常说明 |
|
||||
| created_at | DateTimeField | datetime | 是 | 生成时间 |
|
||||
|
||||
索引:
|
||||
|
||||
| 索引名 | 字段 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| idx_ra_export_batch_type | batch_id, export_type | 查询批次导出文件 |
|
||||
| idx_ra_export_batch_created | batch_id, created_at | 按生成时间查询 |
|
||||
|
||||
---
|
||||
|
||||
## 四、枚举设计
|
||||
|
||||
本功能不建立枚举表,枚举通过 Django `TextChoices` 定义,数据库存储字符串。
|
||||
|
||||
### 4.1 附件状态 upload_status
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| uploaded | 已上传 | 上传完成,尚未绑定批次 |
|
||||
| bound | 已绑定 | 已被某个批次使用 |
|
||||
| deleted | 已删除 | 用户逻辑删除,不再作为默认候选 |
|
||||
|
||||
### 4.2 批次状态 batch.status
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| pending | 待执行 | 批次已创建 |
|
||||
| running | 执行中 | 后台工作流运行中 |
|
||||
| success | 成功 | 工作流完成 |
|
||||
| failed | 失败 | 批次级失败 |
|
||||
|
||||
### 4.3 节点状态 node.status
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| pending | 等待中 | 节点未开始 |
|
||||
| running | 执行中 | 节点正在执行 |
|
||||
| retrying | 重试中 | 单文件解析失败后重试 |
|
||||
| success | 成功 | 节点执行成功 |
|
||||
| failed | 失败 | 节点失败 |
|
||||
| skipped | 跳过 | 当前批次不需要执行该节点 |
|
||||
|
||||
### 4.4 文件统计状态 statistics_status
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| success | 成功 | 页数统计成功 |
|
||||
| failed | 失败 | 重试后仍失败 |
|
||||
| unsupported | 不支持 | 文件类型不在支持范围 |
|
||||
| uncertain | 不确定 | 文件可读,但无可靠页数元数据 |
|
||||
| skipped | 跳过 | 空文件、隐藏文件或规则跳过 |
|
||||
|
||||
### 4.5 导出类型 export_type
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| markdown | Markdown | Markdown 汇总报告 |
|
||||
| excel | Excel | Excel 明细文件 |
|
||||
|
||||
### 4.6 导出状态 export.status
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| success | 成功 | 导出文件生成成功 |
|
||||
| failed | 失败 | 导出失败 |
|
||||
|
||||
---
|
||||
|
||||
## 五、关系与业务规则
|
||||
|
||||
### 5.1 对话与附件
|
||||
|
||||
```text
|
||||
Conversation 1:N ra_file_attachment
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
| 规则 | 说明 |
|
||||
| --- | --- |
|
||||
| 上传即存储 | 用户上传后立即创建 FileAttachment |
|
||||
| 对话隔离 | 附件只能被同一 Conversation 下的批次使用 |
|
||||
| 多版本 | 同一 conversation + original_name 可存在多个 version_no |
|
||||
| 默认版本 | is_active=true 的记录作为默认候选版本 |
|
||||
| 逻辑删除 | 删除附件时设置 upload_status=deleted,不立即物理删除 |
|
||||
|
||||
### 5.2 对话与批次
|
||||
|
||||
```text
|
||||
Conversation 1:N ra_file_summary_batch
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
| 规则 | 说明 |
|
||||
| --- | --- |
|
||||
| 多次汇总 | 同一对话允许多次触发自动汇总 |
|
||||
| 提示词触发 | 批次由用户消息触发,可关联 trigger_message_id |
|
||||
| 批次固化 | 批次启动时固化本次使用的附件版本 |
|
||||
|
||||
### 5.3 批次与附件版本
|
||||
|
||||
```text
|
||||
ra_file_summary_batch N:M ra_file_attachment
|
||||
```
|
||||
|
||||
通过 `ra_file_summary_batch_attachment` 实现。
|
||||
|
||||
规则:
|
||||
|
||||
| 规则 | 说明 |
|
||||
| --- | --- |
|
||||
| 不串文件 | 工作流只能读取中间表绑定的附件 |
|
||||
| 保留历史 | 即使附件后续上传新版本,历史批次仍指向旧版本 |
|
||||
| 版本选择 | 用户未选择时默认使用同名文件的最新 active 版本 |
|
||||
|
||||
### 5.4 批次与文件明细
|
||||
|
||||
```text
|
||||
ra_file_summary_batch 1:N ra_file_summary_item
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
| 规则 | 说明 |
|
||||
| --- | --- |
|
||||
| 相对路径唯一 | 同一批次下 relative_path 唯一 |
|
||||
| 处理路径保留 | relative_path 用于展示,storage_path 用于后台处理 |
|
||||
| 单文件失败不阻断 | 文件解析失败记录 failed,批次继续处理其他文件 |
|
||||
|
||||
---
|
||||
|
||||
## 六、索引设计汇总
|
||||
|
||||
| 表 | 索引/约束 | 字段 | 用途 |
|
||||
| --- | --- | --- | --- |
|
||||
| ra_file_attachment | uq_ra_attachment_conv_name_version | conversation_id, original_name, version_no | 同名附件版本唯一 |
|
||||
| ra_file_attachment | idx_ra_attachment_conv_created | conversation_id, created_at | 对话附件列表 |
|
||||
| ra_file_attachment | idx_ra_attachment_user_created | user_id, created_at | 用户上传记录 |
|
||||
| ra_file_attachment | idx_ra_attachment_active | conversation_id, original_name, is_active | 默认版本查询 |
|
||||
| ra_file_summary_batch | uq_ra_batch_no | batch_no | 批次编号唯一 |
|
||||
| ra_file_summary_batch | idx_ra_batch_conv_created | conversation_id, created_at | 对话批次列表 |
|
||||
| ra_file_summary_batch | idx_ra_batch_user_created | user_id, created_at | 用户批次列表 |
|
||||
| ra_file_summary_batch | idx_ra_batch_status | status, created_at | 查询运行中/失败批次 |
|
||||
| ra_file_summary_batch_attachment | uq_ra_batch_attachment | batch_id, attachment_id | 批次附件唯一 |
|
||||
| ra_file_summary_item | uq_ra_item_batch_relative_path | batch_id, relative_path | 批次内文件唯一 |
|
||||
| ra_file_summary_item | idx_ra_item_batch_index | batch_id, file_index | 文件明细排序 |
|
||||
| ra_file_summary_item | idx_ra_item_batch_status | batch_id, statistics_status | 查询异常文件 |
|
||||
| ra_workflow_node_run | uq_ra_node_batch_code | batch_id, node_code | 每批次每节点唯一 |
|
||||
| ra_workflow_event | idx_ra_event_batch_id | batch_id, id | SSE 断点续传 |
|
||||
| ra_exported_summary_file | idx_ra_export_batch_type | batch_id, export_type | 查询导出文件 |
|
||||
|
||||
---
|
||||
|
||||
## 七、SQLite 参考 DDL
|
||||
|
||||
> 说明:以下 DDL 为设计参考,实际落地以 Django migration 为准。
|
||||
|
||||
```sql
|
||||
CREATE TABLE ra_file_attachment (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
conversation_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
original_name VARCHAR(255) NOT NULL,
|
||||
version_no INTEGER NOT NULL DEFAULT 1,
|
||||
is_active BOOLEAN NOT NULL DEFAULT 1,
|
||||
storage_path VARCHAR(500) NOT NULL,
|
||||
file_size BIGINT NOT NULL DEFAULT 0,
|
||||
content_type VARCHAR(120) NOT NULL DEFAULT '',
|
||||
upload_status VARCHAR(20) NOT NULL DEFAULT 'uploaded',
|
||||
created_at DATETIME NOT NULL,
|
||||
UNIQUE (conversation_id, original_name, version_no)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ra_attachment_conv_created
|
||||
ON ra_file_attachment (conversation_id, created_at);
|
||||
|
||||
CREATE INDEX idx_ra_attachment_user_created
|
||||
ON ra_file_attachment (user_id, created_at);
|
||||
|
||||
CREATE INDEX idx_ra_attachment_active
|
||||
ON ra_file_attachment (conversation_id, original_name, is_active);
|
||||
```
|
||||
|
||||
```sql
|
||||
CREATE TABLE ra_file_summary_batch (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
conversation_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
trigger_message_id BIGINT NULL,
|
||||
batch_no VARCHAR(64) NOT NULL UNIQUE,
|
||||
product_name VARCHAR(200) NOT NULL DEFAULT '',
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
total_files INTEGER NOT NULL DEFAULT 0,
|
||||
supported_files INTEGER NOT NULL DEFAULT 0,
|
||||
success_files INTEGER NOT NULL DEFAULT 0,
|
||||
failed_files INTEGER NOT NULL DEFAULT 0,
|
||||
unsupported_files INTEGER NOT NULL DEFAULT 0,
|
||||
uncertain_files INTEGER NOT NULL DEFAULT 0,
|
||||
total_pages INTEGER NOT NULL DEFAULT 0,
|
||||
work_dir VARCHAR(500) NOT NULL DEFAULT '',
|
||||
error_message TEXT NOT NULL DEFAULT '',
|
||||
created_at DATETIME NOT NULL,
|
||||
started_at DATETIME NULL,
|
||||
finished_at DATETIME NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ra_batch_conv_created
|
||||
ON ra_file_summary_batch (conversation_id, created_at);
|
||||
|
||||
CREATE INDEX idx_ra_batch_user_created
|
||||
ON ra_file_summary_batch (user_id, created_at);
|
||||
|
||||
CREATE INDEX idx_ra_batch_status
|
||||
ON ra_file_summary_batch (status, created_at);
|
||||
```
|
||||
|
||||
```sql
|
||||
CREATE TABLE ra_file_summary_batch_attachment (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
batch_id BIGINT NOT NULL,
|
||||
attachment_id BIGINT NOT NULL,
|
||||
source_role VARCHAR(20) NOT NULL DEFAULT 'multi_file',
|
||||
created_at DATETIME NOT NULL,
|
||||
UNIQUE (batch_id, attachment_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ra_batch_attachment_batch
|
||||
ON ra_file_summary_batch_attachment (batch_id, created_at);
|
||||
|
||||
CREATE INDEX idx_ra_batch_attachment_attachment
|
||||
ON ra_file_summary_batch_attachment (attachment_id);
|
||||
```
|
||||
|
||||
```sql
|
||||
CREATE TABLE ra_file_summary_item (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
batch_id BIGINT NOT NULL,
|
||||
file_index INTEGER NOT NULL,
|
||||
directory_level VARCHAR(300) NOT NULL DEFAULT '',
|
||||
file_name VARCHAR(255) NOT NULL,
|
||||
file_type VARCHAR(20) NOT NULL,
|
||||
relative_path VARCHAR(500) NOT NULL,
|
||||
storage_path VARCHAR(500) NOT NULL,
|
||||
page_count INTEGER NULL,
|
||||
statistics_status VARCHAR(20) NOT NULL DEFAULT 'skipped',
|
||||
retry_count INTEGER NOT NULL DEFAULT 0,
|
||||
error_message TEXT NOT NULL DEFAULT '',
|
||||
created_at DATETIME NOT NULL,
|
||||
updated_at DATETIME NOT NULL,
|
||||
UNIQUE (batch_id, relative_path)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ra_item_batch_index
|
||||
ON ra_file_summary_item (batch_id, file_index);
|
||||
|
||||
CREATE INDEX idx_ra_item_batch_status
|
||||
ON ra_file_summary_item (batch_id, statistics_status);
|
||||
|
||||
CREATE INDEX idx_ra_item_batch_type
|
||||
ON ra_file_summary_item (batch_id, file_type);
|
||||
```
|
||||
|
||||
```sql
|
||||
CREATE TABLE ra_workflow_node_run (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
batch_id BIGINT NOT NULL,
|
||||
node_code VARCHAR(40) NOT NULL,
|
||||
node_name VARCHAR(80) NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
progress INTEGER NOT NULL DEFAULT 0,
|
||||
message TEXT NOT NULL DEFAULT '',
|
||||
started_at DATETIME NULL,
|
||||
finished_at DATETIME NULL,
|
||||
UNIQUE (batch_id, node_code)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ra_node_batch_status
|
||||
ON ra_workflow_node_run (batch_id, status);
|
||||
```
|
||||
|
||||
```sql
|
||||
CREATE TABLE ra_workflow_event (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
batch_id BIGINT NOT NULL,
|
||||
event_type VARCHAR(40) NOT NULL,
|
||||
payload TEXT NOT NULL DEFAULT '{}',
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ra_event_batch_id
|
||||
ON ra_workflow_event (batch_id, id);
|
||||
|
||||
CREATE INDEX idx_ra_event_batch_created
|
||||
ON ra_workflow_event (batch_id, created_at);
|
||||
```
|
||||
|
||||
```sql
|
||||
CREATE TABLE ra_exported_summary_file (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
batch_id BIGINT NOT NULL,
|
||||
export_type VARCHAR(20) NOT NULL,
|
||||
file_name VARCHAR(255) NOT NULL,
|
||||
storage_path VARCHAR(500) NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'success',
|
||||
error_message TEXT NOT NULL DEFAULT '',
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ra_export_batch_type
|
||||
ON ra_exported_summary_file (batch_id, export_type);
|
||||
|
||||
CREATE INDEX idx_ra_export_batch_created
|
||||
ON ra_exported_summary_file (batch_id, created_at);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、Django ORM 落地注意事项
|
||||
|
||||
### 8.1 db_table
|
||||
|
||||
每个模型通过 `class Meta: db_table = "ra_xxx"` 固定表名,避免 Django 默认生成较长表名。
|
||||
|
||||
### 8.2 JSONField
|
||||
|
||||
`WorkflowEvent.payload` 使用 Django `models.JSONField(default=dict)`。SQLite 下实际以文本形式存储,Django 负责序列化与反序列化。
|
||||
|
||||
### 8.3 版本号生成
|
||||
|
||||
同一对话同名文件上传时:
|
||||
|
||||
```text
|
||||
version_no = max(existing version_no) + 1
|
||||
```
|
||||
|
||||
若新版本设为默认版本,需要将旧版本 `is_active` 更新为 false。
|
||||
|
||||
### 8.4 逻辑删除
|
||||
|
||||
附件删除时:
|
||||
|
||||
```text
|
||||
upload_status = deleted
|
||||
is_active = false
|
||||
```
|
||||
|
||||
历史批次仍可通过中间表追溯该附件。
|
||||
|
||||
### 8.5 批次选择附件
|
||||
|
||||
用户发送提示词触发工作流时:
|
||||
|
||||
| 场景 | 处理 |
|
||||
| --- | --- |
|
||||
| 用户显式选择附件版本 | 使用所选 attachment_id |
|
||||
| 用户未选择版本 | 使用当前对话下 is_active=true 且未删除的附件 |
|
||||
| 存在多个同名 active 异常 | 取 created_at 最新,并记录待修复数据异常 |
|
||||
|
||||
---
|
||||
|
||||
## 九、数据保留策略
|
||||
|
||||
| 数据 | Demo 策略 | 正式部署建议 |
|
||||
| --- | --- | --- |
|
||||
| 上传附件记录 | 永久保留 | 随会话归档周期清理 |
|
||||
| 上传原始文件 | 永久保留 | 可按用户/项目配置保留期限 |
|
||||
| 汇总批次 | 永久保留 | 保留用于审计追溯 |
|
||||
| 文件明细 | 永久保留 | 保留用于历史报告复现 |
|
||||
| 工作流事件 | 永久保留 | 可定期清理已完成批次的事件 |
|
||||
| 导出文件 | 永久保留 | 可设置下载有效期或归档 |
|
||||
|
||||
---
|
||||
|
||||
## 十、待确认事项
|
||||
|
||||
| 序号 | 问题 | 当前设计 | 状态 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | 正式部署是否从 SQLite 迁移到 PostgreSQL/MySQL | 当前按 SQLite/Django ORM 设计,保留 ORM 兼容性 | 待后续确认 |
|
||||
| 2 | 同名附件 active 是否允许多个 | 设计上不允许,代码更新时应关闭旧 active | 待开发实现 |
|
||||
| 3 | 文件物理删除时机 | Demo 不物理删除 | 待后续确认 |
|
||||
|
||||
---
|
||||
|
||||
## 十一、开发顺序建议
|
||||
|
||||
1. 在 `review_agent/models.py` 中新增上述 7 个模型。
|
||||
2. 为状态字段定义 Django `TextChoices`。
|
||||
3. 配置 `db_table`、`indexes`、`constraints`。
|
||||
4. 执行 `python manage.py makemigrations review_agent` 生成迁移。
|
||||
5. 执行 `python manage.py migrate` 验证 SQLite 可落表。
|
||||
6. 编写模型级测试,覆盖同名附件版本、批次附件绑定、唯一约束和权限查询。
|
||||
930
docs/详细设计/1.自动汇总.md
Normal file
930
docs/详细设计/1.自动汇总.md
Normal file
@@ -0,0 +1,930 @@
|
||||
# 自动汇总文件夹文件目录与页数流程详细设计
|
||||
|
||||
## 文档信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 需求分析文档 | docs/需求分析/1.自动汇总.md |
|
||||
| 功能设计文档 | docs/功能设计/1.自动汇总.md |
|
||||
| 功能名称 | 自动汇总文件夹文件目录与页数 |
|
||||
| 所属模块 | 审核智能体 review_agent |
|
||||
| 设计日期 | 2026-06-05 |
|
||||
| 设计版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 一、详细设计目标
|
||||
|
||||
本详细设计用于指导“自动汇总文件夹文件目录与页数”功能开发落地,覆盖代码目录、数据模型、接口契约、后台工作流、Skill 拆分、轻量依赖、前端三栏布局、SSE 实时状态、异常重试和测试用例。
|
||||
|
||||
核心约束:
|
||||
|
||||
| 约束 | 说明 |
|
||||
| --- | --- |
|
||||
| 对话绑定 | 上传文件与当前 Conversation 绑定,一个对话对应一套文件,不能串文件 |
|
||||
| 上传即存储 | 用户拖拽或选择文件后立即保存,但不启动工作流 |
|
||||
| 提示词触发 | 用户发送消息后,根据提示词判断是否启动自动汇总工作流 |
|
||||
| 后台异步 | 工作流后台执行,右侧第三栏工作流卡片实时更新 |
|
||||
| 轻量依赖 | 优先使用 Python 内部库和轻量第三方库,不强依赖 LibreOffice |
|
||||
| 老格式支持 | doc、xls、ppt 进入处理流程,能读到页数则统计,读不到则记录异常 |
|
||||
| 结果存档 | 批次、文件、节点、事件、明细、导出文件全部入库 |
|
||||
|
||||
---
|
||||
|
||||
## 二、代码结构设计
|
||||
|
||||
### 2.1 目录结构
|
||||
|
||||
在现有 `review_agent` 应用内按模块重新划分文件处理能力。Django 模型仍集中放在 `review_agent/models.py`,其余代码放入 `review_agent/file_summary/`。
|
||||
|
||||
```text
|
||||
review_agent/
|
||||
models.py
|
||||
urls.py
|
||||
views.py
|
||||
services.py
|
||||
file_summary/
|
||||
__init__.py
|
||||
constants.py
|
||||
schemas.py
|
||||
storage.py
|
||||
workflow.py
|
||||
events.py
|
||||
urls.py
|
||||
views.py
|
||||
services/
|
||||
__init__.py
|
||||
archive.py
|
||||
inventory.py
|
||||
page_count.py
|
||||
product_detect.py
|
||||
report.py
|
||||
export_excel.py
|
||||
workflow_trigger.py
|
||||
skills/
|
||||
__init__.py
|
||||
base.py
|
||||
registry.py
|
||||
upload_intake.py
|
||||
archive_extract.py
|
||||
file_inventory.py
|
||||
document_page_count.py
|
||||
product_detect.py
|
||||
summary_report.py
|
||||
excel_export.py
|
||||
```
|
||||
|
||||
### 2.2 文件职责
|
||||
|
||||
| 文件 | 职责 |
|
||||
| --- | --- |
|
||||
| review_agent/models.py | 集中定义 Conversation、Message、文件汇总相关模型 |
|
||||
| file_summary/constants.py | 状态、节点、文件类型、事件类型常量 |
|
||||
| file_summary/schemas.py | dataclass 入参出参结构,避免业务层直接传散乱 dict |
|
||||
| file_summary/storage.py | 上传文件、工作目录、导出文件路径生成与保存 |
|
||||
| file_summary/workflow.py | WorkflowExecutor,串行执行节点图 |
|
||||
| file_summary/events.py | 工作流事件持久化与 SSE 格式化 |
|
||||
| file_summary/views.py | 上传暂存、启动工作流、状态查询、SSE、下载接口 |
|
||||
| services/archive.py | 压缩包识别、zip/7z/rar 解压 |
|
||||
| services/inventory.py | 文件遍历与清单生成 |
|
||||
| services/page_count.py | 文件页数统计与 3 次重试 |
|
||||
| services/product_detect.py | 产品名识别 |
|
||||
| services/report.py | Markdown 报告和对话简表生成 |
|
||||
| services/export_excel.py | Excel 文件导出 |
|
||||
| services/workflow_trigger.py | 根据提示词判断是否触发自动汇总工作流 |
|
||||
| skills/base.py | Skill 基类与统一返回结构 |
|
||||
| skills/registry.py | Skill 注册与按需加载 |
|
||||
| skills/*.py | 各工作流节点对应 Skill |
|
||||
|
||||
---
|
||||
|
||||
## 三、依赖设计
|
||||
|
||||
### 3.1 requirements 建议
|
||||
|
||||
```text
|
||||
Django==5.2.14
|
||||
pypdf
|
||||
python-docx
|
||||
python-pptx
|
||||
openpyxl
|
||||
xlrd
|
||||
olefile
|
||||
py7zr
|
||||
```
|
||||
|
||||
### 3.2 格式处理策略
|
||||
|
||||
| 格式 | 处理库 | 统计口径 | 失败策略 |
|
||||
| --- | --- | --- | --- |
|
||||
| pdf | pypdf | PDF 页面数 | 重试 3 次,仍失败记录异常 |
|
||||
| docx | python-docx | 优先读取内置页数属性 | 读不到记录“页数不可确定” |
|
||||
| doc | olefile | 读取 OLE 元数据页数 | 读不到记录“页数不可确定” |
|
||||
| pptx | python-pptx | 幻灯片数量 | 重试 3 次,仍失败记录异常 |
|
||||
| ppt | olefile | 读取 OLE 元数据页数/幻灯片数 | 读不到记录“页数不可确定” |
|
||||
| xlsx | openpyxl | 工作表数量 | 重试 3 次,仍失败记录异常 |
|
||||
| xls | xlrd | 工作表数量 | 重试 3 次,仍失败记录异常 |
|
||||
|
||||
### 3.3 压缩包处理策略
|
||||
|
||||
| 格式 | 处理方式 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| zip | Python 标准库 zipfile | 必须支持 |
|
||||
| 7z | py7zr | 必须支持 |
|
||||
| rar | 优先系统 7z 命令 | Docker 镜像需安装 7-Zip/p7zip |
|
||||
|
||||
### 3.4 Docker 部署说明
|
||||
|
||||
Demo 运行不强依赖 LibreOffice。若未来要求 doc/docx/ppt/pptx 页数与 Office 打开后的分页完全一致,可在 Docker 镜像中额外安装 LibreOffice headless,再通过“转换 PDF 后统计页数”的增强策略实现。
|
||||
|
||||
RAR 解压如需稳定支持,Docker 镜像需要安装 7-Zip/p7zip,并确保 `7z` 命令在 PATH 中可调用。
|
||||
|
||||
---
|
||||
|
||||
## 四、数据模型详细设计
|
||||
|
||||
模型集中放在 `review_agent/models.py`,按“会话模型”和“文件汇总模型”分段。
|
||||
|
||||
### 4.1 FileAttachment
|
||||
|
||||
用户上传即存储的文件记录。此时尚未启动工作流。
|
||||
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| id | BigAutoField | PK | 主键 |
|
||||
| conversation | ForeignKey(Conversation) | CASCADE, db_index | 绑定对话 |
|
||||
| user | ForeignKey(User) | CASCADE, db_index | 上传用户 |
|
||||
| original_name | CharField(255) | required | 原始文件名 |
|
||||
| storage_path | CharField(500) | required | 本地保存路径 |
|
||||
| file_size | BigIntegerField | default=0 | 文件大小 |
|
||||
| content_type | CharField(120) | blank | MIME 类型 |
|
||||
| upload_status | CharField(20) | choices | uploaded、bound、deleted |
|
||||
| created_at | DateTimeField | auto_now_add | 上传时间 |
|
||||
|
||||
索引:
|
||||
|
||||
```text
|
||||
(conversation, created_at)
|
||||
(user, created_at)
|
||||
```
|
||||
|
||||
### 4.2 FileSummaryBatch
|
||||
|
||||
一次自动汇总工作流批次。
|
||||
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| id | BigAutoField | PK | 主键 |
|
||||
| conversation | ForeignKey(Conversation) | CASCADE, db_index | 绑定对话 |
|
||||
| user | ForeignKey(User) | CASCADE, db_index | 执行用户 |
|
||||
| trigger_message | ForeignKey(Message) | SET_NULL, null | 触发工作流的用户消息 |
|
||||
| batch_no | CharField(64) | unique | 批次编号 |
|
||||
| product_name | CharField(200) | blank | 产品名称 |
|
||||
| status | CharField(20) | choices | pending、running、success、failed |
|
||||
| total_files | IntegerField | default=0 | 文件总数 |
|
||||
| supported_files | IntegerField | default=0 | 支持统计数 |
|
||||
| success_files | IntegerField | default=0 | 成功数 |
|
||||
| failed_files | IntegerField | default=0 | 失败数 |
|
||||
| unsupported_files | IntegerField | default=0 | 不支持数 |
|
||||
| uncertain_files | IntegerField | default=0 | 页数不可确定数 |
|
||||
| total_pages | IntegerField | default=0 | 总页数 |
|
||||
| work_dir | CharField(500) | blank | 工作目录 |
|
||||
| error_message | TextField | blank | 批次错误 |
|
||||
| created_at | DateTimeField | auto_now_add | 创建时间 |
|
||||
| started_at | DateTimeField | null | 开始时间 |
|
||||
| finished_at | DateTimeField | null | 结束时间 |
|
||||
|
||||
### 4.3 FileSummaryBatchAttachment
|
||||
|
||||
批次与上传文件的绑定表,确保工作流只读取本批次文件。
|
||||
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| id | BigAutoField | PK | 主键 |
|
||||
| batch | ForeignKey(FileSummaryBatch) | CASCADE | 批次 |
|
||||
| attachment | ForeignKey(FileAttachment) | CASCADE | 上传文件 |
|
||||
| created_at | DateTimeField | auto_now_add | 绑定时间 |
|
||||
|
||||
唯一约束:
|
||||
|
||||
```text
|
||||
unique(batch, attachment)
|
||||
```
|
||||
|
||||
### 4.4 FileSummaryItem
|
||||
|
||||
文件明细记录。
|
||||
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| id | BigAutoField | PK | 主键 |
|
||||
| batch | ForeignKey(FileSummaryBatch) | CASCADE, db_index | 所属批次 |
|
||||
| file_index | IntegerField | required | 文件序号 |
|
||||
| directory_level | CharField(300) | blank | 目录层级 |
|
||||
| file_name | CharField(255) | required | 文件名 |
|
||||
| file_type | CharField(20) | required | 扩展名 |
|
||||
| relative_path | CharField(500) | required | 相对路径 |
|
||||
| storage_path | CharField(500) | required | 实际处理路径 |
|
||||
| page_count | IntegerField | null | 页数 |
|
||||
| statistics_status | CharField(20) | choices | success、failed、unsupported、uncertain、skipped |
|
||||
| retry_count | IntegerField | default=0 | 重试次数 |
|
||||
| error_message | TextField | blank | 异常说明 |
|
||||
| created_at | DateTimeField | auto_now_add | 创建时间 |
|
||||
| updated_at | DateTimeField | auto_now | 更新时间 |
|
||||
|
||||
唯一约束:
|
||||
|
||||
```text
|
||||
unique(batch, relative_path)
|
||||
```
|
||||
|
||||
### 4.5 WorkflowNodeRun
|
||||
|
||||
工作流节点状态记录。
|
||||
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| id | BigAutoField | PK | 主键 |
|
||||
| batch | ForeignKey(FileSummaryBatch) | CASCADE, db_index | 批次 |
|
||||
| node_code | CharField(40) | required | 节点编码 |
|
||||
| node_name | CharField(80) | required | 节点名称 |
|
||||
| status | CharField(20) | choices | pending、running、retrying、success、failed、skipped |
|
||||
| progress | IntegerField | default=0 | 进度百分比 |
|
||||
| message | TextField | blank | 节点说明 |
|
||||
| started_at | DateTimeField | null | 开始时间 |
|
||||
| finished_at | DateTimeField | null | 完成时间 |
|
||||
|
||||
唯一约束:
|
||||
|
||||
```text
|
||||
unique(batch, node_code)
|
||||
```
|
||||
|
||||
### 4.6 WorkflowEvent
|
||||
|
||||
SSE 事件持久化记录,用于页面刷新后恢复和调试。
|
||||
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| id | BigAutoField | PK | 主键 |
|
||||
| batch | ForeignKey(FileSummaryBatch) | CASCADE, db_index | 批次 |
|
||||
| event_type | CharField(40) | required | 事件类型 |
|
||||
| payload | JSONField | default=dict | 事件载荷 |
|
||||
| created_at | DateTimeField | auto_now_add | 创建时间 |
|
||||
|
||||
### 4.7 ExportedSummaryFile
|
||||
|
||||
导出文件记录。
|
||||
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| id | BigAutoField | PK | 主键 |
|
||||
| batch | ForeignKey(FileSummaryBatch) | CASCADE, db_index | 批次 |
|
||||
| export_type | CharField(20) | choices | markdown、excel |
|
||||
| file_name | CharField(255) | required | 文件名 |
|
||||
| storage_path | CharField(500) | required | 保存路径 |
|
||||
| status | CharField(20) | choices | success、failed |
|
||||
| error_message | TextField | blank | 异常 |
|
||||
| created_at | DateTimeField | auto_now_add | 生成时间 |
|
||||
|
||||
下载链接运行时根据 `export_id` 生成,不建议长期存储静态 URL。
|
||||
|
||||
---
|
||||
|
||||
## 五、常量与状态设计
|
||||
|
||||
### 5.1 支持格式
|
||||
|
||||
```python
|
||||
SUPPORTED_PAGE_TYPES = {"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx"}
|
||||
ARCHIVE_TYPES = {"zip", "7z", "rar"}
|
||||
```
|
||||
|
||||
### 5.2 工作流节点
|
||||
|
||||
```python
|
||||
WORKFLOW_NODES = [
|
||||
("upload", "上传中"),
|
||||
("extract", "解压中"),
|
||||
("inventory", "扫描中"),
|
||||
("page_count", "解析页数中"),
|
||||
("product_detect", "识别产品名中"),
|
||||
("report", "输出 Markdown 中"),
|
||||
("excel_export", "输出 Excel 中"),
|
||||
("completed", "已完成"),
|
||||
]
|
||||
```
|
||||
|
||||
### 5.3 触发词规则
|
||||
|
||||
`workflow_trigger.py` 先用规则判断,后续可升级为 LLM 意图识别。
|
||||
|
||||
```python
|
||||
SUMMARY_TRIGGER_KEYWORDS = [
|
||||
"自动汇总",
|
||||
"文件目录",
|
||||
"页数",
|
||||
"统计文件",
|
||||
"汇总目录",
|
||||
"目录与页数",
|
||||
]
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
| 条件 | 结果 |
|
||||
| --- | --- |
|
||||
| 当前对话存在未绑定或最近上传文件,且提示词命中关键词 | 启动自动汇总工作流 |
|
||||
| 未命中关键词 | 走普通 LLM 对话 |
|
||||
| 命中关键词但没有上传文件 | AI 回复提示“请先上传文件或压缩包” |
|
||||
|
||||
---
|
||||
|
||||
## 六、服务与方法签名
|
||||
|
||||
### 6.1 storage.py
|
||||
|
||||
```python
|
||||
def save_attachment(conversation, user, uploaded_file) -> FileAttachment:
|
||||
"""保存上传文件并绑定当前对话。"""
|
||||
|
||||
def build_batch_work_dir(batch: FileSummaryBatch) -> Path:
|
||||
"""生成批次工作目录。"""
|
||||
|
||||
def build_export_path(batch: FileSummaryBatch, suffix: str) -> Path:
|
||||
"""生成导出文件路径。"""
|
||||
```
|
||||
|
||||
存储目录:
|
||||
|
||||
```text
|
||||
media/review_agent/
|
||||
user_{user_id}/
|
||||
conversation_{conversation_id}/
|
||||
attachments/
|
||||
batches/
|
||||
batch_{batch_id}/
|
||||
input/
|
||||
extracted/
|
||||
exports/
|
||||
```
|
||||
|
||||
### 6.2 archive.py
|
||||
|
||||
```python
|
||||
def is_archive(path: Path) -> bool:
|
||||
"""判断是否压缩包。"""
|
||||
|
||||
def extract_archive(source: Path, target_dir: Path) -> list[Path]:
|
||||
"""解压 zip、7z、rar,返回解压后的文件路径列表。"""
|
||||
|
||||
def extract_zip(source: Path, target_dir: Path) -> list[Path]:
|
||||
"""使用 zipfile 解压。"""
|
||||
|
||||
def extract_7z(source: Path, target_dir: Path) -> list[Path]:
|
||||
"""使用 py7zr 解压。"""
|
||||
|
||||
def extract_rar(source: Path, target_dir: Path) -> list[Path]:
|
||||
"""优先调用系统 7z 命令解压 rar。"""
|
||||
```
|
||||
|
||||
安全规则:
|
||||
|
||||
| 规则 | 说明 |
|
||||
| --- | --- |
|
||||
| 路径穿越检查 | 解压后的最终路径必须仍在 target_dir 内 |
|
||||
| 文件名清理 | 保留原名,但禁止绝对路径和上级目录跳转 |
|
||||
| 解压失败 | 抛出 ArchiveExtractError,批次失败 |
|
||||
|
||||
### 6.3 inventory.py
|
||||
|
||||
```python
|
||||
def scan_files(batch: FileSummaryBatch, roots: list[Path]) -> list[FileSummaryItem]:
|
||||
"""扫描目录或散装文件,创建 FileSummaryItem。"""
|
||||
|
||||
def build_directory_level(relative_path: Path) -> str:
|
||||
"""根据相对路径生成目录层级。"""
|
||||
|
||||
def normalize_file_type(path: Path) -> str:
|
||||
"""返回小写扩展名,不含点。"""
|
||||
```
|
||||
|
||||
### 6.4 page_count.py
|
||||
|
||||
```python
|
||||
def count_pages(item: FileSummaryItem) -> PageCountResult:
|
||||
"""根据文件类型分发页数统计。"""
|
||||
|
||||
def count_pages_with_retry(item: FileSummaryItem, max_retry: int = 3) -> PageCountResult:
|
||||
"""失败最多重试 3 次。"""
|
||||
|
||||
def count_pdf(path: Path) -> int:
|
||||
"""使用 pypdf 统计 PDF 页数。"""
|
||||
|
||||
def count_docx(path: Path) -> PageCountResult:
|
||||
"""使用 python-docx 读取内置页数属性。"""
|
||||
|
||||
def count_doc(path: Path) -> PageCountResult:
|
||||
"""使用 olefile 读取老 doc 的 OLE 元数据页数。"""
|
||||
|
||||
def count_xlsx(path: Path) -> int:
|
||||
"""使用 openpyxl 统计工作表数量。"""
|
||||
|
||||
def count_xls(path: Path) -> int:
|
||||
"""使用 xlrd 统计工作表数量。"""
|
||||
|
||||
def count_pptx(path: Path) -> int:
|
||||
"""使用 python-pptx 统计幻灯片数量。"""
|
||||
|
||||
def count_ppt(path: Path) -> PageCountResult:
|
||||
"""使用 olefile 读取老 ppt 的 OLE 元数据页数或幻灯片数。"""
|
||||
```
|
||||
|
||||
`PageCountResult`:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class PageCountResult:
|
||||
status: str
|
||||
page_count: int | None = None
|
||||
error_message: str = ""
|
||||
```
|
||||
|
||||
状态规则:
|
||||
|
||||
| 情况 | status | page_count |
|
||||
| --- | --- | --- |
|
||||
| 成功读取页数 | success | 整数 |
|
||||
| 不支持类型 | unsupported | None |
|
||||
| 文件可读但页数无元数据 | uncertain | None |
|
||||
| 解析异常且重试失败 | failed | None |
|
||||
|
||||
### 6.5 product_detect.py
|
||||
|
||||
```python
|
||||
def detect_product_name(batch: FileSummaryBatch) -> ProductDetectResult:
|
||||
"""从目录名、文件名和少量元数据中识别产品名。"""
|
||||
|
||||
def update_conversation_title(batch: FileSummaryBatch, product_name: str) -> None:
|
||||
"""按规则更新对话标题。"""
|
||||
```
|
||||
|
||||
产品名识别优先级:
|
||||
|
||||
| 优先级 | 来源 |
|
||||
| --- | --- |
|
||||
| 1 | 顶层目录名 |
|
||||
| 2 | 文件名中包含“产品”“试剂盒”“说明书”等关键词的片段 |
|
||||
| 3 | docx 文档属性 title |
|
||||
| 4 | PDF 元数据 title |
|
||||
|
||||
### 6.6 report.py
|
||||
|
||||
```python
|
||||
def build_summary_stats(batch: FileSummaryBatch) -> dict:
|
||||
"""汇总统计数据。"""
|
||||
|
||||
def build_chat_markdown(batch: FileSummaryBatch) -> str:
|
||||
"""生成对话框展示 Markdown 简表。"""
|
||||
|
||||
def build_full_markdown_report(batch: FileSummaryBatch) -> str:
|
||||
"""生成完整 Markdown 报告。"""
|
||||
|
||||
def save_markdown_report(batch: FileSummaryBatch) -> ExportedSummaryFile:
|
||||
"""保存 Markdown 报告并创建导出记录。"""
|
||||
```
|
||||
|
||||
### 6.7 export_excel.py
|
||||
|
||||
```python
|
||||
def build_excel_workbook(batch: FileSummaryBatch) -> Workbook:
|
||||
"""构建 Excel Workbook。"""
|
||||
|
||||
def save_excel(batch: FileSummaryBatch) -> ExportedSummaryFile:
|
||||
"""保存 Excel 并创建导出记录。"""
|
||||
```
|
||||
|
||||
工作表:
|
||||
|
||||
| Sheet | 字段 |
|
||||
| --- | --- |
|
||||
| 汇总信息 | 批次编号、产品名、文件总数、成功数、失败数、不可确定数、总页数 |
|
||||
| 文件明细 | 序号、目录层级、文件名、类型、页数、相对路径、状态、重试次数、异常说明 |
|
||||
|
||||
---
|
||||
|
||||
## 七、Skill 详细设计
|
||||
|
||||
### 7.1 BaseSkill
|
||||
|
||||
```python
|
||||
class BaseSkill:
|
||||
name: str
|
||||
node_code: str
|
||||
|
||||
def run(self, context: WorkflowContext) -> SkillResult:
|
||||
raise NotImplementedError
|
||||
```
|
||||
|
||||
`WorkflowContext`:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class WorkflowContext:
|
||||
batch_id: int
|
||||
conversation_id: int
|
||||
user_id: int
|
||||
message_id: int | None = None
|
||||
```
|
||||
|
||||
`SkillResult`:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class SkillResult:
|
||||
success: bool
|
||||
message: str = ""
|
||||
data: dict = field(default_factory=dict)
|
||||
```
|
||||
|
||||
### 7.2 Skill 列表
|
||||
|
||||
| Skill 类名 | 节点 | 调用服务 |
|
||||
| --- | --- | --- |
|
||||
| UploadIntakeSkill | upload | storage.py |
|
||||
| ArchiveExtractSkill | extract | archive.py |
|
||||
| FileInventorySkill | inventory | inventory.py |
|
||||
| DocumentPageCountSkill | page_count | page_count.py |
|
||||
| ProductDetectSkill | product_detect | product_detect.py |
|
||||
| SummaryReportSkill | report | report.py |
|
||||
| ExcelExportSkill | excel_export | export_excel.py |
|
||||
|
||||
---
|
||||
|
||||
## 八、工作流执行器详细设计
|
||||
|
||||
### 8.1 执行入口
|
||||
|
||||
```python
|
||||
def start_file_summary_workflow(batch_id: int) -> None:
|
||||
thread = threading.Thread(
|
||||
target=WorkflowExecutor().run,
|
||||
args=(batch_id,),
|
||||
daemon=True,
|
||||
)
|
||||
thread.start()
|
||||
```
|
||||
|
||||
### 8.2 执行伪代码
|
||||
|
||||
```python
|
||||
class WorkflowExecutor:
|
||||
def run(self, batch_id: int) -> None:
|
||||
batch = FileSummaryBatch.objects.get(pk=batch_id)
|
||||
self.mark_batch_running(batch)
|
||||
self.emit("workflow_started", batch, {"batch_id": batch.id})
|
||||
|
||||
try:
|
||||
for node_code in self.resolve_nodes(batch):
|
||||
self.run_node(batch, node_code)
|
||||
self.mark_batch_success(batch)
|
||||
self.emit("workflow_completed", batch, self.build_completed_payload(batch))
|
||||
except Exception as exc:
|
||||
self.mark_batch_failed(batch, str(exc))
|
||||
self.emit("workflow_failed", batch, {"message": str(exc)})
|
||||
```
|
||||
|
||||
### 8.3 节点跳过规则
|
||||
|
||||
| 节点 | 跳过条件 |
|
||||
| --- | --- |
|
||||
| extract | 当前批次没有压缩包 |
|
||||
| product_detect | 没有任何可用于识别的文件名、目录名或元数据 |
|
||||
|
||||
---
|
||||
|
||||
## 九、接口详细设计
|
||||
|
||||
### 9.1 上传暂存接口
|
||||
|
||||
```text
|
||||
POST /api/review-agent/conversations/{conversation_id}/attachments/
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
请求:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| files[] | File[] | 是 | 一个或多个文件 |
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"attachments": [
|
||||
{
|
||||
"id": 101,
|
||||
"original_name": "注册资料.zip",
|
||||
"file_size": 204800,
|
||||
"upload_status": "uploaded"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
权限:
|
||||
|
||||
```text
|
||||
conversation.user 必须等于 request.user
|
||||
```
|
||||
|
||||
### 9.2 发送消息并按需触发工作流
|
||||
|
||||
沿用现有 `POST /chat/stream/` SSE 能力,在 `stream_chat` 中增加判断:
|
||||
|
||||
```text
|
||||
用户发送 prompt
|
||||
-> 保存 Message
|
||||
-> 判断 prompt 是否命中自动汇总工作流
|
||||
-> 命中则创建 FileSummaryBatch 并启动后台工作流
|
||||
-> SSE 返回 workflow_meta
|
||||
-> 未命中则走原 LLM 流式回复
|
||||
```
|
||||
|
||||
新增 SSE meta:
|
||||
|
||||
```json
|
||||
{
|
||||
"conversation_id": 1,
|
||||
"title": "新对话",
|
||||
"workflow": {
|
||||
"type": "file_summary",
|
||||
"batch_id": 12,
|
||||
"status": "running"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 9.3 查询批次状态
|
||||
|
||||
```text
|
||||
GET /api/review-agent/file-summary/{batch_id}/
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"batch": {
|
||||
"id": 12,
|
||||
"batch_no": "FS202606050001",
|
||||
"status": "running",
|
||||
"product_name": "",
|
||||
"total_files": 24,
|
||||
"success_files": 10,
|
||||
"failed_files": 1,
|
||||
"uncertain_files": 2,
|
||||
"total_pages": 180
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"node_code": "page_count",
|
||||
"node_name": "解析页数中",
|
||||
"status": "running",
|
||||
"progress": 45,
|
||||
"message": "正在解析 11/24"
|
||||
}
|
||||
],
|
||||
"exports": []
|
||||
}
|
||||
```
|
||||
|
||||
### 9.4 工作流事件流
|
||||
|
||||
```text
|
||||
GET /api/review-agent/file-summary/{batch_id}/events/?after={event_id}
|
||||
```
|
||||
|
||||
响应类型:`text/event-stream`
|
||||
|
||||
事件:
|
||||
|
||||
```text
|
||||
event: node_progress
|
||||
data: {"event_id": 301, "batch_id": 12, "node_code": "page_count", "status": "running", "progress": 45, "message": "正在解析 11/24"}
|
||||
```
|
||||
|
||||
### 9.5 下载导出文件
|
||||
|
||||
```text
|
||||
GET /api/review-agent/file-summary/exports/{export_id}/download/
|
||||
```
|
||||
|
||||
权限:
|
||||
|
||||
```text
|
||||
ExportedSummaryFile -> batch -> conversation -> user 必须为当前用户
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十、前端详细设计
|
||||
|
||||
### 10.1 三栏布局
|
||||
|
||||
页面调整为三栏:
|
||||
|
||||
| 区域 | 内容 |
|
||||
| --- | --- |
|
||||
| 左侧栏 | 对话历史 |
|
||||
| 中间栏 | 聊天消息、输入框 |
|
||||
| 右侧栏上半部分 | 拖拽式文件导入区 |
|
||||
| 右侧栏下半部分 | 工作流卡片列表 |
|
||||
|
||||
HTML 结构建议:
|
||||
|
||||
```html
|
||||
<main class="workspace three-column">
|
||||
<aside class="sidebar"></aside>
|
||||
<section class="chat-shell"></section>
|
||||
<aside class="workflow-panel">
|
||||
<section class="upload-dropzone" id="uploadDropzone"></section>
|
||||
<section class="workflow-card-list" id="workflowCardList"></section>
|
||||
</aside>
|
||||
</main>
|
||||
```
|
||||
|
||||
### 10.2 上传交互
|
||||
|
||||
JS 方法:
|
||||
|
||||
```javascript
|
||||
function bindUploadDropzone()
|
||||
function uploadConversationFiles(files)
|
||||
function renderAttachmentList(attachments)
|
||||
```
|
||||
|
||||
流程:
|
||||
|
||||
```text
|
||||
用户拖拽或选择文件
|
||||
-> POST attachments 接口
|
||||
-> 保存成功后右侧上传区展示文件名
|
||||
-> 不启动工作流
|
||||
-> 用户发送提示词
|
||||
-> 命中工作流后创建工作流卡片
|
||||
```
|
||||
|
||||
### 10.3 工作流卡片
|
||||
|
||||
JS 方法:
|
||||
|
||||
```javascript
|
||||
function createWorkflowCard(batch)
|
||||
function updateWorkflowNode(batchId, nodePayload)
|
||||
function markWorkflowCompleted(batchId, payload)
|
||||
function markWorkflowFailed(batchId, payload)
|
||||
function connectWorkflowEvents(batchId)
|
||||
function restoreWorkflowCards()
|
||||
```
|
||||
|
||||
卡片结构:
|
||||
|
||||
```html
|
||||
<article class="workflow-card" data-batch-id="12">
|
||||
<header>
|
||||
<strong>文件目录与页数汇总</strong>
|
||||
<span class="workflow-status">运行中</span>
|
||||
</header>
|
||||
<ol class="workflow-nodes">
|
||||
<li data-node-code="upload">上传中</li>
|
||||
<li data-node-code="extract">解压中</li>
|
||||
<li data-node-code="inventory">扫描中</li>
|
||||
<li data-node-code="page_count">解析页数中</li>
|
||||
<li data-node-code="product_detect">识别产品名中</li>
|
||||
<li data-node-code="report">输出 Markdown 中</li>
|
||||
<li data-node-code="excel_export">输出 Excel 中</li>
|
||||
</ol>
|
||||
</article>
|
||||
```
|
||||
|
||||
### 10.4 Markdown 渲染
|
||||
|
||||
现有消息使用 `nl2br`,无法正常渲染 Markdown 表格。需要改造:
|
||||
|
||||
| 消息类型 | 渲染策略 |
|
||||
| --- | --- |
|
||||
| 普通用户消息 | escapeHtml + nl2br |
|
||||
| 普通助手消息 | 安全 Markdown 渲染 |
|
||||
| 文件汇总结果 | 安全 Markdown 渲染,允许 table、a、strong、code |
|
||||
|
||||
可选方案:
|
||||
|
||||
| 方案 | 说明 |
|
||||
| --- | --- |
|
||||
| 前端 marked + DOMPurify | 渲染体验好,但增加前端依赖 |
|
||||
| 后端 markdown + bleach | 后端输出安全 HTML,前端直接展示 |
|
||||
|
||||
Demo 建议使用前端 `marked` + `DOMPurify` CDN 或本地静态文件。
|
||||
|
||||
---
|
||||
|
||||
## 十一、对话标题更新设计
|
||||
|
||||
产品名识别成功后更新标题:
|
||||
|
||||
```python
|
||||
def update_conversation_title(batch, product_name):
|
||||
conversation = batch.conversation
|
||||
if conversation.title.startswith("新对话"):
|
||||
conversation.title = f"{product_name}-文件汇总"[:120]
|
||||
conversation.save(update_fields=["title", "updated_at"])
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
| 场景 | 处理 |
|
||||
| --- | --- |
|
||||
| 新对话默认标题 | 更新为产品名 |
|
||||
| 用户已有自定义标题 | 不覆盖 |
|
||||
| 产品名为空 | 不更新 |
|
||||
|
||||
---
|
||||
|
||||
## 十二、测试设计
|
||||
|
||||
### 12.1 单元测试
|
||||
|
||||
| 用例 | 目标 |
|
||||
| --- | --- |
|
||||
| test_trigger_keywords | 提示词命中时触发自动汇总 |
|
||||
| test_save_attachment_binds_conversation | 上传文件绑定当前对话 |
|
||||
| test_zip_extract_safe_path | zip 解压禁止路径穿越 |
|
||||
| test_scan_files_builds_relative_path | 扫描生成正确相对路径 |
|
||||
| test_count_pdf_pages | PDF 页数统计 |
|
||||
| test_count_xlsx_sheets | xlsx 工作表数量统计 |
|
||||
| test_count_pptx_slides | pptx 幻灯片数量统计 |
|
||||
| test_retry_three_times | 单文件失败重试 3 次 |
|
||||
| test_uncertain_old_doc | 老 doc 元数据缺失时标记 uncertain |
|
||||
|
||||
### 12.2 接口测试
|
||||
|
||||
| 用例 | 目标 |
|
||||
| --- | --- |
|
||||
| test_upload_attachment_api | 上传接口返回 attachment_id |
|
||||
| test_upload_permission_denied | 不能向他人对话上传文件 |
|
||||
| test_stream_triggers_workflow | 发送命中提示词后返回 workflow meta |
|
||||
| test_batch_status_permission | 不能查询他人批次 |
|
||||
| test_export_download_permission | 不能下载他人导出文件 |
|
||||
|
||||
### 12.3 集成测试
|
||||
|
||||
| 用例 | 目标 |
|
||||
| --- | --- |
|
||||
| test_file_summary_zip_workflow | zip 上传后完整工作流成功 |
|
||||
| test_file_summary_multi_file_workflow | 多文件上传后完整工作流成功 |
|
||||
| test_single_file_failure_not_blocking | 单文件失败不阻断批次 |
|
||||
| test_workflow_events_created | 节点事件按顺序写入数据库 |
|
||||
| test_markdown_and_excel_exports | Markdown 与 Excel 文件生成成功 |
|
||||
|
||||
### 12.4 前端验证
|
||||
|
||||
| 用例 | 目标 |
|
||||
| --- | --- |
|
||||
| 拖拽上传 | 右侧上传区展示文件列表 |
|
||||
| 提示词触发 | 发送“自动汇总文件目录与页数”后创建工作流卡片 |
|
||||
| 状态实时更新 | SSE 事件驱动节点状态变化 |
|
||||
| 页面刷新恢复 | 刷新后右侧卡片恢复当前批次状态 |
|
||||
| Markdown 表格 | 对话消息中表格和下载链接正常显示 |
|
||||
|
||||
---
|
||||
|
||||
## 十三、开发顺序
|
||||
|
||||
1. 增加依赖与模型字段,生成迁移。
|
||||
2. 实现文件上传暂存接口和存储目录策略。
|
||||
3. 实现 workflow_trigger,根据提示词决定是否启动工作流。
|
||||
4. 实现 SkillRegistry、WorkflowExecutor 和 WorkflowEvent。
|
||||
5. 实现压缩包解压、文件扫描、页数统计服务。
|
||||
6. 实现 Markdown 报告与 Excel 导出。
|
||||
7. 改造前端三栏布局、拖拽上传区和工作流卡片。
|
||||
8. 增加 Markdown 渲染能力。
|
||||
9. 补齐权限测试、工作流测试和前端手工验证。
|
||||
|
||||
---
|
||||
|
||||
## 十四、参考依据
|
||||
|
||||
本设计采用轻量 Python 库优先方案,依据如下:
|
||||
|
||||
| 能力 | 依据 |
|
||||
| --- | --- |
|
||||
| PDF 页数 | pypdf 的 PdfReader 可读取 pages |
|
||||
| docx 元数据 | python-docx 支持 core properties |
|
||||
| pptx 幻灯片 | python-pptx 可读取 presentation slides |
|
||||
| xlsx 工作表 | openpyxl 可读取 workbook worksheets |
|
||||
| xls 工作表 | xlrd 支持读取历史 xls 工作簿 |
|
||||
| 老 Office 元数据 | olefile 可读取 OLE2 复合文档结构 |
|
||||
| 7z 解压 | py7zr 支持 7z 压缩格式处理 |
|
||||
| rar 解压 | rarfile 通常依赖外部 unrar/unar/7z 工具,故本设计优先系统 7z |
|
||||
328
docs/需求分析/1.自动汇总.md
Normal file
328
docs/需求分析/1.自动汇总.md
Normal file
@@ -0,0 +1,328 @@
|
||||
# 自动汇总文件夹文件目录与页数流程需求分析
|
||||
|
||||
## 文档信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 原始材料 | docs/原始材料/【模拟题二】试剂盒临床注册文件准备与审核Agent.docx |
|
||||
| 功能主题 | 自动汇总注册申报资料文件目录与页数 |
|
||||
| 分析日期 | 2026-06-05 |
|
||||
| 分析版本 | V1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 一、需求背景
|
||||
|
||||
试剂盒 NMPA 注册申报过程中,研发或注册人员需要准备大量文件,包括产品技术要求、说明书、检测报告、临床评估资料等。原始材料中明确提出智能体需要具备“自动汇总注册申报文件夹中的所有文件及页数”的能力,作为后续法规完整性检查、缺失文件预警、信息提取和一致性核查的基础数据来源。
|
||||
|
||||
本功能目标是:用户在 AI 对话框上传注册申报资料压缩包、文件夹或多个散装文件后,系统自动扫描资料结构,统计各文件的目录层级、文件类型、页数、路径和处理状态,生成 Markdown 汇总报告与 Excel 文件,并在 AI 对话框中展示简表和下载链接,同时将汇总结果存入数据库。
|
||||
|
||||
---
|
||||
|
||||
## 二、需求范围
|
||||
|
||||
### 2.1 本期范围
|
||||
|
||||
| 序号 | 范围项 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 1 | 文件上传入口 | 用户通过现有 AI 对话框上传压缩包、文件夹或多个散装文件 |
|
||||
| 2 | 文件解析 | 系统识别上传内容并展开为待扫描文件清单 |
|
||||
| 3 | 目录汇总 | 保留原始目录层级,散装文件归入默认上传批次目录 |
|
||||
| 4 | 页数统计 | 默认支持 pdf、doc、docx、xls、xlsx、ppt、pptx |
|
||||
| 5 | 结果展示 | AI 对话框中展示 Markdown 简表 |
|
||||
| 6 | 文件导出 | 生成 Markdown 报告与 Excel 汇总表,并在对话框提供下载链接 |
|
||||
| 7 | 数据存档 | 上传批次、文件明细、页数、异常状态、导出文件信息写入数据库 |
|
||||
|
||||
### 2.2 非本期范围
|
||||
|
||||
| 序号 | 范围项 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 1 | NMPA 法规完整性核查 | 依赖目录汇总结果,属于后续流程 |
|
||||
| 2 | 产品关键信息抽取 | 依赖具体文档内容解析,属于后续流程 |
|
||||
| 3 | 文档一致性核查 | 依赖信息抽取结果,属于后续流程 |
|
||||
| 4 | 合规风险预警 | 本功能仅输出文件扫描异常,不输出法规合规风险 |
|
||||
|
||||
---
|
||||
|
||||
## 三、用户角色与使用场景
|
||||
|
||||
| 角色 | 诉求 | 典型场景 |
|
||||
| --- | --- | --- |
|
||||
| 注册人员 | 快速了解申报资料是否完整、页数是否可统计 | 上传供应商或研发团队整理好的注册资料压缩包 |
|
||||
| 审核人员 | 获得可复核的文件目录和页数清单 | 在审核前获取 Markdown 简表和 Excel 明细 |
|
||||
| 系统管理员 | 追踪上传批次和处理异常 | 查看数据库存档,定位文件解析失败原因 |
|
||||
|
||||
---
|
||||
|
||||
## 四、业务流程分析
|
||||
|
||||
### 4.1 主流程
|
||||
|
||||
```text
|
||||
用户进入 AI 对话框
|
||||
-> 上传压缩包、文件夹或多个散装文件
|
||||
-> 用户发送“自动汇总文件目录与页数”类指令
|
||||
-> 系统创建上传批次
|
||||
-> 系统保存原始上传文件
|
||||
-> 系统识别上传类型
|
||||
-> 如为压缩包则解压,如为文件夹或散装文件则直接扫描
|
||||
-> 系统遍历文件并识别目录层级
|
||||
-> 系统过滤支持的文件类型
|
||||
-> 系统计数每个支持文件的页数
|
||||
-> 系统记录不支持或统计失败的文件异常
|
||||
-> 系统生成 Markdown 报告与 Excel 文件
|
||||
-> 系统在 AI 对话框展示 Markdown 简表和下载链接
|
||||
-> 系统将批次和明细数据写入数据库
|
||||
-> 结束
|
||||
```
|
||||
|
||||
### 4.2 分支流程
|
||||
|
||||
| 分支场景 | 流程说明 |
|
||||
| --- | --- |
|
||||
| 上传压缩包 | 系统校验压缩包格式,解压至临时工作目录,按解压后的目录结构统计 |
|
||||
| 上传文件夹 | 系统保留文件夹层级,扫描所有子目录文件 |
|
||||
| 上传多个散装文件 | 系统生成默认批次目录,将所有文件视为同一层级 |
|
||||
| 存在不支持类型 | 文件进入明细表,页数为空,统计状态为“不支持”,异常说明记录文件类型 |
|
||||
| 单个文件页数统计失败 | 不中断整个批次,统计状态为“失败”,异常说明记录失败原因 |
|
||||
| 重名文件 | 以完整相对路径区分;散装文件重名时使用系统保存路径或自动重命名策略避免覆盖 |
|
||||
|
||||
### 4.3 异常流程
|
||||
|
||||
| 异常场景 | 处理方式 |
|
||||
| --- | --- |
|
||||
| 上传文件为空 | 对话框提示“未检测到可处理文件”,不生成报告 |
|
||||
| 压缩包损坏或无法解压 | 批次状态标记为失败,对话框展示错误原因 |
|
||||
| 文件被加密或受保护 | 明细记录该文件,页数统计失败,异常说明标记“文件加密或无法读取” |
|
||||
| 文件过大或处理超时 | 明细记录超时状态,批次可继续处理其他文件 |
|
||||
| Excel/Markdown 导出失败 | 对话框提示导出失败,数据库保留扫描明细与失败原因 |
|
||||
|
||||
---
|
||||
|
||||
## 五、功能模块梳理
|
||||
|
||||
### 5.1 核心功能
|
||||
|
||||
| 序号 | 功能名称 | 功能描述 | 优先级 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | 上传资料接收 | 支持用户在 AI 对话框上传压缩包、文件夹或多个散装文件 | P0 |
|
||||
| 2 | 上传批次管理 | 为每次汇总创建批次编号,记录用户、时间、来源会话和处理状态 | P0 |
|
||||
| 3 | 压缩包解压 | 支持压缩包上传后的安全解压和目录结构保留 | P0 |
|
||||
| 4 | 文件遍历扫描 | 递归扫描上传范围内所有文件,生成相对路径和目录层级 | P0 |
|
||||
| 5 | 文件类型识别 | 根据扩展名和必要的文件头信息识别 pdf、doc、docx、xls、xlsx、ppt、pptx | P0 |
|
||||
| 6 | 页数统计 | 对支持文件统计页数或页签/幻灯片数量 | P0 |
|
||||
| 7 | Markdown 简表展示 | 在 AI 对话框展示可解析的 Markdown 表格摘要 | P0 |
|
||||
| 8 | Markdown 报告导出 | 生成完整 Markdown 汇总报告 | P0 |
|
||||
| 9 | Excel 明细导出 | 生成 Excel 文件,包含所有文件明细和统计状态 | P0 |
|
||||
| 10 | 数据库存档 | 保存批次、文件明细、统计结果、异常说明和导出文件记录 | P0 |
|
||||
|
||||
### 5.2 辅助功能
|
||||
|
||||
| 序号 | 功能名称 | 功能描述 | 优先级 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | 下载链接生成 | 在 AI 回复中提供 Markdown 报告和 Excel 文件下载链接 | P0 |
|
||||
| 2 | 处理进度提示 | 对较大批次展示“上传中、解析中、统计中、导出中、已完成”等状态 | P1 |
|
||||
| 3 | 文件过滤规则 | 支持过滤系统隐藏文件、临时文件和空文件 | P1 |
|
||||
| 4 | 统计摘要 | 输出文件总数、支持文件数、统计成功数、失败数、总页数 | P1 |
|
||||
| 5 | 历史记录查询 | 可通过对话记录或批次记录回看历史汇总结果 | P1 |
|
||||
|
||||
### 5.3 隐含功能
|
||||
|
||||
| 序号 | 功能名称 | 功能描述 | 来源说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | AI 对话框附件上传能力 | 现有对话框需要支持上传附件并关联会话消息 | 用户要求“在 AI 对话框中提供下载连接” |
|
||||
| 2 | Markdown 渲染能力 | 现有对话框需要能解析表格、链接等 Markdown 内容 | 用户要求“对话框能正常解析 md 格式” |
|
||||
| 3 | 文件访问权限控制 | 下载链接只能允许当前用户或授权用户访问 | 涉及注册资料敏感文件 |
|
||||
| 4 | 临时文件清理 | 解压目录和处理中间文件需要定期清理 | 压缩包和大文件处理会产生临时文件 |
|
||||
|
||||
---
|
||||
|
||||
## 六、数据实体分析
|
||||
|
||||
### 6.1 实体列表
|
||||
|
||||
| 序号 | 实体名称 | 字段说明 | 关联实体 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | 汇总批次 | 批次编号、用户、会话、上传类型、文件数量、总页数、状态、创建时间、完成时间 | 会话、文件明细、导出文件 |
|
||||
| 2 | 文件明细 | 文件序号、目录层级、文件名、文件类型、页数、路径、统计状态、异常说明 | 汇总批次 |
|
||||
| 3 | 导出文件 | 文件类型、文件名、保存路径、下载地址、生成状态、生成时间 | 汇总批次 |
|
||||
| 4 | 上传原始文件 | 原始文件名、保存路径、大小、文件类型、上传时间 | 汇总批次 |
|
||||
|
||||
### 6.2 文件明细字段
|
||||
|
||||
| 字段 | 类型建议 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| file_index | Integer | 是 | 文件序号,按目录遍历顺序生成 |
|
||||
| directory_level | String | 是 | 目录层级,如“1/2/3”或“注册资料/产品技术要求” |
|
||||
| file_name | String | 是 | 文件名,不含目录 |
|
||||
| file_type | String | 是 | 文件扩展名或识别后的类型 |
|
||||
| page_count | Integer | 否 | 页数,统计失败或不支持时为空 |
|
||||
| relative_path | String | 是 | 相对上传根目录的路径 |
|
||||
| statistics_status | String | 是 | 成功、失败、不支持、跳过 |
|
||||
| error_message | Text | 否 | 异常说明 |
|
||||
|
||||
### 6.3 实体关系
|
||||
|
||||
```text
|
||||
会话 1:N 汇总批次
|
||||
汇总批次 1:N 上传原始文件
|
||||
汇总批次 1:N 文件明细
|
||||
汇总批次 1:N 导出文件
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、输出要求
|
||||
|
||||
### 7.1 AI 对话框简表
|
||||
|
||||
AI 回复内容必须使用前端可解析的 Markdown 格式,至少包含统计摘要、文件简表和下载链接。
|
||||
|
||||
示例:
|
||||
|
||||
```markdown
|
||||
已完成文件目录与页数汇总。
|
||||
|
||||
| 指标 | 数量 |
|
||||
| --- | --- |
|
||||
| 文件总数 | 24 |
|
||||
| 统计成功 | 21 |
|
||||
| 统计失败 | 2 |
|
||||
| 不支持 | 1 |
|
||||
| 总页数 | 386 |
|
||||
|
||||
| 序号 | 目录层级 | 文件名 | 类型 | 页数 | 状态 | 异常说明 |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| 1 | 注册资料/说明书 | 说明书.docx | docx | 12 | 成功 | |
|
||||
| 2 | 注册资料/检测报告 | 检测报告.pdf | pdf | 38 | 成功 | |
|
||||
|
||||
[下载 Markdown 报告](download-url)
|
||||
[下载 Excel 明细](download-url)
|
||||
```
|
||||
|
||||
### 7.2 Markdown 报告
|
||||
|
||||
Markdown 报告应包含:
|
||||
|
||||
| 模块 | 内容 |
|
||||
| --- | --- |
|
||||
| 汇总信息 | 批次编号、上传用户、上传时间、处理完成时间、上传类型 |
|
||||
| 统计摘要 | 文件总数、支持文件数、成功数、失败数、不支持数、总页数 |
|
||||
| 文件明细 | 文件序号、目录层级、文件名、文件类型、页数、路径、统计状态、异常说明 |
|
||||
| 异常清单 | 统计失败、不支持、解压失败、加密文件、超时文件 |
|
||||
| 处理说明 | 支持范围、页数统计口径、待确认事项 |
|
||||
|
||||
### 7.3 Excel 导出
|
||||
|
||||
Excel 至少包含两个工作表:
|
||||
|
||||
| 工作表 | 内容 |
|
||||
| --- | --- |
|
||||
| 汇总信息 | 批次信息、统计摘要 |
|
||||
| 文件明细 | 文件序号、目录层级、文件名、文件类型、页数、路径、统计状态、异常说明 |
|
||||
|
||||
---
|
||||
|
||||
## 八、页数统计口径
|
||||
|
||||
| 文件类型 | 统计口径 | 备注 |
|
||||
| --- | --- | --- |
|
||||
| pdf | PDF 页面数量 | 可使用 pdfplumber 或 PyMuPDF |
|
||||
| doc | Word 文档页数 | 可通过 LibreOffice 转 PDF 后统计,或读取文档属性;实现方案待技术验证 |
|
||||
| docx | Word 文档页数 | 可通过 LibreOffice 转 PDF 后统计,或读取文档属性;实现方案待技术验证 |
|
||||
| xls | Excel 工作表打印页数或页签数量 | 具体口径待确认 |
|
||||
| xlsx | Excel 工作表打印页数或页签数量 | 具体口径待确认 |
|
||||
| ppt | 幻灯片数量 | 可转换或读取演示文稿结构 |
|
||||
| pptx | 幻灯片数量 | 可读取演示文稿结构 |
|
||||
|
||||
> 待确认:Excel 的“页数”是否按打印分页统计,还是按工作表数量统计。建议 Demo 阶段先按工作表数量统计,并在报告中明确口径。
|
||||
|
||||
---
|
||||
|
||||
## 九、非功能性需求
|
||||
|
||||
### 9.1 性能要求
|
||||
|
||||
| 项目 | 要求 |
|
||||
| --- | --- |
|
||||
| 小批次文件 | 50 个文件以内,应在 30 秒内完成汇总 |
|
||||
| 中等批次文件 | 200 个文件以内,应支持异步处理或进度提示 |
|
||||
| 大文件处理 | 单文件超时不影响其他文件统计 |
|
||||
|
||||
### 9.2 安全要求
|
||||
|
||||
| 项目 | 要求 |
|
||||
| --- | --- |
|
||||
| 上传安全 | 限制可处理类型,避免执行上传文件中的脚本或宏 |
|
||||
| 解压安全 | 防止路径穿越,限制解压目录在系统工作目录内 |
|
||||
| 下载权限 | 下载链接需要校验登录用户和会话权限 |
|
||||
| 数据隔离 | 不同用户上传文件和统计结果不可互相访问 |
|
||||
| 敏感资料保护 | 原始上传文件、报告和 Excel 文件应存储在受控目录 |
|
||||
|
||||
### 9.3 可用性要求
|
||||
|
||||
| 项目 | 要求 |
|
||||
| --- | --- |
|
||||
| 对话展示 | Markdown 表格、链接在现有 AI 对话框中可正常渲染 |
|
||||
| 失败可见 | 失败文件需要展示具体原因,便于用户补传或修复 |
|
||||
| 可追溯 | 每份导出报告能关联到具体上传批次和会话 |
|
||||
|
||||
---
|
||||
|
||||
## 十、疑问点与待确认事项
|
||||
|
||||
### 10.1 功能疑问
|
||||
|
||||
| 序号 | 疑问内容 | 建议 | 状态 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | “文件目录参考附件”尚未上传,无法确认标准目录样例 | 附件上传后补充目录样例和字段映射 | 待确认 |
|
||||
| 2 | 文件夹上传在浏览器端是否采用目录上传能力 | 前端可使用 webkitdirectory 或改为要求用户上传压缩包 | 待确认 |
|
||||
| 3 | 散装文件是否需要用户手动指定归属目录 | Demo 阶段统一归入“散装上传”目录 | 待确认 |
|
||||
|
||||
### 10.2 业务规则疑问
|
||||
|
||||
| 序号 | 疑问内容 | 建议 | 状态 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | Excel 页数统计口径不明确 | Demo 阶段按工作表数量,正式版可按打印分页 | 待确认 |
|
||||
| 2 | doc/docx 页数是否必须与 Word 打开后的实际页数完全一致 | 建议通过转换 PDF 后统计,提高一致性 | 待确认 |
|
||||
| 3 | 是否需要按 NMPA 申报资料目录自动排序 | 本功能先按上传目录顺序,后续法规完整性检查再做标准目录匹配 | 待确认 |
|
||||
|
||||
### 10.3 技术方案疑问
|
||||
|
||||
| 序号 | 疑问内容 | 可选方案 | 状态 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | Office 文档页数统计依赖 | LibreOffice 转 PDF、文档属性读取、商业 Office 自动化 | 待确认 |
|
||||
| 2 | 大文件处理模式 | 同步处理、后台任务、队列任务 | 待确认 |
|
||||
| 3 | Markdown 渲染方案 | 前端引入 Markdown 渲染库,或后端转换为安全 HTML | 待确认 |
|
||||
|
||||
### 10.4 数据定义疑问
|
||||
|
||||
| 序号 | 疑问内容 | 建议 | 状态 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | 导出文件保存周期未明确 | Demo 阶段永久保存,正式版增加过期清理策略 | 待确认 |
|
||||
| 2 | 原始上传文件是否长期保留 | 建议至少保留到关联审核流程结束 | 待确认 |
|
||||
| 3 | 下载链接是否需要一次性或有效期控制 | 注册资料敏感,建议正式版增加有效期和权限校验 | 待确认 |
|
||||
|
||||
---
|
||||
|
||||
## 十一、验收标准
|
||||
|
||||
| 序号 | 验收项 | 验收标准 |
|
||||
| --- | --- | --- |
|
||||
| 1 | 压缩包上传汇总 | 上传包含多层目录的压缩包后,系统能保留层级并统计支持文件页数 |
|
||||
| 2 | 散装文件上传汇总 | 上传多个散装文件后,系统能生成同一批次汇总结果 |
|
||||
| 3 | Markdown 展示 | AI 对话框能展示摘要表和文件简表,表格格式正常 |
|
||||
| 4 | 下载链接 | AI 回复中提供 Markdown 报告和 Excel 明细下载链接,点击可下载 |
|
||||
| 5 | 数据存档 | 数据库中能查询到批次记录、文件明细和导出文件记录 |
|
||||
| 6 | 异常不中断 | 单个文件失败时不影响其他文件统计,失败原因可见 |
|
||||
| 7 | 支持类型覆盖 | pdf、doc、docx、xls、xlsx、ppt、pptx 均能进入处理流程 |
|
||||
|
||||
---
|
||||
|
||||
## 十二、下一步建议
|
||||
|
||||
1. 上传“文件目录参考附件”,补充标准目录样例和演示数据。
|
||||
2. 明确 Excel 页数统计口径,决定 Demo 版是否按工作表数量统计。
|
||||
3. 设计数据库表结构,包括汇总批次、文件明细、上传原始文件和导出文件。
|
||||
4. 改造 AI 对话框,增加附件上传、Markdown 渲染和下载链接展示能力。
|
||||
5. 实现文件扫描与页数统计服务,优先打通 pdf、docx、xlsx、pptx 的 Demo 流程。
|
||||
Reference in New Issue
Block a user