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
75 lines
3.1 KiB
Python
75 lines
3.1 KiB
Python
"""admin 比价记录 debug 页 schema(独立于 C 端 app/schemas/compare_record:admin 看任意用户、
|
|
无 debug_trace 权限闸、展示全量 debug 字段)。phone/nickname 由 queries 瞬态挂在 ORM 实例上。"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from pydantic import BaseModel, ConfigDict
|
|
|
|
|
|
class AdminComparisonListItem(BaseModel):
|
|
"""列表项:概要 + debug 维度概览(不含大字段 raw_payload/llm_calls/明细数组)。"""
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: int
|
|
user_id: int
|
|
phone: str | None = None # join User 瞬态(非 DB 列)
|
|
nickname: str | None = None # join User 瞬态
|
|
business_type: str
|
|
trace_id: str
|
|
# admin 是 debug 工具,无条件下发 trace_url(不看 user.debug_trace_enabled)
|
|
trace_url: str | None = None
|
|
status: str
|
|
information: str | None = None
|
|
store_name: str | None = None
|
|
product_names: str | None = None # 下单商品名派生串(顿号分隔;「商品」列展示 + 商品搜索)
|
|
source_platform_name: str | None = None
|
|
best_platform_name: str | None = None
|
|
source_price_cents: int | None = None
|
|
best_price_cents: int | None = None
|
|
saved_amount_cents: int | None = None
|
|
# debug 概览
|
|
total_ms: int | None = None
|
|
step_count: int | None = None
|
|
llm_call_count: int | None = None
|
|
retry_count: int | None = None
|
|
input_tokens: int | None = None # Σ usage.prompt_tokens(server 派生)
|
|
output_tokens: int | None = None # Σ usage.completion_tokens(server 派生)
|
|
device_model: str | None = None
|
|
rom_vendor: str | None = None
|
|
rom_name: str | None = None
|
|
android_version: str | None = None
|
|
app_version: str | None = None
|
|
ad_revenue_yuan: float = 0.0 # 本次比价看的信息流广告预估收益(元),queries 瞬态挂 ORM 实例上
|
|
created_at: datetime
|
|
|
|
|
|
class AdminComparisonDetail(AdminComparisonListItem):
|
|
"""详情:概要 + 全量明细(逐平台对比 / LLM 每次调用 / 原始 payload)。"""
|
|
|
|
source_platform_id: str | None = None
|
|
source_package: str | None = None
|
|
best_platform_id: str | None = None
|
|
best_deeplink: str | None = None
|
|
is_source_best: bool | None = None
|
|
total_dish_count: int | None = None
|
|
skipped_dish_count: int | None = None
|
|
device_id: str | None = None
|
|
items: list = []
|
|
comparison_results: list = [] # 逐平台对比(价格/rank/coupon/打烊...)
|
|
skipped_dish_names: list = []
|
|
# 全量环境
|
|
device_manufacturer: str | None = None
|
|
rom_version: int | None = None
|
|
android_sdk: int | None = None
|
|
app_version_code: int | None = None
|
|
source_app_version: str | None = None
|
|
longitude: float | None = None
|
|
latitude: float | None = None
|
|
# 每次 LLM 调用明细 [{scene,model,input_messages,output,usage,latency_ms,error}]
|
|
llm_calls: list | None = None
|
|
# 原始上报全量;「卡在哪一步」从 raw_payload.platform_results[*].status 读
|
|
# (store_not_found/items_not_found/below_minimum/unsupported = 卡在 找店/加菜/起送/读价)。
|
|
raw_payload: dict | None = None
|