0bddce516f
- eCPM 口径修正:getEcpm 原值是分/千次,新增 parse_ecpm_fen;parse_ecpm_yuan 改为 ÷100 转元,因子1 阈值按元判档(100/200/400),收益换算用元 - test-grant 用客户端按 ad_session_id 上报的真实 eCPM 发奖(取不到/≤0 兜底 200) - 新增 GET /admin/api/ad-coin-audit 只读复算对账(router/repo/schema),复用发奖公式 - 同步更新 docs/api、docs/database 的分/元口径 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: OuYingJun1024 <1034284404@qq.com> Reviewed-on: #32 Co-authored-by: ouzhou <ouzhou@wonderable.ai> Co-committed-by: ouzhou <ouzhou@wonderable.ai>
52 lines
2.5 KiB
Python
52 lines
2.5 KiB
Python
"""广告展示 eCPM 上报记录(内部收益统计/对账)。
|
|
|
|
每条 = 客户端一次广告展示(`onAdShow`)后读到的 eCPM 信息。`ad_session_id`
|
|
由客户端生成,并通过激励视频 extra 透传给 S2S 回调,用于把"展示 eCPM"与
|
|
"奖励完成"绑定。没有 session id 的旧上报仍可作为按天对账补充。
|
|
|
|
⚠️ `ecpm_raw` 原样存客户端上报的字符串——eCPM 单位(分 / 元)截至 2026-05-31 尚未最终确认,
|
|
确认后再加一列解析好的数值;在此之前对账按"待定单位"处理。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import DateTime, ForeignKey, Integer, String, UniqueConstraint, func
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.db.base import Base
|
|
|
|
|
|
class AdEcpmRecord(Base):
|
|
__tablename__ = "ad_ecpm_record"
|
|
__table_args__ = (
|
|
UniqueConstraint("ad_session_id", name="uq_ad_ecpm_record_session"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
user_id: Mapped[int] = mapped_column(
|
|
Integer, ForeignKey("user.id"), index=True, nullable=False
|
|
)
|
|
# 广告类型:reward_video(激励视频) / draw(Draw 信息流) 等;不强行统一代码位,各类型各自上报
|
|
ad_type: Mapped[str] = mapped_column(String(32), nullable=False)
|
|
# 客户端生成的一次广告会话 id;激励视频 S2S 回调 extra 会透传同值
|
|
ad_session_id: Mapped[str | None] = mapped_column(String(64), index=True, nullable=True)
|
|
# 实际投放的 ADN(穿山甲 getShowEcpm().getSdkName(),如 pangle / gdt)
|
|
adn: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
|
# 实际展示用的代码位(底层 mediation rit,非客户端配置位)
|
|
slot_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
|
# 客户端上报的 eCPM 原始字符串(单位:分/千次展示,SDK getEcpm 原值,原样存)
|
|
ecpm_raw: Mapped[str] = mapped_column(String(32), nullable=False)
|
|
# 北京时间日期串 'YYYY-MM-DD',按它等值做"按天聚合"(不在 SQL 里做跨时区 date 比较)
|
|
report_date: Mapped[str] = mapped_column(String(10), index=True, nullable=False)
|
|
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), index=True, nullable=False
|
|
)
|
|
|
|
def __repr__(self) -> str: # pragma: no cover
|
|
return (
|
|
f"<AdEcpmRecord user_id={self.user_id} {self.ad_type} "
|
|
f"ecpm={self.ecpm_raw} adn={self.adn}>"
|
|
)
|