feat: WebView/媒体/红包详情全量实现 (#25~#30)
#25 MiniAppWebViewPage + MiniAppRouter - webview_flutter 加载 {apiBaseUrl}/miniapp/{appId}/index.html?gameId=...&token=... - MiniAppFloatButton 接收 chatId/chatType,默认打开 WebView - BannerState 新增 appId 字段,由 GameBannerData.appId 填充 #26 open_filex 文件打开 - FileMessageBubble 下载完成后调用 OpenFilex.open(localPath) - 打开失败时 SnackBar 提示 #27 audioplayers 音频播放 - AudioPlaybackService(Notifier):单例 AudioPlayer,togglePlay/pause/seek - AudioMessageBubble 接入:播放态图标切换、进度 mm:ss 显示 #28 video_player + chewie 视频全屏 - VideoPlayerPage:本地文件 / HTTP 双模,chewie 控制栏 - VideoMessageBubble 默认 onTap → push VideoPlayerPage #29 红包领取排行榜详情页 - GET /payment/rp/detail → RpDetailData + RpRecordItem DTO - GetRpDetailUseCase + getRpDetailUseCaseProvider - RedEnvelopeDetailSheet:汇总行 + 领取排行列表,头像/昵称/金额/时间 #30 MINE_RP 地雷红包发包 UI - _RpType 新增 mine(MINE_RP),显示地雷金额输入框 - SendRpRequest.parameters 携带 mineAmount - RedEnvelopeBubble:非活跃状态直接打开详情,活跃状态领取后打开排行榜 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@ class SendRpData {
|
||||
|
||||
/// 发送红包请求
|
||||
///
|
||||
/// 对应 Gitea issue #19 + #22
|
||||
/// 对应 Gitea issue #19 + #22 + #30
|
||||
///
|
||||
/// ## currencyType 规则(#19 bug fix)
|
||||
///
|
||||
@@ -26,6 +26,10 @@ class SendRpData {
|
||||
/// Flutter 修复:`currencyType` 由调用方传入,UseCase 层根据 workspaceId 动态决定:
|
||||
/// - workspaceId > 0 → 从 `/workspace/workspace/get` 取 workspace.currency
|
||||
/// - workspaceId == 0 → 默认 `"PEA"`
|
||||
///
|
||||
/// ## mineAmount(#30 MINE_RP)
|
||||
///
|
||||
/// 地雷红包时附带 `mineAmount` 参数,null 时不传。
|
||||
class SendRpRequest extends ApiRequestable<SendRpData> {
|
||||
final String amount;
|
||||
final String currencyType;
|
||||
@@ -36,6 +40,7 @@ class SendRpRequest extends ApiRequestable<SendRpData> {
|
||||
final int rpNum;
|
||||
final String remark;
|
||||
final int msgSendTime;
|
||||
final String? mineAmount;
|
||||
|
||||
const SendRpRequest({
|
||||
required this.amount,
|
||||
@@ -47,6 +52,7 @@ class SendRpRequest extends ApiRequestable<SendRpData> {
|
||||
required this.rpNum,
|
||||
required this.remark,
|
||||
required this.msgSendTime,
|
||||
this.mineAmount,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -66,6 +72,8 @@ class SendRpRequest extends ApiRequestable<SendRpData> {
|
||||
'rpNum': rpNum,
|
||||
'remark': remark,
|
||||
'msgSendTime': msgSendTime,
|
||||
if (mineAmount != null && mineAmount!.isNotEmpty)
|
||||
'mineAmount': mineAmount,
|
||||
};
|
||||
|
||||
@override
|
||||
@@ -268,6 +276,107 @@ class WorkspaceData {
|
||||
}
|
||||
}
|
||||
|
||||
// ── 红包领取详情 ───────────────────────────────────────────────────────────────
|
||||
|
||||
/// 单条领取记录
|
||||
class RpRecordItem {
|
||||
final int userId;
|
||||
final String nickname;
|
||||
final String avatarUrl;
|
||||
final String amount;
|
||||
final int grabTime;
|
||||
|
||||
const RpRecordItem({
|
||||
required this.userId,
|
||||
required this.nickname,
|
||||
required this.avatarUrl,
|
||||
required this.amount,
|
||||
required this.grabTime,
|
||||
});
|
||||
|
||||
factory RpRecordItem.fromJson(Map<String, dynamic> json) {
|
||||
return RpRecordItem(
|
||||
userId: json['userId'] as int? ?? 0,
|
||||
nickname: json['nickname'] as String? ?? '',
|
||||
avatarUrl: json['avatarUrl'] as String? ?? '',
|
||||
amount: (json['amount'] ?? '0').toString(),
|
||||
grabTime: json['grabTime'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 红包领取详情响应
|
||||
///
|
||||
/// 对应 Gitea issue #29
|
||||
class RpDetailData {
|
||||
final String rpId;
|
||||
final String rpType;
|
||||
final String remark;
|
||||
final String totalAmount;
|
||||
final int totalNum;
|
||||
final String receivedAmount;
|
||||
final int receivedNum;
|
||||
final int status;
|
||||
final List<RpRecordItem> records;
|
||||
|
||||
const RpDetailData({
|
||||
required this.rpId,
|
||||
required this.rpType,
|
||||
required this.remark,
|
||||
required this.totalAmount,
|
||||
required this.totalNum,
|
||||
required this.receivedAmount,
|
||||
required this.receivedNum,
|
||||
required this.status,
|
||||
required this.records,
|
||||
});
|
||||
|
||||
factory RpDetailData.fromJson(Map<String, dynamic> json) {
|
||||
final raw = json['records'];
|
||||
final records = raw is List
|
||||
? raw.whereType<Map<String, dynamic>>().map(RpRecordItem.fromJson).toList()
|
||||
: <RpRecordItem>[];
|
||||
return RpDetailData(
|
||||
rpId: json['rpId'] as String? ?? '',
|
||||
rpType: json['rpType'] as String? ?? '',
|
||||
remark: json['remark'] as String? ?? '恭喜发财',
|
||||
totalAmount: (json['totalAmount'] ?? '0').toString(),
|
||||
totalNum: json['totalNum'] as int? ?? 0,
|
||||
receivedAmount: (json['receivedAmount'] ?? '0').toString(),
|
||||
receivedNum: json['receivedNum'] as int? ?? 0,
|
||||
status: json['status'] as int? ?? 0,
|
||||
records: records,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取红包领取详情请求
|
||||
///
|
||||
/// GET /payment/rp/detail?rpId=xxx
|
||||
class GetRpDetailRequest extends ApiRequestable<RpDetailData> {
|
||||
final String rpId;
|
||||
|
||||
const GetRpDetailRequest({required this.rpId});
|
||||
|
||||
@override
|
||||
String get path => ApiPaths.rpDetail;
|
||||
|
||||
@override
|
||||
HttpMethod get method => HttpMethod.get;
|
||||
|
||||
@override
|
||||
Map<String, dynamic> get parameters => {'rpId': rpId};
|
||||
|
||||
@override
|
||||
RpDetailData? decodeResponse(dynamic response) {
|
||||
final data = (response as dynamic).data;
|
||||
if (data is! Map<String, dynamic>) return null;
|
||||
return RpDetailData.fromJson(data);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Workspace ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// 获取 Workspace 信息请求
|
||||
class GetWorkspaceRequest extends ApiRequestable<WorkspaceData> {
|
||||
final int workspaceId;
|
||||
|
||||
Reference in New Issue
Block a user