export_record_service.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. """export_record_service.py — 导出记录 CRUD。"""
  2. from pathlib import Path
  3. from sqlalchemy import func, select
  4. from sqlalchemy.ext.asyncio import AsyncSession
  5. from app.core.exceptions import RecordNotFoundError
  6. from app.models.export_record import ExportRecord
  7. class ExportRecordService:
  8. def __init__(self, db: AsyncSession) -> None:
  9. self.db = db
  10. # ------------------------------------------------------------------ #
  11. # 写入
  12. # ------------------------------------------------------------------ #
  13. async def create_record(
  14. self,
  15. *,
  16. user_id: str,
  17. file_name: str,
  18. file_path: str,
  19. file_size: int,
  20. download_url: str,
  21. document_id: str | None,
  22. style_id: str,
  23. ) -> ExportRecord:
  24. record = ExportRecord(
  25. user_id=user_id,
  26. file_name=file_name,
  27. file_path=file_path,
  28. file_size=file_size,
  29. download_url=download_url,
  30. document_id=document_id,
  31. style_id=style_id,
  32. )
  33. self.db.add(record)
  34. await self.db.commit()
  35. await self.db.refresh(record)
  36. return record
  37. # ------------------------------------------------------------------ #
  38. # 查询列表
  39. # ------------------------------------------------------------------ #
  40. async def list_records(
  41. self,
  42. user_id: str,
  43. page: int = 1,
  44. page_size: int = 20,
  45. sort_order: str = "desc",
  46. ) -> tuple[list[ExportRecord], int]:
  47. """返回 (records, total)。"""
  48. base_q = select(ExportRecord).where(ExportRecord.user_id == user_id)
  49. # 总数
  50. count_q = select(func.count()).select_from(base_q.subquery())
  51. total: int = (await self.db.execute(count_q)).scalar_one()
  52. # 分页 + 排序
  53. order_col = (
  54. ExportRecord.created_at.desc()
  55. if sort_order.lower() != "asc"
  56. else ExportRecord.created_at.asc()
  57. )
  58. offset = (page - 1) * page_size
  59. result = await self.db.execute(
  60. base_q.order_by(order_col).offset(offset).limit(page_size)
  61. )
  62. records = list(result.scalars().all())
  63. return records, total
  64. # ------------------------------------------------------------------ #
  65. # 查询单条(校验归属)
  66. # ------------------------------------------------------------------ #
  67. async def get_record(self, record_id: str, user_id: str) -> ExportRecord:
  68. result = await self.db.execute(
  69. select(ExportRecord).where(
  70. ExportRecord.id == record_id,
  71. ExportRecord.user_id == user_id,
  72. )
  73. )
  74. record = result.scalar_one_or_none()
  75. if record is None:
  76. raise RecordNotFoundError(record_id)
  77. return record
  78. # ------------------------------------------------------------------ #
  79. # 硬删除
  80. # ------------------------------------------------------------------ #
  81. async def delete_record(self, record_id: str, user_id: str) -> None:
  82. """删除数据库记录并同步删除磁盘文件。"""
  83. record = await self.get_record(record_id, user_id)
  84. # 先删磁盘文件,文件不存在时静默忽略
  85. try:
  86. Path(record.file_path).unlink(missing_ok=True)
  87. except OSError:
  88. pass
  89. await self.db.delete(record)
  90. await self.db.commit()