diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 5058a2d1..66cbf6b9 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -272,7 +272,15 @@ "Bash($env:DATABASE_URL=\"postgresql://postgres:password@localhost:5432/rwa_identity?schema=public\")", "Bash(docker cp:*)", "Bash(timeout 120 docker compose:*)", - "Bash(docker network create:*)" + "Bash(docker network create:*)", + "Bash(find backend/services -type d -name migrations -exec sh -c 'echo \"\"=== {} ===\"\" && ls -1 \"\"$1\"\" | wc -l' _ {} ;)", + "Bash(git commit -m \"$\\(cat <<''EOF''\nrefactor: 简化启动流程和优化向导页文案\n\n- 简化 splash 页面跳转逻辑:账号已创建直接进主页,首次启动进向导页\n- 优化向导页第5页文案:更亲切的标题和更清晰的说明\n- 改进推荐码输入框和扫码图标颜色:黑色文字与白色底色形成更好对比\n- 简化\"恢复账号\"按钮文字\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Sonnet 4.5 \nEOF\n\\)\")", + "Bash(timeout 30 git push:*)", + "Bash(ping:*)", + "Bash(ipconfig:*)", + "Bash(flutter run:*)", + "Bash(flutter devices:*)", + "Bash(npx qrcode:*)" ], "deny": [], "ask": [] diff --git a/SEED01-qrcode.png b/SEED01-qrcode.png new file mode 100644 index 00000000..7b526361 Binary files /dev/null and b/SEED01-qrcode.png differ diff --git a/backend/services/identity-service/src/infrastructure/external/sms/sms.service.ts b/backend/services/identity-service/src/infrastructure/external/sms/sms.service.ts index 18f57f63..452fb8d7 100644 --- a/backend/services/identity-service/src/infrastructure/external/sms/sms.service.ts +++ b/backend/services/identity-service/src/infrastructure/external/sms/sms.service.ts @@ -10,6 +10,7 @@ export interface SmsSendResult { bizId?: string; code?: string; message?: string; + uncertain?: boolean; // 超时情况下短信可能已发送 } // 重试配置 @@ -24,11 +25,14 @@ const RETRY_CONFIG = { 'ENOTFOUND', // DNS 解析失败 'ServiceUnavailable', ], - // 不重试的错误(可能短信已发送成功,只是响应慢) - noRetryErrors: [ + // 超时错误(短信可能已发送成功,返回成功但标记为不确定) + timeoutErrors: [ 'ConnectTimeout', 'ReadTimeout', 'ETIMEDOUT', + ], + // 不重试的业务错误 + noRetryErrors: [ 'BUSINESS_LIMIT_CONTROL', // 流控 ], }; @@ -167,9 +171,23 @@ export class SmsService implements OnModuleInit { (lastError as any).code = result.code; } catch (error: any) { lastError = error; + const errorInfo = error.message || error.code || ''; + + // 超时错误:短信可能已发送成功,返回成功但标记为不确定 + if (this.isTimeoutError(errorInfo)) { + this.logger.warn( + `[SMS] 超时但可能已发送成功: ${error.message}`, + ); + return { + success: true, + uncertain: true, + code: 'TIMEOUT_UNCERTAIN', + message: '请求超时,验证码可能已发送,请稍等片刻', + }; + } // 检查是否是可重试的错误 - if (!this.isRetryableError(error.message || error.code || '')) { + if (!this.isRetryableError(errorInfo)) { this.logger.error(`[SMS] 不可重试错误: ${error.message}`); return { success: false, @@ -250,6 +268,15 @@ export class SmsService implements OnModuleInit { ); } + /** + * 检查是否是超时错误 + */ + private isTimeoutError(errorInfo: string): boolean { + return RETRY_CONFIG.timeoutErrors.some( + (timeout) => errorInfo.includes(timeout), + ); + } + /** * 睡眠函数 */ diff --git a/frontend/mobile-app/lib/features/deposit/presentation/pages/deposit_usdt_page.dart b/frontend/mobile-app/lib/features/deposit/presentation/pages/deposit_usdt_page.dart index d29c254a..ae336339 100644 --- a/frontend/mobile-app/lib/features/deposit/presentation/pages/deposit_usdt_page.dart +++ b/frontend/mobile-app/lib/features/deposit/presentation/pages/deposit_usdt_page.dart @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:qr_flutter/qr_flutter.dart'; import '../../../../core/di/injection_container.dart'; +import '../../../../core/storage/storage_keys.dart'; /// 充值 USDT 页面 /// 显示用户ID(accountSequence)二维码作为充值地址 @@ -67,14 +68,29 @@ class _DepositUsdtPageState extends ConsumerState { _accountSequence = userSerialNum; - // 检查钱包状态 - try { - final walletInfo = await accountService.getWalletInfo(userSerialNum); - _isWalletReady = walletInfo.isReady; - debugPrint('[DepositUsdtPage] 钱包状态: ${walletInfo.status}, isReady: $_isWalletReady'); - } catch (e) { - debugPrint('[DepositUsdtPage] 获取钱包状态失败: $e'); - _isWalletReady = false; + // 先从本地存储检查钱包状态 + final secureStorage = ref.read(secureStorageProvider); + final cachedWalletReady = await secureStorage.read(key: StorageKeys.isWalletReady); + + if (cachedWalletReady == 'true') { + // 本地已标记为 ready,直接使用 + debugPrint('[DepositUsdtPage] 本地存储显示钱包已就绪'); + _isWalletReady = true; + } else { + // 本地未标记,调用API检查 + try { + final walletInfo = await accountService.getWalletInfo(userSerialNum); + _isWalletReady = walletInfo.isReady; + debugPrint('[DepositUsdtPage] 钱包状态: ${walletInfo.status}, isReady: $_isWalletReady'); + + // 如果API返回ready,保存到本地存储 + if (_isWalletReady) { + await secureStorage.write(key: StorageKeys.isWalletReady, value: 'true'); + } + } catch (e) { + debugPrint('[DepositUsdtPage] 获取钱包状态失败: $e'); + _isWalletReady = false; + } } // 如果钱包已就绪,查询余额和真实钱包地址