12 KiB
合同文档上传与接口说明(v2)
本文档详细说明 DocAuditAI 中与“合同文档上传”相关的 API 接口、参数、请求示例、响应示例以及后端处理流程。路径均以后端实际路由挂载为准:所有业务接口统一前缀为 /admin,本文档涉及的文档管理接口统一前缀为 /admin/documents。
鉴权与基础信息
- 基础前缀:
/admin/documents - 认证方式:
- 推荐:
Authorization: Bearer <JWT>(后端中间件会将用户信息注入req.state.current_user) - 参考文档:
docs/接口认证方式统一说明.md
- 推荐:
- 存储后端: MinIO(参考
core/storage/minio_client.py与core/config.py中的MINIO_CONFIG) - 异步任务: Celery,队列名称默认为
f"{REDIS_KEY_PREFIX}_tasks"
文档类型枚举(后端基准)
来自 core/utils/enums.py:
1合同文档(代码: HT,完整中文: 合同文档)2行政许可决定书(代码: XZXK,完整中文: 行政许可决定书)3行政处罚决定书(代码: XZCF,完整中文: 行政处罚决定书)4合同对比模板(代码: HTMB,完整中文: 合同对比模板)99其他文档(代码: QT,完整中文: 其他文档)
说明:客户端在
upload_info中通常只需要传type_id,后端会基于枚举推导短代码与中文全称。
接口清单
1) 上传通用文档(支持合同/许可/处罚/其他)
-
方法与路径:
POST /admin/documents/upload -
表单参数:
file: 文件(支持 PDF、Word:.doc/.docx 会自动转换为 PDF)upload_info: JSON 字符串,示例:{ "type_id": 1, "document_number": "HT_20250111_120000_001", "is_test_document": true, "remark": "示例备注", "evaluation_level": "普通" }
-
cURL 示例:
curl -X POST "http://127.0.0.1:8008/admin/documents/upload" \ -H "Authorization: Bearer <YOUR_TOKEN>" \ -F "file=@./samples/contract.pdf" \ -F 'upload_info={"type_id":1,"remark":"示例备注","evaluation_level":"普通"}' -
JavaScript fetch 示例:
const form = new FormData(); form.append('file', fileInput.files[0]); form.append('upload_info', JSON.stringify({ type_id: 1, remark: '示例备注' })); const res = await fetch('/admin/documents/upload', { method: 'POST', headers: { Authorization: 'Bearer ' + token }, body: form }); const data = await res.json(); -
响应示例:
{ "success": true, "result": { "id": 123, "file_name": "contract.pdf", "file_size": 1024000, "file_url": "https://minio.example.com/bucket/documents/...", "type_id": 1, "type_description": "HT", "type_full_description": "合同文档", "document_number": "HT_20250111_120000_001", "storage_type": "minio", "background_processing": true, "api_version": "v2", "is_test_document": true, "remark": "示例备注", "evaluation_level": "普通" } }
说明:合同(type_id=1)、许可(2)、处罚(3)会自动投递 Celery 任务进行 OCR/抽取/评查。
2) 上传合同模板(用于与合同文档结构对比)
-
方法与路径:
POST /admin/documents/upload_contract_template -
表单参数:
file: 模板 PDF/Word(Word将自动转PDF)upload_info: JSON 字符串,字段:document_id(必填):源合同文档IDcomparison_id(可选):已有对比记录ID,未提供将自动创建
-
cURL 示例:
curl -X POST "http://127.0.0.1:8008/admin/documents/upload_contract_template" \ -H "Authorization: Bearer <YOUR_TOKEN>" \ -F "file=@./samples/contract_template.pdf" \ -F 'upload_info={"document_id":123}' -
响应示例:
{ "success": true, "result": { "id": 456, // comparison_id(新建或传入) "document_id": 789, // 模板对应的新建文档ID "file_name": "contract_template.pdf", "file_size": 204800, "file_url": "https://minio.example.com/...", "template_path": "documents/.../contract_template.pdf", "template_contract_name": "contract_template.pdf", "status": "Waiting", "api_version": "v2" } }
3) 合同文档追加附件并合并
-
方法与路径:
POST /admin/documents/contracts/{document_id}/append_attachments -
路径参数:
document_id: 合同文档ID(type_id 必须为 1)
-
表单参数:
files: 多个附件(支持 PDF、Word(doc/docx)、ZIP、RAR;ZIP/RAR 内仅合并其中的 PDF)merge_mode:overwrite(默认,覆盖原文档路径)或new(新建一条文档记录)is_reprocess: 是否触发 OCR/抽取/评查(默认 true)remark: 备注
-
cURL 示例(支持压缩包作为附件来源):
curl -X POST "http://127.0.0.1:8008/admin/documents/contracts/123/append_attachments" \ -H "Authorization: Bearer <YOUR_TOKEN>" \ -F "files=@./samples/appendix1.pdf" \ -F "files=@./samples/attachments.zip" \ -F "merge_mode=overwrite" -F "is_reprocess=true" -F "remark=附加材料" -
响应示例(new 模式):
{ "success": true, "result": { "id": 901, // 新文档ID "file_name": "contract_merged_20250111123000.pdf", "file_size": 512000, "file_url": "https://minio.example.com/...", "type_id": 1, "document_number": "HT_20250111_...", "storage_type": "minio", "is_merged": true, "attachments_count": 2, "merge_mode": "new", "background_processing": true, "api_version": "v2" } }
4) 合同模板记录追加附件并合并
-
方法与路径:
POST /admin/documents/contract_templates/{comparison_id}/append_attachments -
路径参数:
comparison_id: 合同结构对比记录ID
-
表单参数:
files: 多个 PDF 附件is_reprocess: 是否触发模板 OCR 与对比(默认 true)remark: 备注
-
cURL 示例:
curl -X POST "http://127.0.0.1:8008/admin/documents/contract_templates/456/append_attachments" \ -H "Authorization: Bearer <YOUR_TOKEN>" \ -F "files=@./samples/template_appendix.pdf" \ -F "is_reprocess=true" -F "remark=补充分条款" -
响应示例:
{ "success": true, "result": { "id": 456, "document_id": 790, // 新模板文档ID "file_name": "template_merged_20250111123500.pdf", "file_size": 256000, "file_url": "https://minio.example.com/...", "template_path": "documents/.../template_merged_20250111123500.pdf", "status": "Waiting", "api_version": "v2", "is_merged": true, "attachments_count": 1 } }
5) 一体化:上传并分配交叉评查任务(支持单 PDF 或压缩包)
-
方法与路径:
POST /admin/documents/cross_review/documents/upload_and_assign -
表单参数:
file: 单个 PDF、Word 或压缩包(zip/rar/7z/tar)upload_info: JSON 字符串(至少包含type_id)assign_user_ids: JSON 数组字符串(如[1,2,3])
-
cURL 示例(批量:zip 内多个 PDF 将逐个入库并触发处理):
curl -X POST "/admin/documents/cross_review/documents/upload_and_assign" \ -H "Authorization: Bearer <YOUR_TOKEN>" \ -F "file=@./samples/contracts.zip" \ -F 'upload_info={"type_id":1,"task_name":"自动分配任务","doc_type":"合同文档"}' \ -F 'assign_user_ids=[1,2,3]' -
响应示例(压缩包):
{ "success": true, "message": "已处理3个PDF。", "pdf_files": ["a.pdf","b.pdf","c.pdf"], "results": [ {"success": true, "document_id": 101, ... }, ... ], "assigned_users": { "result": { /* 任务分配详情 */ } } }
6) 获取文档列表(按用户权限过滤)
-
方法与路径:
GET /admin/documents/list -
查询参数:
page(默认 1)、page_size(默认 20,最大 100)search(可选,模糊匹配 name 或 document_number)type_id(可选)、status(可选)- 认证后端会基于
req.state.current_user.user_id过滤用户可见文档
-
cURL 示例:
curl -G "/admin/documents/list" \ -H "Authorization: Bearer <YOUR_TOKEN>" \ --data-urlencode "page=1" \ --data-urlencode "page_size=20" \ --data-urlencode "search=合同" -
响应示例:
{ "total": 42, "items": [ { "id": 123, "name": "contract.pdf", "document_number": "HT_20250111_120000_001", "type_id": 1, "status": "Processed", "upload_time": "2025-01-11T12:01:02", "created_at": "2025-01-11T12:00:00", "updated_at": "2025-01-11T12:10:00" } ], "page": 1, "page_size": 20 }
处理流程说明(后端链路)
关键实现文件:
app/routes/v2/documents/documents.py
- 校验与预处理
- 校验文件大小(
MAX_FILE_SIZE)与类型 - Word 自动转换为 PDF(
core/utils/office_convert.ensure_pdf_from_upload) - 生成安全文件名
- 校验文件大小(
- 生成存储路径并上传 MinIO
- 路径规则:
documents/{INSTANCE_NAME}/{中文类型}/{YYYY}/{MM月DD日}/{文件名_时分秒}/{文件名} - 上传后得到
storage_path与file_url
- 路径规则:
- 写入数据库(PostgREST)
- 组装文档数据(含
user_id、file_size、remark等) document_service.async_insert_document入库,返回文档ID
- 组装文档数据(含
- 追加合并详细逻辑(仅合同附件追加支持压缩包)
- 入口:
POST /admin/documents/contracts/{document_id}/append_attachments - 校验:
document_id必须为合同文档(type_id=1),否则返回错误 - 下载原 PDF:从 MinIO 将原文档
path下载到本地临时文件 - 处理附件输入:
- 支持的外层类型:
.pdf、.doc、.docx、.zip、.rar - 当为 PDF:直接保存到临时目录,加入合并列表
- 当为 Word(.doc/.docx):先转换为 PDF(同单文件上传逻辑),再加入合并列表
- 当为 ZIP/RAR:先落地为临时压缩包,再调用
document_upload_service.extract_archive解压,仅收集其中的 PDF 路径加入合并列表 - 对于其他类型:返回错误(仅支持 PDF/ZIP/RAR/Word)
- 若最终未收集到任何 PDF:返回错误
- 支持的外层类型:
- 合并顺序:始终使用【原PDF】+【附件PDF列表】顺序,调用
merge_pdfs生成合并结果临时文件 - 目标路径:沿用原
path的父目录,文件名追加_merged_{时间戳}.pdf - 入库与更新:
merge_mode=overwrite(默认):更新原文档path/name/file_size/status=Waiting/remarkmerge_mode=new:调用文档写入流程新建记录,并带上remark
- 触发处理:当
is_reprocess=true时,读入合并后的字节内容,调用_submit_ocr_task_v2投递后续 OCR/抽取/评查 - 返回:新/原文档标识、URL、大小、是否合并、附件数量、
merge_mode等
- 入口:
- 投递异步任务(Celery)
- 合同(1):
process_contract_ocr - 行政许可(2)、行政处罚(3):
process_document_ocr_v2 - 合同模板(4):
process_contract_template_ocr - 队列:
{REDIS_KEY_PREFIX}_tasks,默认过期 24h,time_limit 600s
- 合同(1):
- 后处理与状态更新
- OCR完成后按需进入抽取与评查
- 文档状态:
Cutting/Extractioning/Evaluationing/Processed/Failed
错误处理与返回
- 统一响应模型:
- 成功:
{"success": true, "result": { ... }} - 失败:
{"success": false, "error": "错误消息"}
- 成功:
- 常见错误:
400:upload_info非法、文件为空或类型不支持413:文件过大(超过MAX_FILE_SIZE)500:存储/数据库/任务投递异常
使用建议与注意事项
- 合同类建议始终以 PDF 上传,可减少转换耗时与兼容性问题
- 压缩包仅支持解出 PDF 后逐一入库与处理(zip/rar/7z/tar)
- 建议合理设置
remark与evaluation_level便于后续筛选 - 若需追踪异步任务状态,请结合 Flower 与后端日志(
logs/)
变更记录
- 2025-09-11:首版编制,依据
app/routes/v2/documents/documents.py与相关服务模块撰写