cce3a01de1
Co-authored-by: OuYingJun1024 <1034284404@qq.com> Reviewed-on: #49 Co-authored-by: ouzhou <ouzhou@wonderable.ai> Co-committed-by: ouzhou <ouzhou@wonderable.ai>
63 lines
3.3 KiB
Python
63 lines
3.3 KiB
Python
"""看广告金币审计 schemas。
|
||
|
||
只读对账视图:把"看视频"(ad_reward_record)和"信息流"(ad_feed_reward_record)两类发奖记录,
|
||
用与正式发奖相同的公式 [app.core.rewards.calculate_ad_reward_coin] 复算一遍 expected_coin,
|
||
和实际入账的 actual_coin 对比,核对金币公式是否生效。字段 snake_case、金额按金币整数。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from datetime import datetime
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
|
||
class AdCoinAuditRow(BaseModel):
|
||
"""单条发奖记录的复算明细。"""
|
||
|
||
scene: str = Field(..., description="reward_video(看视频) / feed(信息流)")
|
||
record_id: int = Field(..., description="对应记录表主键")
|
||
user_id: int
|
||
created_at: datetime
|
||
status: str = Field(..., description="granted / capped / ecpm_missing")
|
||
ecpm: str | None = Field(None, description="本次采用的 eCPM 原始值(分/千次展示,SDK getEcpm 原值)")
|
||
ecpm_factor: float | None = Field(None, description="因子1(eCPM 档)")
|
||
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="复算与实发是否一致(capped/ecpm_missing 校验是否确为 0)")
|
||
|
||
|
||
class AdCoinFormulaOut(BaseModel):
|
||
"""当前金币公式参数(给前端展示规则参照)。"""
|
||
|
||
description: str = Field(
|
||
"eCPM元 = getEcpm分 ÷ 100;单份金币 = round(eCPM元 ÷ 1000 × 因子1 × 因子2 × coin_per_yuan);"
|
||
"因子1 按 eCPM元 判档(阈值 100/200/400 元)",
|
||
description="公式说明",
|
||
)
|
||
coin_per_yuan: int = Field(..., description="金币:元 汇率")
|
||
ecpm_unit: str = Field("分/千次展示(SDK getEcpm 原值)", description="eCPM 口径")
|
||
feed_unit_seconds: int = Field(..., description="信息流每多少秒折 1 份")
|
||
# [(因子值, 区间下限, 区间上限或 null)]
|
||
ecpm_factor_tiers: list[tuple[float, int, int | None]] = Field(..., description="因子1 档位表")
|
||
lt_factor_tiers: list[tuple[float, int, int | None]] = Field(..., description="因子2 LT 档位表")
|
||
|
||
|
||
class AdCoinAuditOut(BaseModel):
|
||
"""审计响应:公式参照 + 全量统计 + 明细。"""
|
||
|
||
date: str = Field(..., description="审计日期(北京时间 YYYY-MM-DD)")
|
||
formula: AdCoinFormulaOut
|
||
total: int = Field(..., description="该筛选下复算总条数(全量,不受 limit/only_mismatch 影响)")
|
||
mismatch_count: int = Field(
|
||
..., description="全量不一致条数(=0 说明公式全部生效;在截断前统计,可信)"
|
||
)
|
||
truncated: bool = Field(
|
||
..., description="展示集是否被 limit 截断(true=还有未返回的明细,请缩小范围或调大 limit)"
|
||
)
|
||
items: list[AdCoinAuditRow] = Field(..., description="展示明细;only_mismatch=true 时只含 ✗ 行")
|