# 2.0 召集令「召回你的 1.0 团队」前端对接 & 运营配置文档

## 概述

上级的**直属**下级（`inviter_fans[1]` 指向该上级的人）在**活动期内首次**选定 1.0 资产解锁方案时，上级获得硬币补贴。

| 规则 | 值 |
|---|---|
| 首选**加速方案** | 每人 **+5 硬币**，不封顶 |
| 首选**普通方案** | 每满 **10 人 +5 硬币**；计入人数封顶 **50 人**（即最多 25 硬币） |
| 首选口径 | **严格**：首次选择必须完整落在活动期内 |
| 领取 | **增量领取**（总补贴 − 已领 = 待领），**活动结束后仍可领，不设截止** |

两条关键口径，前端和客服都会被问到：

- **活动期开始前选过任何方案的下级，永远不计补贴** —— 哪怕他在活动期内又升级到了加速方案。
- **只认第一次选择**。活动期内先选普通、之后升级到加速的下级，按**普通**计（`status = REGULAR`，但 `scheme = ACCELERATED`）。这不是 bug，见下面「状态枚举」一节。

### 设计稿 → 接口字段对照

| 稿面元素 | 取哪个字段 |
|---|---|
| 倒计时 `6天 10时 00分 20秒` | `ended_at` − `server_now` |
| 「活动已结束」态 | `ended` |
| 直属成员总数 `38` | `direct_member_total` |
| 加速方案 `9` | `accelerated_count` |
| 普通方案 `14` | `regular_count` |
| 待召回 `15` | `pending_count` + `before_activity_count`（见接口 1 的说明） |
| 三段堆叠进度条 | 上面四个数 |
| WhatsApp「提醒 N 个成员」 | `pending_count` |
| 「加速方案 +5 硬币」 | `accelerated_subsidy` |
| 「普通方案 +5 硬币」 | `regular_tier_subsidy` |
| 档位进度条 `4 / 10` | `regular_tier_progress` / `regular_tier_size` |
| 总补贴 `50 硬币` | `total_subsidy` |
| 待领取 `35 硬币` | `pending_subsidy` |
| 领取按钮可点 | `can_claim` |
| 细则「最多计 50 人」 | `regular_counted_cap` |

---

## 通用约定

- 所有接口 **POST + JSON**，无入参的接口传 `{}`。
- 外层响应固定为 `{"code":200,"message":"success","data":{...}}`，下文只描述 `data` 内容。
- 业务错误同样返回 **HTTP 200**，形如 `{"code":24028,"message":"..."}`，靠 `code` 判断。
- **`int64` 在 JSON 里是字符串，计数字段是 `int32` 裸数字。** 这是最容易出 bug 的点：

  | 类型 | 字段 | JSON 形态 |
  |---|---|---|
  | int64 → **字符串** | `started_at` `ended_at` `server_now` `last_claimed_at` `chosen_at` `user_id` `total` | `"1786032000"` |
  | int32 → **数字** | `direct_member_total` `accelerated_count` `regular_count` `before_activity_count` `pending_count` `regular_tier_size` `regular_counted_cap` `regular_counted_count` `regular_completed_tiers` `regular_tier_progress` | `38` |
  | 金额 → **字符串小数** | `total_subsidy` `claimed_subsidy` `pending_subsidy` `accelerated_subsidy` `regular_tier_subsidy` `claimed` `available_point` | `"50"` |

  所有要参与算术（百分比、取模、进度条宽度）的数字都是 int32，不用 `parseInt`；时间戳只需转换一次。
- **零值必然出现**，不会缺字段，不用判 `undefined`。
- 枚举出参是**名字符串**（如 `"LEGACY_RECALL_MEMBER_STATUS_PENDING"`），入参传名或数字都可以。
- 鉴权头：

  | 接入方 | 路径前缀 | 鉴权 |
  |---|---|---|
  | 用户端 | `/v1/legacy_recall/...` | `Authorization: Bearer <token>` + `X-Meta-Sign` / `X-Meta-Device-Id` / `X-Meta-Timestamp` 签名头 |
  | 管理后台 | `/v1/man/legacy_recall/...` | 仅 `Authorization`，无需签名 |

> **活动未开启不是错误**。此时接口正常返回 `200`，`enabled=false`。前端据 `enabled` / `started` / `ended` 三态渲染，不要靠错误码判断。

