Files
customer-im-client-dev/Doc/red_envelope_game_architecture.md
pp-bot d9539d391c feat(redpacket): 红包与游戏横幅全量实现 (#19~#24)
- #19 fix: SendRedEnvelopeUseCase 动态取 currencyType(workspaceId>0 取
  workspace.currency,修复 iOS 硬编码 PEA → 150001 错误)
- #20: RedEnvelopeBubble typ=8,四态(橙色领取/已领/过期/抢完)+ 领取按钮
- #21: ReceiveRedEnvelopeUseCase POST /app/api/wallet/rp/receive,
  typed JSON body(避免 code=30007),SnackBar 反馈
- #22: SendRedEnvelopeSheet BottomSheet,STANDARD_RP + LUCKY_RP,
  发送成功后构建 typ=8 content JSON 回调给 ChatPage
- #23: BannerViewModel Notifier,Group.topic 双格式解析(JSON object/string),
  FetchBannerUseCase + Timer 倒计时 + applyNewRound WS 接口
- #24: BannerView 游戏横幅条(状态/倒计时/上期结果),
  MiniAppFloatButton 悬浮按钮(hasGame 显示/隐藏,onTap TODO #25)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:11:29 +09:00

4.9 KiB
Raw Blame History

红包与游戏横幅 — 架构文档

对应 Gitea issues #19#24 参考实现:im-client-ios-swift-demo RedEnvelopeSendView + BannerViewModel + PlatformBannerViewModel Bug 参考bug 截图文件夹currencyType=PEA 错误 + [红包] 文本未渲染)


1. 功能范围

Issue 功能 状态
#19 currencyType 动态化(修复 PEA 硬编码) 已实现
#20 红包消息气泡typ=8三态 UI 已实现
#21 领取红包(/app/api/wallet/rp/receive 已实现
#22 发送红包 UISTANDARD_RP + LUCKY_RP 已实现
#23 BannerViewModel — 游戏横幅 + WS NewRound 已实现
#24 游戏悬浮按钮MiniAppFloatButton 已实现

2. 目录结构

data/remote/
├── red_envelope_request.dart      # 发送/领取 API Request
└── banner_request.dart            # 游戏横幅 API Request

features/chat/
├── di/
│   └── red_envelope_provider.dart # 红包 DI 装配
├── presentation/
│   └── banner_view_model.dart     # 游戏横幅 ViewModel
├── usecases/
│   ├── send_red_envelope_usecase.dart    # 发送红包
│   ├── receive_red_envelope_usecase.dart # 领取红包
│   └── fetch_banner_usecase.dart         # 拉取游戏横幅
└── view/widgets/
    ├── red_envelope_bubble.dart    # 红包气泡typ=8
    ├── send_red_envelope_sheet.dart # 发送 BottomSheet
    ├── banner_view.dart            # 游戏横幅条
    └── miniapp_float_button.dart   # 游戏悬浮按钮

3. 关键数据格式

3.1 红包消息 contenttyp=8

{
  "id":           "rp_xxx",
  "rp_type":      "STANDARD_RP",
  "remark":       "恭喜发财",
  "total_amount": "10.00",
  "total_num":    5,
  "rp_status":    0
}

rp_status 含义:

含义 UI
0/1 未领取 橙色 + 领取按钮
2 已领取 灰色 "已领取"
3 已过期 灰色 "红包已过期"
4 已抢完 灰色 "手慢了"
6 等待开奖NN_RP 橙色 + 等待

3.2 发送红包 API

POST /payment/rp/send

{
  "amount":      "10.00",
  "currencyType": "PEA",
  "chatID":      12345,
  "chatType":    2,
  "rpType":      "STANDARD_RP",
  "recipientIDs": [],
  "rpNum":       5,
  "remark":      "恭喜发财",
  "msgSendTime": 1234567890000000
}

⚠️ currencyType 规则:

  • workspaceId > 0 → 从 /workspace/workspace/getworkspace.currency(如 "USDT"
  • workspaceId == 0"PEA"(默认)

3.3 领取红包 API

POST /app/api/wallet/rp/receive

{
  "rpID":            "rp_xxx",
  "chatID":          12345,
  "rpType":          "STANDARD_RP",
  "sendRpMsgID":     99999,
  "supportMask":     true,
  "supportHideTail": true
}

⚠️ 必须 JSON typed body非 form 字符串),否则 code=30007


4. 数据流

4.1 发送红包

ChatPage → SendRedEnvelopeSheet
  └─ SendRedEnvelopeViewModel.send(params)
       ├─ currencyType = workspaceId > 0 ? workspace.currency : "PEA"
       ├─ SendRedEnvelopeUseCase.execute() → POST /payment/rp/send → rpID
       └─ 构建 rawContent JSON → 通过 ChatViewModel.sendMessage(typ=8, content)

4.2 领取红包

RedEnvelopeBubble.onTap
  └─ ReceiveRedEnvelopeUseCase.execute(rpID, chatID, rpType, messageId)
       └─ POST /app/api/wallet/rp/receive
            → grabFlag=true → SnackBar 显示金额
            → grabFlag=false → SnackBar 显示原因

4.3 游戏横幅

ChatPage (群聊)
  └─ BannerViewModel(chatId).build()
       ├─ 解析 Group.topic JSON → gameId
       ├─ FetchBannerUseCase(gameId) → POST /lucky/banner/get
       ├─ Timer 每秒 tick → countdown
       └─ SocketManager 消息流 → 过滤 miniapp.NewRound
            → applyGameInfo(bean) → 刷新横幅

5. Provider 设计

sendRedEnvelopeUseCaseProvider      → networkSdkApiProvider
receiveRedEnvelopeUseCaseProvider   → networkSdkApiProvider
fetchBannerUseCaseProvider          → networkSdkApiProvider
bannerViewModelProvider.family(chatId)

6. Bug 修复说明

Bug 原因 修复
code=150001 iOS 硬编码 currencyType=PEAworkspace 用 USDT Flutter 动态取 workspace.currency
[红包] 文本 旧消息 typ=1 content="[红包]",未路由到红包气泡 typ=8 → RedEnvelopeBubbletyp=1 content=="[红包]" fallback 检测

7. 待完成

  • 地雷红包 / 牛牛红包MINE_RP + NN_RP 发送 UI需 rp-config/get
  • Mini-app WebViewMiniAppFloatButton 点击后打开小程序(#25
  • 领取详情页抢红包排行榜iOS RedEnvelopeDetailSheet
  • Game settle pollingdrawTime 后每 3s 轮询直到新一期