356d6e52b9
签到加成(boost): - 新增 signin_boost_record 表(user+date 唯一)+ POST /api/v1/signin/boost - signin 状态/结算逻辑调整 信息流广告结算: - 新增 ad_feed_reward_record 表(client_event_id 幂等)+ POST /api/v1/ad/feed-reward - ad_feed_reward 模型/repo、ad schema、按 ecpm/时长结算金币 金币数值:core/rewards.py 数值调整、config_schema/admin config 适配 迁移 coin_reward_phase1(down_revision pstat3growth);测试 + API/DB 文档同步 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
41 lines
1.9 KiB
Python
41 lines
1.9 KiB
Python
"""看激励视频观看时长记录(旧版每日总时长字段)。
|
|
|
|
每条 = 客户端 onAdClose 上报的一次激励视频实际观看秒数。按 (user_id, watch_date) 聚合
|
|
SUM(watch_seconds) 得当日总观看时长。当前产品只保留每日 500 次上限,
|
|
core.rewards.DAILY_AD_WATCH_SECONDS_LIMIT=0 表示不启用时长闸;本表保留用于旧客户端兼容和排查。
|
|
|
|
这是看广告的第三条数据流,与另两条并列、互不关联:
|
|
- ad_reward(AdRewardRecord):穿山甲 S2S 回调发金币(后端,有 trans_id);
|
|
- ad_ecpm(AdEcpmRecord):客户端上报展示收益(前端,有 ecpm);
|
|
- ad_watch_log(本表):客户端上报观看时长(前端,有 watch_seconds)。
|
|
前端上报的时长不可信,不能作为唯一发奖依据;当前发奖闸口以 ad_reward 的每日次数上限为准。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import DateTime, ForeignKey, Integer, String, func
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.db.base import Base
|
|
|
|
|
|
class AdWatchLog(Base):
|
|
__tablename__ = "ad_watch_log"
|
|
|
|
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
|
|
)
|
|
# 本次激励视频实际观看秒数(前端 onAdShow→onAdClose);服务端已夹 [0, MAX_SINGLE_WATCH_SECONDS]
|
|
watch_seconds: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
# 北京时间日期串 'YYYY-MM-DD',按它等值做"当日总时长"聚合(不在 SQL 里跨时区比较)
|
|
watch_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"<AdWatchLog user_id={self.user_id} sec={self.watch_seconds} {self.watch_date}>"
|