diff --git a/app/admin/repositories/coupon_data.py b/app/admin/repositories/coupon_data.py index 0450404..cd523c1 100644 --- a/app/admin/repositories/coupon_data.py +++ b/app/admin/repositories/coupon_data.py @@ -86,7 +86,12 @@ def _success_rates(rows: list) -> dict: } -def _session_to_row(r, phone: str | None = None, nickname: str | None = None, ad_revenue_yuan: float = 0.0) -> dict: +def _session_to_row( + r, + phone: str | None = None, + nickname: str | None = None, + ad_revenue_yuan: float | None = None, +) -> dict: """CouponSession ORM → 明细行 dict(主表「领券数据」与「用户全部领券」抽屉共用)。""" return { "id": r.id, @@ -252,7 +257,7 @@ def coupon_data_report( items = [] for r in page: phone, nickname = user_map.get(r.user_id, (None, None)) if r.user_id is not None else (None, None) - items.append(_session_to_row(r, phone, nickname, ad_revenue_yuan=rev_map.get(r.trace_id, 0.0))) + items.append(_session_to_row(r, phone, nickname, ad_revenue_yuan=rev_map.get(r.trace_id))) return { "summary": summary, @@ -276,7 +281,7 @@ def coupon_user_records(db: Session, *, user_id: int, limit: int = 100) -> dict: ).scalar_one() rev_map = crud_ecpm.revenue_yuan_by_trace(db, [r.trace_id for r in rows]) return { - "items": [_session_to_row(r, ad_revenue_yuan=rev_map.get(r.trace_id, 0.0)) for r in rows], + "items": [_session_to_row(r, ad_revenue_yuan=rev_map.get(r.trace_id)) for r in rows], "total": int(total), } diff --git a/app/admin/schemas/coupon_data.py b/app/admin/schemas/coupon_data.py index c560397..08bb516 100644 --- a/app/admin/schemas/coupon_data.py +++ b/app/admin/schemas/coupon_data.py @@ -70,8 +70,9 @@ class CouponDataRow(BaseModel): started_at: datetime = Field(..., description="发起时刻(明细「时间」列)") claimed_count: int | None = None trace_url: str | None = Field(None, description="pricebot 公网 trace 链接(仅 completed 有);admin 渲染可点链接,无则显示可复制 trace_id") - ad_revenue_yuan: float = Field( - 0.0, description="本次领券看的信息流广告预估收益(元);按 trace_id 聚合 ad_ecpm_record" + ad_revenue_yuan: float | None = Field( + None, + description="本次领券看的信息流广告预估收益(元);按 trace_id 聚合 ad_ecpm_record;无填充记录为 null", ) diff --git a/tests/test_board_ad_revenue.py b/tests/test_board_ad_revenue.py index 6f54d86..9b01160 100644 --- a/tests/test_board_ad_revenue.py +++ b/tests/test_board_ad_revenue.py @@ -43,6 +43,35 @@ def test_coupon_data_report_includes_ad_revenue() -> None: db.close() +def test_coupon_data_report_distinguishes_unfilled_from_zero_revenue() -> None: + """无 eCPM 记录返回 None;已填充但 eCPM=0 返回 0.0。""" + db = SessionLocal() + try: + db.add_all([ + CouponSession( + trace_id="rev-cp-unfilled", device_id="d-unfilled", status="completed", app_env="prod", + platforms=["meituan-waimai"], platform_success=["meituan-waimai"], + started_at=datetime(2020, 1, 3, tzinfo=UTC), started_date=date(2020, 1, 3), + ), + CouponSession( + trace_id="rev-cp-zero", device_id="d-zero", status="completed", app_env="prod", + platforms=["meituan-waimai"], platform_success=["meituan-waimai"], + started_at=datetime(2020, 1, 3, tzinfo=UTC), started_date=date(2020, 1, 3), + ), + _ecpm("rev-cp-zero", "0", "cp-sess-zero", "coupon"), + ]) + db.flush() + + res = coupon_data_report(db, date_from="2020-01-03", date_to="2020-01-03", app_env="prod") + rows = {row["trace_id"]: row for row in res["items"]} + + assert rows["rev-cp-unfilled"]["ad_revenue_yuan"] is None + assert rows["rev-cp-zero"]["ad_revenue_yuan"] == 0.0 + finally: + db.rollback() + db.close() + + def test_comparison_list_includes_ad_revenue() -> None: """比价记录列表项带本次广告收益;200 分 → 0.002 元。