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>
This commit is contained in:
173
Doc/red_envelope_game_architecture.md
Normal file
173
Doc/red_envelope_game_architecture.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# 红包与游戏横幅 — 架构文档
|
||||
|
||||
> 对应 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 | 发送红包 UI(STANDARD_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 红包消息 content(typ=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` → RedEnvelopeBubble;typ=1 content=="[红包]" fallback 检测 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 待完成
|
||||
|
||||
- **地雷红包 / 牛牛红包**:MINE_RP + NN_RP 发送 UI(需 rp-config/get)
|
||||
- **Mini-app WebView**:MiniAppFloatButton 点击后打开小程序(#25)
|
||||
- **领取详情页**:抢红包排行榜(iOS RedEnvelopeDetailSheet)
|
||||
- **Game settle polling**:drawTime 后每 3s 轮询直到新一期
|
||||
Reference in New Issue
Block a user