account_repository.dart 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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/data/repositories/message_repository.dart';
  15. import 'package:location/data/repositories/urgent_contact_repository.dart';
  16. import 'package:location/di/get_it.dart';
  17. import 'package:location/push_notification/ios_push_notification_service.dart';
  18. import 'package:location/resource/string.gen.dart';
  19. import 'package:location/socket/atmob_location_client.dart';
  20. import 'package:location/utils/async_util.dart';
  21. import 'package:location/utils/atmob_log.dart';
  22. import 'package:location/utils/http_handler.dart';
  23. import 'package:location/utils/mmkv_util.dart';
  24. import '../../sdk/map/map_helper.dart';
  25. import '../api/request/notification_report_request.dart';
  26. import '../api/request/user_avatar_update_request.dart';
  27. import '../api/response/login_response.dart';
  28. import '../api/response/member_status_response.dart';
  29. @lazySingleton
  30. class AccountRepository {
  31. final AtmobApi atmobApi;
  32. final String tag = "AccountRepository";
  33. static final String keyAccountLoginPhoneNum = 'key_account_login_phone_num';
  34. static final String keyAccountLoginToken = 'key_account_login_token';
  35. static final String keyAccountLoginUserId = 'key_account_login_user_id';
  36. RxnString loginPhoneNum = RxnString();
  37. RxBool isLogin = RxBool(false);
  38. Rxn<MemberStatusInfo> memberStatusInfo = Rxn<MemberStatusInfo>();
  39. int? _lastRequestCodeTime;
  40. int _errorCodeTimes = 0;
  41. Timer? refreshMemberHandler;
  42. CancelableFuture? memberStatusFuture;
  43. static String? token = KVUtil.getString(keyAccountLoginToken, null);
  44. late final FriendsRepository friendsRepository;
  45. late final MessageRepository messageRepository;
  46. late final UrgentContactRepository urgentContactRepository;
  47. final Rx<UserInfo> mineUserInfo = Rx<UserInfo>(UserInfo(
  48. id: Constants.mineLocationId,
  49. phoneNumber: StringName.locationMine,
  50. isMine: true));
  51. AccountRepository(this.atmobApi) {
  52. AtmobLog.d(tag, '$tag....init');
  53. friendsRepository = FriendsRepository.getInstance();
  54. messageRepository = MessageRepository.getInstance();
  55. urgentContactRepository = UrgentContactRepository.getInstance();
  56. isLogin.bindStream(
  57. loginPhoneNum.map((value) {
  58. return value?.isNotEmpty == true;
  59. }),
  60. );
  61. loginPhoneNum.value = KVUtil.getString(keyAccountLoginPhoneNum, null);
  62. refreshMemberStatus();
  63. MapHelper.addLocationListener((location) {
  64. mineUserInfo.value.lastLocation.value =
  65. LocationInfo.fromMapLocation(location);
  66. });
  67. }
  68. static AccountRepository getInstance() {
  69. return getIt.get<AccountRepository>();
  70. }
  71. Future<void> loginSendCode(String phoneNum) {
  72. final currentTime = DateTime.now().millisecondsSinceEpoch;
  73. // 检查是否在 60 秒内重复请求
  74. if (currentTime - (_lastRequestCodeTime ?? 0) < 60 * 1000) {
  75. throw RequestCodeTooOftenException();
  76. }
  77. return atmobApi
  78. .loginSendCode(SendCodeRequest(phoneNum))
  79. .then(HttpHandler.handle(true))
  80. .then((value) {
  81. _lastRequestCodeTime = currentTime;
  82. _errorCodeTimes = 0;
  83. });
  84. }
  85. Future<LoginResponse> loginUserLogin(
  86. String phoneNum, String verificationCode) {
  87. if (_errorCodeTimes >= 5) {
  88. return Future.error(LoginTooOftenException());
  89. }
  90. return atmobApi
  91. .loginUserLogin(LoginRequest(phoneNum, verificationCode))
  92. .then(HttpHandler.handle(true))
  93. .then((response) {
  94. _errorCodeTimes = 0;
  95. onLoginSuccess(phoneNum, response.authToken);
  96. return response;
  97. }).catchError((error) {
  98. if (error is ServerErrorException &&
  99. error.code == ErrorCode.verificationCodeError) {
  100. _errorCodeTimes++;
  101. }
  102. throw error;
  103. });
  104. }
  105. void onLoginSuccess(String phoneNum, String authToken) {
  106. AccountRepository.token = authToken;
  107. loginPhoneNum.value = phoneNum;
  108. KVUtil.putString(keyAccountLoginPhoneNum, phoneNum);
  109. KVUtil.putString(keyAccountLoginToken, authToken);
  110. AtmobLocationClient.connectWebSocket();
  111. refreshMemberStatus();
  112. friendsRepository.refreshFriends();
  113. messageRepository.refreshFriendWaitingCount();
  114. messageRepository.refreshUnreadMessage();
  115. urgentContactRepository.requestUrgentContactList();
  116. //上传推送toekn
  117. onRequestNotificationReport();
  118. }
  119. void logout() {
  120. token = null;
  121. refreshMemberHandler?.cancel();
  122. AtmobLocationClient.disConnectWebSocket();
  123. KVUtil.putString(keyAccountLoginPhoneNum, null);
  124. KVUtil.putString(keyAccountLoginToken, null);
  125. KVUtil.putString(keyAccountLoginUserId, null);
  126. KVUtil.putString(Constants.keyLastSelectFriendId, null);
  127. loginPhoneNum.value = null;
  128. memberStatusInfo.value = null;
  129. friendsRepository.clearFriends();
  130. messageRepository.clearMessage();
  131. urgentContactRepository.clearContactList();
  132. }
  133. void refreshMemberStatus() {
  134. memberStatusFuture?.cancel();
  135. memberStatusFuture = AsyncUtil.retryWithExponentialBackoff(
  136. () => getMemberStatus(), 10, predicate: (error) {
  137. if (error is ServerErrorException) {
  138. return error.code != ErrorCode.noLoginError;
  139. }
  140. return true;
  141. });
  142. memberStatusFuture?.then((data) {
  143. AtmobLog.d(tag, "getMemberStatus success: ${memberStatusInfo.value}");
  144. }).catchError((error) {
  145. AtmobLog.e(tag, "getMemberStatus error: $error");
  146. });
  147. }
  148. Future<MemberStatusResponse> getMemberStatus() {
  149. return atmobApi
  150. .getMemberStatus(AppBaseRequest())
  151. .then(HttpHandler.handle(false))
  152. .then((response) {
  153. refreshMemberHandler?.cancel();
  154. updateAvatar(response.avatar);
  155. KVUtil.putString(keyAccountLoginUserId, response.deviceId);
  156. if (!response.permanent && !response.expired) {
  157. refreshMemberHandler = Timer(
  158. Duration(
  159. milliseconds: response.endTimestamp - response.serverTimestamp),
  160. () => refreshMemberStatus());
  161. }
  162. return response;
  163. }).then((response) {
  164. MemberStatusInfo memberStatusInfo = MemberStatusInfo(
  165. level: response.level,
  166. endTimestamp: response.endTimestamp,
  167. expired: response.expired,
  168. permanent: response.permanent,
  169. trialed: response.trialed,
  170. avatar: response.avatar,
  171. trialEndTimestamp: response.trialEndTimestamp
  172. );
  173. this.memberStatusInfo.value = memberStatusInfo;
  174. return response;
  175. });
  176. }
  177. void updateAvatar(String? avatar) {
  178. mineUserInfo.value.avatar = avatar;
  179. mineUserInfo.refresh();
  180. }
  181. Future<bool> userAvatarUpdate(String avatar) {
  182. if (avatar == mineUserInfo.value.avatar) {
  183. return Future.value(true);
  184. }
  185. return atmobApi
  186. .userAvatarUpdate(UserAvatarUpdateRequest(avatar))
  187. .then(HttpHandler.handle(true))
  188. .then((_) {
  189. updateAvatar(avatar);
  190. AtmobLog.d(tag, "userAvatarUpdate success: $avatar");
  191. return true;
  192. });
  193. }
  194. bool memberIsExpired() {
  195. return memberStatusInfo.value == null ||
  196. memberStatusInfo.value?.expired == true;
  197. }
  198. Future<void> userClear() {
  199. return atmobApi.userClear(AppBaseRequest()).then(HttpHandler.handle(true));
  200. }
  201. ///请求推送的数据
  202. Future<void> onRequestNotificationReport() async {
  203. // 初始化推送服务
  204. var tokenStr = await IosPushNotificationService.getDeviceToken();
  205. print("tokenStrsfsdf---${tokenStr}");
  206. return atmobApi
  207. .notificationReport(NotificationReportRequest(deviceToken: tokenStr as String))
  208. .then(HttpHandler.handle(false))
  209. .then((response) {})
  210. .catchError((_) {});
  211. }
  212. }
  213. class RequestCodeTooOftenException implements Exception {
  214. final String message;
  215. /// 可选的构造函数,支持自定义错误信息
  216. RequestCodeTooOftenException([this.message = '请求验证码过于频繁']);
  217. @override
  218. String toString() => message;
  219. }
  220. class LoginTooOftenException implements Exception {
  221. final String message;
  222. /// 可选的构造函数,支持自定义错误信息
  223. LoginTooOftenException([this.message = '登录过于频繁']);
  224. @override
  225. String toString() => message;
  226. }