# ad_reward_record — 看激励视频发奖记录(S2S 回调) > 模型 `app/models/ad_reward.py` · 仓库 `app/repositories/ad_reward.py` · 接口 [ad-pangle-callback](../api/ad-pangle-callback.md) / [ad-reward-status](../api/ad-reward-status.md) / [ad-test-grant](../api/ad-test-grant.md) · [← 索引](./README.md) · [总览](./OVERVIEW.md) 每条 = 穿山甲一次**服务端发奖回调**(用户看完激励视频)。是看广告三条数据流之一(**发奖**;另两条:`ad_watch_log` 时长、`ad_ecpm_record` 收益,三者无公共键)。`trans_id` 唯一做幂等键(穿山甲会重试,同号只发一次)。`reward_date`(北京时间日期串)给"每日上限"计数用。 ## 用在哪 / 增删改查 - **C(插入)**:`POST /ad/pangle-callback`(穿山甲 S2S,经 SHA256 验签;`grant_ad_reward`)或 `POST /ad/test-grant`(本地联调)。三道闸:① 验签不过 → API 层 403,不进库;② `trans_id` 已存在 → 原样返回不重复发;③ **当日发奖次数(`DAILY_AD_REWARD_LIMIT`)或观看总时长(`DAILY_AD_WATCH_SECONDS_LIMIT`=50min,查 `ad_watch_log`)任一到顶** → 记一行 `status='capped'`、`coin=0`、不发币。否则 `granted` + `grant_coins(biz_type='ad_reward', ref_id=trans_id)` 加币,同事务。 - **U / D**:无。 - **R**:`GET /ad/reward-status`(看广告页:今日已发次数/上限、单次金币、本轮已看/冷却结束、今日已看时长/上限);审计/对账整表回溯。 ## 字段 | 列 | 类型 | 约束 / 默认 | 说明(取值 / join) | |---|---|---|---| | `id` | Integer | PK, autoincrement | | | `trans_id` | String(64) | UNIQUE, index, NOT NULL | 穿山甲交易号(幂等键)。**被 `coin_transaction.ref_id` 引用**(biz_type=ad_reward) | | `user_id` | Integer | FK→user.id, index, NOT NULL | 归属用户(回调 media_extra 带回;不存在抛 UnknownUserError) | | `coin` | Integer | NOT NULL, default 0 | 实发金币;`capped` 时为 0 | | `status` | String(16) | NOT NULL, default `granted` | 取值:`granted`(已发)/ `capped`(当日次数或时长超限,记录但不发) | | `reward_date` | String(10) | index, NOT NULL | 北京时间日期串 `YYYY-MM-DD`,按它等值统计当日发奖次数 | | `reward_name` | String(64) | nullable | 穿山甲上报奖励名(参考,不作发奖依据) | | `raw` | String(1024) | nullable | 回调原始参数(审计排查) | | `created_at` | DateTime(tz) | server_default now(), index | 时间 | ## 关系 / Join Key - `user_id` → `user.id`(多对一)。 - `trans_id` ← 被 `coin_transaction.ref_id` 引用(`granted` 那条发币流水);`capped` 行不发币、无对应流水。 - 与 `ad_watch_log` / `ad_ecpm_record` **无公共键**,仅在发奖时读 `ad_watch_log` 当日时长做闸。 ## 索引与约束 - PK `id`;UNIQUE+index `trans_id`;index `user_id`、`reward_date`、`created_at`。 ## 注意 - 实发金币以穿山甲回调 `reward_amount` 为准(`resolve_ad_reward_coin` 解析,缺/坏回退默认、超上限夹紧),单次金币/每日上限/单次上限均从 `app_config` 读(运营后台可改)。 - 并发同 `trans_id` 撞唯一约束 → catch IntegrityError 回滚返回已存在那条(幂等兜底)。