---

## 补贴怎么算

```
计入的普通人数 = min(普通方案人数, regular_counted_cap)
总补贴 = 加速方案人数 × accelerated_subsidy
       + floor(计入的普通人数 / regular_tier_size) × regular_tier_subsidy
待领取 = max(总补贴 − 已领取, 0)
```

设计稿那组数字：加速 9 人、普通 14 人 →

```
9 × 5 + floor(min(14, 50) / 10) × 5 = 45 + 5 = 50 硬币
档位进度 = 14 % 10 = 4，即 4 / 10
```

> **前端不要自己算金额和进度，服务端全算好了。** `total_subsidy` / `pending_subsidy` / `regular_tier_progress` / `regular_completed_tiers` 直接渲染即可。
>
> 回传 `accelerated_subsidy` / `regular_tier_subsidy` / `regular_tier_size` / `regular_counted_cap` **是给细则文案做插值用的**，请把 H5 里的 `var SUB_ACCEL = 5; var SUB_REG = 5; var REG_CAP = 50;` 这三个硬编码删掉，改读接口——运营改配置时文案要跟着变。

**封顶后进度条显示满格**：普通人数达到或超过 50 时，`regular_tier_progress` 返回的是 `regular_tier_size`（即 `10 / 10`），而不是 `50 % 10 = 0`。直接渲染即可，不要自己取模。

---

## 接口 1：活动状态 — POST `/v1/legacy_recall/state/get`

**请求**：空，传 `{}`。

**响应 `data`：**

```json
{
  "enabled": true,
  "started": true,
  "ended": false,
  "started_at": "1786032000",
  "ended_at": "1786636799",
  "server_now": "1786204820",

  "direct_member_total": 38,
  "accelerated_count": 9,
  "regular_count": 14,
  "before_activity_count": 0,
  "pending_count": 15,

  "accelerated_subsidy": "5",
  "regular_tier_subsidy": "5",
  "regular_tier_size": 10,
  "regular_counted_cap": 50,

  "regular_counted_count": 14,
  "regular_completed_tiers": 1,
  "regular_tier_progress": 4,

  "total_subsidy": "50",
  "claimed_subsidy": "15",
  "pending_subsidy": "35",
  "can_claim": true,
  "last_claimed_at": "1786190400"
}
```

**字段说明：**

| 字段 | 类型 | 说明 |
|---|---|---|
| `enabled` | bool | 活动总开关。`false` 时整个活动入口不展示 |
| `started` | bool | 是否已开始。`enabled && server_now >= started_at` |
| `ended` | bool | 是否已结束。`true` 时渲染「活动已结束」并置灰 WhatsApp 按钮；**领取按钮不受影响** |
| `started_at` | int64 | 活动开始时间 unix 秒，未配置为 `0` |
| `ended_at` | int64 | 活动结束时间 unix 秒，未配置为 `0`。倒计时目标 |
| `server_now` | int64 | 服务端当前时间 unix 秒 |
| `direct_member_total` | int32 | 直属 1.0 老用户总数（只算有金库、能选方案的下级） |
| `accelerated_count` | int32 | 活动期内首选加速方案的人数（计补贴） |
| `regular_count` | int32 | 活动期内首选普通方案的人数（**封顶前的原始值**） |
| `before_activity_count` | int32 | 活动期开始前已选过方案的人数（永不计补贴） |
| `pending_count` | int32 | 至今仍未选方案的人数（真正可召回的人） |
| `accelerated_subsidy` | string | 每个加速方案成员的补贴硬币数 |
| `regular_tier_subsidy` | string | 普通方案每满一档的补贴硬币数 |
| `regular_tier_size` | int32 | 普通方案每档人数 |
| `regular_counted_cap` | int32 | 普通方案计入人数上限 |
| `regular_counted_count` | int32 | 实际计入的普通人数 = `min(regular_count, regular_counted_cap)` |
| `regular_completed_tiers` | int32 | 已满档数 |
| `regular_tier_progress` | int32 | 当前档进度分子；已封顶时等于 `regular_tier_size` |
| `total_subsidy` | string | 截至当前应得总补贴（硬币） |
| `claimed_subsidy` | string | 累计已领取（硬币） |
| `pending_subsidy` | string | 待领取 = `max(总补贴 − 已领, 0)`（硬币） |
| `can_claim` | bool | 领取按钮是否可点。已含「待领取 > 0」判断 |
| `last_claimed_at` | int64 | 最近一次领取时间 unix 秒，未领过为 `0` |

