account_repository.dart 6.2 KB

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