document.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. """文档处理相关 API 路由。"""
  2. import logging
  3. from fastapi import APIRouter, File, HTTPException, UploadFile
  4. from fastapi.responses import StreamingResponse
  5. from ..models.schemas import AnalysisRequest, FileUploadResponse, WordExportRequest
  6. from ..services.file_service import FileService
  7. from ..services.analysis_service import AnalysisService
  8. from ..services.word_export_service import WordExportService
  9. from ..utils.errors import AppError
  10. from ..utils.sse import sse_chunk, sse_done, sse_error, sse_response
  11. logger = logging.getLogger(__name__)
  12. router = APIRouter(prefix="/api/document", tags=["文档处理"])
  13. @router.post("/upload", response_model=FileUploadResponse)
  14. async def upload_file(file: UploadFile = File(...)):
  15. """上传文档文件并提取文本内容。"""
  16. try:
  17. if not FileService.is_supported_document(file.content_type):
  18. return FileUploadResponse(
  19. success=False, message="不支持的文件类型,请上传PDF或Word文档"
  20. )
  21. file_content = await FileService.process_uploaded_file(file)
  22. return FileUploadResponse(
  23. success=True,
  24. message=f"文件 {file.filename} 上传成功",
  25. file_content=file_content,
  26. )
  27. except Exception as exc:
  28. logger.exception("文件上传失败")
  29. return FileUploadResponse(success=False, message=f"文件处理失败: {exc}")
  30. @router.post("/analyze-stream")
  31. async def analyze_document_stream(request: AnalysisRequest):
  32. """流式分析文档内容。"""
  33. try:
  34. analysis_service = AnalysisService()
  35. except AppError as exc:
  36. raise HTTPException(status_code=exc.status_code, detail=exc.message) from exc
  37. async def generate():
  38. try:
  39. async for chunk in analysis_service.stream_document_analysis(
  40. file_content=request.file_content,
  41. analysis_type=request.analysis_type.value,
  42. ):
  43. yield sse_chunk(chunk)
  44. except AppError as exc:
  45. yield sse_error(exc.message)
  46. except Exception:
  47. logger.exception("文档分析失败")
  48. yield sse_error("文档分析失败,请稍后重试")
  49. finally:
  50. yield sse_done()
  51. return sse_response(generate())
  52. @router.post("/export-word")
  53. async def export_word(request: WordExportRequest):
  54. """根据目录数据导出 Word 文档。"""
  55. try:
  56. buffer, headers = WordExportService.export_outline(request)
  57. return StreamingResponse(
  58. buffer,
  59. media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  60. headers=headers,
  61. )
  62. except Exception as exc:
  63. logger.exception("导出 Word 失败")
  64. raise HTTPException(status_code=500, detail=f"导出Word失败: {exc}") from exc