# 邀请嘉年华（千万补贴）前端对接文档

面向 App/H5 和管理后台。协议约定与 [Splay 2.0 前端对接文档](./splay_2_0_frontend_integration.md) 第 2 节相同：`POST`、登录态、`snake_case`、金额为 decimal 字符串、时间为 Unix 秒（`int64` 可能是字符串或数字）。

## 1. 活动规则（给前端展示用）

| 项 | 说明 |
| --- | --- |
| 活动名 | 邀请嘉年华 / 千万补贴 |
| 开始时间 | **2026-08-15 00:00:00（东七区）** |
| 结束时间 | 不配固定结束日；奖池耗尽后活动结束 |
| 谁能拿补贴 | 下级用户完成**人生第一笔硬币支持**，且该笔是购买**普通机器人**或**管家（固定收益）**时，给其**直推上级**记待领补贴 |
| 上级门槛 | 直推上级支持等级 ≥ Lv1 |
| 补贴比例 | 仅 1 层，**10%**（按该笔机器人单份支持金额计算） |
| 不算首笔 | 历史上只要有过任意硬币支持（短剧、等级机、申购机、普通机、管家等都算），之后再买参与机型也不发 |
| 不发奖的支持 | 短剧打赏、申购机、等级机、复购机、智投专区、经验值/支持券支付等 |
| 停发条件 | 奖池剩余余额 **<** 当前开放的普通/管家机器人最大支持金额 × 10% |
| 领取 | 上级在活动页主动领取，入账可用硬币；领取不扣奖池 |

前端**不要自己算**活动是否结束，一律以后端返回的 `activity_started` / `activity_stopped` 为准。

## 2. 活动状态

主接口：`POST /v1/mega_subsidy/get`

| `activity_started` | `activity_stopped` | 页面状态 | 建议 UI |
| --- | --- | --- | --- |
| `false` | `false` | 未开始 | 倒计时 / 敬请期待，隐藏领取 |
| `true` | `false` | 进行中 | 正常展示奖池、邀请、领取 |
| `true` | `true` | 已结束（奖池耗尽） | 展示「活动已结束」，已产生的待领仍可领取 |

加载奖池失败时：只保证 `activity_started` 正确，`activity_stopped` 为 `false`，奖池金额可能为空。不要把空金额当成「余额为 0 / 已结束」。

```ts
type CarnivalState = "not_started" | "running" | "stopped";

function carnivalState(started: boolean, stopped: boolean): CarnivalState {
  if (!started) return "not_started";
  if (stopped) return "stopped";
  return "running";
}
```

## 3. C 端接口

所有接口需登录。金额字段均为字符串。

### 3.1 活动页主数据

`POST /v1/mega_subsidy/get`

请求：空 body `{}`

响应：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `activity_started` | bool | 是否已到开始时间 |
| `activity_stopped` | bool | 奖池是否已按阈值耗尽 |
| `mega_subsidy_pool_amount` | string | 奖池**剩余**余额 |
| `total_subsidy_amount_limit` | string | 奖池额度（后台配置的总盘） |
| `total_subsidy_amount_used` | string | 已消耗 ≈ 额度 − 剩余；若后台把剩余调得比额度大，则为 `"0"` |
| `wait_send_amount` | string | **当前用户**待领取金额；无记录时可能是 `""`，按 `"0"` 处理 |
| `total_send_amount` | string | **当前用户**已领取累计 |
| `mega_subsidy_ratio_1_floor` | string | 1 层比例，本期为 `"10"`（单位 %） |
| `mega_subsidy_ratio_2_floor` | string | 本期不返，可能为空 |
| `mega_subsidy_ratio_3_floor` | string | 本期不返，可能为空 |

页面建议：

- 奖池进度：`used / limit`，分母为 `total_subsidy_amount_limit`
- 剩余展示：`mega_subsidy_pool_amount`
- 「领取」按钮：`wait_send_amount > 0` 时可用；活动结束后只要待领 > 0 仍应可点
- 比例文案：直推下级首笔普通/管家机器人支持金额的 10%

### 3.2 领取补贴

`POST /v1/mega_subsidy/receive`

请求：空 body `{}`

成功：空对象。待领全部入账可用硬币，待领清零。

失败：

