d7c29c0883
- rewards: 激励视频实发改按 calculate_ad_reward_coin(eCPM, 当日第N次) 公式;AD_REWARD_COIN/MAX_AD_REWARD_COIN 降为历史兼容口径;新增 SIGNIN_BOOST_COIN=2000 - 签到膨胀: Day1-13 看完激励视频额外发固定金币、Day14 不允许 (signin.py / signin-boost) - ad_session_id 幂等: ad_ecpm/ad_reward/ad_feed_reward 记录加 ad_session_id 列 + 唯一索引(新迁移 coin_reward_phase2 / ad_feed_reward_session) - ad.py + schemas + repositories: ecpm-report / feed-reward / reward-status / test-grant 改造;config_schema 增 signin_boost_coin;admin stats overview 补充 - 同步 docs/api + docs/database + docs/integrations/pangle 及 tests(test_ad_reward / test_welfare) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: OuYingJun1024 <1034284404@qq.com> Reviewed-on: #28 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 原始字符串(单位:元/千次展示,原样存)
|
|
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}>"
|
|
)
|