自动汇总文件夹文件目录与页数流程数据库设计
文档信息
| 项目 |
内容 |
| 需求分析文档 |
docs/1.需求分析/1.自动汇总.md |
| 功能设计文档 |
docs/2.功能设计/1.自动汇总.md |
| 详细设计文档 |
docs/3.详细设计/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 图
三、表结构设计
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 对话与附件
规则:
| 规则 |
说明 |
| 上传即存储 |
用户上传后立即创建 FileAttachment |
| 对话隔离 |
附件只能被同一 Conversation 下的批次使用 |
| 多版本 |
同一 conversation + original_name 可存在多个 version_no |
| 默认版本 |
is_active=true 的记录作为默认候选版本 |
| 逻辑删除 |
删除附件时设置 upload_status=deleted,不立即物理删除 |
5.2 对话与批次
规则:
| 规则 |
说明 |
| 多次汇总 |
同一对话允许多次触发自动汇总 |
| 提示词触发 |
批次由用户消息触发,可关联 trigger_message_id |
| 批次固化 |
批次启动时固化本次使用的附件版本 |
5.3 批次与附件版本
通过 ra_file_summary_batch_attachment 实现。
规则:
| 规则 |
说明 |
| 不串文件 |
工作流只能读取中间表绑定的附件 |
| 保留历史 |
即使附件后续上传新版本,历史批次仍指向旧版本 |
| 版本选择 |
用户未选择时默认使用同名文件的最新 active 版本 |
5.4 批次与文件明细
规则:
| 规则 |
说明 |
| 相对路径唯一 |
同一批次下 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 为准。
八、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 版本号生成
同一对话同名文件上传时:
若新版本设为默认版本,需要将旧版本 is_active 更新为 false。
8.4 逻辑删除
附件删除时:
历史批次仍可通过中间表追溯该附件。
8.5 批次选择附件
用户发送提示词触发工作流时:
| 场景 |
处理 |
| 用户显式选择附件版本 |
使用所选 attachment_id |
| 用户未选择版本 |
使用当前对话下 is_active=true 且未删除的附件 |
| 存在多个同名 active 异常 |
取 created_at 最新,并记录待修复数据异常 |
九、数据保留策略
| 数据 |
Demo 策略 |
正式部署建议 |
| 上传附件记录 |
永久保留 |
随会话归档周期清理 |
| 上传原始文件 |
永久保留 |
可按用户/项目配置保留期限 |
| 汇总批次 |
永久保留 |
保留用于审计追溯 |
| 文件明细 |
永久保留 |
保留用于历史报告复现 |
| 工作流事件 |
永久保留 |
可定期清理已完成批次的事件 |
| 导出文件 |
永久保留 |
可设置下载有效期或归档 |
十、待确认事项
| 序号 |
问题 |
当前设计 |
状态 |
| 1 |
正式部署是否从 SQLite 迁移到 PostgreSQL/MySQL |
当前按 SQLite/Django ORM 设计,保留 ORM 兼容性 |
待后续确认 |
| 2 |
同名附件 active 是否允许多个 |
设计上不允许,代码更新时应关闭旧 active |
待开发实现 |
| 3 |
文件物理删除时机 |
Demo 不物理删除 |
待后续确认 |
十一、开发顺序建议
- 在
review_agent/models.py 中新增上述 7 个模型。
- 为状态字段定义 Django
TextChoices。
- 配置
db_table、indexes、constraints。
- 执行
python manage.py makemigrations review_agent 生成迁移。
- 执行
python manage.py migrate 验证 SQLite 可落表。
- 编写模型级测试,覆盖同名附件版本、批次附件绑定、唯一约束和权限查询。