attachment.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import mimetypes
  2. from io import BytesIO
  3. from typing import List
  4. from urllib.parse import quote
  5. from fastapi import APIRouter, Depends, HTTPException, Query, UploadFile, File, status
  6. from fastapi.responses import StreamingResponse
  7. from sqlalchemy.orm import Session
  8. from app.database import get_db
  9. from app.core.deps import get_current_user
  10. from app.models.user import User
  11. from app.schemas.common import ApiResponse
  12. from app.schemas.attachment import AttachmentOut
  13. from app.services.attachment import AttachmentService
  14. from app.services.minio import MinioService
  15. router = APIRouter(prefix="/api/attachments", tags=["附件管理"])
  16. @router.post("/upload", response_model=ApiResponse[AttachmentOut])
  17. def upload_file(
  18. file: UploadFile = File(...),
  19. biz_type: str = "",
  20. biz_id: int = 0,
  21. db: Session = Depends(get_db),
  22. current_user: User = Depends(get_current_user),
  23. ):
  24. file_data = file.file.read()
  25. content_type = file.content_type or mimetypes.guess_type(file.filename or "")[0] or "application/octet-stream"
  26. object_name = MinioService.upload_file(file.filename or "unknown", file_data, content_type)
  27. data = {
  28. "biz_type": biz_type,
  29. "biz_id": biz_id,
  30. "file_name": file.filename or "unknown",
  31. "file_path": object_name,
  32. "file_type": content_type,
  33. "file_size": len(file_data),
  34. "create_by": str(current_user.id),
  35. }
  36. attachment = AttachmentService.create(db, data)
  37. return ApiResponse(data=AttachmentOut.model_validate(attachment))
  38. @router.get("/download/{attachment_id}")
  39. def download_file(
  40. attachment_id: int,
  41. db: Session = Depends(get_db),
  42. current_user: User = Depends(get_current_user),
  43. ):
  44. attachment = AttachmentService.get_by_id(db, attachment_id)
  45. if not attachment:
  46. raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="附件不存在")
  47. file_data = MinioService.download_file(attachment.file_path)
  48. file_stream = BytesIO(file_data)
  49. encoded_name = quote(attachment.file_name)
  50. headers = {"Content-Disposition": f"attachment; filename*=UTF-8''{encoded_name}"}
  51. return StreamingResponse(file_stream, media_type=attachment.file_type or "application/octet-stream", headers=headers)
  52. @router.delete("/{attachment_id}", response_model=ApiResponse[bool])
  53. def delete_attachment(
  54. attachment_id: int,
  55. db: Session = Depends(get_db),
  56. current_user: User = Depends(get_current_user),
  57. ):
  58. attachment = AttachmentService.get_by_id(db, attachment_id)
  59. if not attachment:
  60. raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="附件不存在")
  61. success = AttachmentService.soft_delete(db, attachment_id)
  62. return ApiResponse(data=success)
  63. @router.get("/preview/{attachment_id}", response_model=ApiResponse[dict])
  64. def preview_attachment(
  65. attachment_id: int,
  66. db: Session = Depends(get_db),
  67. current_user: User = Depends(get_current_user),
  68. ):
  69. attachment = AttachmentService.get_by_id(db, attachment_id)
  70. if not attachment:
  71. raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="附件不存在")
  72. url = MinioService.get_presigned_url(attachment.file_path, expires=3600)
  73. return ApiResponse(data={"url": url, "file_name": attachment.file_name, "file_type": attachment.file_type})
  74. @router.get("/list", response_model=ApiResponse[List[AttachmentOut]])
  75. def list_by_biz(
  76. biz_type: str = "",
  77. biz_id: int = 0,
  78. db: Session = Depends(get_db),
  79. current_user: User = Depends(get_current_user),
  80. ):
  81. attachments = AttachmentService.get_by_biz(db, biz_type=biz_type, biz_id=biz_id)
  82. return ApiResponse(data=[AttachmentOut.model_validate(a) for a in attachments])
  83. @router.put("/bind-biz", response_model=ApiResponse[int])
  84. def bind_biz(
  85. ids: str = Query(...),
  86. biz_type: str = Query(...),
  87. biz_id: int = Query(...),
  88. db: Session = Depends(get_db),
  89. current_user: User = Depends(get_current_user),
  90. ):
  91. id_list = [int(x) for x in ids.split(",") if x.strip()]
  92. count = AttachmentService.bind_biz(db, id_list, biz_type, biz_id)
  93. return ApiResponse(data=count)