import 'dart:developer' as developer; import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../core/network/api_response.dart'; import '../core/network/dio_client.dart'; import '../data/services/auth_service.dart'; // ── State ────────────────────────────────────────────────── class FundPasswordState { final bool isLoading; final bool isSendingCode; final String? errorMessage; final String? successMessage; final int codeCooldown; const FundPasswordState({ this.isLoading = false, this.isSendingCode = false, this.errorMessage, this.successMessage, this.codeCooldown = 0, }); FundPasswordState copyWith({ bool? isLoading, bool? isSendingCode, String? errorMessage, String? successMessage, int? codeCooldown, }) => FundPasswordState( isLoading: isLoading ?? this.isLoading, isSendingCode: isSendingCode ?? this.isSendingCode, errorMessage: errorMessage, successMessage: successMessage, codeCooldown: codeCooldown ?? this.codeCooldown, ); } // ── Notifier ─────────────────────────────────────────────── class FundPasswordNotifier extends Notifier { AuthService get _service => AuthService(ref.read(dioClientProvider)); @override FundPasswordState build() => const FundPasswordState(); void clearError() => state = state.copyWith(errorMessage: null); void clearSuccess() => state = state.copyWith(successMessage: null); /// 密码校验:7-20位字母+数字混合 static final _passwordRegex = RegExp(r'^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{7,20}$'); static bool validatePassword(String password) => _passwordRegex.hasMatch(password); /// 首次设置资金密码 — POST uc/approve/transaction/password Future setPassword(String password) async { state = state.copyWith(isLoading: true, errorMessage: null); try { await _service.setFundPassword(password); state = state.copyWith( isLoading: false, successMessage: 'setSuccess', ); return true; } catch (e) { state = state.copyWith( isLoading: false, errorMessage: _parseError(e), ); return false; } } /// 发送邮箱验证码 — POST uc/transaction/email/code Future sendEmailCode() async { state = state.copyWith(isSendingCode: true, errorMessage: null); try { await _service.sendFundPasswordEmailCode(); state = state.copyWith(isSendingCode: false); _startCountdown(); return true; } catch (e) { state = state.copyWith( isSendingCode: false, errorMessage: _parseError(e), ); return false; } } /// 重置资金密码 — POST uc/approve/reset/transaction/password Future resetPassword({ required String newPassword, required String vcode, }) async { state = state.copyWith(isLoading: true, errorMessage: null); try { await _service.resetFundPassword( newPassword: newPassword, vcode: vcode, ); state = state.copyWith( isLoading: false, successMessage: 'changeSuccess', ); return true; } catch (e) { state = state.copyWith( isLoading: 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; }); } String _parseError(Object e) { developer.log('FundPassword error: $e', name: 'FUND_PWD', 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 ?? '网络请求异常'; } if (e is ApiException) return e.message; return e.toString(); } } // ── Provider ────────────────────────────────────────────── final fundPasswordProvider = NotifierProvider( FundPasswordNotifier.new, );