Files
leaudit-platform-frontend/docs/合同文档上传与接口说明.md
T

12 KiB
Raw Blame History

合同文档上传与接口说明(v2

本文档详细说明 DocAuditAI 中与“合同文档上传”相关的 API 接口、参数、请求示例、响应示例以及后端处理流程。路径均以后端实际路由挂载为准:所有业务接口统一前缀为 /admin,本文档涉及的文档管理接口统一前缀为 /admin/documents


鉴权与基础信息

  • 基础前缀: /admin/documents
  • 认证方式:
    • 推荐:Authorization: Bearer <JWT>(后端中间件会将用户信息注入 req.state.current_user
    • 参考文档:docs/接口认证方式统一说明.md
  • 存储后端: MinIO(参考 core/storage/minio_client.pycore/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/WordWord将自动转PDF
    • upload_info: JSON 字符串,字段:
      • document_id(必填):源合同文档ID
      • comparison_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: 合同文档IDtype_id 必须为 1
  • 表单参数:

    • files: 多个附件(支持 PDF、Word(doc/docx)、ZIP、RARZIP/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

  1. 校验与预处理
    • 校验文件大小(MAX_FILE_SIZE)与类型
    • Word 自动转换为 PDFcore/utils/office_convert.ensure_pdf_from_upload
    • 生成安全文件名
  2. 生成存储路径并上传 MinIO
    • 路径规则:documents/{INSTANCE_NAME}/{中文类型}/{YYYY}/{MM月DD日}/{文件名_时分秒}/{文件名}
    • 上传后得到 storage_pathfile_url
  3. 写入数据库(PostgREST
    • 组装文档数据(含 user_idfile_sizeremark 等)
    • document_service.async_insert_document 入库,返回文档ID
  4. 追加合并详细逻辑(仅合同附件追加支持压缩包)
    • 入口: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/remark
      • merge_mode=new:调用文档写入流程新建记录,并带上 remark
    • 触发处理:当 is_reprocess=true 时,读入合并后的字节内容,调用 _submit_ocr_task_v2 投递后续 OCR/抽取/评查
    • 返回:新/原文档标识、URL、大小、是否合并、附件数量、merge_mode
  5. 投递异步任务(Celery
    • 合同(1):process_contract_ocr
    • 行政许可(2)、行政处罚(3):process_document_ocr_v2
    • 合同模板(4):process_contract_template_ocr
    • 队列:{REDIS_KEY_PREFIX}_tasks,默认过期 24htime_limit 600s
  6. 后处理与状态更新
    • OCR完成后按需进入抽取与评查
    • 文档状态:Cutting/Extractioning/Evaluationing/Processed/Failed

错误处理与返回

  • 统一响应模型:
    • 成功:{"success": true, "result": { ... }}
    • 失败:{"success": false, "error": "错误消息"}
  • 常见错误:
    • 400upload_info 非法、文件为空或类型不支持
    • 413:文件过大(超过 MAX_FILE_SIZE
    • 500:存储/数据库/任务投递异常

使用建议与注意事项

  • 合同类建议始终以 PDF 上传,可减少转换耗时与兼容性问题
  • 压缩包仅支持解出 PDF 后逐一入库与处理(zip/rar/7z/tar
  • 建议合理设置 remarkevaluation_level 便于后续筛选
  • 若需追踪异步任务状态,请结合 Flower 与后端日志(logs/

变更记录

  • 2025-09-11:首版编制,依据 app/routes/v2/documents/documents.py 与相关服务模块撰写