From 0c0750ce933cf3a6820b7fcd9fe542ba3059f5f5 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 28 Jan 2026 07:26:36 -0800 Subject: [PATCH] =?UTF-8?q?feat(c2c):=20=E5=89=8D=E7=AB=AF=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20Bot=20=E8=87=AA=E5=8A=A8=E8=B4=AD=E4=B9=B0=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - C2cOrderModel 添加 botPurchased/sellerKavaAddress/paymentTxHash 字段 - 订单详情页新增 Bot 支付信息卡片,显示 dUSDT 交易哈希 - 支持复制 Kava 地址和交易哈希 Co-Authored-By: Claude Opus 4.5 --- .../lib/data/models/c2c_order_model.dart | 13 ++ .../pages/c2c/c2c_order_detail_page.dart | 141 +++++++++++++++++- 2 files changed, 152 insertions(+), 2 deletions(-) diff --git a/frontend/mining-app/lib/data/models/c2c_order_model.dart b/frontend/mining-app/lib/data/models/c2c_order_model.dart index 6774a27c..8d96d4b6 100644 --- a/frontend/mining-app/lib/data/models/c2c_order_model.dart +++ b/frontend/mining-app/lib/data/models/c2c_order_model.dart @@ -46,6 +46,10 @@ class C2cOrderModel { final int confirmTimeoutMinutes; // 确认收款超时时间(分钟) final DateTime? paymentDeadline; // 付款截止时间 final DateTime? confirmDeadline; // 确认收款截止时间 + // Bot 自动购买相关 + final bool botPurchased; // 是否被 Bot 购买 + final String? sellerKavaAddress; // 卖家 Kava 地址 + final String? paymentTxHash; // dUSDT 支付交易哈希 // 其他 final C2cOrderStatus status; final String? remark; @@ -79,6 +83,10 @@ class C2cOrderModel { this.confirmTimeoutMinutes = 60, this.paymentDeadline, this.confirmDeadline, + // Bot 自动购买相关 + this.botPurchased = false, + this.sellerKavaAddress, + this.paymentTxHash, // 其他 required this.status, this.remark, @@ -118,6 +126,10 @@ class C2cOrderModel { confirmDeadline: json['confirmDeadline'] != null ? DateTime.parse(json['confirmDeadline']) : null, + // Bot 自动购买相关 + botPurchased: json['botPurchased'] ?? false, + sellerKavaAddress: json['sellerKavaAddress'], + paymentTxHash: json['paymentTxHash'], // 其他 status: _parseOrderStatus(json['status']), remark: json['remark'], @@ -190,6 +202,7 @@ class C2cOrderModel { bool get isPaid => status == C2cOrderStatus.paid; bool get isCompleted => status == C2cOrderStatus.completed; bool get isCancelled => status == C2cOrderStatus.cancelled; + bool get isBotPurchased => botPurchased && paymentTxHash != null; String get typeText => isBuy ? '买入' : '卖出'; diff --git a/frontend/mining-app/lib/presentation/pages/c2c/c2c_order_detail_page.dart b/frontend/mining-app/lib/presentation/pages/c2c/c2c_order_detail_page.dart index c31c5194..57b1a8d2 100644 --- a/frontend/mining-app/lib/presentation/pages/c2c/c2c_order_detail_page.dart +++ b/frontend/mining-app/lib/presentation/pages/c2c/c2c_order_detail_page.dart @@ -113,6 +113,13 @@ class _C2cOrderDetailPageState extends ConsumerState { // 订单信息 _buildOrderInfoCard(order, isMaker), + // Bot 支付信息卡片(Bot 购买的卖单显示) + if (order.isBotPurchased) + ...[ + const SizedBox(height: 16), + _buildBotPaymentInfoCard(order), + ], + // 收款信息卡片(已匹配或已付款状态时显示) if (order.hasPaymentInfo && (order.isMatched || order.isPaid)) @@ -177,7 +184,7 @@ class _C2cOrderDetailPageState extends ConsumerState { case C2cOrderStatus.completed: statusColor = _green; statusText = '已完成'; - statusDesc = '交易已完成'; + statusDesc = order.isBotPurchased ? 'Bot 已自动购买并支付 dUSDT' : '交易已完成'; statusIcon = Icons.verified; break; case C2cOrderStatus.cancelled: @@ -460,11 +467,141 @@ class _C2cOrderDetailPageState extends ConsumerState { ); } + /// Bot 支付信息卡片 + Widget _buildBotPaymentInfoCard(C2cOrderModel order) { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 16), + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: _green.withOpacity(0.05), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: _green.withOpacity(0.3)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: const [ + Icon(Icons.smart_toy, size: 20, color: _green), + SizedBox(width: 8), + Text( + 'Bot 自动购买', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: _darkText, + ), + ), + ], + ), + const SizedBox(height: 16), + + // 支付说明 + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: _green.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: const [ + Icon(Icons.check_circle, size: 16, color: _green), + SizedBox(width: 8), + Expanded( + child: Text( + 'Bot 已自动购买您的卖单,dUSDT 已转入您的 Kava 地址', + style: TextStyle(fontSize: 13, color: _darkText), + ), + ), + ], + ), + ), + + const SizedBox(height: 12), + + // 收款地址 + if (order.sellerKavaAddress != null) + _buildPaymentInfoRow( + label: '收款地址', + value: _truncateAddress(order.sellerKavaAddress!), + canCopy: true, + copyValue: order.sellerKavaAddress, + ), + + // 支付金额 + _buildPaymentInfoRow( + label: '支付金额', + value: '${formatAmount(order.totalAmount)} dUSDT', + ), + + // 交易哈希 + if (order.paymentTxHash != null) + _buildPaymentInfoRow( + label: '交易哈希', + value: _truncateAddress(order.paymentTxHash!), + canCopy: true, + copyValue: order.paymentTxHash, + ), + + // 查看交易链接 + if (order.paymentTxHash != null) ...[ + const SizedBox(height: 12), + InkWell( + onTap: () => _openExplorer(order.paymentTxHash!), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + Icon(Icons.open_in_new, size: 16, color: _orange), + SizedBox(width: 4), + Text( + '在区块链浏览器中查看', + style: TextStyle( + fontSize: 13, + color: _orange, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ), + ], + ], + ), + ); + } + + /// 截断地址显示 + String _truncateAddress(String address) { + if (address.length <= 16) return address; + return '${address.substring(0, 8)}...${address.substring(address.length - 8)}'; + } + + /// 打开区块链浏览器 + Future _openExplorer(String txHash) async { + // Kava 区块链浏览器 + final url = 'https://kavascan.com/tx/$txHash'; + // 这里可以使用 url_launcher 包打开链接 + // 暂时只复制交易哈希 + Clipboard.setData(ClipboardData(text: url)); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('区块链浏览器链接已复制'), + duration: Duration(seconds: 2), + ), + ); + } + } + Widget _buildPaymentInfoRow({ required String label, required String value, IconData? icon, bool canCopy = false, + String? copyValue, }) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), @@ -493,7 +630,7 @@ class _C2cOrderDetailPageState extends ConsumerState { const SizedBox(width: 8), GestureDetector( onTap: () { - Clipboard.setData(ClipboardData(text: value)); + Clipboard.setData(ClipboardData(text: copyValue ?? value)); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('已复制'),