693 lines
15 KiB
Markdown
693 lines
15 KiB
Markdown
# 文档上传与评查接口
|
||
|
||
这份文档描述当前已经落地的文档上传、文档列表、自动评查、手动评查、状态查询、结果查询接口。
|
||
|
||
当前接口围绕以下业务语义设计:
|
||
|
||
- 每次前端上传都会形成一个平台内部文档实例
|
||
- 同名文档会尝试归入同一个版本组
|
||
- 同名且内容相同:
|
||
- 不新建版本
|
||
- `duplicateUpload=true`
|
||
- 如果 `autoRun=true`,仍然可以重新走一次评查流程
|
||
- 同名但内容变化:
|
||
- 新建版本
|
||
- 形成 `v2 / v3 / ...`
|
||
- 评查任务走 worker 异步执行
|
||
- 队列只有两档:
|
||
- `urgent`
|
||
- `normal`
|
||
|
||
---
|
||
|
||
## 1. 上传接口
|
||
|
||
### 路径
|
||
|
||
```http
|
||
POST /upload
|
||
```
|
||
|
||
### Content-Type
|
||
|
||
```http
|
||
multipart/form-data
|
||
```
|
||
|
||
### 用途
|
||
|
||
- 上传文档
|
||
- 创建或命中文档版本
|
||
- 建立 `leaudit_documents / leaudit_document_files`
|
||
- 可选自动触发评查
|
||
|
||
### 请求参数
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|---|---|---:|---|
|
||
| `file` | file | 是 | 上传文件 |
|
||
| `typeId` | int | 否 | 文档类型 ID,和 `typeCode` 二选一至少传一个 |
|
||
| `typeCode` | string | 否 | 文档类型编码,例如 `contract.sale` |
|
||
| `region` | string | 否 | 区域,默认 `default` |
|
||
| `fileRole` | string | 否 | 文件角色,默认 `primary` |
|
||
| `createdBy` | int | 否 | 上传用户 ID |
|
||
| `autoRun` | bool | 否 | 是否上传后自动触发评查,默认 `false` |
|
||
| `speed` | string | 否 | 执行速度档位:`normal` / `urgent`,默认 `normal` |
|
||
|
||
### 版本匹配逻辑
|
||
|
||
上传时会先做版本候选匹配:
|
||
|
||
1. 归一化文件名,得到 `normalized_name`
|
||
2. 按以下条件查找最新版本候选:
|
||
- `type_id` 相同
|
||
- `region` 相同
|
||
- `normalized_name` 相同
|
||
- `is_latest_version = true`
|
||
- 主文件 `file_role = 'primary'`
|
||
3. 比较最新版本主文件的 `sha256`
|
||
|
||
结果分三种:
|
||
|
||
- 找不到候选
|
||
- 新建版本组
|
||
- 当前版本为 `v1`
|
||
- 找到候选且 `sha256` 相同
|
||
- 视为重复上传
|
||
- 不新建版本
|
||
- `duplicateUpload=true`
|
||
- 找到候选但 `sha256` 不同
|
||
- 新建版本
|
||
- 当前版本为 `v2 / v3 / ...`
|
||
- 旧版本 `is_latest_version=false`
|
||
- 新版本 `is_latest_version=true`
|
||
|
||
### 队列路由逻辑
|
||
|
||
- `speed=urgent` -> 投递 `leaudit.urgent`
|
||
- `speed=normal` -> 投递 `leaudit.normal`
|
||
|
||
### 请求示例:普通上传,不自动评查
|
||
|
||
```bash
|
||
curl -X POST 'http://127.0.0.1:8096/api/upload' \
|
||
-F 'file=@/path/to/合同.docx' \
|
||
-F 'typeCode=contract.sale' \
|
||
-F 'region=default' \
|
||
-F 'fileRole=primary' \
|
||
-F 'autoRun=false' \
|
||
-F 'speed=normal'
|
||
```
|
||
|
||
### 返回示例:首次上传,命中 `v1`
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"documentId": 11,
|
||
"internalDocumentNo": 1777426812904262854,
|
||
"versionGroupKey": "4e02e455aa504cb9b75a254727f1bb4c",
|
||
"versionNo": 1,
|
||
"previousVersionId": null,
|
||
"rootVersionId": 11,
|
||
"duplicateUpload": false,
|
||
"fileId": 12,
|
||
"typeId": 9,
|
||
"typeCode": "contract.sale",
|
||
"region": "default",
|
||
"fileName": "版本归档验证合同.docx",
|
||
"ossUrl": "bdocs/default/contract.sale/2026/04/11/v1/primary__版本归档验证合同.docx",
|
||
"speed": "normal",
|
||
"processingStatus": "waiting",
|
||
"autoRunTriggered": false,
|
||
"run": null
|
||
}
|
||
}
|
||
```
|
||
|
||
### 返回示例:重复上传,不升版
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"documentId": 11,
|
||
"internalDocumentNo": 1777426812904262854,
|
||
"versionGroupKey": "4e02e455aa504cb9b75a254727f1bb4c",
|
||
"versionNo": 1,
|
||
"previousVersionId": null,
|
||
"rootVersionId": 11,
|
||
"duplicateUpload": true,
|
||
"fileId": 12,
|
||
"typeId": 9,
|
||
"typeCode": "contract.sale",
|
||
"region": "default",
|
||
"fileName": "版本归档验证合同.docx",
|
||
"ossUrl": "bdocs/default/contract.sale/2026/04/11/v1/primary__版本归档验证合同.docx",
|
||
"speed": "normal",
|
||
"processingStatus": "waiting",
|
||
"autoRunTriggered": false,
|
||
"run": null
|
||
}
|
||
}
|
||
```
|
||
|
||
### 返回示例:同名但内容变化,自动形成 `v2`
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"documentId": 12,
|
||
"internalDocumentNo": 1777426813574315361,
|
||
"versionGroupKey": "4e02e455aa504cb9b75a254727f1bb4c",
|
||
"versionNo": 2,
|
||
"previousVersionId": 11,
|
||
"rootVersionId": 11,
|
||
"duplicateUpload": false,
|
||
"fileId": 13,
|
||
"typeId": 9,
|
||
"typeCode": "contract.sale",
|
||
"region": "default",
|
||
"fileName": "版本归档验证合同.docx",
|
||
"ossUrl": "bdocs/default/contract.sale/2026/04/12/v2/primary__版本归档验证合同.docx",
|
||
"speed": "normal",
|
||
"processingStatus": "waiting",
|
||
"autoRunTriggered": false,
|
||
"run": null
|
||
}
|
||
}
|
||
```
|
||
|
||
### 返回示例:重复上传但自动重新评查
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"documentId": 13,
|
||
"internalDocumentNo": 1777427235286905027,
|
||
"versionGroupKey": "4e02e455aa504cb9b75a254727f1bb4c",
|
||
"versionNo": 3,
|
||
"previousVersionId": 12,
|
||
"rootVersionId": 11,
|
||
"duplicateUpload": true,
|
||
"fileId": 14,
|
||
"typeId": 9,
|
||
"typeCode": "contract.sale",
|
||
"region": "default",
|
||
"fileName": "版本归档验证合同.docx",
|
||
"ossUrl": "bdocs/default/contract.sale/2026/04/13/v3/primary__版本归档验证合同.docx",
|
||
"speed": "normal",
|
||
"processingStatus": "queued",
|
||
"autoRunTriggered": true,
|
||
"run": {
|
||
"runId": 13,
|
||
"documentId": 13,
|
||
"runNo": 2,
|
||
"documentFileId": 14,
|
||
"status": "queued",
|
||
"phase": "dispatch",
|
||
"resultStatus": null,
|
||
"ruleSetId": 29,
|
||
"ruleVersionId": 9,
|
||
"ruleTypeId": "contract.sale",
|
||
"rescueApplied": false,
|
||
"totalScore": null,
|
||
"passedCount": null,
|
||
"failedCount": null,
|
||
"skippedCount": null,
|
||
"startedAt": null,
|
||
"finishedAt": null
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 文档列表接口
|
||
|
||
### 路径
|
||
|
||
```http
|
||
GET /documents/list
|
||
```
|
||
|
||
### 用途
|
||
|
||
- 返回文档主列表
|
||
- 只返回每个版本组的最新版本
|
||
- 每条记录附带历史版本摘要,前端可以直接做“展开历史版本”
|
||
|
||
### 查询参数
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|---|---|---:|---|
|
||
| `page` | int | 否 | 页码,从 `1` 开始,默认 `1` |
|
||
| `pageSize` | int | 否 | 每页数量,默认 `20`,最大 `100` |
|
||
| `keyword` | string | 否 | 文件名 / 归一化名称模糊搜索 |
|
||
| `typeCode` | string | 否 | 文档类型编码,例如 `contract.sale` |
|
||
| `region` | string | 否 | 区域过滤 |
|
||
| `processingStatus` | string | 否 | 文档处理状态过滤 |
|
||
| `resultStatus` | string | 否 | 最新 run 的结果状态过滤 |
|
||
|
||
### 查询逻辑
|
||
|
||
- 主查询只看 `leaudit_documents.is_latest_version = true`
|
||
- 只关联主文件:
|
||
- `leaudit_document_files.is_active = true`
|
||
- `leaudit_document_files.file_role = 'primary'`
|
||
- 当前评查状态来自 `leaudit_audit_runs`
|
||
- 历史版本按 `version_group_key` 再查一次并挂到 `historyVersions`
|
||
|
||
### 请求示例
|
||
|
||
```bash
|
||
curl 'http://127.0.0.1:8096/api/documents/list?page=1&pageSize=5'
|
||
```
|
||
|
||
### 带筛选请求示例
|
||
|
||
```bash
|
||
curl 'http://127.0.0.1:8096/api/documents/list?page=1&pageSize=2&keyword=版本归档&typeCode=contract.sale®ion=default'
|
||
```
|
||
|
||
### 返回示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"total": 1,
|
||
"page": 1,
|
||
"pageSize": 2,
|
||
"totalPages": 1,
|
||
"documents": [
|
||
{
|
||
"documentId": 13,
|
||
"internalDocumentNo": 1777427235286905027,
|
||
"versionGroupKey": "4e02e455aa504cb9b75a254727f1bb4c",
|
||
"versionNo": 3,
|
||
"rootVersionId": 11,
|
||
"previousVersionId": 12,
|
||
"typeId": 9,
|
||
"typeCode": "contract.sale",
|
||
"region": "default",
|
||
"normalizedName": "版本归档验证合同",
|
||
"fileId": 14,
|
||
"fileName": "版本归档验证合同.docx",
|
||
"fileExt": "docx",
|
||
"mimeType": "application/octet-stream",
|
||
"fileSize": 587279,
|
||
"ossUrl": "bdocs/default/contract.sale/2026/04/13/v3/primary__版本归档验证合同.docx",
|
||
"processingStatus": "completed",
|
||
"currentRunId": 13,
|
||
"runStatus": "completed",
|
||
"resultStatus": "review",
|
||
"totalScore": 92.0,
|
||
"passedCount": 25,
|
||
"failedCount": 3,
|
||
"skippedCount": 0,
|
||
"updatedAt": "2026-04-29T01:50:05.241397+00:00",
|
||
"hasHistory": true,
|
||
"totalVersions": 3,
|
||
"historyVersions": [
|
||
{
|
||
"documentId": 12,
|
||
"fileId": 13,
|
||
"versionNo": 2,
|
||
"fileName": "版本归档验证合同.docx",
|
||
"fileExt": "docx",
|
||
"processingStatus": "waiting",
|
||
"runStatus": null,
|
||
"resultStatus": null,
|
||
"updatedAt": "2026-04-29T01:47:15.250697+00:00"
|
||
},
|
||
{
|
||
"documentId": 11,
|
||
"fileId": 12,
|
||
"versionNo": 1,
|
||
"fileName": "版本归档验证合同.docx",
|
||
"fileExt": "docx",
|
||
"processingStatus": "waiting",
|
||
"runStatus": null,
|
||
"resultStatus": null,
|
||
"updatedAt": "2026-04-29T01:40:13.538839+00:00"
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### 返回字段说明
|
||
|
||
| 字段 | 说明 |
|
||
|---|---|
|
||
| `documents[]` | 主列表,仅最新版本 |
|
||
| `versionGroupKey` | 同一版本链的归档组键 |
|
||
| `versionNo` | 当前版本号 |
|
||
| `rootVersionId` | 版本链根文档 ID |
|
||
| `previousVersionId` | 上一版本文档 ID |
|
||
| `hasHistory` | 是否存在历史版本 |
|
||
| `totalVersions` | 该版本组的总版本数 |
|
||
| `historyVersions[]` | 历史版本摘要,按 `versionNo DESC` 排序 |
|
||
|
||
---
|
||
|
||
## 3. 手动触发评查
|
||
|
||
### 路径
|
||
|
||
```http
|
||
POST /audit/run
|
||
```
|
||
|
||
### 用途
|
||
|
||
- 对指定 `documentId` 手动触发一次新的评查 run
|
||
- 不改变文档版本
|
||
- 只新增 `leaudit_audit_runs`
|
||
|
||
### 请求体
|
||
|
||
```json
|
||
{
|
||
"documentId": 13,
|
||
"ruleType": null,
|
||
"force": false,
|
||
"speed": "normal"
|
||
}
|
||
```
|
||
|
||
### 参数说明
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---:|---|
|
||
| `documentId` | int | 是 | 文档 ID |
|
||
| `ruleType` | string/null | 否 | 指定规则类型编码 |
|
||
| `force` | bool | 否 | 是否强制重跑 |
|
||
| `speed` | string | 否 | `normal` / `urgent` |
|
||
|
||
### 请求示例
|
||
|
||
```bash
|
||
curl -X POST 'http://127.0.0.1:8096/api/audit/run' \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"documentId": 13,
|
||
"force": false,
|
||
"speed": "urgent"
|
||
}'
|
||
```
|
||
|
||
### 返回示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"runId": 15,
|
||
"documentId": 13,
|
||
"runNo": 3,
|
||
"documentFileId": 14,
|
||
"status": "queued",
|
||
"phase": "dispatch",
|
||
"resultStatus": null,
|
||
"ruleSetId": 29,
|
||
"ruleVersionId": 9,
|
||
"ruleTypeId": "contract.sale",
|
||
"rescueApplied": false,
|
||
"totalScore": null,
|
||
"passedCount": null,
|
||
"failedCount": null,
|
||
"skippedCount": null,
|
||
"startedAt": null,
|
||
"finishedAt": null
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 查询运行状态
|
||
|
||
### 路径
|
||
|
||
```http
|
||
GET /audit/run/{runId}
|
||
```
|
||
|
||
### 用途
|
||
|
||
- 查询 run 当前状态
|
||
- 适合前端轮询
|
||
|
||
### 状态说明
|
||
|
||
常见状态:
|
||
|
||
- `queued`
|
||
- `running`
|
||
- `completed`
|
||
- `failed`
|
||
|
||
常见阶段:
|
||
|
||
- `dispatch`
|
||
- `prepare`
|
||
- `ocr`
|
||
- `extract`
|
||
- `evaluate`
|
||
- `rescue`
|
||
- `persist`
|
||
- `executed`
|
||
|
||
### 请求示例
|
||
|
||
```bash
|
||
curl 'http://127.0.0.1:8096/api/audit/run/11'
|
||
```
|
||
|
||
### 返回示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"runId": 11,
|
||
"documentId": 10,
|
||
"runNo": 1,
|
||
"documentFileId": 11,
|
||
"status": "completed",
|
||
"phase": "executed",
|
||
"resultStatus": "review",
|
||
"ruleSetId": 29,
|
||
"ruleVersionId": 9,
|
||
"ruleTypeId": "contract.sale",
|
||
"rescueApplied": true,
|
||
"totalScore": 89.0,
|
||
"passedCount": 24,
|
||
"failedCount": 4,
|
||
"skippedCount": 0,
|
||
"startedAt": "2026-04-28T19:01:01.766352+08:00",
|
||
"finishedAt": "2026-04-28T19:03:11.044894+08:00"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 查询评查结果
|
||
|
||
### 路径
|
||
|
||
```http
|
||
GET /audit/result/{runId}
|
||
```
|
||
|
||
### 用途
|
||
|
||
- 查询本次 run 的完整结果
|
||
- 包括:
|
||
- 规则结果
|
||
- 抽取字段
|
||
- 运行错误
|
||
- rescue 结果
|
||
- metrics
|
||
- artifacts
|
||
|
||
### 请求示例
|
||
|
||
```bash
|
||
curl 'http://127.0.0.1:8096/api/audit/result/11'
|
||
```
|
||
|
||
### 返回结构说明
|
||
|
||
顶层字段:
|
||
|
||
| 字段 | 说明 |
|
||
|---|---|
|
||
| `runId` | 运行 ID |
|
||
| `documentId` | 文档 ID |
|
||
| `documentFileId` | 本次锁定的文件 ID |
|
||
| `status` | 运行状态 |
|
||
| `totalScore` | 总分 |
|
||
| `passedCount` | 通过数 |
|
||
| `failedCount` | 失败数 |
|
||
| `skippedCount` | 跳过数 |
|
||
| `phase` | 当前阶段 |
|
||
| `resultStatus` | 总体结果 |
|
||
| `rescueApplied` | 是否执行 rescue |
|
||
| `ruleSetId` | 规则集 ID |
|
||
| `ruleVersionId` | 规则版本 ID |
|
||
| `startedAt` / `finishedAt` | 起止时间 |
|
||
| `rules` | 规则结果列表 |
|
||
| `fields` | 抽取字段列表 |
|
||
| `errors` | 错误列表 |
|
||
| `rescueOutcomes` | 补救结果列表 |
|
||
| `metrics` | 阶段指标 |
|
||
| `artifacts` | 产物列表 |
|
||
|
||
### 返回示例(节选)
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"runId": 11,
|
||
"documentId": 10,
|
||
"documentFileId": 11,
|
||
"status": "completed",
|
||
"totalScore": 89.0,
|
||
"passedCount": 24,
|
||
"failedCount": 4,
|
||
"skippedCount": 0,
|
||
"phase": "executed",
|
||
"resultStatus": "review",
|
||
"rescueApplied": true,
|
||
"ruleSetId": 29,
|
||
"ruleVersionId": 9,
|
||
"startedAt": "2026-04-28T19:01:01.766352+08:00",
|
||
"finishedAt": "2026-04-28T19:03:11.044894+08:00",
|
||
"rules": [
|
||
{
|
||
"ruleId": "MM-SALE-012",
|
||
"ruleName": "甲方信用代码校验",
|
||
"passed": false,
|
||
"status": "executed",
|
||
"risk": "medium",
|
||
"score": 3.0,
|
||
"failMessage": "甲方统一社会信用代码校验位错误"
|
||
}
|
||
],
|
||
"fields": [
|
||
{
|
||
"fieldName": "合同名称",
|
||
"valueText": "智慧法务平台建设采购项目合同",
|
||
"confidence": 0.9991
|
||
}
|
||
],
|
||
"rescueOutcomes": [
|
||
{
|
||
"ruleId": "MM-SALE-012",
|
||
"status": "final_fail",
|
||
"finalStatus": "review",
|
||
"requiresHumanReview": true,
|
||
"failureReason": "Agent (4 iter, requires_human): token_budget_exhausted"
|
||
}
|
||
],
|
||
"metrics": {
|
||
"ocrSeconds": 79.06,
|
||
"extractSeconds": 11.87,
|
||
"evaluateSeconds": 9.9,
|
||
"totalSeconds": 100.83,
|
||
"pageCount": 2,
|
||
"fieldCount": 35,
|
||
"ruleCount": 28,
|
||
"rescueRuleCount": 5,
|
||
"artifactCount": 8
|
||
},
|
||
"artifacts": [
|
||
{
|
||
"artifactType": "ocr_json",
|
||
"fileName": "ocr_result.json",
|
||
"fileExt": "json",
|
||
"mimeType": "application/json"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. worker 日志怎么看
|
||
|
||
worker 关键日志已经做了可读化,重点看这两类:
|
||
|
||
### 投递日志
|
||
|
||
```text
|
||
run_id=13 已投递到 worker 队列: queue=leaudit.normal, speed=normal, task_id=...
|
||
```
|
||
|
||
### 执行日志
|
||
|
||
```text
|
||
run_id=13 worker开始执行: queue=leaudit.normal, speed=normal, filename=版本归档验证合同.docx
|
||
```
|
||
|
||
结合状态查询接口可以快速判断:
|
||
|
||
- 是否已经成功投递
|
||
- 是否已被 worker 消费
|
||
- 跑的是 `urgent` 还是 `normal`
|
||
|
||
---
|
||
|
||
## 7. 前端建议接法
|
||
|
||
### 文档上传页
|
||
|
||
1. 调 `POST /upload`
|
||
2. 读取返回:
|
||
- `documentId`
|
||
- `versionGroupKey`
|
||
- `versionNo`
|
||
- `duplicateUpload`
|
||
- `run`
|
||
|
||
### 自动评查场景
|
||
|
||
如果 `autoRun=true` 且返回里 `run != null`:
|
||
|
||
1. 取 `run.runId`
|
||
2. 轮询 `GET /audit/run/{runId}`
|
||
3. `status=completed/failed` 后停止轮询
|
||
4. 再调 `GET /audit/result/{runId}`
|
||
|
||
### 列表页
|
||
|
||
列表页建议默认只展示:
|
||
|
||
- `is_latest_version = true` 的 document
|
||
|
||
点击某条后,再按:
|
||
|
||
- `versionGroupKey`
|
||
|
||
展开其历史版本。
|