From 03cc5bc324050b736ef74cdffad12fbe4d50889c Mon Sep 17 00:00:00 2001 From: hailin Date: Sat, 31 Jan 2026 05:54:34 -0800 Subject: [PATCH] =?UTF-8?q?fix(mining-app):=20QR=E7=A0=81=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E5=8A=A0=E5=9B=BA=20+=20debugPrint=E6=8E=92=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 清除不可见字符(BOM/零宽空格) - 三级宽松解析:正则→Uri.parse→兜底phone=提取 - 添加 [QR_SCAN] 调试日志定位实际扫码值 Co-Authored-By: Claude Opus 4.5 --- .../pages/asset/send_shares_page.dart | 1 + .../widgets/qr_scanner_sheet.dart | 49 ++++++++++--------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/frontend/mining-app/lib/presentation/pages/asset/send_shares_page.dart b/frontend/mining-app/lib/presentation/pages/asset/send_shares_page.dart index 9c59fbc1..518329f0 100644 --- a/frontend/mining-app/lib/presentation/pages/asset/send_shares_page.dart +++ b/frontend/mining-app/lib/presentation/pages/asset/send_shares_page.dart @@ -570,6 +570,7 @@ class _SendSharesPageState extends ConsumerState { if (result == null) return; // 解析二维码内容 + debugPrint('[QR_SCAN] scanner returned: "$result"'); final phone = parseTransferQrCode(result); if (phone != null) { setState(() { diff --git a/frontend/mining-app/lib/presentation/widgets/qr_scanner_sheet.dart b/frontend/mining-app/lib/presentation/widgets/qr_scanner_sheet.dart index 42223199..5eb4bc4b 100644 --- a/frontend/mining-app/lib/presentation/widgets/qr_scanner_sheet.dart +++ b/frontend/mining-app/lib/presentation/widgets/qr_scanner_sheet.dart @@ -250,37 +250,42 @@ class _QrScannerSheetState extends State { /// 返回手机号,解析失败返回 null String? parseTransferQrCode(String qrCode) { try { - final trimmed = qrCode.trim(); + // 去除首尾空白及不可见字符(BOM、零宽空格等) + final trimmed = qrCode.trim().replaceAll(RegExp(r'[\u200B-\u200D\uFEFF\u00A0]'), ''); - // 使用正则表达式匹配,兼容有无 / 的格式 - // durian://transfer/?phone=xxx 或 durian://transfer?phone=xxx - final regex = RegExp(r'^durian://transfer/?[?]phone=(\d{11})$'); + debugPrint('[QR_SCAN] raw length=${qrCode.length}, trimmed length=${trimmed.length}'); + debugPrint('[QR_SCAN] raw codeUnits=${qrCode.codeUnits}'); + debugPrint('[QR_SCAN] trimmed="$trimmed"'); + + // 方案1:宽松正则 - 只要包含 durian://transfer 和 phone=11位数字 + final regex = RegExp(r'durian://transfer[/?].*?phone=(\d{11})'); final match = regex.firstMatch(trimmed); - if (match != null) { + debugPrint('[QR_SCAN] regex matched, phone=${match.group(1)}'); return match.group(1); } - // 备用方案:使用 Uri.parse - final uri = Uri.parse(trimmed); - - // 检查 scheme - if (uri.scheme != 'durian') return null; - - // 检查 host (即 authority 部分) - if (uri.host != 'transfer') return null; - - // 获取 phone 参数 - final phone = uri.queryParameters['phone']; - if (phone == null || phone.isEmpty) return null; - - // 简单验证手机号格式 - if (phone.length != 11 || !RegExp(r'^\d{11}$').hasMatch(phone)) { - return null; + // 方案2:Uri.parse + final uri = Uri.tryParse(trimmed); + if (uri != null && uri.scheme == 'durian') { + final phone = uri.queryParameters['phone']; + if (phone != null && RegExp(r'^\d{11}$').hasMatch(phone)) { + debugPrint('[QR_SCAN] Uri.parse matched, phone=$phone'); + return phone; + } } - return phone; + // 方案3:最宽松兜底 - 字符串中任何 phone=11位数字 + final fallback = RegExp(r'phone=(\d{11})').firstMatch(trimmed); + if (fallback != null) { + debugPrint('[QR_SCAN] fallback matched, phone=${fallback.group(1)}'); + return fallback.group(1); + } + + debugPrint('[QR_SCAN] all parsing failed for: "$trimmed"'); + return null; } catch (e) { + debugPrint('[QR_SCAN] exception: $e'); return null; } }