docs: add document upload/list API analysis and integration plan

This commit is contained in:
wren
2026-04-30 12:29:00 +08:00
parent 8f307aecba
commit 969c3aaf35
@@ -0,0 +1,294 @@
# 文档上传与列表 — 接口分析 & 接入方案
> 所有信息已逐文件验证。源代码位置均已标注。
---
## 一、后端接口(已验证)
### 1.1 POST /api/upload — 文档上传
**源码**: `fastapi_modules/fastapi_leaudit/controllers/documentController.py:20-45`
**实现**: `fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py:44-246`
```
URL: POST /api/upload
格式: multipart/form-data (扁平字段)
```
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `file` | UploadFile | **是** | — | 文件二进制 |
| `typeId` | int | typeId/typeCode 二选一 | None | 文档类型 ID |
| `typeCode` | str | typeId/typeCode 二选一 | None | 文档类型编码,如 `contract.construction` |
| `region` | str | 否 | `"default"` | 地区标识 |
| `fileRole` | str | 否 | `"primary"` | 文件角色:primary/attachment/template |
| `createdBy` | int | 否 | None | 上传用户 ID |
| `autoRun` | bool | 否 | `false` | 是否上传后自动触发评查 |
| `speed` | str | 否 | `"normal"` | urgent / normal |
**内部流程**:
1. 校验文件名/内容非空 → 解析文档类型(查 `leaudit_document_types`
2. SHA-256 去重(仅 primary 文件):同名+同类型+同区域+同 hash → 复用已有记录,`duplicateUpload=true`
3. 非重复:生成 `internalDocumentNo`,匹配前一版本,递增 `versionNo`,写 `leaudit_documents`
4. 存 MinIO OSS,路径 `bdocs/{Region}/{TypeCode}/{Year}/...`
5.`leaudit_document_files`
6.`autoRun=true` → 调 `AuditService.Run()` 触发评查
**响应** `Result[DocumentUploadVO]` (`domian/vo/documentVo.py:8-27`):
```json
{
"code": 200,
"message": "ok",
"data": {
"documentId": 1,
"internalDocumentNo": 1712345678000000001,
"versionGroupKey": "abc123...",
"versionNo": 2,
"previousVersionId": 1,
"rootVersionId": 1,
"duplicateUpload": false,
"fileId": 5,
"typeId": 1,
"typeCode": "contract.construction",
"region": "meizhou",
"fileName": "建设工程合同.pdf",
"ossUrl": "http://minio/bdocs/...",
"speed": "normal",
"processingStatus": "waiting",
"autoRunTriggered": true,
"run": { "runId": 10, "status": "pending", ... }
}
}
```
---
### 1.2 GET /api/documents/list — 文档列表
**源码**: `controllers/documentController.py:47-67`
**实现**: `documentServiceImpl.py:248-443`
```
URL: GET /api/documents/list
格式: query params
```
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `page` | int | 否 (1) | 页码 |
| `pageSize` | int | 否 (20, max 100) | 每页条数 |
| `keyword` | str | 否 | 搜索文件名或归一化名 |
| `typeCode` | str | 否 | 文档类型编码过滤 |
| `region` | str | 否 | 地区过滤 |
| `processingStatus` | str | 否 | waiting/processing/completed/failed |
| `resultStatus` | str | 否 | pass/fail/partial/review/error |
**内部流程**:
1. `leaudit_documents d` JOIN `leaudit_document_files f` ON `f.document_id = d.id`
2. 条件: `d.is_latest_version=true`, `d.deleted_at IS NULL`, `f.is_active=true`, `f.file_role='primary'`
3. LEFT JOIN `leaudit_document_types dt`, `leaudit_audit_runs ar`
4. 子查询统计 `total_versions`(按 `version_group_key` 分组)
5. 批量加载历史版本(`is_latest_version=false` 的同组文档)
**响应** `Result[DocumentListPageVO]` (`domian/vo/documentVo.py:77-84`):
```json
{
"code": 200, "message": "ok",
"data": {
"total": 100, "page": 1, "pageSize": 20, "totalPages": 5,
"documents": [
{
"documentId": 1, "internalDocumentNo": 1712345678000000001,
"versionGroupKey": "abc123", "versionNo": 2,
"typeId": 1, "typeCode": "contract.construction",
"region": "meizhou", "normalizedName": "建设工程合同",
"fileId": 5, "fileName": "建设工程合同.pdf",
"fileExt": ".pdf", "mimeType": "application/pdf",
"fileSize": 1048576, "ossUrl": "http://minio/...",
"processingStatus": "completed",
"runStatus": "completed", "resultStatus": "pass",
"totalScore": 85, "passedCount": 10,
"failedCount": 2, "skippedCount": 0,
"hasHistory": true, "totalVersions": 2,
"historyVersions": [
{ "documentId": 1, "versionNo": 1, "fileName": "...", ... }
]
}
]
}
}
```
---
### 1.3 缺失的后端接口
| 操作 | 当前 | 需要 |
|------|------|------|
| 删除文档 | ❌ | `DELETE /api/documents/{id}` — 软删除 |
| 编辑元数据 | ❌ | `PUT /api/documents/{id}` — 更新备注、测试标记等 |
| 文档详情 | ❌ | `GET /api/documents/{id}` — 单文档查询 |
| 附件追加 | ❌ | `POST /api/documents/{id}/attachments` — fileRole=attachment |
---
## 二、前端调用现状(已验证)
### 2.1 上传 — `uploadDocumentToServer()`
**源码**: `new_doc_review/app/api/files/files-upload.ts:374-443`
```
URL: ${UPLOAD_URL}/upload (例: http://172.16.0.59:8096/upload)
格式: FormData { file: Blob, upload_info: JSON字符串 }
```
前端实际发送的 FormData:
```
file: Blob (文件二进制)
upload_info: '{"type_id":1,"evaluation_level":"normal","document_number":null,"remark":null,"is_test_document":false,"document_id":null,"is_reupload":false,"attribute_type":null}'
```
**❌ 与后端完全不兼容。** 后端期望扁平字段 `typeId`/`typeCode`/`region`/`fileRole`/`autoRun`/`speed`,不接受嵌套的 `upload_info` JSON。
### 2.2 附件追加 — `appendContractAttachments()`
**源码**: `files-upload.ts:320-336`
```
URL: ${UPLOAD_URL}/contracts/{documentId}/append_attachments
格式: FormData { attachments: File[] }
```
后端没有对应接口,需要补。
### 2.3 合同模板上传 — `uploadContractTemplate()`
**源码**: `files-upload.ts:244-259`
```
URL: ${UPLOAD_URL}/upload_contract_template
格式: FormData { file: Blob }
```
后端没有对应接口。
### 2.4 列表 — `getDocumentsListFromAPI()`
**源码**: `new_doc_review/app/api/files/documents.ts:627-784`
```
URL: ${API_BASE_URL}/api/documents/list ✅ 正确
```
✅ 列表已接入新后端,但存在两个问题:
1. **字段映射层太厚**`LeauditListItem → DocumentUI` 转换 50+ 行,且把 `fileExt` 映射为 `fileType``ossUrl` 映射为 `path`
2. **筛选项不全**`documentNumber``auditStatus``dateFrom/dateTo` 在后端列表接口未支持,前端写了 `void` 占位
### 2.5 删除 — `deleteDocument()`
**源码**: `documents.ts:540-560`
```
URL: /api/postgrest/proxy/documents?id=eq.{id}&user_id=eq.{userId}
方法: DELETE
```
❌ 走 PostgREST 直删,未接入新后端。
### 2.6 编辑 — `updateDocument()`
**源码**: `documents.ts:485-510`
```
URL: /api/postgrest/proxy/documents?id=eq.{id}
方法: PATCH
```
❌ 走 PostgREST,未接入新后端。
---
## 三、接入方案
### 第 1 步:上传接口对齐 ⭐ 最高优先级
**前端改造** `uploadDocumentToServer()`
```
改前: FormData { file, upload_info: JSON.stringify({ type_id, evaluation_level, ... }) }
POST ${UPLOAD_URL}/upload
改后: FormData { file, typeId, typeCode, region, fileRole, createdBy, autoRun, speed }
POST ${API_BASE_URL}/api/upload
```
字段映射:
| 前端旧参数 | 新后端参数 | 说明 |
|-----------|-----------|------|
| `upload_info.type_id` | `typeId` | 直接传 |
| — | `typeCode` | 补充,从 document types 查 |
| — | `region` | 从 sessionStorage / user info 取当前用户地区 |
| — | `fileRole` | `"primary"` |
| — | `createdBy` | 从 JWT payload 取 user_id |
| `evaluation_level` | `speed` | urgent/normal 映射 |
| — | `autoRun` | 默认 true(参考前端现有流程:上传后自动 treatment) |
附件和模板上传合并方案:
- 主文件:`POST /api/upload` + `fileRole=primary`
- 附件:`POST /api/upload` + `fileRole=attachment`(或批量复用主上传)
- 模板:`POST /api/upload` + `fileRole=template`
影响文件:
- `new_doc_review/app/api/files/files-upload.ts``uploadDocumentToServer()` 重写
- `new_doc_review/app/routes/files.upload.tsx``startUpload()` 适配新参数
### 第 2 步:列表字段精简
**前端字段直接对齐**,逐步去掉 `mapLeauditDocToAuditStatus``mapProcessingStatusToFileStatus` 等映射函数。`DocumentUI` 接口增加新字段,旧字段保留兼容。
影响文件:
- `new_doc_review/app/api/files/documents.ts` — 简化 `LeauditListItem → DocumentUI` 转换
### 第 3 步:补后端 CRUD
后端新增:
```
DELETE /api/documents/{id} → 软删除 (deleted_at = now())
PUT /api/documents/{id} → 更新元数据 (备注、测试标记)
GET /api/documents/{id} → 文档详情(含所有版本)
POST /api/documents/{id}/attachments → 追加附件
```
影响文件:
- `fastapi_modules/fastapi_leaudit/controllers/documentController.py`
- `fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py`
### 第 4 步:前端切割 PostgREST
`deleteDocument``updateDocument` 从前端 PostgREST 调用改为走新后端 `/api/documents/...`
### 第 5 步:清理旧依赖
确认无其他 PostgREST 调用后,移除旧 RPC 函数。
---
## 四、涉及文件清单
| 层 | 文件 | 改动类型 |
|----|------|---------|
| 后端 | `documentController.py` | 新增 3 个端点 |
| 后端 | `documentServiceImpl.py` | 新增 Delete/Update/GetById/AppendAttachments |
| 后端 | `documentVo.py` | 可能新增 VO |
| 前端 | `files-upload.ts` | 重写 uploadDocumentToServer |
| 前端 | `files.upload.tsx` | 适配新上传参数 |
| 前端 | `documents.ts` | 简化列表映射 + 切割删除/编辑到新后端 |
| 前端 | `documents.list.tsx` | 小改适配新字段 |
| 前端 | `documents.edit.tsx` | 切割到新后端 PUT |