hyapp-server/docs/送礼连击V2协议.md
2026-07-12 00:47:20 +08:00

95 lines
4.4 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.

# 送礼连击 V2 协议
## 边界
V2 只改变客户端封批、可靠重试和结算结果重放,不改变房间状态 owner
- Flutter 立即播放连击反馈,第一批立即请求,后续点击按远程窗口聚合。
- HTTP 仍是账务入口;`room-service` 的 Room Cell 串行提交房间状态。
- `wallet-service` 负责扣费,`lucky-gift-service` 负责逐份独立抽奖,`room-service` 只编排持久化 Saga。
- V1 路由和顶层响应字段只为已发布的旧客户端保留;当前 Flutter 固定调用 V2不做 V1 降级。
## HTTP 契约
新客户端调用:
```text
POST /api/v2/rooms/gift/send
```
请求沿用 V1 字段,并要求:
```json
{
"room_id": "room-id",
"command_id": "stable-combo-session_b1",
"combo_session_id": "stable-combo-session",
"batch_seq": 1,
"target_type": "user",
"target_user_ids": [1001, 1002],
"gift_id": "gift-id",
"gift_count": 20,
"pool_id": "optional-lucky-pool"
}
```
`command_id` 是唯一财务幂等键。超时、429 和临时 5xx 必须复用同一请求体与同一 `command_id``combo_session_id + batch_seq` 只用于客户端批次顺序和审计。当前 Flutter 不在 404 或旧响应下改调 V1发布前必须先保证 V2 路由可用。
响应继续返回 V1 顶层的 `billing_receipt_id``coin_balance_after``lucky_gift(s)``batch_display` 等字段,同时增加 `v2`
```json
{
"billing_receipt_id": "legacy-compatible-receipt",
"coin_balance_after": 9000,
"v2": {
"api_version": 2,
"command_id": "stable-combo-session_b1",
"combo_session_id": "stable-combo-session",
"batch_seq": 1,
"idempotent_replay": false,
"committed_room_version": 123,
"billing_receipt_id": "legacy-compatible-receipt",
"coin_balance_after": 9000,
"balance_version": 456,
"settled_gift_count": 20,
"settled_target_user_ids": [1001, 1002],
"lucky_hits": [],
"operation_status": "committed"
}
}
```
相同 `command_id` 的重试返回首次提交的同一账务回执、余额版本、实际目标、幸运结果和批次展示,仅把 `idempotent_replay` 置为 `true`。历史 V1 command log 没有 typed 结果时保持原有 snapshot-only 行为,不伪造结算结果。
## 客户端封批规则
礼物面板响应的 `combo_config` 控制客户端策略:
- `enabled`:紧急关闭微批;关闭后仍调用 V2只是不再聚合连续点击。
- `api_version`:服务端契约版本标记,当前 Flutter 只接受 V2不用它选择 V1 入口。
- `window_ms`:后续点击固定封批窗口,默认 300ms。
- `max_gift_count_per_batch`:单目标数量上限,最大 999。
- `max_gift_units_per_batch``gift_count * 去重收礼人数` 上限,最大 5000。
- `max_in_flight`:最多 2 个 HTTP 批次在途。
- `max_pending_batches`:本地有界队列上限。
- `retry_max_attempts``retry_base_delay_ms`可靠重试上限和指数退避基数429 优先遵守 `Retry-After`
- `rollout_basis_points`:按 `app_code + user_id` 稳定分桶0 到 10000只灰度微批策略不改变 V2 HTTP 入口。
后台通过以下 app-scoped 接口读取和全量替换配置:
```text
GET /admin/app-config/gift-combo
PUT /admin/app-config/gift-combo
```
服务端在 gateway、room、wallet、lucky 四层都强制校验 `gift_count <= 999`、总 gift units `<= 5000` 和幂等键长度。远程配置只能收紧客户端行为,不能提高 owner 上限。
## 上线顺序
1. 先执行 `services/room-service/deploy/mysql/migrations/002_gift_operation_v2.sql`,再滚动发布 protobuf、wallet、lucky、room、gateway确认 V2 可用后发布当前 Flutter已发布的旧 Flutter 继续走服务端 V1。
2. 首次上线把 `rollout_basis_points` 设为 0验证 V2 单批发送、重复 `command_id` 重放和 Gift Saga 恢复指标;该值不会让当前 Flutter 回退 V1。
3. 按 App 从小流量提高微批灰度;异常时把 `enabled=false` 或灰度归零,客户端仍使用 V2 单批发送。
4. MQ 先保持 `wallet_event_tag_mode=legacy``room_event_tag_mode=legacy`。确认所有兼容 consumer 已上线并排空存量后,再分别切为 `event_type`;不要在同一批发布中同时切 producer 和删除 legacy selector。
数据库迁移、服务发布、Flutter 微批灰度和 MQ Tag 切换是四个独立开关。任何一步都不能用“客户端已限流”替代 owner 幂等、Saga 恢复或 Broker 侧过滤。