# 常驻三榜 — 前端对接

面向 **C 端** 和 **管理后台**。范围：硬币榜、粉丝新增榜、团队活跃值榜。周年庆 / 斋月假数据不在本方案。

业务日、周一切一律按 **东七区（UTC+7）**。不要用设备本地时区或 UTC 判断「今天」「本周一」。金额字段都是十进制 **string**，不要先转 `number` 再算。

---

## 1. 产品约定（两边都要懂）

展示分 = 真实分 + 已生效加成（`applied`）。C 端看到的分数已经含加成，没有单独的「假分」字段。

| 规则 | 说明 |
|---|---|
| 只设名次 | 运营填「至少第 N」，加成由系统算，后台只读 |
| 只加一次 | 执行当下把人推到至少第 N，然后 **冻结**。不追分、不钉死第 N。真人继续入账可以超过 |
| 加成记在自然日 | 日榜 = 当天 `applied`；周榜 = 本周一到今天之和；总榜 = 历史已生效之和 |
| 控周 / 总会抬今日日榜 | 加成只有一套账，切 Tab 数字对得上。后台会返回「日榜大约第几」 |
| 未来日 10:00 才加 | 预设只存那天的 **日榜** 名次；东七区当天 10:00 执行（10:05 / 10:15 会重试）。0 点不执行 |
| 同一日同一榜同一名次 | 已被别人占用则保存失败 |
| 过往日期 | 锁定，不能改、不能停用 |
| 停用 | 已生效加成 **保留**，人不会瞬间掉榜。本轮没有「缓降撤榜」 |

没有「慢慢爬 / 立刻到位 / 停止追分」选项，后台不要做这三项控件。

---

## 2. C 端（路径和字段不变）

三个 List **HTTP path、proto、请求参数都不变**。变化是：入账后几秒内上榜（不再等整点 Cron）；今天还没人得分时日榜是 **空列表**，不是报错。

鉴权与现网相同，需登录。

| 榜 | 方法 / 路径 | RPC | 请求 |
|---|---|---|---|
| 硬币 | `POST /v1/point/rank/list` | `ListPointRank` | `{ "type": 1\|2\|3 }` |
| 粉丝新增 | `POST /v1/fans/rank/list` | `ListFansRank` | `{ "type": 1\|2\|3 }` |
| 团队活跃值 | `POST /v1/active_value/rank/list` | `ListActiveValueRank` | `{ "type": 1\|2\|3 }` |

`type`：

| 值 | 含义 | 周期 |
|---|---|---|
| `1` `TYPE_TODAY` | 今日 | 东七区当天 0 点起 |
| `2` `TYPE_WEEK` | 本周 | 东七区本周一 0 点起 |
| `3` `TYPE_TOTAL` | 总榜 | 累计 |

`type = 0` 会参数错误。最多 **100** 条；封号 / 非正常用户不出现在列表里，`rank` 按返回列表重排（1 起）。

同一人切 Tab 时，展示分满足：**周 ≥ 今日，总 ≥ 周**（加成汇总一致）。不要自己用三日相加去对总榜。

空榜：`items` 空、`total` 为 0。不要当成接口失败，也不要自己补假数据。

下拉刷新即可拿到最新名次；入账后若要立刻看到自己，延迟 1～2 秒再拉一次足够。

### 2.1 硬币榜 `PointRankItem`

```json
{
  "rank": 1,
  "amount": "123.4500",
  "user": { "...": "UserFans" },
  "playlets": []
}
```

| 字段 | 说明 |
|---|---|
| `amount` | 该周期展示分（真实收入 + 加成），string |
| `user.tips_level` | 可能被后台控榜档案覆盖，直接展示即可 |
| `playlets` | 该周期内支持过的短剧，最多 5 个；逻辑与现网相同 |

### 2.2 粉丝榜 `FansRankItem`

| 字段 | 说明 |
|---|---|
| `fans_count` | 该周期 1～5 层新增粉丝（铁粉 + 间推合计，不含加成） |
| `total_fans_count` | 总 1～5 层粉丝 |

