export.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. from pathlib import Path
  2. from fastapi import APIRouter
  3. from fastapi.responses import FileResponse
  4. from app.config import settings
  5. from app.core.exceptions import DocumentNotFoundError, ExportError
  6. from app.schemas.export import ExportDocRequest, ExportDocResponse
  7. from app.services.export_service import export_doc
  8. router = APIRouter(tags=["Export"])
  9. def _ok(data: dict) -> dict:
  10. return {"code": 0, "message": "Success", "data": data}
  11. # ------------------------------------------------------------------ #
  12. # POST /export/doc 导出 .doc 并返回下载链接
  13. # ------------------------------------------------------------------ #
  14. @router.post("/export/doc", summary="导出 .doc 文件")
  15. async def export_document(body: ExportDocRequest) -> dict:
  16. # 若传入 documentId,按需建立 DB session 同步更新草稿
  17. if body.document_id:
  18. try:
  19. from app.core.database import AsyncSessionLocal
  20. from app.services.document_service import DocumentService
  21. from app.schemas.document import UpdateDocumentRequest
  22. async with AsyncSessionLocal() as db:
  23. svc = DocumentService(db)
  24. await svc.update_document(
  25. body.document_id,
  26. UpdateDocumentRequest(content=body.content),
  27. )
  28. except DocumentNotFoundError:
  29. pass # 文档不存在不阻塞导出
  30. except Exception:
  31. pass # DB 不可用时同样不阻塞导出
  32. result = await export_doc(
  33. file_name=body.file_name,
  34. content=body.content,
  35. style_id=body.style_id,
  36. )
  37. resp = ExportDocResponse(
  38. download_url=result["download_url"],
  39. file_name=result["file_name"],
  40. expires_at=result["expires_at"],
  41. style_id=result["style_id"],
  42. )
  43. return _ok(resp.model_dump(by_alias=True))
  44. # ------------------------------------------------------------------ #
  45. # GET /files/{filename} 临时文件下载端点
  46. # ------------------------------------------------------------------ #
  47. @router.get("/files/{filename}", summary="下载导出文件", include_in_schema=False)
  48. async def download_file(filename: str) -> FileResponse:
  49. # FastAPI 有时不会自动解码路径参数,手动解码一次确保中文还原
  50. from urllib.parse import unquote
  51. decoded = unquote(filename)
  52. safe = Path(decoded).name
  53. file_path = Path(settings.temp_dir) / safe
  54. if not file_path.exists():
  55. raise ExportError(f"文件不存在或已过期: {safe}")
  56. return FileResponse(
  57. path=str(file_path),
  58. filename=safe,
  59. media_type="application/msword",
  60. )