import 'dart:developer' as developer; import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../core/network/api_response.dart'; import '../data/services/auth_service.dart'; import '../core/network/dio_client.dart'; // ── State ────────────────────────────────────────────────── class GoogleAuthState { /// 秘钥加载中 final bool isSecretLoading; /// 发送验证码中 final bool isSendingCode; /// 提交绑定中 final bool isSubmitting; final String? errorMessage; final int codeCooldown; /// TOTP 秘钥 final String secret; /// otpauth:// URI(用于生成 QR 码) final String otpauthUrl; /// 脱敏邮箱(从用户信息获取) final String maskedEmail; const GoogleAuthState({ this.isSecretLoading = false, this.isSendingCode = false, this.isSubmitting = false, this.errorMessage, this.codeCooldown = 0, this.secret = '', this.otpauthUrl = '', this.maskedEmail = '', }); GoogleAuthState copyWith({ bool? isSecretLoading, bool? isSendingCode, bool? isSubmitting, String? errorMessage, int? codeCooldown, String? secret, String? otpauthUrl, String? maskedEmail, }) => GoogleAuthState( isSecretLoading: isSecretLoading ?? this.isSecretLoading, isSendingCode: isSendingCode ?? this.isSendingCode, isSubmitting: isSubmitting ?? this.isSubmitting, errorMessage: errorMessage, codeCooldown: codeCooldown ?? this.codeCooldown, secret: secret ?? this.secret, otpauthUrl: otpauthUrl ?? this.otpauthUrl, maskedEmail: maskedEmail ?? this.maskedEmail, ); } // ── Notifier ─────────────────────────────────────────────── class GoogleAuthNotifier extends Notifier { AuthService get _service => AuthService(ref.read(dioClientProvider)); @override GoogleAuthState build() => const GoogleAuthState(); void clearError() => state = state.copyWith(errorMessage: null); /// 页面加载时获取秘钥 — GET uc/google/sendgoogle /// 同时获取用户邮箱用于脱敏显示 Future fetchSecret() async { state = state.copyWith(isSecretLoading: true, errorMessage: null); try { final results = await Future.wait([ _service.getGoogleSecret(), _service.getMyInfo(), ]); final secretData = results[0]; final userInfo = results[1]; final email = userInfo['email']?.toString() ?? ''; state = state.copyWith( isSecretLoading: false, secret: secretData['secret']?.toString() ?? '', otpauthUrl: secretData['link']?.toString() ?? '', maskedEmail: _maskEmail(email), ); } catch (e) { state = state.copyWith( isSecretLoading: false, errorMessage: _parseError(e), ); } } /// 发送邮箱验证码 — POST uc/googleAuth/email/code Future sendEmailCode() async { state = state.copyWith(isSendingCode: true, errorMessage: null); try { await _service.sendGoogleAuthEmailCode(); state = state.copyWith(isSendingCode: false); _startCountdown(); return true; } catch (e) { state = state.copyWith( isSendingCode: false, errorMessage: _parseError(e), ); return false; } } /// 绑定谷歌验证器 — POST uc/google/googleAuthWithSms Future bindGoogleAuth({ required String googleCode, required String emailCode, }) async { state = state.copyWith(isSubmitting: true, errorMessage: null); try { await _service.bindGoogleAuth( codes: googleCode, vcode: emailCode, secret: state.secret, vtype: '2', ); state = state.copyWith(isSubmitting: false); return true; } catch (e) { state = state.copyWith( isSubmitting: false, errorMessage: _parseError(e), ); return false; } } void _startCountdown() { state = state.copyWith(codeCooldown: 60); Future.doWhile(() async { await Future.delayed(const Duration(seconds: 1)); final remaining = state.codeCooldown - 1; state = state.copyWith(codeCooldown: remaining); return remaining > 0; }); } /// 邮箱脱敏:user@gmail.com → u**@gmail.com String _maskEmail(String email) { if (email.isEmpty) return ''; final parts = email.split('@'); if (parts.length != 2) return email; final name = parts[0]; if (name.length <= 1) return '$name**@${parts[1]}'; return '${name[0]}**@${parts[1]}'; } String _parseError(Object e) { developer.log('GoogleAuth error: $e', name: 'GOOGLE_AUTH', error: e); if (e is DioException) { if (e.error is ApiException) { return (e.error as ApiException).message; } final responseData = e.response?.data; if (responseData is Map) { final msg = responseData['message'] as String? ?? responseData['msg'] as String?; if (msg != null && msg.isNotEmpty) return msg; } return e.message ?? 'errNetworkError'; } if (e is ApiException) return e.message; return e.toString(); } } // ── Providers ────────────────────────────────────────────── final googleAuthProvider = NotifierProvider( GoogleAuthNotifier.new, );