> **设计稿的「待召回 15」= `pending_count + before_activity_count`。** 稿面算式 `待召回 = 总数 − 加速 − 普通` 把「还没选」和「活动期前已选」混在了一起，后端把它们拆成了两个字段。
>
> **WhatsApp 提醒人数建议只用 `pending_count`** —— 活动期前已选过的成员再提醒也不会产生补贴，纯属骚扰。如果产品坚持要和稿面数字一致，前端自行相加。

> **倒计时请用 `server_now` 打底，不要用 `Date.now()`。** 用户本地时钟不准会导致倒计时和实际结束时间对不上。做法：进页面时记下 `offset = server_now - Date.now()/1000`，之后按 `ended_at - (Date.now()/1000 + offset)` 走。

> 四个分桶之和可能**略小于** `direct_member_total`，差额是「活动结束后才选方案」的人。堆叠进度条请按四个分桶画，`direct_member_total` 只作数字展示；如果自己算残差，记得夹到 0 以上。

> 本接口的分桶计数有 **60 秒缓存**，新下级选完方案后最多 1 分钟才会反映到数字上。金额（`claimed_subsidy` / `pending_subsidy`）不走缓存，领取后立即准确。

---

## 接口 2：直属成员明细 — POST `/v1/legacy_recall/member/list`

**请求：**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `offset` | int32 | 否 | 分页偏移，从 0 开始 |
| `limit` | int32 | 否 | 每页条数，`0` 按 20 处理，最大 100 |
| `statuses` | 枚举数组 | 否 | 状态筛选，空数组 = 全部，可多选 |

常用取值：

```jsonc
// 「谁还没选」列表 —— 召回页主列表 + WhatsApp 提醒名单
{"offset": 0, "limit": 20, "statuses": ["LEGACY_RECALL_MEMBER_STATUS_PENDING"]}

// 与设计稿「待召回」口径一致
{"offset": 0, "limit": 20,
 "statuses": ["LEGACY_RECALL_MEMBER_STATUS_PENDING", "LEGACY_RECALL_MEMBER_STATUS_BEFORE_ACTIVITY"]}

// 全部直属成员
{"offset": 0, "limit": 20}
```

**响应 `data`：**

```json
{
  "total": "38",
  "items": [
    {
      "user_id": "10000123",
      "nickname": "Budi",
      "avatar": "https://cdn.example.com/a/123.png",
      "status": "LEGACY_RECALL_MEMBER_STATUS_PENDING",
      "scheme": "LegacyUnlockScheme_NONE",
      "chosen_at": "0"
    },
    {
      "user_id": "10000456",
      "nickname": "Siti",
      "avatar": "",
      "status": "LEGACY_RECALL_MEMBER_STATUS_REGULAR",
      "scheme": "LegacyUnlockScheme_ACCELERATED",
      "chosen_at": "1786120000"
    }
  ]
}
```

**字段说明（`LegacyRecallMember`）：**

| 字段 | 类型 | 说明 |
|---|---|---|
| `user_id` | int64 | 成员用户 ID |
| `nickname` | string | 昵称，**可能为空串**，需要占位兜底 |
| `avatar` | string | 头像 URL，**可能为空串**，需要默认头像兜底 |
| `status` | 枚举 | 在本活动中的状态，按**首选**判定 |
| `scheme` | 枚举 | 当前生效的方案（会随升级变化） |
| `chosen_at` | int64 | 首次选择方案的时间 unix 秒，未选为 `0` |

**排序**（服务端固定，不支持自定义）：未选方案的排最前 → 其后按选择时间倒序 → `user_id` 升序兜底。

> 上面示例中的第二条就是那个容易被当成 bug 的情况：Siti 活动期内先选了普通方案、之后升级到加速，所以 `status = REGULAR`（按首选计补贴）而 `scheme = ACCELERATED`（当前生效方案）。

> 成员的**手机号、邮箱、1.0 资产金额一律不下发**。WhatsApp 按钮走通用分享链接 `https://wa.me/?text=...`，不需要收件人号码。若后续要做定向提醒，正确做法是新增服务端代发 + 限流的接口，而不是把手机号返给客户端。

---