名次按展示分排（真实 1～5 层 + 加成）。不要再读 `direct_count` / `total_direct_count`，这两个字段已删除。

### 2.3 活跃值榜 `ActiveValueRankItem`

| 字段 | 说明 |
|---|---|
| `active_value` | 该周期展示分（自己 + 向上 5 层团队增量 + 加成），string |
| `total_active_value` | 总榜展示分；没有总榜分时回落用户身上的活跃值 |

### 2.4 家族页名次（顺带修了现网空名次）

| 项 | 值 |
|---|---|
| 路径 | `POST /v1/family/data/overview/get` |
| 字段 | `active_value_rank` |

现在读的是 **活跃值日榜** 的真实名次（`ZREVRANK`）。未上榜为 `0`。不改请求。

---

## 3. 管理后台（新页，新接口）

常驻三榜控榜走下面 4 个接口。旧的 `ListUserFakeRankData` / `UpdateUserFakeRankData` **只留给周年庆**，入口请标明「仅周年庆」；不要再拿它们改硬币 / 粉丝 / 活跃值三榜。

鉴权与其它 `/v1/man/*` 相同。

### 3.1 列表 `POST /v1/man/rank_control/list`

RPC：`ListRankControls`

```json
{ "board": 0 }
```

| 字段 | 说明 |
|---|---|
| `board` | `0` 或不传 = 三榜都返回；`1` 硬币 / `2` 粉丝 / `3` 活跃值 |

响应只有 **已经保存过的控榜行**，不是 14 天空日历。前端自己铺格子：

- 起点：东七区 **本周一 0 点**
- 终点：再加 13 天（本周 7 天 + 下周 7 天）
- 把 `items[]` 按 `date`（东七区自然日）+ `board` + `user_id` 填进格子

```json
{
  "items": [
    {
      "user_id": 10001,
      "board": 1,
      "date": 1756771200,
      "target_rank": 3,
      "target_boost": "12.0001",
      "applied": "12.0001",
      "applied_at": 1756807200,
      "enabled": true,
      "current_rank": 3,
      "estimated_daily_rank": 1,
      "tips_level": 0,
      "support_playlet_count": 0,
      "period": 1
    }
  ]
}
```

| 字段 | 说明 |
|---|---|
| `date` | unix 秒。按东七区取自然日展示，不要按浏览器时区 |
| `target_rank` | 当时目标：至少第 N |
| `target_boost` | 系统算出的目标加成，**只读**，不要做成输入框 |
| `applied` | 已生效加成。未来日未执行时为 `"0"` |
| `applied_at` | 生效时间 unix；**`0` = 还没执行**（未来日或 10:00 尚未跑完） |
| `enabled` | `false` = 已停用，加成仍保留 |
| `current_rank` | 当前实际名次（按这条当时设的 `period`）。未来日或未上榜为 `0` |
| `estimated_daily_rank` | 控周榜 / 总榜后，**今日日榜大约第几**。设日榜时与 `current_rank` 接近 |
| `period` | 设名次时用的周期；未来日固定为日榜 |
| `tips_level` / `support_playlet_count` | C 端展示覆盖，未设则为 0 |

列表建议展示两列名次：**目标第 N** / **当前第 X**。当前第 X 比目标差，说明已经被真人超过，这是预期，不是 bug。

### 3.2 设名次 `POST /v1/man/rank_control/set`

RPC：`SetRankTarget`

```json
{
  "user_id": 10001,
  "board": 1,
  "period": 1,
  "date": 1756771200,
  "target_rank": 3,
  "tips_level": 0,
  "support_playlet_count": 0
}
```

| 字段 | 必填 | 说明 |
|---|---|---|
| `user_id` | 是 | 被控榜用户 |
| `board` | 是 | `1` / `2` / `3`，不能 `0` |
| `period` | 今天有效 | `1` 日 / `2` 周 / `3` 总。不传按日榜。**未来日会被忽略，只存日榜名次** |
| `date` | 是 | unix，后端按东七区取自然日。建议传那天东七区 0 点的 unix |
| `target_rank` | 是 | ≥ 1，至少第 N |
| `tips_level` | 否 | `0` 表示不改覆盖 |
| `support_playlet_count` | 否 | `0` 表示不改 |