| 错误码 | 含义 | 前端处理 |
| --- | --- | --- |
| `24003`（`CODE_VIP_INVITE_ALREADY_RECEIVED`） | 没有待领，或已经领过 | Toast「暂无可领取」或刷新主接口 |

领取成功后重新拉 `GET` 主接口，刷新 `wait_send_amount` / `total_send_amount` 和用户硬币余额。

### 3.3 领取滚动列表（广场）

`POST /v1/mega_subsidy/record/list`

请求：

| 字段 | 说明 |
| --- | --- |
| `offset` | ≥ 0 |
| `limit` | 0～100 |
| `sort` / `order` | 可选，`ASC` / `DESC` |

响应：

| 字段 | 说明 |
| --- | --- |
| `total` | 展示用总数，**含填充和加码，不是真实领取人数** |
| `records[].user_id` | 用户 ID |
| `records[].send_at` | 领取时间，Unix 秒 |

说明：真实记录不足 100 条时会补假数据；`total` 还会额外加大。只做滚动播报，不要当统计指标。接口有 **60 秒**进程内缓存，TTL 内假数据不会变。

### 3.4 我的直推首笔数据

`POST /v1/mega_subsidy/first_data/get`

请求：

```json
{ "floor": 1 }
```

本期只支持 `floor = 1`。

响应：

| 字段 | 说明 |
| --- | --- |
| `new_user_count` | 给当前用户贡献过补贴的下级人数 |
| `total_support_amount` | 这些下级的累计支持硬币 |
| `total_subsidy_amount` | 当前用户因此获得的累计补贴 |
| `subsidy_ratio` | 该层比例，1 层为 `"10"` |

## 4. 管理后台接口

需管理员登录。

### 4.1 查询奖池

`POST /v1/man/invite_carnival/pool/get`

响应：`quota`、`remaining_balance`、`activity_started`、`activity_stopped`。状态判断与 C 端相同。

### 4.2 配置奖池

`POST /v1/man/invite_carnival/pool/set`

| 字段 | 说明 |
| --- | --- |
| `quota` | 额度。不传或空字符串表示不改 |
| `remaining_balance` | 剩余余额。不传或空字符串表示不改 |

至少传其中一个。必须 ≥ 0 的合法小数。

行为：

- 第一次配**正数**额度、且还没有余额行时：剩余 = 额度 − 本期已计提（待领+已领）
- `quota = "0"` 只改额度，不创建余额行
- 奖池耗尽后只调高额度，**不会**自动把剩余灌满，要恢复发放请同时改 `remaining_balance`
- 改剩余会与发放扣减互斥，不会被并发覆盖

示例：

```json
{ "quota": "100000" }
```

```json
{ "remaining_balance": "85000" }
```

未配正数额度时，C 端会看到「已开始 + 已结束」。测试前先配额度。

### 4.3 补贴列表（原有，带奖池字段）

`POST /v1/man/mega_subsidy/list`

响应新增：

| 字段 | 说明 |
| --- | --- |
| `pool_quota` | 奖池额度 |
| `pool_remaining` | 奖池剩余 |

## 5. 页面状态机建议

```
进入活动页
  → POST /v1/mega_subsidy/get
  → 未开始：倒计时（目标 2026-08-15 00:00 东七区）
  → 进行中：奖池 + 我的待领 + 邀请引导
  → 已结束：结束态；wait_send_amount > 0 仍显示领取
领取
  → POST /v1/mega_subsidy/receive
  → 成功：刷新 get + 用户硬币
  → 24003：刷新 get
滚动播报
  → POST /v1/mega_subsidy/record/list
直推数据卡
  → POST /v1/mega_subsidy/first_data/get  { "floor": 1 }
```

邀请引导文案建议：

> 邀请好友完成人生第一笔硬币支持，且购买普通机器人或管家（固定收益），你可获得该笔支持金额 10% 的补贴。好友如果已经支持过短剧或其他产品，将不再计入。

## 6. 联调注意

- 金额用字符串展示，不要用 JS `number` 做累加。
- `wait_send_amount` 为空时按 0。
- 活动结束不等于不能领：已记待领的仍可 `receive`。
- 停发阈值由后端按「当前开放的普通/管家机器人最高价 × 10%」计算，前端无需实现。
- 列表 `total` / 部分 `user_id` 含展示填充，不能当真实 UV。
