file_upload.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. """
  2. 文档上传接口实现
  3. 模拟文件上传功能,返回文件ID和回调任务ID
  4. """
  5. import ast
  6. import uuid
  7. import time
  8. from datetime import datetime
  9. from fastapi import APIRouter, UploadFile, File, Form, HTTPException
  10. from pydantic import BaseModel
  11. from typing import Optional,List
  12. from core.construction_review.workflows.document_ans import DocumentParse
  13. from foundation.logger.loggering import server_logger as logger
  14. from foundation.base.config import config_handler
  15. from .schemas.error_schemas import FileUploadErrors
  16. file_upload_router = APIRouter(prefix="/sgsc", tags=["文档上传"])
  17. uploaded_files = {}
  18. class FileUploadResponse(BaseModel):
  19. code: int
  20. data: dict
  21. def validate_file(file: UploadFile) -> None:
  22. """验证文件格式和大小"""
  23. # 检查文件是否存在
  24. if not file or not file.filename:
  25. raise FileUploadErrors.file_missing()
  26. # 检查文件大小(Mock中假设文件大小合理,实际应该读取文件内容)
  27. # 这里可以添加文件大小检查逻辑
  28. file_size = getattr(file, 'size', None)
  29. if file_size is not None and file_size == 0:
  30. raise FileUploadErrors.file_rejected("文件为空")
  31. # 支持的文件类型
  32. allowed_mime_types = {
  33. 'application/pdf',
  34. 'application/msword',
  35. 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
  36. }
  37. # 检查文件格式
  38. if file.content_type not in allowed_mime_types:
  39. raise FileUploadErrors.file_format_unsupported()
  40. @file_upload_router.post("/file_upload", response_model=FileUploadResponse)
  41. async def file_upload(
  42. file: List[UploadFile] = File([]), # 改为文件列表,支持多文件检测
  43. callback_url: str = Form(None),
  44. project_plan_type: str = Form(None),
  45. user: str = Form(None) # 用户参数从表单获取,不从配置获取
  46. ):
  47. """
  48. 文件上传接口
  49. """
  50. # 调试日志信息
  51. logger.info(f"文件上传请求 - 用户: {user}, 文件数量: {len(file) if file else 0}",
  52. log_type="upload", trace_id=f"upload-{int(time.time())}")
  53. # 记录每个文件的信息
  54. if file:
  55. for i, f in enumerate(file):
  56. file_size = getattr(f, 'size', 0) # 安全获取文件大小,避免属性不存在错误
  57. logger.info(f"文件 {i+1}: {f.filename}, 大小: {file_size}, 类型: {f.content_type}", log_type="upload")
  58. logger.info(f"请求参数 - 回调URL: {callback_url}, 工程类型: {project_plan_type}",
  59. log_type="upload")
  60. logger.info(f"用户标识: {user}")
  61. try:
  62. # 验证工程方案类型
  63. valid_project_types = {
  64. 'bridge_up_part', # 桥梁上部结构
  65. 'tunnel_construction', # 隧道施工
  66. 'road_repair' # 道路维修
  67. }
  68. valid_users = ast.literal_eval(config_handler.get("user_lists", "USERS"))
  69. # 验证文件上传
  70. if not file or len(file) == 0:
  71. raise FileUploadErrors.file_missing()
  72. elif len(file) > 1:
  73. raise FileUploadErrors.file_multiple()
  74. # 验证文件格式和大小(只验证第一个文件)
  75. if file and len(file) > 0:
  76. validate_file(file[0])
  77. # 验证回调地址
  78. if callback_url is '':
  79. raise FileUploadErrors.callback_url_missing()
  80. # 验证用户标识
  81. if user is None or user not in valid_users:
  82. raise FileUploadErrors.invalid_user()
  83. # 工程方案类型校验
  84. if project_plan_type not in valid_project_types:
  85. raise FileUploadErrors.project_plan_type_invalid()
  86. # 生成文件ID和回调任务ID
  87. file_id = str(uuid.uuid4())
  88. created_at = int(time.time())
  89. callback_task_id = f"{file_id}-{created_at}"
  90. # 保存文件信息
  91. file_info = {
  92. "id": file_id,
  93. "name": file[0].filename,
  94. "size": 1024 * 1024, # 文件大小 1MB
  95. "created_at": created_at,
  96. "status": "success",
  97. "callback_task_id": callback_task_id,
  98. "callback_url": callback_url,
  99. "project_plan_type": project_plan_type,
  100. "user": user,
  101. "upload_time": datetime.now().isoformat()
  102. }
  103. # 文档处理(暂时注释,等文件保存逻辑实现后再启用)
  104. # DocumentParse.document_parse(file_path)
  105. uploaded_files[file_id] = file_info
  106. uploaded_files[callback_task_id] = {
  107. "file_id": file_id,
  108. "user": user,
  109. "review_task_status": "processing",
  110. "overall_progress": 0,
  111. "stages": [
  112. {"stage_name": "文件上传", "progress": 100, "stage_status": "completed"},
  113. {"stage_name": "格式校验", "progress": 0, "stage_status": "pending"},
  114. {"stage_name": "内容提取", "progress": 0, "stage_status": "pending"},
  115. {"stage_name": "智能审查", "progress": 0, "stage_status": "pending"}
  116. ],
  117. "updated_at": int(time.time()),
  118. "estimated_remaining": 1800 # 预计30分钟
  119. }
  120. return FileUploadResponse(
  121. code=200,
  122. data={
  123. "id": file_id,
  124. "name": file[0].filename,
  125. "size": file_info["size"],
  126. "created_at": file_info["created_at"],
  127. "status": "success",
  128. "callback_task_id": callback_task_id
  129. }
  130. )
  131. except HTTPException:
  132. raise
  133. except Exception as e:
  134. raise FileUploadErrors.internal_error(e)