`board` / `period` 枚举：

| 值 | board | period |
|---|---|---|
| 0 | 未指定（list 可用，set 不可） | 未指定 → 日榜 |
| 1 | 硬币 | 日榜 |
| 2 | 粉丝新增 | 周榜 |
| 3 | 团队活跃值 | 总榜 |

**今天：** 当场按 `period` 对应的榜算缺口，加成记在今天，立刻上榜并冻结。响应里看 `current_rank`、`estimated_daily_rank`。

**未来日：** 只存当天日榜名次，`applied_at = 0`，到那天 10:00 才加分。UI 上 `period` 请锁成日榜，不要让人选「下周周榜第 3」。

**同一天再设：** 按新目标对已有 `applied` 做差值（可以减），不是再累加一笔。

保存成功返回 `{ "item": { ... } }`，结构同列表单项。控周 / 总时务必展示 `estimated_daily_rank`（例如：「日榜大约会变成第 X」）。

### 3.3 停用 `POST /v1/man/rank_control/disable`

```json
{ "user_id": 10001, "board": 1, "date": 1756771200 }
```

空响应。已生效 `applied` 不清。过往日期会失败。

### 3.4 重建榜 `POST /v1/man/rank_control/rebuild`（应急）

```json
{ "board": 1, "period": 0 }
```

| 字段 | 说明 |
|---|---|
| `board` | 必填，重建哪一张榜 |
| `period` | `0` 或不传 = 日 + 周 + 总；否则只重建该周期 |

给运营 / 开发应急用（分数对不齐、Redis 丢了）。会扫库，可能较慢，按钮要二次确认，不要进日常设名次流程。成功空响应。

### 3.5 错误码

| 业务码 | 场景 | 建议文案 |
|---|---|---|
| `27001` `CODE_RANK_TARGET_OCCUPIED` | 该日该榜该名次已被别人占用 | 该名次已被占用，请换一个 |
| `27002` `CODE_RANK_DATE_LOCKED` | 过往日期不可改 / 不可停用 | 过往日期已锁定 |
| `CODE_USER_NOT_FOUND` | `user_id` 不存在 | 用户不存在 |
| `CODE_NOT_FOUND` | 停用时没有这条控榜 | 记录不存在 |
| `CODE_ARGUMENT` | 参数非法，或正在加分请重试 | 参数错误 / 请稍后重试 |
| `CODE_DATABASE` | 内部错误 | 请稍后重试 |

---

## 4. 后台页面怎么铺（建议）

1. 顶部 Tab：硬币 / 粉丝 / 活跃值（对应 `board`），或一张表三榜都展示。
2. 横向 14 天：本周一～下周日，标出「今天」。今天之前的格子只读。
3. 每个格子：用户、目标第 N、当前第 X、已生效加成、是否已执行（看 `applied_at`）。
4. 今天可设 `period`（日 / 周 / 总）；未来日只能设日榜第 N。
5. 保存后如果是周 / 总，提示「日榜大约第 `estimated_daily_rank`」。
6. 不要提供手填加成、不要每周一键套同一套数、不要「钉死第 N」。

今天 00:00–10:00 未来日的格子还没执行，C 端是真榜，这是预期。10:00 之后刷新列表，`applied_at` 应非 0。

---

## 5. 验收（前端可自测）

**C 端**

- 入账 / 绑邀请后下拉，日 / 周 / 总名次几秒内变化；退款后硬币榜下降
- 今天 0 点刚过、还没人得分：日榜空列表
- 封号用户从榜上消失
- 切日 / 周 Tab，同一人分数周 ≥ 日

**管理端**

- 今天设第 3，保存后 C 端至少第 3；过一会儿被真人超过，当前名次变差，加成不变
- 同一天同一榜两个用户设同一名次：第二个失败 `27001`
- 改昨天：失败 `27002`
- 设未来某日第 1：列表 `applied_at = 0`；当天 10:00 后刷新应已生效
- 控周榜后，提示里的预估日榜名次有值，且今日日榜分数升高