## 接口 3：领取补贴 — POST `/v1/legacy_recall/subsidy/claim`

**请求**：空，传 `{}`。一次领走当前全部待领取金额。

**响应 `data`：**

```json
{
  "claimed": "35",
  "total_subsidy": "50",
  "claimed_subsidy": "50",
  "pending_subsidy": "0",
  "available_point": "1285"
}
```

| 字段 | 类型 | 说明 |
|---|---|---|
| `claimed` | string | **本次**实际到账硬币数，用于「+35 硬币」的成功提示 |
| `total_subsidy` | string | 领取后的应得总补贴 |
| `claimed_subsidy` | string | 领取后的累计已领 |
| `pending_subsidy` | string | 领取后的待领取，正常为 `"0"` |
| `available_point` | string | 领取后用户的通用可用硬币余额 |

> **领取成功后直接用返回值刷新页面数字，不要再调一次 `state/get`** —— 状态接口的分桶计数有 60 秒缓存，重复调用没有意义。
>
> **按钮必须做防重点击**（请求期间置灰）。重复点击会返回 `24028`，这不是异常，静默刷新即可。

> **活动结束后领取入口保持开放，不设截止时间。** 倒计时归零时只需置灰 WhatsApp 按钮，领取按钮照常可点。

---

## 前端交互流程示意

```
进入活动页
  └─ POST /v1/legacy_recall/state/get
       ├─ enabled = false                  → 不展示活动入口（整页隐藏或跳走）
       ├─ enabled && !started              → 展示预热态，倒计时到 started_at
       ├─ enabled && started && !ended     → 进行中（主态）
       └─ ended                            → 「活动已结束」，WhatsApp 按钮置灰
                                              领取区照常展示

进行中主态渲染
  ├─ 倒计时          ← ended_at − (本地时间 + server_now 校正量)
  ├─ 成员总数         ← direct_member_total
  ├─ 三段堆叠条       ← accelerated_count / regular_count / (pending_count + before_activity_count)
  ├─ 档位进度条       ← regular_tier_progress / regular_tier_size
  ├─ 细则文案插值     ← accelerated_subsidy / regular_tier_subsidy / regular_tier_size / regular_counted_cap
  ├─ 总补贴 / 待领取  ← total_subsidy / pending_subsidy
  └─ 领取按钮         ← can_claim

点「WhatsApp 提醒 N 个成员」
  ├─ N = pending_count
  └─（可选）先拉 member/list?statuses=[PENDING] 展示名单，再唤起分享

点「领取」
  ├─ 按钮置灰
  ├─ POST /v1/legacy_recall/subsidy/claim
  ├─ 成功 → 用返回值就地更新 总补贴/已领/待领/余额，弹「+claimed 硬币」
  ├─ 24028 → 静默把待领取置 0、按钮置灰（多为重复点击）
  └─ 其他错误 → 提示后恢复按钮
```

---

## 状态枚举

### LegacyRecallMemberStatus（成员在本活动中的状态）

| 名 | 值 | 说明 |
|---|---|---|
| `LEGACY_RECALL_MEMBER_STATUS_UNSPECIFIED` | 0 | 未指定。请求中不传或传空数组表示不过滤 |
| `LEGACY_RECALL_MEMBER_STATUS_PENDING` | 1 | 至今未选任何方案（待召回，仍可为上级带来补贴） |
| `LEGACY_RECALL_MEMBER_STATUS_ACCELERATED` | 2 | 活动期内**首选**加速方案（计补贴，不封顶） |
| `LEGACY_RECALL_MEMBER_STATUS_REGULAR` | 3 | 活动期内**首选**普通方案（计补贴，满档给币） |
| `LEGACY_RECALL_MEMBER_STATUS_BEFORE_ACTIVITY` | 4 | 活动期开始前已选过方案（永不计补贴） |

> **这个字段表示「首选」，不是「当前方案」。** 活动期内先选普通、之后升级到加速的成员，`status` 永远是 `REGULAR`，`scheme` 是 `ACCELERATED`。两者不一致是**预期行为**，不要当成数据错误上报。

### LegacyUnlockScheme（当前生效的解锁方案，复用 `basepb.User.LegacyUnlockScheme`）

