# 投注排行榜接口

**bet_rank**

投注排行榜按**商户时区**的自然日 / 周 / 月统计全站积分（**积分 = 有效投注 × 投注系数 + 充值到账 × 充值系数**，系数由商户后台配置、经 `detail.score_rule` 下发，默认投注 ×1、充值 ×3），实时排名，期末按名次占比阶梯把「实际派发基数 = 展示奖池 × 派发系数」分给前 N 名。日榜常开，周榜 / 月榜由商户后台开关（`boards` 只返回已开启且已进入首期的榜，顺序固定 日 → 周 → 月）。

- **展示奖池** `display_pool` 是头卡上的大数字，= 本期累计积分；**不是**实际派发金额。榜单每行的 `prize` 已是该名次实际可得金额（= `percent` × `payout_base`），全站口径一致。
- 榜单上所有昵称已按全站规则脱敏（如 `Use**0NR`），前端直接展示；只有「我的」行返回完整昵称。
- 结算在本期结束后次日的商户配置时刻进行；真人名次的奖金进入**待领取**（`claimable`），须调 `claim` 手动领取，领取有效期由后台配置，逾期作废不累积。
- 领取经奖励入账，金额按配置的打码倍数建立奖励任务，按 [任务列表](201_wager_tasks.md#list) 返回值展示；整个账户须满足 [全部任务与旧打码门槛](400_promotion.md#wager_rules) 才能通过提现打码校验。
- 真人进入有奖名次或名次上升时会收到【[实时提示推送（SSE）](510_message.md#realtime-promotion-trigger)】`type=msg` 的 toast（「恭喜！您已进入 … 第 N 名」，同人同榜 10 分钟内最多一条）；结算后有奖者收到 `type=msg` toast 与站内信，并推 `type=promotion_trigger` / `data.kind=bet_rank` 提示去领取。全员另收到一条含前 5 名表格的活动站内信。

`list` / `detail` / `claim` 走通用活动接口（`kind=bet_rank`，详见 [活动接口](400_promotion.md)）；完整榜单与历史走本页的扩展接口。

---

## Board 对象 {#board}

`detail` 的 `boards[]` 元素与 `GET /promotion_bet_rank/board` 的 `data` 共用以下字段：

字段 | 类型 | 说明
---|---|---
board | string | 榜类型：`daily` / `weekly` / `monthly`
period_key | string | 期次键：日 `2026-10-13` / 周 `2026-W42` / 月 `2026-10`（商户时区）
start_at | string | 本期开始（UTC ISO 时间）
end_at | string | 本期结束（UTC ISO 时间，不含）
settle_at | string | 计划结算时刻；期次尚未创建时为 `null`
progress | number | 本期已过时间占比 `0~1`（头卡进度条）
end_date | string | 该榜截止日 `YYYY-MM-DD`（商户时区，含当日；头卡「截止于」），空为不限。周期**不截断**：后台配置的日期已按自然周期向后对齐（周榜 → 所在周周日、月榜 → 所在月月末），包含截止日的那一期照常跑完并结算
ended | boolean | 该榜是否已截止：当前自然周期的起点已越过 `end_date`，不再开新期（此时 `period_key` / `start_at` / `end_at` 仍是当前自然周期，`settle_at` 为 `null`、榜单为空）；前端据此展示「已结束」并引导看 `/history`
status | number | `0` 进行中 / `1` 已结算
settled_at | string | 实际结算时刻，未结算为 `null`
display_pool | string | 展示奖池（本期累计积分，含真人与填充）
payout_base | string | 实际派发基数 = 展示奖池 × 派发系数
prize_ranks | number | 有奖名次数（「前 N 名有奖」）
last_prize_rank | number | 当前榜单上最后一个有奖名次（上榜人数不足时小于 `prize_ranks`；无人时 `0`）
ranked_count | number | 本期有积分的总人数（含有奖名次外）
tiers | list<Tier\> | 名次占比阶梯，见下
description | string | 规则区富文本（HTML）
claim_hours | number | 领取有效期（小时），`0` 不过期
top | list<Entry\> | 领奖台 + 第 4~6 名（最多 6 条；仅 `detail` 返回）
last | Entry | 最后一个有奖名次（第 `last_prize_rank` 名；有奖名次 ≤ 6 时为 `null`；仅 `detail` 返回）
list | list<Entry\> | 完整有奖榜单（仅 `/board` 与 `/history` 返回）
me | Me | 「我的」行，见下

**Tier**

字段 | 类型 | 说明
---|---|---
start_rank | number | 起始名次
end_rank | number | 结束名次
percent | string | 区间内每名占比（%）

**Entry**（榜单一行）

字段 | 类型 | 说明
---|---|---
rank | number | 名次
nickname | string | 脱敏昵称
avatar | string | 头像 URL（可能为空）
vip_level | number | VIP 等级角标
score | string | 积分
percent | string | 名次占比（%）
prize | string | 名次奖金（实际可得）

**Me**（「我的」行）

字段 | 类型 | 说明
---|---|---
status | string | `guest` 未登录 / `zero` 本期零积分 / `unranked` 有积分但未进有奖名次 / `ranked` 已在有奖名次内
nickname | string | 自己的完整昵称（不脱敏；`guest` 无）
avatar | string | 头像
vip_level | number | VIP 等级
rank | number | 实际名次；`zero` / `guest` 为 `0`
score | string | 我的积分
percent | string | 预计占比（%），仅 `ranked`
prize | string | 预计奖金，仅 `ranked`
gap_rank | number | 差距目标名次：`ranked` 为上一名，`unranked` / `zero` 为最后一个有奖名次；第 1 名为 `0`
gap | string | 距目标名次的积分差 = 目标积分 − 我的积分 + 0.01（换算成金额请按 `score_rule`，如投注系数 1 即「再投 Y 即超越 / 即可上榜」）；第 1 名为 `0`
lead | string | 第 1 名时领先第 2 名的积分差
qualified | boolean | 当前名次的奖励是否已锁定。`false` 表示处于进榜宽限期、投注尚未达到奖励门槛，稍后会被他人超越（前端可在名次旁显示 🔒 并引导追加投注）；`ranked` 以外恒为 `false`
entry_bet | string | 锁定目标名次奖励还需追加的**有效投注**金额（`ranked` 为当前名次，否则为末名）；`0` 表示已达门槛；后台硬上限为 0 时为 `null`
projected | boolean | `true` 表示本行与榜单中自己的位置是按**实时积分**投影得出的乐观预览（见下），榜单快照约 1 分钟后同步

> **实时性**：榜单快照约每 1 分钟刷新，但已登录会员**自己的积分实时读取**；若实时积分高于快照，服务端会把自己按积分插到榜单应在的名次（其后各行顺移、末行丢弃、奖金重算），`me.projected=true`。因此投注结算后立即刷新即可看到自己的新名次，不必等下一次快照；未登录或分数未变化时 `projected=false`。
>
> **进榜宽限**：真人首次进入有奖名次后有一段宽限（后台按榜配置，越接近期末越短），期间即使投注未达奖励门槛也会留在榜上，之后才会被超越；`gap` 始终是**进榜**所需积分差，能否**保住**名次看 `qualified` / `entry_bet`。

---

## 活动详情 {#detail}

**URL:** `GET` /promotion/bet_rank/detail

允许未登录访问（`boards[].me.status=guest`，`claimable` 为空）。

**输出:** `data`

字段 | 类型 | 说明
---|---|---
title | string | 活动名称（头卡标题前半，括号内的「每日 / 每周 / 每月」由前端按 `board` 拼）
end_date | string | 整个活动的实际截止日 `YYYY-MM-DD` = 各启用榜 `end_date` 中最晚的一个；任一启用榜不限则为空。头卡「截止于」请优先用各榜自己的 `end_date`
is_active | boolean | 活动是否启用
can_abandon | boolean | 领取后奖励任务是否允许放弃
score_rule | ScoreRule | 积分规则，见下；规则区文案与「再投 Y」换算用它
boards | list<Board\> | 已开启的榜（顺序日 → 周 → 月），各含 `top` / `last` / `me`，不含 `list`
claimable | list<Prize\> | 当前可领取的奖金（已登录时）

**ScoreRule**

字段 | 类型 | 说明
---|---|---
bet_weight | string | 每 1 货币单位有效投注折算积分
recharge_weight | string | 每 1 货币单位充值到账本金折算积分（不含充值赠送）

**Prize**

字段 | 类型 | 说明
---|---|---
id | string | 奖金记录 ID（领取时传 `prize_id`）
board | string | 榜类型
period_key | string | 期次键
rank | number | 最终名次
amount | string | 奖金
bet_multiple | number | 打码倍数
status | number | `0` 待领取 / `1` 已领取 / `2` 已过期
expire_at | string | 领取截止，`null` 不过期
claimed_at | string | 领取时刻
created_at | string | 结算时刻

---

## 领取奖金 {#claim}

**URL:** `POST` /promotion/bet_rank/claim

需登录。领取后金额进奖金余额并按 `bet_multiple` 建立奖励任务；同一记录重复领取返回错误。

**输入:**

字段 | 类型 | 必填 | 说明
---|---|---|---
prize_id | string | 否 | 指定领取一条；缺省领取全部可领记录

**输出:** `data`

字段 | 类型 | 说明
---|---|---
claimed | list<Prize\> | 本次成功领取的记录
total_amount | string | 本次领取合计

---

## 全部榜单 {#board_list}

**URL:** `GET` /promotion_bet_rank/board

当期完整有奖榜单 + 「我的」行（弹层）。允许未登录。

**Query:**

字段 | 类型 | 必填 | 说明
---|---|---|---
board | string | 否 | `daily`（默认）/ `weekly` / `monthly`；榜未开启返回错误

**输出:** `data` 为 [Board 对象](#board)（含 `list` 与 `me`，不含 `top` / `last`）

---

## 历史榜单 {#history}

**URL:** `GET` /promotion_bet_rank/history

上一期已结算榜单（只提供「上一期」一档）。允许未登录。

**Query:**

字段 | 类型 | 必填 | 说明
---|---|---|---
board | string | 否 | `daily`（默认）/ `weekly` / `monthly`

**输出:** `data`

字段 | 类型 | 说明
---|---|---
period | Board | 上一期头信息（`status=1`，`payout_base` 即「上一期实际派发基数」，`progress=1`）；尚无已结算期次时为 `null`（前端显示「首期进行中，暂无历史」）
list | list<Entry\> | 上一期最终名次与实发奖金（含填充名次）

---

## 我的奖金记录 {#prizes}

**URL:** `GET` /promotion_bet_rank/prizes

需登录。最近 50 条获奖记录（待领 / 已领 / 已过期）。

**输出:**

字段 | 类型 | 说明
---|---|---
list | list<Prize\> | 奖金记录
total | number | 条数
