account_repository.dart 9.6 KB

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