import 'dart:async'; import 'package:get/get.dart'; import 'package:injectable/injectable.dart'; import 'package:location/base/app_base_request.dart'; import 'package:location/data/api/atmob_api.dart'; import 'package:location/data/api/request/login_request.dart'; import 'package:location/data/api/request/send_code_request.dart'; import 'package:location/data/bean/location_info.dart'; import 'package:location/data/bean/member_status_info.dart'; import 'package:location/data/bean/user_info.dart'; import 'package:location/data/consts/constants.dart'; import 'package:location/data/consts/error_code.dart'; import 'package:location/data/repositories/friends_repository.dart'; import 'package:location/data/repositories/message_repository.dart'; import 'package:location/data/repositories/phone_event_repository.dart'; import 'package:location/data/repositories/urgent_contact_repository.dart'; import 'package:location/di/get_it.dart'; import 'package:location/module/main/today_track_helper.dart'; import 'package:location/push_notification/ios_push_notification_service.dart'; import 'package:location/resource/string.gen.dart'; import 'package:location/sdk/gravity/gravity_helper.dart'; import 'package:location/socket/atmob_location_client.dart'; import 'package:location/utils/async_util.dart'; import 'package:location/utils/atmob_log.dart'; import 'package:location/utils/http_handler.dart'; import 'package:location/utils/mmkv_util.dart'; import '../../sdk/map/map_helper.dart'; import '../api/request/notification_report_request.dart'; import '../api/request/one_click_login_request.dart'; import '../api/request/user_avatar_update_request.dart'; import '../api/response/login_response.dart'; import '../api/response/member_status_response.dart'; @lazySingleton class AccountRepository { final AtmobApi atmobApi; final String tag = "AccountRepository"; static final String keyAccountLoginPhoneNum = 'key_account_login_phone_num'; static final String keyAccountLoginToken = 'key_account_login_token'; static final String keyAccountLoginUserId = 'key_account_login_user_id'; RxnString loginPhoneNum = RxnString(); RxBool isLogin = RxBool(false); Rxn memberStatusInfo = Rxn(); int? _lastRequestCodeTime; int _errorCodeTimes = 0; Timer? refreshMemberHandler; CancelableFuture? memberStatusFuture; static String? token = KVUtil.getString(keyAccountLoginToken, null); late final FriendsRepository friendsRepository; late final MessageRepository messageRepository; late final UrgentContactRepository urgentContactRepository; late final PhoneEventRepository phoneEventRepository; final Rx mineUserInfo = Rx(UserInfo( id: Constants.mineLocationId, phoneNumber: "我", isMine: true)); AccountRepository(this.atmobApi) { AtmobLog.d(tag, '$tag....init'); friendsRepository = FriendsRepository.getInstance(); messageRepository = MessageRepository.getInstance(); urgentContactRepository = UrgentContactRepository.getInstance(); phoneEventRepository = PhoneEventRepository.getInstance(); isLogin.bindStream( loginPhoneNum.map((value) { return value?.isNotEmpty == true; }), ); loginPhoneNum.value = KVUtil.getString(keyAccountLoginPhoneNum, null); refreshMemberStatus(); MapHelper.addLocationListener((location) { mineUserInfo.value.lastLocation.value = LocationInfo.fromMapLocation(location); }); } static AccountRepository getInstance() { return getIt.get(); } Future loginSendCode(String phoneNum) { final currentTime = DateTime.now().millisecondsSinceEpoch; // 检查是否在 60 秒内重复请求 if (currentTime - (_lastRequestCodeTime ?? 0) < 60 * 1000) { throw RequestCodeTooOftenException(); } return atmobApi .loginSendCode(SendCodeRequest(phoneNum)) .then(HttpHandler.handle(true)) .then((value) { _lastRequestCodeTime = currentTime; _errorCodeTimes = 0; }); } Future loginUserLogin( String phoneNum, String verificationCode) { if (_errorCodeTimes >= 5) { return Future.error(LoginTooOftenException()); } return atmobApi .loginUserLogin(LoginRequest(phoneNum, verificationCode)) .then(HttpHandler.handle(true)) .then((response) { _errorCodeTimes = 0; onLoginSuccess(phoneNum, response.authToken); return response; }).catchError((error) { if (error is ServerErrorException && error.code == ErrorCode.verificationCodeError) { _errorCodeTimes++; } throw error; }); } void onLoginSuccess(String phoneNum, String authToken) { AccountRepository.token = authToken; loginPhoneNum.value = phoneNum; KVUtil.putString(keyAccountLoginPhoneNum, phoneNum); KVUtil.putString(keyAccountLoginToken, authToken); AtmobLocationClient.connectWebSocket(); refreshMemberStatus(); friendsRepository.refreshFriends(); messageRepository.refreshFriendWaitingCount(); messageRepository.refreshUnreadMessage(); urgentContactRepository.requestUrgentContactList(); //上传推送token onRequestNotificationReport(); //上报事件 phoneEventRepository.startReportPhoneEvent(); TodayTrackHelper.getInstance().clear(); GravityHelper.onLogin(); } void logout() { token = null; refreshMemberHandler?.cancel(); AtmobLocationClient.disConnectWebSocket(); KVUtil.putString(keyAccountLoginPhoneNum, null); KVUtil.putString(keyAccountLoginToken, null); KVUtil.putString(keyAccountLoginUserId, null); KVUtil.putString(Constants.keyLastSelectFriendId, null); loginPhoneNum.value = null; memberStatusInfo.value = null; friendsRepository.clearFriends(); messageRepository.clearMessage(); urgentContactRepository.clearContactList(); phoneEventRepository.stopReportPhoneEvent(); GravityHelper.onLogout(); } void refreshMemberStatus() { memberStatusFuture?.cancel(); memberStatusFuture = AsyncUtil.retryWithExponentialBackoff( () => getMemberStatus(), 10, predicate: (error) { if (error is ServerErrorException) { return error.code != ErrorCode.noLoginError; } return true; }); memberStatusFuture?.then((data) { AtmobLog.d(tag, "getMemberStatus success: ${memberStatusInfo.value}"); }).catchError((error) { AtmobLog.e(tag, "getMemberStatus error: $error"); }); } Future getMemberStatus() { return atmobApi .getMemberStatus(AppBaseRequest()) .then(HttpHandler.handle(false)) .then((response) { refreshMemberHandler?.cancel(); updateAvatar(response.avatar); KVUtil.putString(keyAccountLoginUserId, response.deviceId); if (!response.permanent && !response.expired) { refreshMemberHandler = Timer( Duration( milliseconds: response.endTimestamp - response.serverTimestamp), () => refreshMemberStatus()); } return response; }).then((response) { MemberStatusInfo memberStatusInfo = MemberStatusInfo( level: response.level, endTimestamp: response.endTimestamp, expired: response.expired, permanent: response.permanent, trialed: response.trialed, avatar: response.avatar, trialEndTimestamp: response.trialEndTimestamp); this.memberStatusInfo.value = memberStatusInfo; return response; }); } void updateAvatar(String? avatar) { mineUserInfo.value.avatar = avatar; mineUserInfo.refresh(); } Future userAvatarUpdate(String avatar) { if (avatar == mineUserInfo.value.avatar) { return Future.value(true); } return atmobApi .userAvatarUpdate(UserAvatarUpdateRequest(avatar)) .then(HttpHandler.handle(true)) .then((_) { updateAvatar(avatar); AtmobLog.d(tag, "userAvatarUpdate success: $avatar"); return true; }); } Future oneClickLogin( {required String token, required String accessToken}) { return atmobApi .oneClickLogin(OneClickLoginRequest(token, accessToken)) .then(HttpHandler.handle(false)) .then((response) { onLoginSuccess(response.phone ?? '', response.authToken); }); } bool memberIsExpired() { return memberStatusInfo.value == null || memberStatusInfo.value?.expired == true; } Future userClear() { return atmobApi.userClear(AppBaseRequest()).then(HttpHandler.handle(true)); } ///请求推送的数据 Future onRequestNotificationReport() async { // 初始化推送服务 var tokenStr = await IosPushNotificationService.getDeviceToken(); print("tokenStrsfsdf---${tokenStr}"); return atmobApi .notificationReport( NotificationReportRequest(deviceToken: tokenStr as String)) .then(HttpHandler.handle(false)) .then((response) {}) .catchError((_) {}); } } class RequestCodeTooOftenException implements Exception { final String message; /// 可选的构造函数,支持自定义错误信息 RequestCodeTooOftenException([this.message = '请求验证码过于频繁']); @override String toString() => message; } class LoginTooOftenException implements Exception { final String message; /// 可选的构造函数,支持自定义错误信息 LoginTooOftenException([this.message = '登录过于频繁']); @override String toString() => message; }