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

4.4 KiB
Raw Blame History

送礼连击 V2 协议

边界

V2 只改变客户端封批、可靠重试和结算结果重放,不改变房间状态 owner

  • Flutter 立即播放连击反馈,第一批立即请求,后续点击按远程窗口聚合。
  • HTTP 仍是账务入口;room-service 的 Room Cell 串行提交房间状态。
  • wallet-service 负责扣费,lucky-gift-service 负责逐份独立抽奖,room-service 只编排持久化 Saga。
  • V1 路由和顶层响应字段只为已发布的旧客户端保留;当前 Flutter 固定调用 V2不做 V1 降级。

HTTP 契约

新客户端调用:

POST /api/v2/rooms/gift/send

请求沿用 V1 字段,并要求:

{
  "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_idcombo_session_id + batch_seq 只用于客户端批次顺序和审计。当前 Flutter 不在 404 或旧响应下改调 V1发布前必须先保证 V2 路由可用。

响应继续返回 V1 顶层的 billing_receipt_idcoin_balance_afterlucky_gift(s)batch_display 等字段,同时增加 v2

{
  "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_batchgift_count * 去重收礼人数 上限,最大 5000。
  • max_in_flight:最多 2 个 HTTP 批次在途。
  • max_pending_batches:本地有界队列上限。
  • retry_max_attemptsretry_base_delay_ms可靠重试上限和指数退避基数429 优先遵守 Retry-After
  • rollout_basis_points:按 app_code + user_id 稳定分桶0 到 10000只灰度微批策略不改变 V2 HTTP 入口。

后台通过以下 app-scoped 接口读取和全量替换配置:

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=legacyroom_event_tag_mode=legacy。确认所有兼容 consumer 已上线并排空存量后,再分别切为 event_type;不要在同一批发布中同时切 producer 和删除 legacy selector。

数据库迁移、服务发布、Flutter 微批灰度和 MQ Tag 切换是四个独立开关。任何一步都不能用“客户端已限流”替代 owner 幂等、Saga 恢复或 Broker 侧过滤。