account_repository.dart 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import 'dart:async';
  2. import 'package:get/get.dart';
  3. import 'package:injectable/injectable.dart';
  4. import 'package:keyboard/data/api/atmob_api.dart';
  5. import 'package:keyboard/data/api/request/user_info_setting_request.dart';
  6. import 'package:keyboard/data/bean/member_info.dart';
  7. import '../../base/app_base_request.dart';
  8. import '../../di/get_it.dart';
  9. import '../../utils/async_util.dart';
  10. import '../../utils/atmob_log.dart';
  11. import '../../utils/http_handler.dart';
  12. import '../../utils/mmkv_util.dart';
  13. import '../api/request/complaint_submit_request.dart';
  14. import '../api/request/login_request.dart';
  15. import '../api/request/send_code_request.dart';
  16. import '../api/response/login_response.dart';
  17. import '../api/response/user_info_response.dart';
  18. import '../consts/error_code.dart';
  19. @lazySingleton
  20. class AccountRepository {
  21. final AtmobApi atmobApi;
  22. final String tag = "AccountRepository";
  23. static final String keyAccountLoginPhoneNum = 'key_account_login_phone_num';
  24. static final String keyAccountLoginToken = 'key_account_login_token';
  25. final Rxn<UserInfoResponse> _userInfo = Rxn<UserInfoResponse>();
  26. Rxn<UserInfoResponse> get userInfo => _userInfo;
  27. RxnString loginPhoneNum = RxnString();
  28. RxBool isLogin = RxBool(false);
  29. Rxn<MemberInfo> memberStatusInfo = Rxn<MemberInfo>();
  30. int? _lastRequestCodeTime;
  31. int _errorCodeTimes = 0;
  32. Timer? refreshUserInfoHandler;
  33. CancelableFuture? userInfoFuture;
  34. static String? token = KVUtil.getString(keyAccountLoginToken, null);
  35. AccountRepository(this.atmobApi) {
  36. AtmobLog.d(tag, '$tag....init');
  37. isLogin.bindStream(
  38. loginPhoneNum.map((value) {
  39. return value?.isNotEmpty == true;
  40. }),
  41. );
  42. loginPhoneNum.value = KVUtil.getString(keyAccountLoginPhoneNum, null);
  43. refreshUserInfo();
  44. }
  45. // 检查是否在 60 秒内重复请求
  46. Future<void> loginSendCode(String phoneNum) {
  47. final currentTime = DateTime.now().millisecondsSinceEpoch;
  48. if (currentTime - (_lastRequestCodeTime ?? 0) < 60 * 1000) {
  49. throw RequestCodeTooOftenException();
  50. }
  51. return atmobApi
  52. .loginSendCode(SendCodeRequest(phoneNum))
  53. .then(HttpHandler.handle(true))
  54. .then((value) {
  55. _lastRequestCodeTime = currentTime;
  56. _errorCodeTimes = 0;
  57. });
  58. }
  59. Future<LoginResponse> loginUserLogin(
  60. String phoneNum,
  61. String verificationCode,
  62. ) {
  63. if (_errorCodeTimes >= 5) {
  64. throw LoginTooOftenException();
  65. }
  66. return atmobApi
  67. .loginUserLogin(LoginRequest(phoneNum, verificationCode))
  68. .then(HttpHandler.handle(true))
  69. .then((response) {
  70. _errorCodeTimes = 0;
  71. onLoginSuccess(phoneNum, response.authToken);
  72. return response;
  73. })
  74. .catchError((error) {
  75. if (error is ServerErrorException &&
  76. error.code == ErrorCode.verificationCodeError) {
  77. _errorCodeTimes++;
  78. }
  79. throw error;
  80. });
  81. }
  82. void refreshUserInfo() {
  83. userInfoFuture?.cancel();
  84. userInfoFuture = AsyncUtil.retryWithExponentialBackoff(
  85. () => getUserInfo(),
  86. 10,
  87. predicate: (error) {
  88. if (error is ServerErrorException) {
  89. return error.code != ErrorCode.noLoginError;
  90. }
  91. return true;
  92. },
  93. );
  94. userInfoFuture
  95. ?.then((userInfo) {
  96. AtmobLog.d(tag, "refreshUserInfo success: ${memberStatusInfo.value}");
  97. })
  98. .catchError((error) {
  99. AtmobLog.e(tag, "refreshUserInfo error: $error");
  100. });
  101. }
  102. Future<UserInfoResponse> getUserInfo() {
  103. return atmobApi
  104. .getUserInfo(AppBaseRequest())
  105. .then(HttpHandler.handle(true))
  106. .then((response) {
  107. _userInfo.value = response;
  108. memberStatusInfo.value = response.memberInfo;
  109. return response;
  110. });
  111. }
  112. Future<void> setUserInfo({
  113. String? name,
  114. String? birthday,
  115. int? gender,
  116. String? imageUrl,
  117. List<String>? hobbies,
  118. List<String>? characters,
  119. }) {
  120. return atmobApi
  121. .setUserInfo(
  122. UserInfoSettingRequest(
  123. name: name,
  124. birthday: birthday,
  125. gender: gender,
  126. imageUrl: imageUrl,
  127. hobbies: hobbies,
  128. characters: characters,
  129. ),
  130. )
  131. .then(HttpHandler.handle(true));
  132. }
  133. void onLoginSuccess(String phoneNum, String authToken) {
  134. AccountRepository.token = authToken;
  135. loginPhoneNum.value = phoneNum;
  136. refreshUserInfo();
  137. KVUtil.putString(keyAccountLoginPhoneNum, phoneNum);
  138. KVUtil.putString(keyAccountLoginToken, authToken);
  139. }
  140. void logout() {
  141. token = null;
  142. KVUtil.putString(keyAccountLoginPhoneNum, null);
  143. KVUtil.putString(keyAccountLoginToken, null);
  144. memberStatusInfo.value = null;
  145. loginPhoneNum.value = null;
  146. }
  147. // 意见反馈
  148. Future<void> complaintSubmit(String? phone, String content) {
  149. return atmobApi
  150. .complaintSubmit(ComplaintSubmitRequest(phone, content))
  151. .then(HttpHandler.handle(true));
  152. }
  153. static AccountRepository getInstance() {
  154. return getIt.get<AccountRepository>();
  155. }
  156. }
  157. class RequestCodeTooOftenException implements Exception {
  158. final String message;
  159. /// 可选的构造函数,支持自定义错误信息
  160. RequestCodeTooOftenException([this.message = '请求验证码过于频繁']);
  161. @override
  162. String toString() => message;
  163. }
  164. class LoginTooOftenException implements Exception {
  165. final String message;
  166. /// 可选的构造函数,支持自定义错误信息
  167. LoginTooOftenException([this.message = '登录过于频繁']);
  168. @override
  169. String toString() => message;
  170. }