diff --git a/backend/services/auth-service/src/application/services/password.service.ts b/backend/services/auth-service/src/application/services/password.service.ts index c0bf6bb5..58c45fbd 100644 --- a/backend/services/auth-service/src/application/services/password.service.ts +++ b/backend/services/auth-service/src/application/services/password.service.ts @@ -45,8 +45,15 @@ export class PasswordService { SmsVerificationType.RESET_PASSWORD, ); - if (!verification || verification.code !== dto.smsCode) { - throw new BadRequestException('验证码错误或已过期'); + if (!verification) { + throw new BadRequestException('验证码已过期或不存在'); + } + if (verification.attempts >= 5) { + throw new BadRequestException('验证码尝试次数过多,请重新获取'); + } + if (verification.code !== dto.smsCode) { + await this.smsVerificationRepository.incrementAttempts(verification.id); + throw new BadRequestException('验证码错误'); } // 标记验证码已使用 diff --git a/frontend/mining-app/lib/presentation/pages/auth/forgot_password_page.dart b/frontend/mining-app/lib/presentation/pages/auth/forgot_password_page.dart index 58816b8b..78919971 100644 --- a/frontend/mining-app/lib/presentation/pages/auth/forgot_password_page.dart +++ b/frontend/mining-app/lib/presentation/pages/auth/forgot_password_page.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../core/router/routes.dart'; import '../../../core/constants/app_colors.dart'; +import '../../../core/error/exceptions.dart'; import '../../providers/user_providers.dart'; class ForgotPasswordPage extends ConsumerStatefulWidget { @@ -22,6 +23,7 @@ class _ForgotPasswordPageState extends ConsumerState { bool _obscurePassword = true; bool _obscureConfirmPassword = true; int _countDown = 0; + bool _sendingSms = false; @override void dispose() { @@ -41,21 +43,27 @@ class _ForgotPasswordPageState extends ConsumerState { return; } + setState(() => _sendingSms = true); try { await ref.read(userNotifierProvider.notifier).sendSmsCode(phone, 'RESET_PASSWORD'); - setState(() => _countDown = 60); - _startCountDown(); if (mounted) { + setState(() => _countDown = 60); + _startCountDown(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('验证码已发送')), ); } } catch (e) { if (mounted) { + final msg = e is ServerException || e is NetworkException + ? (e as dynamic).message as String + : e.toString().replaceFirst(RegExp(r'^[A-Za-z]+Exception:\s*'), ''); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('发送失败: $e')), + SnackBar(content: Text(msg)), ); } + } finally { + if (mounted) setState(() => _sendingSms = false); } } @@ -86,8 +94,11 @@ class _ForgotPasswordPageState extends ConsumerState { } } catch (e) { if (mounted) { + final msg = e is ServerException || e is NetworkException + ? (e as dynamic).message as String + : e.toString().replaceFirst(RegExp(r'^[A-Za-z]+Exception:\s*'), ''); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('重置失败: $e')), + SnackBar(content: Text(msg)), ); } } @@ -191,7 +202,7 @@ class _ForgotPasswordPageState extends ConsumerState { width: 120, height: 56, child: ElevatedButton( - onPressed: _countDown > 0 ? null : _sendSmsCode, + onPressed: (_countDown > 0 || _sendingSms) ? null : _sendSmsCode, style: ElevatedButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12),