- #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>
4.9 KiB
4.9 KiB
红包与游戏横幅 — 架构文档
对应 Gitea issues #19–#24 参考实现:
im-client-ios-swift-demoRedEnvelopeSendView + 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)
{
"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/get取workspace.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=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 轮询直到新一期