demand_video_expansion_repo.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. from __future__ import annotations
  2. from sqlalchemy import func, select
  3. from sqlalchemy.dialects.mysql import insert
  4. from supply_infra.db.models.demand_video_expansion import (
  5. DemandVideoExpansion,
  6. DemandVideoExpansionRun,
  7. )
  8. from supply_infra.db.repositories.base import BaseRepository
  9. _BATCH_SIZE = 500
  10. class DemandVideoExpansionRepository(BaseRepository[DemandVideoExpansion]):
  11. """需求视频点位拓展结果 repository。"""
  12. model = DemandVideoExpansion
  13. def get_active_by_id(self, expansion_id: int) -> DemandVideoExpansion | None:
  14. stmt = select(DemandVideoExpansion).where(
  15. DemandVideoExpansion.id == int(expansion_id),
  16. DemandVideoExpansion.is_delete == 0,
  17. )
  18. return self.session.scalars(stmt).first()
  19. def has_video(
  20. self,
  21. *,
  22. biz_dt: str,
  23. source_demand_grade_id: int,
  24. video_id: str,
  25. ) -> bool:
  26. stmt = select(DemandVideoExpansion.id).where(
  27. DemandVideoExpansion.biz_dt == biz_dt,
  28. DemandVideoExpansion.source_demand_grade_id == int(source_demand_grade_id),
  29. DemandVideoExpansion.video_id == video_id,
  30. DemandVideoExpansion.is_delete == 0,
  31. )
  32. return self.session.scalar(stmt.limit(1)) is not None
  33. def list_by_demand_grade(
  34. self, biz_dt: str, source_demand_grade_id: int
  35. ) -> list[DemandVideoExpansion]:
  36. """按业务日与来源需求 id 查询拓展结果(未删除)。"""
  37. stmt = (
  38. select(DemandVideoExpansion)
  39. .where(
  40. DemandVideoExpansion.biz_dt == biz_dt,
  41. DemandVideoExpansion.source_demand_grade_id == int(source_demand_grade_id),
  42. DemandVideoExpansion.is_delete == 0,
  43. )
  44. .order_by(
  45. DemandVideoExpansion.video_id,
  46. DemandVideoExpansion.point_type,
  47. DemandVideoExpansion.id,
  48. )
  49. )
  50. return list(self.session.scalars(stmt).all())
  51. def count_distinct_videos_by_demand_grade(self, biz_dt: str) -> dict[int, int]:
  52. """按业务日统计每条需求的有效命中视频数。"""
  53. stmt = (
  54. select(
  55. DemandVideoExpansion.source_demand_grade_id,
  56. func.count(func.distinct(DemandVideoExpansion.video_id)),
  57. )
  58. .where(
  59. DemandVideoExpansion.biz_dt == biz_dt,
  60. DemandVideoExpansion.is_delete == 0,
  61. DemandVideoExpansion.video_id != "",
  62. DemandVideoExpansion.point_type.in_(("inspiration", "purpose", "key")),
  63. )
  64. .group_by(DemandVideoExpansion.source_demand_grade_id)
  65. )
  66. return {
  67. int(demand_grade_id): int(video_count or 0)
  68. for demand_grade_id, video_count in self.session.execute(stmt).all()
  69. }
  70. def bulk_upsert(self, rows: list[dict]) -> int:
  71. """按唯一键批量 upsert,冲突时更新 reason / point_desc。"""
  72. if not rows:
  73. return 0
  74. affected = 0
  75. for i in range(0, len(rows), _BATCH_SIZE):
  76. batch = rows[i : i + _BATCH_SIZE]
  77. stmt = insert(DemandVideoExpansion).values(batch)
  78. stmt = stmt.on_duplicate_key_update(
  79. reason=stmt.inserted.reason,
  80. point_desc=stmt.inserted.point_desc,
  81. run_id=stmt.inserted.run_id,
  82. is_delete=0,
  83. )
  84. result = self.session.execute(stmt)
  85. affected += result.rowcount or 0
  86. return affected
  87. class DemandVideoExpansionRunRepository(BaseRepository[DemandVideoExpansionRun]):
  88. """拓展任务执行记录 repository。"""
  89. model = DemandVideoExpansionRun
  90. def list_finished_grade_ids(self, biz_dt: str) -> set[int]:
  91. """返回指定业务日已成功完成拓展判断的 demand_grade.id 集合。"""
  92. stmt = select(DemandVideoExpansionRun.source_demand_grade_id).where(
  93. DemandVideoExpansionRun.biz_dt == biz_dt,
  94. DemandVideoExpansionRun.status == "finished",
  95. )
  96. return {int(v) for v in self.session.scalars(stmt).all() if v is not None}
  97. def upsert_run(
  98. self,
  99. *,
  100. biz_dt: str,
  101. run_id: str,
  102. source_demand_grade_id: int,
  103. saved_count: int,
  104. status: str = "finished",
  105. error_message: str | None = None,
  106. ) -> None:
  107. """记录单条需求的拓展判断完成状态。"""
  108. row = {
  109. "biz_dt": biz_dt,
  110. "run_id": run_id,
  111. "source_demand_grade_id": int(source_demand_grade_id),
  112. "saved_count": int(saved_count),
  113. "status": status,
  114. "error_message": error_message,
  115. }
  116. stmt = insert(DemandVideoExpansionRun).values(row)
  117. stmt = stmt.on_duplicate_key_update(
  118. run_id=stmt.inserted.run_id,
  119. saved_count=stmt.inserted.saved_count,
  120. status=stmt.inserted.status,
  121. error_message=stmt.inserted.error_message,
  122. )
  123. self.session.execute(stmt)
  124. def list_by_biz_dt(self, biz_dt: str) -> list[DemandVideoExpansionRun]:
  125. stmt = (
  126. select(DemandVideoExpansionRun)
  127. .where(DemandVideoExpansionRun.biz_dt == biz_dt)
  128. .order_by(DemandVideoExpansionRun.source_demand_grade_id)
  129. )
  130. return list(self.session.scalars(stmt).all())
  131. def get_by_demand_grade(
  132. self, biz_dt: str, source_demand_grade_id: int
  133. ) -> DemandVideoExpansionRun | None:
  134. stmt = select(DemandVideoExpansionRun).where(
  135. DemandVideoExpansionRun.biz_dt == biz_dt,
  136. DemandVideoExpansionRun.source_demand_grade_id == int(source_demand_grade_id),
  137. )
  138. return self.session.scalars(stmt).first()