| 名 | 值 | 说明 |
|---|---|---|
| `LegacyUnlockScheme_NONE` | 0 | 未选择 |
| `LegacyUnlockScheme_REGULAR` | 1 | 普通方案 |
| `LegacyUnlockScheme_ACCELERATED` | 2 | 加速方案 |

---

## 管理后台接口

### 参与用户列表 — POST `/v1/man/legacy_recall/user/list`

按**上级**维度聚合，只读。

**请求：**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `offset` | int32 | 否 | 分页偏移 |
| `limit` | int32 | 否 | 每页条数，`0` 按 25 处理，最大 100 |
| `sort` | string | 否 | 排序字段，见下方白名单，默认 `total_subsidy` |
| `order` | string | 否 | `ASC` / `DESC`，默认 `DESC` |
| `user_id` | int64 | 否 | 按上级用户 ID 精确筛选，`0` = 不筛选 |
| `nickname` | string | 否 | 按上级昵称模糊筛选，空 = 不筛选 |
| `started_at` | int64 | 否 | 行筛选起点 unix 秒，`0` = 不筛选 |
| `ended_at` | int64 | 否 | 行筛选终点 unix 秒，`0` = 不筛选 |

`sort` 白名单（传其他值按默认处理）：`total_subsidy`、`pending_subsidy`、`claimed_subsidy`、`accelerated_count`、`regular_count`、`direct_legacy_member_count`、`user_id`。

**响应 `data`：**

```json
{
  "total": "128",
  "items": [
    {
      "user_id": "10000001",
      "nickname": "Agen Jakarta",
      "direct_legacy_member_count": 38,
      "accelerated_count": 9,
      "regular_count": 14,
      "regular_counted_count": 14,
      "total_subsidy": "50",
      "claimed_subsidy": "15",
      "pending_subsidy": "35",
      "last_claimed_at": "1786190400"
    }
  ],
  "total_direct_legacy_member_count": 4210,
  "total_accelerated_count": 312,
  "total_regular_count": 1580,
  "total_regular_counted_count": 1402,
  "total_subsidy": "2260",
  "total_claimed_subsidy": "980",
  "total_pending_subsidy": "1280"
}
```

`total_*` 七个字段是**命中筛选条件的全量汇总**，不随分页变化，用于财务页脚。`total_regular_counted_count` 是逐上级封顶后再求和。

> **时间筛选只做行过滤，不改变任何统计列的口径。** `started_at` / `ended_at` 的含义是「该区间内至少有 1 个计入的下级首选」，各统计列**始终按完整活动期计算**。
>
> 原因：待领金额天然无法按时间切片（一次领取是针对整个活动期的一笔），如果统计列跟着切，同一行里的「应得总补贴」和「待领」就会回答两个不同的问题。

> **非管理员账号（业务员 / 代理）只能看到自己团队内的上级**（含自己那行）；管理员看全量。

---

## 运营配置（本期无管理后台配置页，走数据库 KV）

配置存在 `configs` 表，`key = legacy_recall.activity`，值是一段 JSON。

### 配置字段

| 字段 | 类型 | 说明 | 默认 |
|---|---|---|---|
| `enabled` | bool | 活动总开关。**只控制前端入口可见性，不影响已产生补贴的领取** | `false` |
| `started_at` | int64 | 活动开始时间 unix 秒 | 必填 |
| `ended_at` | int64 | 活动结束时间 unix 秒 | 必填 |
| `accel_point` | int64 | 每个首选加速方案的直属下级给多少硬币 | `5` |
| `regular_group` | int64 | 普通方案每满多少人给一次 | `10` |
| `regular_point` | int64 | 普通方案每满一档给多少硬币 | `5` |
| `regular_cap` | int64 | 普通方案计入人数上限 | `50` |
| `accel_cap` | int64 | 加速方案计入人数上限，`0` = 不限。财务兜底开关 | `0` |

### 开启 / 更新活动（SQL）

完整脚本见 `contrib/legacy_recall_init.sql`，核心是一条 upsert：

```sql
INSERT INTO configs (key, value, updated_at)
VALUES (
  'legacy_recall.activity',
  json_build_object(
    'enabled',       true,
    'started_at',    extract(epoch FROM timestamptz '2026-08-11 00:00:00+07')::bigint,
    'ended_at',      extract(epoch FROM timestamptz '2026-08-17 23:59:59+07')::bigint,
    'accel_point',   5,
    'regular_group', 10,
    'regular_point', 5,
    'regular_cap',   50,
    'accel_cap',     0
  )::text,
  now()
)
ON CONFLICT (key) DO UPDATE
  SET value = EXCLUDED.value, updated_at = EXCLUDED.updated_at;
```

