hyapp-server/docs/flutter对接/幸运礼物接口闭环Flutter对接.md

188 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 幸运礼物 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`,更新金币余额。