d4000cd292
2. 文档的基本信息修改改用接口。 3. 重新完善角色权限管理的页面逻辑。 4.将评查点列表中的返回逻辑改用浏览器的记忆返回。
13 KiB
13 KiB
文档更新接口详细文档
文件路径:
app/routes/postgrest.py(PostgREST代理)版本: v2.0 (RBAC-Enabled)
接口概述
| 项目 | 说明 |
|---|---|
| 接口名称 | 更新文档信息 |
| 请求方式 | PATCH |
| 接口路径 | /api/postgrest/proxy/documents |
| 认证方式 | JWT Bearer Token (完整签名验证) |
| 权限要求 | 需要 document:update 权限 |
请求说明
请求头 (Headers)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
Authorization |
string | 是 | Bearer Token,格式:Bearer {jwt_token} |
Content-Type |
string | 是 | application/json |
查询参数 (Query Parameters)
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
id |
string | 是 | 文档ID,PostgREST格式 | eq.123 |
安全说明: 系统会自动注入
user_id过滤条件,确保用户只能更新自己的文档。
请求体 (Request Body)
{
"document_number": "string", // 可选,文档编号
"audit_status": 0, // 可选,审核状态
"is_test_document": false, // 可选,是否测试文档
"remark": "string" // 可选,备注信息
}
可更新字段详情
| 字段名 | 类型 | 最大长度 | 说明 |
|---|---|---|---|
document_number |
varchar | 100 | 合同编号、许可证号等 |
audit_status |
integer | - | 审核状态:0=待审核, 1=通过, 2=审核中, -1=不通过, -2=警告 |
is_test_document |
boolean | - | 是否为测试文档,默认 false |
remark |
text | 255 | 备注信息 |
响应说明
成功响应 (HTTP 200)
返回更新后的文档完整信息(数组格式):
[
{
"id": 123,
"user_id": 5,
"type_id": 1,
"name": "合同文档.pdf",
"document_number": "HT-2024-001",
"path": "documents/2024/01/abc123.pdf",
"storage_type": "minio",
"file_size": 1024000,
"upload_time": "2024-01-15T10:30:00",
"is_test_document": false,
"evaluation_level": "普通",
"status": "processed",
"evaluations_status": 1,
"audit_status": 1,
"remark": "已审核通过",
"created_at": "2024-01-15T10:30:00+08:00",
"updated_at": "2024-01-16T14:20:00+08:00"
}
]
错误响应
401 未授权
{
"detail": "Token已过期"
}
或
{
"detail": "无效的Token"
}
403 权限不足
{
"detail": "权限不足:需要 'document:update' 权限才能访问 'documents' 表"
}
404 文档不存在或无权访问
[]
注意: 返回空数组表示没有匹配的记录(可能是文档不存在,或用户无权更新该文档)
数据库字段完整说明
documents 表结构
| 字段名 | 类型 | 可空 | 默认值 | 说明 |
|---|---|---|---|---|
id |
integer | 否 | 自增 | 文档ID,主键 |
user_id |
integer | 是 | - | 上传用户ID,外键→sso_users |
type_id |
integer | 否 | - | 文档类型ID,外键→document_types |
name |
varchar(255) | 否 | - | 原始文件名 |
document_number |
varchar(100) | 是 | - | 合同编号/许可证号 |
path |
varchar(255) | 否 | - | MinIO存储路径 |
storage_type |
varchar(20) | 否 | 'minio' | 存储方式 |
file_size |
integer | 是 | - | 文件大小(字节) |
upload_time |
timestamp | 是 | CURRENT_TIMESTAMP | 上传时间 |
is_test_document |
boolean | 是 | false | 是否测试文档 |
evaluation_level |
varchar(255) | 是 | - | 评查级别 |
status |
varchar(20) | 是 | 'waiting' | 处理状态 |
ocr_result |
jsonb | 是 | - | OCR处理结果 |
extracted_results |
jsonb | 是 | - | 内容抽取结果 |
sumary |
text | 是 | - | 评查结果摘要 |
evaluations_status |
integer | 是 | - | 评查状态 |
audit_status |
integer | 是 | - | 审核状态 |
error_massage |
varchar(500) | 是 | - | 错误信息 |
remark |
text | 是 | - | 备注 |
awareness_enabled |
boolean | 是 | false | 是否启用实体感知 |
awareness_result |
jsonb | 是 | - | VLM实体感知结果 |
awareness_execution_time |
double | 是 | - | 感知执行耗时(秒) |
awareness_created_at |
timestamp | 是 | - | 感知执行时间 |
original_word_path |
varchar(255) | 是 | - | 原始Word文件路径 |
created_by |
integer | 是 | - | 创建者ID |
created_at |
timestamptz | 是 | now() | 创建时间 |
updated_at |
timestamptz | 是 | now() | 更新时间(自动触发器) |
权限与安全机制
1. JWT 验证流程
请求 → JWT签名验证 → 有效期验证 → Audience验证 → 用户身份提取 → RBAC权限检查
验证内容:
- JWT签名验证:使用
JWT_SECRET验证Token未被篡改 - 有效期验证:检查
exp字段确保Token未过期 - Audience验证:检查
aud字段匹配JWT_AUDIENCE - 必填字段:验证
user_id,username,user_role,exp存在
2. RBAC 权限检查
# 权限检查逻辑
permission_key = PostgRESTRBACMapping.get_permission_key("documents", "PATCH")
# → 返回 "document:update"
has_permission = await PermissionChecker.check_permission(
user_id=user_id,
permission_key="document:update"
)
3. 数据范围控制 (Data Scope)
| 角色 | 数据范围 | 说明 |
|---|---|---|
provincial_admin |
ALL | 可访问所有文档,无过滤 |
admin |
DEPT | 仅本地区文档 (area=用户地区) |
user |
SELF | 仅本人文档 (user_id=当前用户ID) |
系统自动注入过滤条件:
# SELF 范围
request_data.params["user_id"] = f"eq.{user_id}"
# DEPT 范围 (V2: 使用area而非ou_id)
request_data.params["area"] = f"eq.{user_area}"
4. 交叉评查权限
若用户参与交叉评查任务,可访问任务相关的跨地区文档:
# 获取用户可访问的交叉评查文档
accessible_doc_ids = await CrossReviewPermission.get_user_accessible_documents(
user_id=user_id,
use_cache=True
)
# PATCH请求:检查目标文档是否在交叉评查权限范围
if target_doc_id in accessible_doc_ids:
# 移除数据范围限制,允许更新
del request_data.params["user_id"]
5. 安全过滤
系统自动过滤危险参数,防止权限绕过:
DANGEROUS_PARAMS = ["select", "columns", "on_conflict", "resolution", ...]
# 自动移除危险参数
for dangerous_param in DANGEROUS_PARAMS:
if dangerous_param in request_data.params:
del request_data.params[dangerous_param]
调用示例
cURL 示例
curl -X PATCH \
'https://api.example.com/api/postgrest/proxy/documents?id=eq.123' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIs...' \
-H 'Content-Type: application/json' \
-d '{
"document_number": "HT-2024-001",
"audit_status": 1,
"remark": "审核通过"
}'
TypeScript/JavaScript 示例
/**
* 更新文档信息
* @param id 文档ID
* @param document 部分文档数据
* @param userId 用户ID
* @param frontendJWT JWT Token
* @returns 更新结果
*/
export async function updateDocument(
id: string,
document: Partial<DocumentUI> & { remark?: string },
userId: string,
frontendJWT?: string
): Promise<{ data?: DocumentUI; error?: string; status?: number }> {
try {
if (!id) {
return { error: '文档ID不能为空', status: 400 };
}
if (!userId) {
return { error: '用户身份验证失败', status: 401 };
}
// 准备API数据 - 将UI数据转换为API格式
const apiDocument: Partial<Document> = {};
if (document.documentNumber !== undefined) {
apiDocument.document_number = document.documentNumber;
}
if (document.auditStatus !== undefined) {
apiDocument.audit_status = document.auditStatus;
}
if (document.isTest !== undefined) {
apiDocument.is_test_document = document.isTest;
}
if (document.remark !== undefined) {
apiDocument.remark = document.remark;
}
// 发送PATCH请求
const response = await postgrestPut<Document, Partial<Document>>(
'/api/postgrest/proxy/documents',
apiDocument,
{
id: parseInt(id),
user_id: parseInt(userId) // 确保只能更新自己的文档
},
frontendJWT
);
if (response.error) {
console.error('更新文档API错误:', response.error);
return { error: response.error, status: response.status };
}
// 获取更新后的完整文档数据
const updatedResponse = await getDocument(id, userId, frontendJWT);
return updatedResponse;
} catch (error) {
console.error('更新文档信息失败:', error);
return {
error: error instanceof Error ? error.message : '更新文档信息失败',
status: 500
};
}
}
Python 示例
import httpx
async def update_document(
document_id: int,
update_data: dict,
jwt_token: str
) -> dict:
"""
更新文档信息
Args:
document_id: 文档ID
update_data: 要更新的字段
jwt_token: JWT Token
Returns:
更新后的文档数据
"""
async with httpx.AsyncClient() as client:
response = await client.patch(
f"https://api.example.com/api/postgrest/proxy/documents",
params={"id": f"eq.{document_id}"},
json=update_data,
headers={
"Authorization": f"Bearer {jwt_token}",
"Content-Type": "application/json"
}
)
if response.status_code == 200:
return {"success": True, "data": response.json()}
else:
return {"success": False, "error": response.text}
# 使用示例
result = await update_document(
document_id=123,
update_data={
"document_number": "HT-2024-001",
"audit_status": 1,
"remark": "审核通过"
},
jwt_token="eyJhbGciOiJIUzI1NiIs..."
)
状态枚举值
status (处理状态)
| 值 | 说明 | 触发条件 |
|---|---|---|
waiting |
上传后默认状态,等待处理 | 文档上传完成 |
Cutting |
切分+OCR处理中 | OCR任务开始 |
extractioning |
大模型抽取信息中 | 信息抽取任务开始 |
evaluationing |
评查中 | 评查任务开始 |
processed |
处理完成,等待审核 | 所有处理完成 |
audit_status (审核状态)
| 值 | 说明 | 操作权限 |
|---|---|---|
| 0 | 待审核 | 等待审核员处理 |
| 1 | 通过 | 审核通过,流程结束 |
| 2 | 审核中 | 审核员正在处理 |
| -1 | 不通过 | 需要修改后重新提交 |
| -2 | 警告 | 有问题但可继续流程 |
evaluations_status (评查状态)
| 值 | 说明 | 后续操作 |
|---|---|---|
| 1 | 通过 | 无需人工干预 |
| 0 | 待人工确认 | 需要人工审核确认 |
| -1 | 不通过 | 存在严重问题 |
| -2 | 警告 | 存在轻微问题 |
后端处理流程
1. 接收 PATCH 请求
↓
2. JWT 完整验证 (签名+有效期+Audience)
↓
3. 提取用户信息 (user_id, username, user_role, area)
↓
4. RBAC 权限检查 (document:update)
↓
5. 安全过滤 (移除危险参数)
↓
6. 数据范围注入 (根据角色添加过滤条件)
↓
7. 交叉评查权限检查 (如适用)
↓
8. 转发请求到 PostgREST
↓
9. 返回更新结果
注意事项
-
自动更新时间:
updated_at字段由数据库触发器update_documents_updated_at自动更新,无需手动传入 -
字段长度限制:
document_number: 最大100字符remark: 前端限制为255字符error_massage: 最大500字符
-
只读字段: 以下字段不可通过此接口修改
id,user_id,type_idname,path,storage_type,file_sizeupload_time,created_atocr_result,extracted_results(由系统处理任务更新)
-
权限隔离: 普通用户只能更新自己上传的文档,系统会强制校验
user_id -
PostgREST 格式: 查询参数需使用 PostgREST 格式
- 等于:
eq.值(如id=eq.123) - 不等于:
neq.值 - 大于:
gt.值 - 包含:
in.(值1,值2,值3)
- 等于:
审计日志
所有更新操作都会记录审计日志:
await AuditLogger.log_permission_check(
user_id=user_id,
permission_key="document:update",
is_success=True,
ip_address=client_ip,
user_agent=user_agent,
context={
'table': 'documents',
'method': 'PATCH',
'path': 'documents'
}
)
错误码说明
| HTTP状态码 | 错误类型 | 说明 | 解决方案 |
|---|---|---|---|
| 400 | Bad Request | 请求参数错误 | 检查请求体格式和字段类型 |
| 401 | Unauthorized | Token无效或过期 | 重新登录获取新Token |
| 403 | Forbidden | 权限不足 | 联系管理员分配权限 |
| 404 | Not Found | 文档不存在 | 检查document_id是否正确 |
| 500 | Internal Error | 服务器内部错误 | 查看服务器日志 |