> **改库后必须删缓存，否则最多 10 分钟才生效：**
> ```
> redis-cli DEL legacy_recall:cfg
> ```

> **部署顺序有硬性约束**：带首选快照逻辑的服务端版本必须在 `started_at` **之前**部署完成。补贴的唯一数据来源是首选快照表，代码上线前发生的选择无法追认。建议流程：先以 `enabled=false` 落库 → 用真实上级验一遍 `state/get` 的数字 → 活动开始时改 `enabled=true` 并删缓存。

### 关闭活动

```sql
UPDATE configs
   SET value = jsonb_set(value::jsonb, '{enabled}', 'false')::text, updated_at = now()
 WHERE key = 'legacy_recall.activity';
```
再 `redis-cli DEL legacy_recall:cfg`。注意 `enabled=false` 只隐藏前端入口，已产生的补贴仍可领取（刻意设计，避免用户因为没来得及点「领取」而丢奖励）。

### 排查 / QA 查询

```sql
-- 某个上级的下级首选明细
SELECT user_id, first_scheme, first_chosen_at, owner_fans_code
  FROM legacy_recall_records WHERE owner_user_id = 10000001 ORDER BY first_chosen_at;

-- 某个上级的领取记录
SELECT amount, claimed_from, claimed_to, ledger_id, claimed_at
  FROM legacy_recall_claim_records WHERE user_id = 10000001 ORDER BY id DESC;

-- 对账 1: 首选时间必须与金库首选时间一致, 应为 0 行
SELECT COUNT(*) FROM legacy_recall_records r
  JOIN legacy_point_vaults v ON v.user_id = r.user_id
 WHERE r.first_chosen_at <> v.scheme_chosen_at;

-- 对账 2: 已领总额必须等于流水总额 (type=70 即 TYPE_LEGACY_RECALL_SUBSIDY)
SELECT (SELECT COALESCE(SUM(claimed_point), 0) FROM legacy_recall_claims) AS claims_total,
       (SELECT COALESCE(SUM(point), 0) FROM point_ledgers WHERE type = 70) AS ledger_total;
```

> **活动细则文案与 WhatsApp 提醒话术全部在 H5 内，服务端不提供文案配置接口，请不要新建。** 服务端只回传细则里要用到的数字（费率、档位人数、封顶人数）。

---

## 错误码 / 边界

| code | 含义 | 前端处理 |
|---|---|---|
| `200` | 成功 | — |
| `24027` | 召集令活动未开启或未配置 | 隐藏活动入口 |
| `24028` | 暂无可领取补贴 | 置灰领取按钮并把待领取置 0（多为重复点击，静默处理） |
| `11007` | 限流（领取并发） | 提示稍后重试 |
| `11009` | 服务端数据库错误 | 通用错误提示 |
| `14xxx` | 未登录 / 无权限 | 跳登录 |

边界行为：

- **活动结束后领取入口仍然开放**，只有 WhatsApp 提醒按钮置灰。赚取侧在 `ended_at` 冻结（之后再选方案的下级不再产生补贴）。
- **四个分桶之和可能略小于 `direct_member_total`**，差额是活动结束后才选方案的人。自己算残差时夹到 0 以上。
- **状态接口的分桶计数有 60 秒缓存**，金额不缓存。领取后立刻调 `state/get` 也能看到正确的 `pending_subsidy = 0`。
- **没有 1.0 金库的直属下级不计入任何分桶**。这类用户（割接时资产为 0）在系统层面就无法选择方案，把他们算进「待召回」会导致进度条永远填不满。

---

## 接口一览

| 接入方 | 方法 | 路径 | 作用 |
|---|---|---|---|
| 用户端 | POST | `/v1/legacy_recall/state/get` | 活动状态 + 成员分桶 + 补贴概览 |
| 用户端 | POST | `/v1/legacy_recall/member/list` | 直属成员明细（可按状态筛选） |
| 用户端 | POST | `/v1/legacy_recall/subsidy/claim` | 增量领取待领补贴 |
| 管理后台 | POST | `/v1/man/legacy_recall/user/list` | 参与用户列表 + 汇总 |
