import 'dart:async'; import 'package:get/get.dart'; import 'package:injectable/injectable.dart'; import 'package:keyboard/data/api/atmob_api.dart'; import 'package:keyboard/data/api/request/user_info_setting_request.dart'; import 'package:keyboard/data/bean/member_info.dart'; import '../../base/app_base_request.dart'; import '../../di/get_it.dart'; import '../../utils/async_util.dart'; import '../../utils/atmob_log.dart'; import '../../utils/http_handler.dart'; import '../../utils/mmkv_util.dart'; import '../api/request/complaint_submit_request.dart'; import '../api/request/login_request.dart'; import '../api/request/send_code_request.dart'; import '../api/response/login_response.dart'; import '../api/response/user_info_response.dart'; import '../consts/error_code.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'; final Rxn _userInfo = Rxn(); Rxn get userInfo => _userInfo; RxnString loginPhoneNum = RxnString(); RxBool isLogin = RxBool(false); Rxn memberStatusInfo = Rxn(); RxBool isMember = RxBool(false); int? _lastRequestCodeTime; int _errorCodeTimes = 0; Timer? refreshUserInfoHandler; CancelableFuture? userInfoFuture; static String? token = KVUtil.getString(keyAccountLoginToken, null); AccountRepository(this.atmobApi) { AtmobLog.d(tag, '$tag....init'); isLogin.bindStream( loginPhoneNum.map((value) { return value?.isNotEmpty == true; }), ); loginPhoneNum.value = KVUtil.getString(keyAccountLoginPhoneNum, null); refreshUserInfo(); } // 检查是否在 60 秒内重复请求 Future loginSendCode(String phoneNum) { final currentTime = DateTime.now().millisecondsSinceEpoch; 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) { throw 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 refreshUserInfo() { userInfoFuture?.cancel(); userInfoFuture = AsyncUtil.retryWithExponentialBackoff( () => getUserInfo(), 10, predicate: (error) { if (error is ServerErrorException) { return error.code != ErrorCode.noLoginError; } return true; }, ); userInfoFuture ?.then((userInfo) { AtmobLog.d(tag, "refreshUserInfo success: ${memberStatusInfo.value}"); }) .catchError((error) { AtmobLog.e(tag, "refreshUserInfo error: $error"); }); } Future getUserInfo() { return atmobApi .getUserInfo(AppBaseRequest()) .then(HttpHandler.handle(true)) .then((response) { _userInfo.value = response; memberStatusInfo.value = response.memberInfo; isMember.value = response.memberInfo?.isMember == true; return response; }); } Future setUserInfo({ String? name, String? birthday, int? gender, String? imageUrl, List? hobbies, List? characters, }) { return atmobApi .setUserInfo( UserInfoSettingRequest( name: name, birthday: birthday, gender: gender, imageUrl: imageUrl, hobbies: hobbies, characters: characters, ), ) .then(HttpHandler.handle(true)); } void onLoginSuccess(String phoneNum, String authToken) { AccountRepository.token = authToken; loginPhoneNum.value = phoneNum; refreshUserInfo(); KVUtil.putString(keyAccountLoginPhoneNum, phoneNum); KVUtil.putString(keyAccountLoginToken, authToken); } void logout() { token = null; KVUtil.putString(keyAccountLoginPhoneNum, null); KVUtil.putString(keyAccountLoginToken, null); memberStatusInfo.value = null; isMember.value = false; loginPhoneNum.value = null; } // 意见反馈 Future complaintSubmit(String? phone, String content) { return atmobApi .complaintSubmit(ComplaintSubmitRequest(phone, content)) .then(HttpHandler.handle(true)); } static AccountRepository getInstance() { return getIt.get(); } } 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; }