2416c59255
- 抽出 stats.period_active_dau(date_from, date_to):任意北京自然日区间的去重活跃用户 (登录 + 开始比价 + 开始领券),口径与数据大盘 period.users.active 一致。today_dau 与 dashboard_overview 的 period 活跃、逐日趋势统一复用它,消除三处重复的并集计算,行为不变。 - ad_revenue_report 去掉「仅今日单天才给 dau」的 is_today 限制,dau 改为按 date_from~date_to 区间统计(含今日、近 7 天、近 30 天);ARPU = 区间预估收益 ÷ 区间活跃用户 对三个时段均可算, dau 与大盘同区间同值。 - dau 为全局口径,不随 user_id/ad_type/feed_scene/app_env 筛选变化;更新 schema 字段说明。 - 验证:admin 读接口测试(含 dashboard_overview)全过;实测多天区间 dau 非空且 == 大盘 period.users.active。前端展示改动见 admin-web 对应 PR。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
167 lines
10 KiB
Python
167 lines
10 KiB
Python
"""广告收益报表 schemas。
|
||
|
||
**逐条广告事件**只读报表:每行一次广告(激励视频展示+发奖按会话合并;信息流展示/发奖各自成行),
|
||
含 展示条数、收益(元)、应发/实发金币、对账。字段 snake_case;收益按元(float),金币按整数。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from datetime import datetime
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
|
||
class AdRevenueImpression(BaseModel):
|
||
"""聚合行下钻的单条**展示**明细(每次广告展示一条,展开该组时展示)。"""
|
||
|
||
id: int = Field(..., description="ad_ecpm_record 主键")
|
||
created_at: datetime
|
||
ecpm: str = Field(..., description="本次展示 eCPM 原始值(分/千次展示)")
|
||
revenue_yuan: float = Field(..., description="本次展示预估收益(元)= eCPM元 ÷ 1000")
|
||
adn: str | None = Field(None, description="实际填充 ADN 子渠道(pangle/gdt…)")
|
||
slot_id: str | None = Field(None, description="底层 mediation rit(非我们配置的广告位 ID)")
|
||
|
||
|
||
class AdRevenueRecord(BaseModel):
|
||
"""聚合行下钻的单条发奖复算明细(与金币审计同源,展开该组时展示)。"""
|
||
|
||
record_id: int
|
||
created_at: datetime
|
||
status: str = Field(..., description="granted / capped / ecpm_missing")
|
||
ecpm: str | None = Field(None, description="本次采用的 eCPM 原始值(分/千次展示)")
|
||
ecpm_factor: float | None = Field(None, description="因子1(eCPM 档);非 granted 为空")
|
||
units: int = Field(..., description="折算份数:激励视频恒 1;信息流 = 满 10 秒份数")
|
||
lt_index_start: int | None = Field(None, description="本条占用「账号累计第几份」的起")
|
||
lt_index_end: int | None = Field(None, description="本条占用「账号累计第几份」的止;激励视频 = 起")
|
||
lt_factor_start: float | None = Field(None, description="因子2(LT)起值")
|
||
lt_factor_end: float | None = Field(None, description="因子2(LT)止值;激励视频 = 起")
|
||
expected_coin: int = Field(..., description="按公式复算应发金币")
|
||
actual_coin: int = Field(..., description="实际入账金币")
|
||
matched: bool = Field(..., description="复算与实发是否一致")
|
||
|
||
|
||
class AdRevenueDaily(BaseModel):
|
||
"""按日期汇总的一天(供前端按天趋势图;全量,不受分页影响)。"""
|
||
|
||
date: str = Field(..., description="北京时间 YYYY-MM-DD")
|
||
impressions: int = Field(..., description="当天展示条数合计")
|
||
revenue_yuan: float = Field(..., description="当天客户端预估收益合计(元;eCPM 折算)")
|
||
pangle_revenue_yuan: float | None = Field(
|
||
None, description="当天穿山甲后台预估收益(元;GroMore revenue);非全量视图/无数据为空"
|
||
)
|
||
pangle_api_revenue_yuan: float | None = Field(
|
||
None, description="当天穿山甲收益Api(元;GroMore api_revenue,更接近结算);未配/当天/无数据为空"
|
||
)
|
||
expected_coin: int = Field(..., description="当天应发金币合计")
|
||
actual_coin: int = Field(..., description="当天实发金币合计")
|
||
|
||
|
||
class AdRevenueHourly(BaseModel):
|
||
"""按北京小时(0–23)汇总的一小时(供前端按小时趋势图;全量,不受分页影响,单日 granularity=hour 时非空)。"""
|
||
|
||
hour: int = Field(..., description="北京时间小时 0–23")
|
||
impressions: int = Field(..., description="该小时展示条数合计")
|
||
revenue_yuan: float = Field(..., description="该小时预估收益合计(元)")
|
||
expected_coin: int = Field(..., description="该小时应发金币合计")
|
||
actual_coin: int = Field(..., description="该小时实发金币合计")
|
||
|
||
|
||
class AdRevenueTypeStat(BaseModel):
|
||
"""按广告类型(ad_type)的小计:展示条数 + 预估收益(eCPM 由前端用 收益÷展示×1000 算)。"""
|
||
|
||
impressions: int = Field(..., description="该类型展示条数合计")
|
||
revenue_yuan: float = Field(..., description="该类型预估收益合计(元)")
|
||
|
||
|
||
class AdRevenueRow(BaseModel):
|
||
"""一次广告事件(逐条一行):激励视频展示与发奖按 ad_session_id 合并;信息流展示 / 发奖各自成行。"""
|
||
|
||
event_key: str = Field(..., description="该事件稳定唯一键(imp-{ecpm_id} / rwd-{reward_id});前端 rowKey 用")
|
||
report_date: str = Field(..., description="该事件所属日期(北京时间 YYYY-MM-DD)")
|
||
user_id: int
|
||
user_phone: str | None = Field(None, description="用户手机号(admin 展示用,完整;用户已删 / 查不到为空)")
|
||
ad_type: str = Field(..., description="reward_video(激励视频) / feed(信息流) / draw(Draw 信息流);历史 NULL 视为 feed")
|
||
feed_scene: str | None = Field(
|
||
None,
|
||
description="点位场景:comparison(比价) / coupon(领券) / welfare(福利);供区分比价/领券 Draw 收益;"
|
||
"激励视频与旧数据为空",
|
||
)
|
||
app_env: str | None = Field(None, description="我们的应用:prod(傻瓜比价正式) / test(测试应用);旧数据为空")
|
||
our_code_id: str | None = Field(None, description="我们后台配置的代码位 ID(104xxx);旧数据为空")
|
||
hour: int | None = Field(None, description="北京时间小时 0–23(granularity=hour 时有值;按天为 null)")
|
||
created_at: datetime = Field(..., description="事件时间(有展示=展示时间,纯发奖=发奖时间)")
|
||
# ── 展示侧 ──
|
||
has_impression: bool = Field(..., description="是否有广告展示(信息流逐条展示=True,纯发奖行=False)")
|
||
impressions: int = Field(..., description="本行展示条数:有展示=1 / 纯发奖=0(供日汇总、趋势图复用)")
|
||
ecpm: str | None = Field(None, description="eCPM 原始值(分/千次);展示行取展示值,纯发奖行取发奖采用值")
|
||
revenue_yuan: float = Field(..., description="本次展示预估收益(元)= eCPM元 ÷ 1000;纯发奖行=0")
|
||
row_revenue_yuan: float | None = Field(
|
||
None,
|
||
description="主表逐行展示用的预估收益(元):一次比价/领券聚合行=该次发奖广告 eCPM 折算之和;"
|
||
"其它行为空(前端回退取 revenue_yuan)。不进合计/趋势,避免与展示侧重复计",
|
||
)
|
||
adn: str | None = Field(None, description="实际填充 ADN 子渠道(pangle/gdt…);纯发奖行为空")
|
||
slot_id: str | None = Field(None, description="底层 mediation rit(非我们配置的广告位 ID);纯发奖行为空")
|
||
# ── 发奖侧 ──
|
||
has_reward: bool = Field(..., description="是否有发奖记录(激励视频合并行 / 信息流整场发奖行=True;纯展示=False)")
|
||
status: str | None = Field(None, description="发奖状态 granted/closed_early/too_short/…;纯展示为空")
|
||
expected_coin: int = Field(..., description="应发金币(公式复算,与金币审计同源);纯展示=0")
|
||
actual_coin: int = Field(..., description="实发金币(实际入账);纯展示=0")
|
||
matched: bool = Field(..., description="本条应发==实发;纯展示恒 True(不计对账)")
|
||
reward_detail: AdRevenueRecord | None = Field(
|
||
None,
|
||
description="发奖复算明细(eCPM/因子1/份数/LT/因子2/应发/实发/一致);点行展开下钻用,纯展示为空",
|
||
)
|
||
sub_rewards: list[AdRevenueRecord] = Field(
|
||
default_factory=list,
|
||
description="一次比价/领券聚合行的组内逐条发奖明细(同一整场 ad_session_id 的多条广告);"
|
||
"点行展开渲染多行。激励视频/纯展示行为空(单条看 reward_detail)",
|
||
)
|
||
sub_count: int = Field(
|
||
1,
|
||
description="本行聚合的发奖条数:一次比价/领券=该次广告条数(≥1);激励视频/纯展示=1",
|
||
)
|
||
|
||
|
||
class AdRevenueReportOut(BaseModel):
|
||
"""报表响应:全量统计 + 按天趋势 + 聚合明细。"""
|
||
|
||
date_from: str = Field(..., description="报表起始日期(北京时间 YYYY-MM-DD)")
|
||
date_to: str = Field(..., description="报表结束日期(北京时间 YYYY-MM-DD,闭区间;单日时与 date_from 相同)")
|
||
daily: list[AdRevenueDaily] = Field(..., description="按日期汇总序列(全量,供按天趋势图)")
|
||
hourly: list[AdRevenueHourly] = Field(
|
||
default_factory=list,
|
||
description="按小时汇总序列(全量,供按小时趋势图;按天查询时为空)",
|
||
)
|
||
type_stats: dict[str, AdRevenueTypeStat] = Field(
|
||
default_factory=dict,
|
||
description="按广告类型(ad_type)小计 {ad_type: {impressions, revenue_yuan}};前端取 draw / reward_video 做分类大盘",
|
||
)
|
||
dau: int | None = Field(
|
||
None,
|
||
description="所选日期区间的去重活跃用户数(口径同数据大盘 period.users.active:登录 + 开始比价 + "
|
||
"开始领券)。按 date_from~date_to 区间统计,含今日、近 7 天、近 30 天等任意区间;全局口径,"
|
||
"不随 user_id / ad_type / feed_scene / app_env 筛选变化",
|
||
)
|
||
total: int = Field(..., description="广告事件总数(全量,不受分页影响;= 当前筛选下的分页总条数)")
|
||
truncated: bool = Field(..., description="当前页之后是否还有更多事件(len(events) > offset + limit)")
|
||
total_impressions: int = Field(..., description="全量展示条数合计")
|
||
total_revenue_yuan: float = Field(..., description="全量客户端预估收益合计(元;eCPM 折算)")
|
||
total_pangle_revenue_yuan: float | None = Field(
|
||
None,
|
||
description="全量穿山甲后台预估收益合计(元;GroMore revenue)。穿山甲无用户/类型/场景维度,"
|
||
"仅「全量视图」(未按 user_id/ad_type/feed_scene 过滤)时有值,否则为 null",
|
||
)
|
||
total_pangle_api_revenue_yuan: float | None = Field(
|
||
None,
|
||
description="全量穿山甲收益Api合计(元;GroMore api_revenue,各 ADN 回传、更接近结算);"
|
||
"未配 Reporting / 查当天 / 非全量视图 时为 null",
|
||
)
|
||
pangle_revenue_available: bool = Field(
|
||
False,
|
||
description="本次结果是否带穿山甲后台收益(=全量视图且已同步到数据)。false 时前端「穿山甲收益」显示「-」",
|
||
)
|
||
total_expected_coin: int = Field(..., description="全量应发金币合计")
|
||
total_actual_coin: int = Field(..., description="全量实发金币合计")
|
||
mismatch_count: int = Field(..., description="应发≠实发的发奖条数(=0 说明全部按公式发放)")
|
||
items: list[AdRevenueRow] = Field(..., description="逐条广告事件(按 日期→用户→时间 排序)")
|