188 lines
7.1 KiB
Markdown
188 lines
7.1 KiB
Markdown
# 幸运礼物 Flutter 对接
|
||
|
||
本文只描述 App 需要接入的 HTTP、送礼返回、错误处理和腾讯云 IM 自定义消息。所有接口响应都使用 gateway 业务 envelope:
|
||
|
||
```json
|
||
{
|
||
"code": "OK",
|
||
"message": "",
|
||
"request_id": "req_xxx",
|
||
"data": {}
|
||
}
|
||
```
|
||
|
||
## 1. 送礼前检查
|
||
|
||
`POST /api/v1/activities/lucky-gifts/check`
|
||
|
||
请求头:
|
||
|
||
| 参数 | 必填 | 说明 |
|
||
| --- | --- | --- |
|
||
| `Authorization` | 是 | `Bearer <access_token>` |
|
||
| `X-App-Code` | 否 | 不传时使用默认 App |
|
||
|
||
请求体:
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `room_id` | string | 是 | 当前房间 ID |
|
||
| `gift_id` | string | 是 | 实际送出的礼物 ID,例如 `rose` |
|
||
| `pool_id` | string | 是 | 幸运礼物奖池 ID,例如 `lucky`、`super_lucky`;当前 check 接口必填 |
|
||
|
||
返回 `data`:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `enabled` | bool | 是否可按幸运礼物发送 |
|
||
| `reason` | string | `enabled`、`not_configured`、`disabled` |
|
||
| `room_id` | string | 请求房间 ID |
|
||
| `gift_id` | string | 请求礼物 ID |
|
||
| `pool_id` | string | 生效奖池 ID |
|
||
| `gift_price` | int | 单次抽奖价格,金币 |
|
||
| `rule_version` | int | 后端规则版本 |
|
||
| `target_rtp_ppm` | int | RTP 目标,ppm |
|
||
| `experience_pool` | string | `novice`、`intermediate`、`advanced` |
|
||
|
||
客户端处理:
|
||
|
||
- `enabled=true`:允许用户继续点击送礼。
|
||
- `enabled=false`:不要走幸运礼物送礼;可降级普通礼物或提示当前不可用。
|
||
- 检查结果只用于展示和拦截,最终仍以送礼接口为准。
|
||
|
||
## 2. 发送礼物
|
||
|
||
`POST /api/v1/rooms/gift/send`
|
||
|
||
请求体关键字段:
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `room_id` | string | 是 | 房间 ID |
|
||
| `command_id` | string | 是 | 本次送礼动作幂等键;同一次超时重试必须复用 |
|
||
| `target_user_id` | int/string | 兼容 | 旧单目标字段;新客户端优先使用 `target_user_ids` |
|
||
| `target_user_ids` | int[] | 是 | 当前 `target_type=user` 时必须且只能 1 个 |
|
||
| `target_type` | string | 否 | 当前只支持 `user`,为空按 `user` |
|
||
| `gift_id` | string | 是 | 礼物 ID,例如 `rose` |
|
||
| `gift_count` | int | 是 | 数量,必须大于 0 |
|
||
| `pool_id` | string | 幸运礼物必填 | `lucky` 或 `super_lucky` |
|
||
|
||
返回 `data.lucky_gift`:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `draw_id` | string | 抽奖 ID,客户端去重锚点 |
|
||
| `command_id` | string | 送礼命令 ID |
|
||
| `pool_id` | string | 生效奖池 |
|
||
| `gift_id` | string | 礼物 ID |
|
||
| `rule_version` | int | 抽奖规则版本 |
|
||
| `experience_pool` | string | 体验池 |
|
||
| `selected_tier_id` | string | 命中奖档 |
|
||
| `multiplier_ppm` | int | 倍率,`2000000` 表示 2 倍 |
|
||
| `base_reward_coins` | int | RTP 基础返奖 |
|
||
| `room_atmosphere_reward_coins` | int | 房间气氛池返奖 |
|
||
| `activity_subsidy_coins` | int | 活动补贴返奖 |
|
||
| `effective_reward_coins` | int | 用户实际获得金币 |
|
||
| `reward_status` | string | 有返奖或阶段反馈时初始通常为 `pending`,到账和房间 IM 由后端 outbox 补偿 |
|
||
| `stage_feedback` | bool | 是否阶段反馈,可播放轻量中奖表现 |
|
||
| `high_multiplier` | bool | 是否高倍 |
|
||
| `created_at_ms` | int | UTC epoch ms |
|
||
|
||
客户端处理:
|
||
|
||
- `data.lucky_gift != null`:立即调用 `_applyLuckyGiftResult()`,播放中奖或阶段反馈表现。
|
||
- `effective_reward_coins > 0 && reward_status == "pending"`:展示中奖金额和“奖励处理中”,余额不要本地相加。
|
||
- `effective_reward_coins > 0 && reward_status == "granted"`:可以展示已到账状态;余额仍以 `WalletBalanceChanged` 或余额刷新为准。
|
||
- `data.lucky_gift == null`:按普通礼物表现处理。
|
||
|
||
## 3. 错误处理
|
||
|
||
| code | 场景 | 客户端处理 |
|
||
| --- | --- | --- |
|
||
| `INVALID_ARGUMENT` | 参数缺失、数量非法、`pool_id` 缺失、`target_user_ids` 非单目标 | 停止提交,提示用户重试或刷新礼物面板 |
|
||
| `UNAUTHENTICATED` | token 失效 | 重新登录 |
|
||
| `INSUFFICIENT_BALANCE` | 金币不足 | 打开充值入口或提示余额不足 |
|
||
| `RULE_NOT_ACTIVE` | 幸运礼物规则未启用 | 降级普通礼物或刷新礼物配置 |
|
||
| `UPSTREAM_ERROR`/`UNAVAILABLE` | 内部服务或 IM/钱包临时不可用 | 不重复扣本地余额;允许用户稍后重试 |
|
||
| `IDEMPOTENCY_CONFLICT`/`CONFLICT` | 同一 `command_id` 对应的请求参数变化,或规则/奖池/风控冲突 | 参数变化时重新生成 `command_id`;规则冲突时刷新礼物面板 |
|
||
|
||
送礼失败时后端不会返回 `lucky_gift`,客户端不要播放中奖 UI。送礼成功但返奖到账延迟时,房间 IM 和钱包 IM 会继续补偿。
|
||
|
||
幂等规则:
|
||
|
||
- HTTP 超时、断网、502:同一次用户动作使用原 `command_id` 重试。
|
||
- 已收到成功响应或钱包余额已扣减:不要生成新 `command_id` 再发同一笔礼物。
|
||
- 需要重新点击送礼:生成新的 `command_id`。
|
||
|
||
## 4. 房间 IM
|
||
|
||
腾讯云 IM 群自定义消息:
|
||
|
||
| 字段 | 值 |
|
||
| --- | --- |
|
||
| `MsgType` | `TIMCustomElem` |
|
||
| `Desc` | `lucky_gift_drawn` |
|
||
| `Ext` | `room_system_message` |
|
||
| `Data` | JSON 字符串 |
|
||
|
||
`Data` 示例:
|
||
|
||
```json
|
||
{
|
||
"event_type": "lucky_gift_drawn",
|
||
"event_id": "lucky_gift_drawn:lucky_draw_xxx",
|
||
"draw_id": "lucky_draw_xxx",
|
||
"command_id": "cmd_xxx",
|
||
"pool_id": "super_lucky",
|
||
"room_id": "room_1",
|
||
"gift_id": "rose",
|
||
"gift_count": 1,
|
||
"user_id": 42,
|
||
"sender_user_id": 42,
|
||
"target_user_id": 99,
|
||
"coin_spent": 100,
|
||
"rule_version": 1,
|
||
"experience_pool": "novice",
|
||
"selected_tier_id": "novice_2x",
|
||
"multiplier_ppm": 2000000,
|
||
"base_reward_coins": 200,
|
||
"room_atmosphere_reward_coins": 0,
|
||
"activity_subsidy_coins": 0,
|
||
"effective_reward_coins": 200,
|
||
"stage_feedback": true,
|
||
"high_multiplier": false,
|
||
"created_at_ms": 1760000000000
|
||
}
|
||
```
|
||
|
||
处理规则:
|
||
|
||
- 只处理 `Desc == "lucky_gift_drawn"` 且 `event_type == "lucky_gift_drawn"`。
|
||
- 用 `event_id` 或 `draw_id` 去重;同一抽奖不要重复播放。
|
||
- 当前用户已通过 HTTP 送礼响应播放过同一 `draw_id` 时,收到 IM 只更新状态,不重复播放。
|
||
- `effective_reward_coins > 0` 但还没有收到钱包余额 IM 时,只展示中奖结果和处理中状态,不本地加金币。
|
||
|
||
## 5. 钱包余额 IM
|
||
|
||
中奖金币到账后,wallet-service 会写 `WalletBalanceChanged`,notice-service 通过腾讯云 IM C2C 投递。
|
||
|
||
| 字段 | 值 |
|
||
| --- | --- |
|
||
| `MsgType` | `TIMCustomElem` |
|
||
| `Desc` | `WalletBalanceChanged` |
|
||
| `Ext` | `wallet_notice` |
|
||
|
||
客户端处理:
|
||
|
||
- 只处理 `Desc == "WalletBalanceChanged"`。
|
||
- 用 `event_id` 去重。
|
||
- 当 `asset_type == "COIN"` 时,用 `available_after` 更新金币余额;字段缺失时调用 `GET /api/v1/wallet/me/balances` 刷新。
|
||
|
||
## 6. 接入顺序
|
||
|
||
1. 礼物面板拿到 `pool_id` 后调用 check。
|
||
2. check 可用后调用送礼接口,并把 `pool_id` 放入请求。
|
||
3. 送礼响应含 `data.lucky_gift` 时立即播放本机中奖 UI。
|
||
4. 房间内监听 `lucky_gift_drawn`,给其他用户同步中奖 UI。
|
||
5. 私聊监听 `WalletBalanceChanged`,更新金币余额。
|