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>
79 lines
2.3 KiB
Python
79 lines
2.3 KiB
Python
"""广告 eCPM 上报 CRUD(内部收益统计/对账)。
|
|
|
|
客户端在广告展示后(onAdShow)读到 eCPM,经鉴权接口上报,这里落库。鉴权接口已确保
|
|
user 存在(JWT),故不做 UnknownUser 校验。best-effort 上报:丢一两条不影响业务,
|
|
穿山甲后台报表是结算权威兜底。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from sqlalchemy import func, select
|
|
from sqlalchemy.exc import IntegrityError
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.core.rewards import cn_today
|
|
from app.models.ad_ecpm import AdEcpmRecord
|
|
|
|
|
|
def create_ecpm_record(
|
|
db: Session,
|
|
user_id: int,
|
|
*,
|
|
ad_type: str,
|
|
ecpm_raw: str,
|
|
ad_session_id: str | None = None,
|
|
adn: str | None = None,
|
|
slot_id: str | None = None,
|
|
) -> AdEcpmRecord:
|
|
"""落一条 eCPM 上报记录。report_date 用北京时间当天,供按天聚合。"""
|
|
if ad_session_id:
|
|
existing = find_by_session(db, user_id=user_id, ad_session_id=ad_session_id)
|
|
if existing is not None:
|
|
return existing
|
|
rec = AdEcpmRecord(
|
|
user_id=user_id,
|
|
ad_type=ad_type,
|
|
ad_session_id=ad_session_id,
|
|
adn=adn,
|
|
slot_id=slot_id,
|
|
ecpm_raw=ecpm_raw,
|
|
report_date=cn_today().isoformat(),
|
|
)
|
|
db.add(rec)
|
|
try:
|
|
db.commit()
|
|
except IntegrityError:
|
|
db.rollback()
|
|
if ad_session_id:
|
|
existing = find_by_session(db, user_id=user_id, ad_session_id=ad_session_id)
|
|
if existing is not None:
|
|
return existing
|
|
raise
|
|
db.refresh(rec)
|
|
return rec
|
|
|
|
|
|
def find_by_session(
|
|
db: Session, *, user_id: int, ad_session_id: str | None
|
|
) -> AdEcpmRecord | None:
|
|
"""按广告会话找 eCPM。旧客户端无 ad_session_id 时返回 None。"""
|
|
if not ad_session_id:
|
|
return None
|
|
return db.execute(
|
|
select(AdEcpmRecord).where(
|
|
AdEcpmRecord.user_id == user_id,
|
|
AdEcpmRecord.ad_session_id == ad_session_id,
|
|
)
|
|
).scalar_one_or_none()
|
|
|
|
|
|
def count_today(db: Session, user_id: int) -> int:
|
|
"""该用户今日(北京时间)上报的 eCPM 条数,排查/对账辅助用。"""
|
|
return db.execute(
|
|
select(func.count())
|
|
.select_from(AdEcpmRecord)
|
|
.where(
|
|
AdEcpmRecord.user_id == user_id,
|
|
AdEcpmRecord.report_date == cn_today().isoformat(),
|
|
)
|
|
).scalar_one()
|