fund_password_provider.dart 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import 'dart:developer' as developer;
  2. import 'package:dio/dio.dart';
  3. import 'package:flutter_riverpod/flutter_riverpod.dart';
  4. import '../core/network/api_response.dart';
  5. import '../core/network/dio_client.dart';
  6. import '../data/services/auth_service.dart';
  7. // ── State ──────────────────────────────────────────────────
  8. class FundPasswordState {
  9. final bool isLoading;
  10. final bool isSendingCode;
  11. final String? errorMessage;
  12. final String? successMessage;
  13. final int codeCooldown;
  14. const FundPasswordState({
  15. this.isLoading = false,
  16. this.isSendingCode = false,
  17. this.errorMessage,
  18. this.successMessage,
  19. this.codeCooldown = 0,
  20. });
  21. FundPasswordState copyWith({
  22. bool? isLoading,
  23. bool? isSendingCode,
  24. String? errorMessage,
  25. String? successMessage,
  26. int? codeCooldown,
  27. }) =>
  28. FundPasswordState(
  29. isLoading: isLoading ?? this.isLoading,
  30. isSendingCode: isSendingCode ?? this.isSendingCode,
  31. errorMessage: errorMessage,
  32. successMessage: successMessage,
  33. codeCooldown: codeCooldown ?? this.codeCooldown,
  34. );
  35. }
  36. // ── Notifier ───────────────────────────────────────────────
  37. class FundPasswordNotifier extends Notifier<FundPasswordState> {
  38. AuthService get _service => AuthService(ref.read(dioClientProvider));
  39. @override
  40. FundPasswordState build() => const FundPasswordState();
  41. void clearError() => state = state.copyWith(errorMessage: null);
  42. void clearSuccess() => state = state.copyWith(successMessage: null);
  43. /// 密码校验:7-20位字母+数字混合
  44. static final _passwordRegex =
  45. RegExp(r'^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{7,20}$');
  46. static bool validatePassword(String password) =>
  47. _passwordRegex.hasMatch(password);
  48. /// 首次设置资金密码 — POST uc/approve/transaction/password
  49. Future<bool> setPassword(String password) async {
  50. state = state.copyWith(isLoading: true, errorMessage: null);
  51. try {
  52. await _service.setFundPassword(password);
  53. state = state.copyWith(
  54. isLoading: false,
  55. successMessage: 'setSuccess',
  56. );
  57. return true;
  58. } catch (e) {
  59. state = state.copyWith(
  60. isLoading: false,
  61. errorMessage: _parseError(e),
  62. );
  63. return false;
  64. }
  65. }
  66. /// 发送邮箱验证码 — POST uc/transaction/email/code
  67. Future<bool> sendEmailCode() async {
  68. state = state.copyWith(isSendingCode: true, errorMessage: null);
  69. try {
  70. await _service.sendFundPasswordEmailCode();
  71. state = state.copyWith(isSendingCode: false);
  72. _startCountdown();
  73. return true;
  74. } catch (e) {
  75. state = state.copyWith(
  76. isSendingCode: false,
  77. errorMessage: _parseError(e),
  78. );
  79. return false;
  80. }
  81. }
  82. /// 重置资金密码 — POST uc/approve/reset/transaction/password
  83. Future<bool> resetPassword({
  84. required String newPassword,
  85. required String vcode,
  86. }) async {
  87. state = state.copyWith(isLoading: true, errorMessage: null);
  88. try {
  89. await _service.resetFundPassword(
  90. newPassword: newPassword,
  91. vcode: vcode,
  92. );
  93. state = state.copyWith(
  94. isLoading: false,
  95. successMessage: 'changeSuccess',
  96. );
  97. return true;
  98. } catch (e) {
  99. state = state.copyWith(
  100. isLoading: false,
  101. errorMessage: _parseError(e),
  102. );
  103. return false;
  104. }
  105. }
  106. void _startCountdown() {
  107. state = state.copyWith(codeCooldown: 60);
  108. Future.doWhile(() async {
  109. await Future.delayed(const Duration(seconds: 1));
  110. final remaining = state.codeCooldown - 1;
  111. state = state.copyWith(codeCooldown: remaining);
  112. return remaining > 0;
  113. });
  114. }
  115. String _parseError(Object e) {
  116. developer.log('FundPassword error: $e', name: 'FUND_PWD', error: e);
  117. if (e is DioException) {
  118. if (e.error is ApiException) {
  119. return (e.error as ApiException).message;
  120. }
  121. final responseData = e.response?.data;
  122. if (responseData is Map<String, dynamic>) {
  123. final msg = responseData['message'] as String? ??
  124. responseData['msg'] as String?;
  125. if (msg != null && msg.isNotEmpty) return msg;
  126. }
  127. return e.message ?? '网络请求异常';
  128. }
  129. if (e is ApiException) return e.message;
  130. return e.toString();
  131. }
  132. }
  133. // ── Provider ──────────────────────────────────────────────
  134. final fundPasswordProvider =
  135. NotifierProvider<FundPasswordNotifier, FundPasswordState>(
  136. FundPasswordNotifier.new,
  137. );