# POST /api/v1/ad/reward-noshow — 激励视频提前关闭/未发奖留痕

激励视频**展示了但用户提前关闭/跳过、未触发 S2S 发奖**时，客户端 best-effort 上报一条留痕记录，
让运营后台「广告数据」能呈现「有展示、没发金币」的原因。**不发金币**。

## 鉴权

需要 Bearer token。`user_id` 由 JWT 取，不在 body。

## 请求体

| 字段 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `ad_session_id` | string | 是 | 本次展示会话 id(与 eCPM 上报、S2S `extra.ad_session_id` 一致)，8-64 字符 |
| `watched_seconds` | int | 否 | 关闭前已观看秒数(仅留痕参考，**不入库**)。默认 0 |
| `ecpm` | string\|null | 否 | 本次展示 eCPM(穿山甲 getEcpm 原值，分/千次展示) |
| `adn` | string\|null | 否 | 实际投放 ADN(聚合后实际填充子渠道) |
| `slot_id` | string\|null | 否 | 实际展示代码位(底层 mediation rit) |
| `app_env` | string\|null | 否 | **我们的**应用环境:`prod`(傻瓜比价正式) / `test`(测试应用) |
| `our_code_id` | string\|null | 否 | **我们后台配置的**代码位 ID(104xxx，非底层 rit) |

## 响应

| 字段 | 类型 | 说明 |
|---|---|---|
| `ok` | bool | 是否处理成功(落库或幂等命中) |
| `status` | string | `closed_early`(已留痕) / `granted`(该次其实已发奖，跳过未写) |

## 说明

- 在 `onAdClose` 时若本次**未触发发奖**(无 `onRewardVerify`/`onRewardArrived`)调用。
- **幂等**:写入 `ad_reward_record`，`trans_id = noreward:{ad_session_id}`(每次展示唯一)，重试不重复。
- **防与正常发奖重复**:若同一 `ad_session_id` 已有 `status='granted'` 记录(S2S 已发，说明用户其实看完了)，
  则跳过不写，返回 `status='granted'`。
- best-effort:客户端 fire-and-forget，失败只 log，不影响主流程。
- 信息流的「用户中途关闭」走 [ad-feed-reward](./ad-feed-reward.md) 的 `aborted=true`（记 `closed_early`），不走本接口。

## 数据写入

- `ad_reward_record` 新增一行(`coin=0`、`status='closed_early'`、`reward_scene='reward_video'`)。
- **不**写 `coin_account` / `coin_transaction`(不发币)。
