930eff822c
## 背景 admin「领券数据」「比价记录」两个看板此前只能看到场景级(所有领券/比价)的广告收益, 无法定位「这一次领券/比价具体赚了多少」。根因:收益表 `ad_ecpm_record` 缺 `trace_id`, 无法与领券会话 / 比价记录按 trace 关联。 ## 改动 - **模型/迁移**:`ad_ecpm_record` 新增 `trace_id`(String(64), index, nullable); 迁移 `ad_ecpm_trace_id` 加列 + 索引 `ix_ad_ecpm_record_trace_id`,并**收敛当前两个 alembic head**(`11c44afbea58` selfstat + `merge_pages_override_coupon_slot`)为单 head。 - **上报链路**:`EcpmReportIn` 增 `trace_id` 字段;`/api/v1/ad/ecpm-report` 透传; `create_ecpm_record` 落库。 - **收益聚合**:新增 `revenue_yuan_by_trace(db, trace_ids)`——按 trace_id 聚合展示收益, 单条 = `min(eCPM元, ¥500钳顶)/1000`,与广告收益报表 `ad_revenue.py` **同口径**; 只吃当前页的 trace_id(逐页批量,索引命中,无 N+1)。 - **两个看板**:`CouponDataRow` / `AdminComparisonListItem` 增 `ad_revenue_yuan`; `coupon_data_report`(主表 + 用户抽屉)与 `list_comparison_records` 分页后逐页补该字段。 - **测试**:`tests/test_ad_ecpm_trace_revenue.py`(聚合/钳顶/落库)、 `tests/test_board_ad_revenue.py`(两看板 + 抽屉)。 --------- Co-authored-by: guke <guke@autohome.com.cn> Reviewed-on: #131
88 lines
3.7 KiB
Python
88 lines
3.7 KiB
Python
"""两个看板逐行「本次广告收益」(元):按 trace_id 聚合 ad_ecpm_record。"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import UTC, date, datetime
|
|
|
|
from sqlalchemy import delete
|
|
|
|
from app.admin.repositories import queries
|
|
from app.admin.repositories.coupon_data import coupon_data_report, coupon_user_records
|
|
from app.db.session import SessionLocal
|
|
from app.models.ad_ecpm import AdEcpmRecord
|
|
from app.models.comparison import ComparisonRecord
|
|
from app.models.coupon_state import CouponSession
|
|
|
|
|
|
def _ecpm(trace_id: str, ecpm_raw: str, session_id: str, scene: str) -> AdEcpmRecord:
|
|
return AdEcpmRecord(
|
|
user_id=1, ad_type="draw", feed_scene=scene, ad_session_id=session_id,
|
|
ecpm_raw=ecpm_raw, trace_id=trace_id, report_date="2020-01-02",
|
|
created_at=datetime(2020, 1, 2, tzinfo=UTC),
|
|
)
|
|
|
|
|
|
def test_coupon_data_report_includes_ad_revenue() -> None:
|
|
"""领券看板明细行带本次广告收益;200+300 分 → 0.005 元。"""
|
|
db = SessionLocal()
|
|
try:
|
|
db.add(CouponSession(
|
|
trace_id="rev-cp-1", device_id="d1", status="completed", app_env="prod",
|
|
platforms=["meituan-waimai"], platform_success=["meituan-waimai"],
|
|
started_at=datetime(2020, 1, 2, tzinfo=UTC), started_date=date(2020, 1, 2),
|
|
))
|
|
db.add_all([
|
|
_ecpm("rev-cp-1", "200", "cp-sess-a", "coupon"),
|
|
_ecpm("rev-cp-1", "300", "cp-sess-b", "coupon"),
|
|
])
|
|
db.flush()
|
|
res = coupon_data_report(db, date_from="2020-01-02", date_to="2020-01-02", app_env="prod")
|
|
row = next(r for r in res["items"] if r["trace_id"] == "rev-cp-1")
|
|
assert row["ad_revenue_yuan"] == 0.005
|
|
finally:
|
|
db.rollback()
|
|
db.close()
|
|
|
|
|
|
def test_comparison_list_includes_ad_revenue() -> None:
|
|
"""比价记录列表项带本次广告收益;200 分 → 0.002 元。
|
|
|
|
用独有 user_id 过滤,确保本行必落在第一页(避免共享测试库里同 user 记录多、分页把它挤掉)。
|
|
"""
|
|
db = SessionLocal()
|
|
try:
|
|
db.add(ComparisonRecord(
|
|
trace_id="rev-cmp-1", user_id=987654, status="success", business_type="food",
|
|
created_at=datetime(2020, 1, 2, tzinfo=UTC),
|
|
))
|
|
db.add(_ecpm("rev-cmp-1", "200", "cmp-sess-a", "comparison"))
|
|
db.commit()
|
|
items, _next, _total = queries.list_comparison_records(db, user_id=987654)
|
|
row = next(it for it in items if it.trace_id == "rev-cmp-1")
|
|
assert row.ad_revenue_yuan == 0.002
|
|
finally:
|
|
db.execute(delete(AdEcpmRecord).where(AdEcpmRecord.trace_id.in_(["rev-cmp-1"])))
|
|
db.execute(delete(ComparisonRecord).where(ComparisonRecord.trace_id == "rev-cmp-1"))
|
|
db.commit()
|
|
db.close()
|
|
|
|
|
|
def test_coupon_user_records_includes_ad_revenue() -> None:
|
|
"""手机号抽屉的用户领券记录也带本次广告收益(400 分 → 0.004 元)。"""
|
|
db = SessionLocal()
|
|
try:
|
|
db.add(CouponSession(
|
|
trace_id="rev-drawer-1", device_id="d2", status="completed", app_env="prod",
|
|
user_id=987655, platforms=["meituan-waimai"], platform_success=["meituan-waimai"],
|
|
started_at=datetime(2020, 1, 2, tzinfo=UTC), started_date=date(2020, 1, 2),
|
|
))
|
|
db.add(_ecpm("rev-drawer-1", "400", "drawer-sess-a", "coupon"))
|
|
db.commit()
|
|
res = coupon_user_records(db, user_id=987655)
|
|
row = next(r for r in res["items"] if r["trace_id"] == "rev-drawer-1")
|
|
assert row["ad_revenue_yuan"] == 0.004
|
|
finally:
|
|
db.execute(delete(AdEcpmRecord).where(AdEcpmRecord.trace_id == "rev-drawer-1"))
|
|
db.execute(delete(CouponSession).where(CouponSession.trace_id == "rev-drawer-1"))
|
|
db.commit()
|
|
db.close()
|