Compare commits

..

6 Commits

Author SHA1 Message Date
guke 4680277c2a fix(admin-coupon-data): fill ad_revenue_yuan in user-records drawer too
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 14:37:18 +08:00
guke e9c0a80d44 feat(admin): 两看板逐行补「本次广告收益」(ad_revenue_yuan)
按本页 trace_id 一次性聚合 ad_ecpm_record 展示收益(元),挂到领券数据
看板明细行(dict 字段)与比价记录列表项(ORM 瞬态属性),防 N+1 范式与
现有 _attach_user_info / _ad_coins_by_trace 一致。

- CouponDataRow: 新增 ad_revenue_yuan: float = 0.0 字段
- AdminComparisonListItem: 新增 ad_revenue_yuan: float = 0.0 字段
- coupon_data.py: _session_to_row 增 ad_revenue_yuan 参数;coupon_data_report
  在分页后调用 crud_ecpm.revenue_yuan_by_trace 按本页批量聚合
- queries.py: list_comparison_records 在 _attach_user_info 后瞬态挂 ad_revenue_yuan
- 新增 tests/test_board_ad_revenue.py:两个看板各一个集成测试

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 14:24:07 +08:00
guke 166fe9e8dd test(ad-ecpm): exercise eCPM ¥500 clamp path + explicit zero-revenue assert
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 14:18:22 +08:00
guke 5e8c9d3028 test(ad-ecpm): revenue_yuan_by_trace TDD — 按 trace 聚合展示收益(元)(A4)
三个测试覆盖:
- 同一 trace 多条 eCPM 求和 + min(eCPM元, ¥500)/1000 钳顶
- 空 trace 列表直接返回 {}(避免 IN () 非法)
- create_ecpm_record 落 trace_id 持久化验证

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 12:25:50 +08:00
guke 107157cdd1 feat(ad-ecpm): trace_id 端到端落库 — 模型/迁移/Schema/Repository/Endpoint(A1-A3+A5)
- AdEcpmRecord 新增 trace_id(String 64, index, nullable)列,与 ad_feed_reward_record 一致
- 迁移 ad_ecpm_trace_id:ADD COLUMN + CREATE INDEX,同时收敛双 head
  (11c44afbea58 selfstat + merge_pages_override_coupon_slot → 单 head)
- EcpmReportIn.trace_id 字段透传客户端本场比价/领券 session ID
- create_ecpm_record 新增 trace_id 参数并落库
- ecpm_report 端点 forward payload.trace_id 到 repository

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 12:25:43 +08:00
guke 80e0fb90db docs: plan for per-session ad revenue in admin boards
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 12:18:20 +08:00
2 changed files with 8 additions and 36 deletions
+8 -7
View File
@@ -1,15 +1,13 @@
"""ad_ecpm_record.trace_id(展示收益归属到比价/领券 trace)
"""ad_ecpm_record.trace_id(展示收益归属到比价/领券 trace)+ 收敛双 head
信息流(Draw)展示 eCPM 上报时带上本场比价/领券 trace_id,落此列;领券数据 / 比价记录看板
按 trace_id 聚合"本次广告收益"。激励视频/福利/旧客户端为 NULL。
本迁移原以 (11c44afbea58, merge_pages_override_coupon_slot) 为双亲、顺带收敛双 head,
但与它并行落 main 的 merge_selfstat_coupon_slot 已用同一对双亲做了纯收敛 → 同一对
父节点出现两个收敛点、main 上又成双 head。故重挂到该 merge 之后成单链(仅改链接、
schema 改动不变;两文件都保留,已 stamp 在 merge 上的库可直接线性升级)。
顺带把当前两个 head(11c44afbea58 selfstat 表 + merge_pages_override_coupon_slot)收敛成
单 head,让 `alembic upgrade head`(单数,部署/run.sh 用)恢复正常。
Revision ID: ad_ecpm_trace_id
Revises: merge_selfstat_coupon_slot
Revises: 11c44afbea58, merge_pages_override_coupon_slot
Create Date: 2026-07-10
"""
from typing import Sequence, Union
@@ -19,7 +17,10 @@ import sqlalchemy as sa
revision: str = "ad_ecpm_trace_id"
down_revision: Union[str, Sequence[str], None] = "merge_selfstat_coupon_slot"
down_revision: Union[str, Sequence[str], None] = (
"11c44afbea58",
"merge_pages_override_coupon_slot",
)
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
@@ -1,29 +0,0 @@
"""合并两个 alembic head:11c44afbea58(#127 埋点健康度 selfstat)+ merge_pages_override_coupon_slot(#130 自带的合并迁移)。
三条分支都从 admin_user_plain_password 分叉(#126 权限 / #127 selfstat / #130 领券成功率)。
#130 自带的 merge 创建时本地 main 尚无 #127 的 11c44afbea58,只收敛了 #126 + 自身两条,
#130 合入后 main 上仍留两个 head → `alembic upgrade head`(单数,部署/run.sh 用)直接报错、服务起不来。
本迁移仅把二者收敛成单 head;**不含任何表结构 / 数据改动**(纯 merge)。
Revision ID: merge_selfstat_coupon_slot
Revises: 11c44afbea58, merge_pages_override_coupon_slot
Create Date: 2026-07-10 00:00:00.000000
"""
from collections.abc import Sequence
revision: str = "merge_selfstat_coupon_slot"
down_revision: str | Sequence[str] | None = (
"11c44afbea58",
"merge_pages_override_coupon_slot",
)
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
"""纯合并 head,无 schema 改动。"""
def downgrade() -> None:
"""拆回两个 head,无 schema 改动。"""