Files
LiangShiyong d4000cd292 fix: 1. 继续对齐交叉评查的接口,完善创建交叉评查的逻辑 和 相关组件的渲染布局。
2. 文档的基本信息修改改用接口。      3. 重新完善角色权限管理的页面逻辑。     4.将评查点列表中的返回逻辑改用浏览器的记忆返回。
2025-12-12 12:00:36 +08:00

13 KiB
Raw Permalink Blame History

文档更新接口详细文档

文件路径: 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 文档IDPostgREST格式 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. 返回更新结果

注意事项

  1. 自动更新时间: updated_at 字段由数据库触发器 update_documents_updated_at 自动更新,无需手动传入

  2. 字段长度限制:

    • document_number: 最大100字符
    • remark: 前端限制为255字符
    • error_massage: 最大500字符
  3. 只读字段: 以下字段不可通过此接口修改

    • id, user_id, type_id
    • name, path, storage_type, file_size
    • upload_time, created_at
    • ocr_result, extracted_results (由系统处理任务更新)
  4. 权限隔离: 普通用户只能更新自己上传的文档,系统会强制校验 user_id

  5. 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 服务器内部错误 查看服务器日志

相关文档