Files
leaudit-platform-backend/docs/接口/文档上传与列表接口分析.md
T

295 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 文档上传与列表 — 接口分析 & 接入方案
> 所有信息已逐文件验证。源代码位置均已标注。
---
## 一、后端接口(已验证)
### 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 |