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

174 lines
4.9 KiB
Markdown
Raw Permalink 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.
# 红包与游戏横幅 — 架构文档
> 对应 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
```json
{
"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**
```json
{
"amount": "10.00",
"currencyType": "PEA",
"chatID": 12345,
"chatType": 2,
"rpType": "STANDARD_RP",
"recipientIDs": [],
"rpNum": 5,
"remark": "恭喜发财",
"msgSendTime": 1234567890000000
}
```
**⚠️ currencyType 规则:**
- `workspaceId > 0` → 从 `/workspace/workspace/get``workspace.currency`(如 `"USDT"`
- `workspaceId == 0``"PEA"`(默认)
### 3.3 领取红包 API
**POST /app/api/wallet/rp/receive**
```json
{
"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=PEA`workspace 用 USDT | Flutter 动态取 workspace.currency |
| `[红包]` 文本 | 旧消息 typ=1 content="[红包]",未路由到红包气泡 | `typ=8` → RedEnvelopeBubbletyp=1 content=="[红包]" fallback 检测 |
---
## 7. 待完成
- **地雷红包 / 牛牛红包**MINE_RP + NN_RP 发送 UI需 rp-config/get
- **Mini-app WebView**MiniAppFloatButton 点击后打开小程序(#25
- **领取详情页**抢红包排行榜iOS RedEnvelopeDetailSheet
- **Game settle polling**drawTime 后每 3s 轮询直到新一期