attachment.py 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. from sqlalchemy import case as sql_case, func, or_
  2. from sqlalchemy.orm import Session
  3. from app.models.attachment import Attachment
  4. from app.services.base import CrudService
  5. class AttachmentService:
  6. @staticmethod
  7. def get_by_id(db: Session, id: int) -> Attachment | None:
  8. return CrudService.get_by_id(Attachment, db, id)
  9. @staticmethod
  10. def get_paged(db: Session, page: int, page_size: int, filters: list | None = None):
  11. return CrudService.get_paged(Attachment, db, page, page_size, filters)
  12. @staticmethod
  13. def create(db: Session, obj_in: dict) -> Attachment:
  14. return CrudService.create(Attachment, db, obj_in)
  15. @staticmethod
  16. def update(db: Session, id: int, obj_in: dict) -> Attachment | None:
  17. return CrudService.update(Attachment, db, id, obj_in)
  18. @staticmethod
  19. def soft_delete(db: Session, id: int) -> bool:
  20. return CrudService.soft_delete(Attachment, db, id)
  21. @staticmethod
  22. def get_by_biz(db: Session, biz_type: str, biz_id: int) -> list[Attachment]:
  23. return (
  24. db.query(Attachment)
  25. .filter(
  26. Attachment.biz_type == biz_type,
  27. Attachment.biz_id == biz_id,
  28. Attachment.del_flag == 0,
  29. )
  30. .order_by(Attachment.id)
  31. .all()
  32. )
  33. @staticmethod
  34. def bind_biz(db: Session, ids: list[int], biz_type: str, biz_id: int) -> int:
  35. """Batch update biz_type and biz_id for given attachment IDs. Returns count updated."""
  36. count = (
  37. db.query(Attachment)
  38. .filter(Attachment.id.in_(ids), Attachment.del_flag == 0)
  39. .update({Attachment.biz_type: biz_type, Attachment.biz_id: biz_id}, synchronize_session="fetch")
  40. )
  41. db.flush()
  42. return count
  43. @staticmethod
  44. def count_invoice_attach_by_biz_ids(db: Session, biz_type: str, biz_ids: list[int]) -> dict[int, tuple[int, int]]:
  45. """按业务主键统计 (invoice_count, attach_count)。
  46. - 显式标记 attach_type='invoice' 的算「张」。
  47. - 报销申请附件:上传接口未写 attach_type 时默认全是 attachment,故对 reimburse_apply
  48. 再把常见票据 MIME(图片、PDF)计入「张」,其余计入「个」,避免列表里永远「0 张」。
  49. """
  50. if not biz_ids:
  51. return {}
  52. if biz_type == "reimburse_apply":
  53. inv_cond = or_(
  54. Attachment.attach_type == "invoice",
  55. Attachment.file_type.isnot(None) & Attachment.file_type.like("image/%"),
  56. Attachment.file_type == "application/pdf",
  57. )
  58. else:
  59. inv_cond = Attachment.attach_type == "invoice"
  60. inv_case = sql_case((inv_cond, 1), else_=0)
  61. att_case = sql_case((inv_cond, 0), else_=1)
  62. rows = (
  63. db.query(
  64. Attachment.biz_id,
  65. func.coalesce(func.sum(inv_case), 0).label("inv"),
  66. func.coalesce(func.sum(att_case), 0).label("att"),
  67. )
  68. .filter(
  69. Attachment.biz_type == biz_type,
  70. Attachment.biz_id.in_(biz_ids),
  71. Attachment.del_flag == 0,
  72. )
  73. .group_by(Attachment.biz_id)
  74. .all()
  75. )
  76. return {int(r.biz_id): (int(r.inv), int(r.att)) for r in rows}