Files
DEMO-AGENT/docs/数据库设计/1.自动汇总.md

23 KiB
Raw Blame History

自动汇总文件夹文件目录与页数流程数据库设计

文档信息

项目 内容
需求分析文档 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 图

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 对话与附件

Conversation 1:N ra_file_attachment

规则:

规则 说明
上传即存储 用户上传后立即创建 FileAttachment
对话隔离 附件只能被同一 Conversation 下的批次使用
多版本 同一 conversation + original_name 可存在多个 version_no
默认版本 is_active=true 的记录作为默认候选版本
逻辑删除 删除附件时设置 upload_status=deleted不立即物理删除

5.2 对话与批次

Conversation 1:N ra_file_summary_batch

规则:

规则 说明
多次汇总 同一对话允许多次触发自动汇总
提示词触发 批次由用户消息触发,可关联 trigger_message_id
批次固化 批次启动时固化本次使用的附件版本

5.3 批次与附件版本

ra_file_summary_batch N:M ra_file_attachment

通过 ra_file_summary_batch_attachment 实现。

规则:

规则 说明
不串文件 工作流只能读取中间表绑定的附件
保留历史 即使附件后续上传新版本,历史批次仍指向旧版本
版本选择 用户未选择时默认使用同名文件的最新 active 版本

5.4 批次与文件明细

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 为准。

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);
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);
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);
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);
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);
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);
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 版本号生成

同一对话同名文件上传时:

version_no = max(existing version_no) + 1

若新版本设为默认版本,需要将旧版本 is_active 更新为 false。

8.4 逻辑删除

附件删除时:

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_tableindexesconstraints
  4. 执行 python manage.py makemigrations review_agent 生成迁移。
  5. 执行 python manage.py migrate 验证 SQLite 可落表。
  6. 编写模型级测试,覆盖同名附件版本、批次附件绑定、唯一约束和权限查询。