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:
@@ -0,0 +1,16 @@
|
||||
import 'package:networks_sdk/networks_sdk.dart';
|
||||
|
||||
import 'package:im_app/data/remote/red_envelope_request.dart';
|
||||
|
||||
/// 获取游戏横幅 UseCase
|
||||
///
|
||||
/// 对应 Gitea issue #23 / iOS BannerViewModel.loadBanner()
|
||||
class FetchBannerUseCase {
|
||||
final NetworksSdkApi _api;
|
||||
|
||||
const FetchBannerUseCase({required NetworksSdkApi api}) : _api = api;
|
||||
|
||||
Future<GameBannerData?> execute(String gameId) async {
|
||||
return _api.executeRequest(FetchBannerRequest(gameId: gameId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import 'package:networks_sdk/networks_sdk.dart';
|
||||
|
||||
import 'package:im_app/data/remote/red_envelope_request.dart';
|
||||
|
||||
/// 领取红包结果
|
||||
class ReceiveRpResult {
|
||||
final bool success;
|
||||
final String amount;
|
||||
final int rpStatus;
|
||||
final String displayMessage;
|
||||
|
||||
const ReceiveRpResult({
|
||||
required this.success,
|
||||
required this.amount,
|
||||
required this.rpStatus,
|
||||
required this.displayMessage,
|
||||
});
|
||||
}
|
||||
|
||||
/// 领取红包 UseCase
|
||||
///
|
||||
/// 对应 Gitea issue #21 / iOS RedEnvelopeDetailSheet.claim()
|
||||
///
|
||||
/// ⚠️ `ReceiveRpRequest` 使用 JSON typed body(非 form string),
|
||||
/// 服务端要求 bool/int 为原生类型,否则返回 code=30007。
|
||||
class ReceiveRedEnvelopeUseCase {
|
||||
final NetworksSdkApi _api;
|
||||
|
||||
const ReceiveRedEnvelopeUseCase({required NetworksSdkApi api}) : _api = api;
|
||||
|
||||
Future<ReceiveRpResult> execute({
|
||||
required String rpId,
|
||||
required int chatId,
|
||||
required String rpType,
|
||||
required int messageId,
|
||||
}) async {
|
||||
final result = await _api.executeRequest(
|
||||
ReceiveRpRequest(
|
||||
rpId: rpId,
|
||||
chatId: chatId,
|
||||
rpType: rpType,
|
||||
sendRpMsgId: messageId,
|
||||
),
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
return const ReceiveRpResult(
|
||||
success: false,
|
||||
amount: '0',
|
||||
rpStatus: 0,
|
||||
displayMessage: '领取失败',
|
||||
);
|
||||
}
|
||||
|
||||
final msg = _statusMessage(result.grabFlag, result.rpStatus, result.amount);
|
||||
return ReceiveRpResult(
|
||||
success: result.grabFlag,
|
||||
amount: result.amount,
|
||||
rpStatus: result.rpStatus,
|
||||
displayMessage: msg,
|
||||
);
|
||||
}
|
||||
|
||||
String _statusMessage(bool grabbed, int status, String amount) {
|
||||
if (grabbed) return '领取成功!获得 $amount';
|
||||
return switch (status) {
|
||||
2 => '您已领取过该红包',
|
||||
3 => '红包已过期',
|
||||
4 => '手慢了,红包已抢完',
|
||||
5 => '不在领取范围内',
|
||||
_ => '领取失败',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import 'package:networks_sdk/networks_sdk.dart';
|
||||
|
||||
import 'package:im_app/data/remote/red_envelope_request.dart';
|
||||
|
||||
/// 发送红包 UseCase
|
||||
///
|
||||
/// 对应 Gitea issue #19 + #22
|
||||
///
|
||||
/// ## currencyType 动态化(#19 bug fix)
|
||||
///
|
||||
/// - `workspaceId > 0`:先请求 `/workspace/workspace/get` 取 workspace.currency(如 USDT)
|
||||
/// - `workspaceId == 0`:默认 `"PEA"`
|
||||
///
|
||||
/// iOS 老项目硬编码 `currencyType=PEA`,导致 workspace 群报错 150001。
|
||||
///
|
||||
/// ## 成功后调用方需要
|
||||
///
|
||||
/// 使用返回的 `rpId` 构建 typ=8 消息 content,通过 ChatViewModel.sendMessage() 发送。
|
||||
class SendRedEnvelopeUseCase {
|
||||
final NetworksSdkApi _api;
|
||||
|
||||
const SendRedEnvelopeUseCase({required NetworksSdkApi api}) : _api = api;
|
||||
|
||||
/// 发送红包,返回 rpId
|
||||
///
|
||||
/// [chatId] 会话 ID
|
||||
/// [chatType] 1=私聊 / 2=群聊
|
||||
/// [workspaceId] 群 workspaceId(0 表示非工作台群)
|
||||
/// [rpType] "STANDARD_RP" | "LUCKY_RP"(初期支持,后续扩展 MINE_RP / NN_RP)
|
||||
/// [amount] 总金额字符串,如 "10.00"
|
||||
/// [rpNum] 红包数量
|
||||
/// [remark] 红包祝福语
|
||||
Future<String> execute({
|
||||
required int chatId,
|
||||
required int chatType,
|
||||
required int workspaceId,
|
||||
required String rpType,
|
||||
required String amount,
|
||||
required int rpNum,
|
||||
required String remark,
|
||||
}) async {
|
||||
// #19 fix: 根据 workspaceId 动态决定 currencyType
|
||||
final currencyType = await _resolveCurrencyType(workspaceId);
|
||||
|
||||
final result = await _api.executeRequest(
|
||||
SendRpRequest(
|
||||
amount: amount,
|
||||
currencyType: currencyType,
|
||||
chatId: chatId,
|
||||
chatType: chatType,
|
||||
rpType: rpType,
|
||||
recipientIds: const [],
|
||||
rpNum: rpNum,
|
||||
remark: remark,
|
||||
msgSendTime: DateTime.now().microsecondsSinceEpoch,
|
||||
),
|
||||
);
|
||||
|
||||
if (result == null || result.rpId.isEmpty) {
|
||||
throw Exception('发送红包失败:未返回 rpId');
|
||||
}
|
||||
return result.rpId;
|
||||
}
|
||||
|
||||
/// 根据 workspaceId 解析 currencyType
|
||||
///
|
||||
/// - `workspaceId > 0` → 请求 workspace 取 currency 字段
|
||||
/// - `workspaceId == 0` → 直接返回 "PEA"
|
||||
Future<String> _resolveCurrencyType(int workspaceId) async {
|
||||
if (workspaceId <= 0) return 'PEA';
|
||||
|
||||
try {
|
||||
final ws = await _api.executeRequest(
|
||||
GetWorkspaceRequest(workspaceId: workspaceId),
|
||||
);
|
||||
final currency = ws?.currency ?? '';
|
||||
return currency.isNotEmpty ? currency : 'PEA';
|
||||
} catch (_) {
|
||||
// 降级:取不到 workspace 信息时使用默认值
|
||||
return 'PEA';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user