feat(ad): expose reward result by session
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,7 @@ from app.repositories import ad_watch as crud_watch
|
||||
from app.repositories import app_config
|
||||
from app.repositories import signin as crud_signin
|
||||
from app.schemas.ad import (
|
||||
AdRewardResultOut,
|
||||
AdRewardStatusOut,
|
||||
EcpmReportIn,
|
||||
EcpmReportOut,
|
||||
@@ -242,6 +243,24 @@ def reward_status(user: CurrentUser, db: DbSession) -> AdRewardStatusOut:
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/reward-result/{ad_session_id}",
|
||||
response_model=AdRewardResultOut,
|
||||
summary="按广告会话查本次服务端权威发奖结果(弹窗展示实发金币用)",
|
||||
)
|
||||
def reward_result(ad_session_id: str, user: CurrentUser, db: DbSession) -> AdRewardResultOut:
|
||||
"""激励视频关闭后,客户端按本次 `ad_session_id` 查服务端到底发了多少金币,弹窗只展示这个权威数字。
|
||||
|
||||
只读、只查本人本会话的 reward_video 记录:没有记录=pending(S2S 尚未到账,客户端继续有限重试,
|
||||
别把"未到账"当 0);granted 返回真实 coin;capped/closed_early/ecpm_missing 返回终态(coin=0,
|
||||
不伪装成 granted)。不返回 eCPM / 回调原文 / 他人信息。
|
||||
"""
|
||||
rec = crud_ad.find_reward_video_result(db, user.id, ad_session_id)
|
||||
if rec is None:
|
||||
return AdRewardResultOut(ad_session_id=ad_session_id, status="pending", coin=None)
|
||||
return AdRewardResultOut(ad_session_id=ad_session_id, status=rec.status, coin=rec.coin)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/watch-report",
|
||||
response_model=WatchReportOut,
|
||||
|
||||
@@ -41,6 +41,27 @@ def find_by_trans(db: Session, trans_id: str) -> AdRewardRecord | None:
|
||||
return _find_by_trans(db, trans_id)
|
||||
|
||||
|
||||
def find_reward_video_result(
|
||||
db: Session, user_id: int, ad_session_id: str
|
||||
) -> AdRewardRecord | None:
|
||||
"""按 (user_id, ad_session_id, reward_scene='reward_video') 查**最新一条**发奖记录,
|
||||
供客户端弹窗查这次广告的权威发奖结果。
|
||||
|
||||
只读、按 user_id 隔离(他人查不到)。取最新:同一会话可能先有 closed_early 留痕、S2S 延迟
|
||||
到账后又写 granted,按 (created_at, id) 倒序让 granted 覆盖早先的 closed_early。查不到返回 None。
|
||||
"""
|
||||
return db.execute(
|
||||
select(AdRewardRecord)
|
||||
.where(
|
||||
AdRewardRecord.user_id == user_id,
|
||||
AdRewardRecord.ad_session_id == ad_session_id,
|
||||
AdRewardRecord.reward_scene == "reward_video",
|
||||
)
|
||||
.order_by(AdRewardRecord.created_at.desc(), AdRewardRecord.id.desc())
|
||||
.limit(1)
|
||||
).scalar_one_or_none()
|
||||
|
||||
|
||||
def _granted_today(db: Session, user_id: int, reward_date: str) -> int:
|
||||
return db.execute(
|
||||
select(func.count())
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
@@ -43,6 +44,23 @@ class AdRewardStatusOut(BaseModel):
|
||||
watch_seconds_remaining: int = Field(0, description="今日剩余可观看秒数;limit=0 时客户端不据此拦截")
|
||||
|
||||
|
||||
class AdRewardResultOut(BaseModel):
|
||||
"""客户端弹窗按 `ad_session_id` 查这次广告服务端**权威发奖结果**,只展示这个数字。
|
||||
|
||||
- `pending`:本人没有该会话的 reward_video 记录(S2S 尚未到账)——客户端继续有限重试,
|
||||
**别把"未到账"当成 0 金币**,`coin=null`。
|
||||
- `granted`:返回真实到账金币 `coin`(>0)。
|
||||
- `capped`:达每日上限,`coin=0`,客户端走上限提示而非"获得 0 金币"。
|
||||
- `closed_early` / `ecpm_missing`:终态,`coin=0`,**不能伪装成 granted**。
|
||||
|
||||
不返回 eCPM、回调原文或其他用户信息。
|
||||
"""
|
||||
|
||||
ad_session_id: str
|
||||
status: Literal["pending", "granted", "capped", "closed_early", "ecpm_missing"]
|
||||
coin: int | None = None
|
||||
|
||||
|
||||
class EcpmReportIn(BaseModel):
|
||||
"""客户端上报一次广告展示的 eCPM(内部收益统计/对账)。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user