guru_sdk/guru_app/lib/account/account_service_extension.dart

275 lines
10 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/// Created by Haoyi on 6/3/21
part of "account_manager.dart";
extension AccountServiceExtension on AccountManager {
Future<bool> _restoreAccount(Account account) async {
AccountAuth? anonymousAuth;
GuruUser? guruUser = account.guruUser;
Log.d("restoreAccount $guruUser", tag: "Account");
try {
if (guruUser == null) {
anonymousAuth = await _retrieveAnonymous();
guruUser = anonymousAuth?.user;
}
} catch (error, stacktrace) {
Log.w("loginWith Anonymous error:$error, $stacktrace");
}
Log.v("_restoreAccount saasUser:$guruUser", tag: "Account");
final device = account.device;
if (device != null) {
_updateDevice(device);
}
final accountProfile = account.accountProfile;
if (accountProfile != null) {
_updateAccountProfile(accountProfile);
}
final credentials = account.credentials;
if (credentials.isNotEmpty) {
_restoreCredentials(credentials);
}
if (guruUser != null) {
await _updateGuruUser(guruUser);
await _verifyOrReportAuthDevice(guruUser);
await authenticateFirebase();
if (accountProfile != null) {
await _checkOrUploadAccountProfile(accountProfile);
}
if (anonymousAuth != null) {
final anonymousCredential = anonymousAuth.credential;
if (anonymousCredential != null) {
_bindCredential(anonymousCredential);
return await _invokeAnonymousLogin(anonymousAuth.user, anonymousCredential);
}
}
return true;
} else {
return false;
}
}
Future switchUser(GuruUser newUser) async {
/// 更新 login 的用户信息
_updateGuruUser(newUser);
try {
await _verifyOrReportAuthDevice(newUser);
// 登陆 firebase 不需要同步等待
authenticateFirebase();
} catch (error, stacktrace) {
Log.w("loginWithCredential error:$error, $stacktrace");
}
}
Future<bool> _switchAccount(Credential credential) async {
GuruUser? loginUser;
GuruUser? logoutUser;
/// 这里只调用接口获取对应的新用户信息,还没有做对应的绑定操作
try {
loginUser = await _loginGuruWithCredential(credential);
} catch (error, stacktrace) {
Log.w("loginWithCredential[${credential.authType}] error:$error, $stacktrace");
return false;
}
if (loginUser.isSame(accountDataStore.user)) {
Log.w("loginWithCredential same user!", tag: "Account");
_bindCredential(credential);
return false;
}
bool result = false;
/// logout 内部进行了拦截,因此这里总是会返回一个 logoutUser
/// logout传入 switch参数表示是一个切换帐号不需要真正的登出
/// 因为在下面的 SwitchAccount方法中会完成后续的过程
logoutUser = await logout(switching: true);
/// 如果这里没有返回出对应的退出用户,将认为退出失败
/// 因为进到 switchAccount 里肯定是非匿名登陆的帐号做登出操作
if (logoutUser != null) {
result = await GuruApp.instance.switchAccount(loginUser, credential, oldUser: logoutUser);
}
return result;
}
Future<bool> _processConflict(Credential credential) async {
final historicalSocialAuths = await AppProperty.getInstance().getHistoricalSocialAuths();
/// 如果是匿名登录,并且在这个设备上同样的用户没有绑定过其它的三方登陆凭证
/// 这种情况下,认定为新用户,中台会静默解决冲突,并且对应的数据库不会发生迁移
if (accountDataStore.isAnonymous && historicalSocialAuths.isEmpty) {
Log.d("associate conflict: _loginGuruWithCredential!");
final user = accountDataStore.user;
final oldUid = user?.uid ?? "";
if (user != null) {
await _invokeAnonymousLogout(user);
}
/// 因为这里是匿名登陆,因此在冲突的时候通过静默的方法切换账户
final guruUser = await _loginGuruWithCredential(credential);
/// 由于是冲突处理,此时的匿名帐号已经和当前新登陆的用户不能配对
/// 因此在新用户登陆成功后,这里需要将匿名帐户的凭证解绑,并清除匿名的密钥
/// 这样做的目的是为了在该帐号退出时,判断匿名帐号是否存在,
/// 如果不存在会创建一个新的匿名帐号,确保数据不被污染
await _unbindCredential(AuthType.anonymous);
/// 将新的用户进行关联,此时当前设备上只有一个登陆凭证
await processLogin(guruUser, credential);
GuruAnalytics.instance.logGuruEvent("switch_account", {
"auth": getAuthName(credential.authType),
"old_uid": oldUid,
"new_uid": guruUser.uid,
"silent": true
});
return true;
} else {
final canSwitch = await _invokeConflict();
if (canSwitch) {
return await _switchAccount(credential);
}
}
return false;
}
Future<DeviceTrack> _buildDevice(GuruUser saasUser) async {
final DeviceInfo? deviceInfo = await AppProperty.getInstance().getAccountDevice();
final firebasePushToken = await RemoteMessagingManager.instance.getToken();
if (firebasePushToken != null) {
final deviceId = await AppProperty.getInstance().getDeviceId();
final newDeviceInfo = await DeviceUtils.buildDeviceInfo(
deviceId: deviceId, firebasePushToken: firebasePushToken, uid: saasUser.uid);
return DeviceTrack(newDeviceInfo, deviceInfo);
}
return DeviceTrack(null, deviceInfo);
}
Future<AccountAuth?> _retrieveAnonymous() async {
final result = await AuthCredentialManager.instance.loginWith(AuthType.anonymous);
final credential = result.credential;
if (!result.isSuccess || credential == null) {
Log.w("_retrieveAnonymous error!", tag: "Account");
return null;
}
final user = await _requestGuruUser(credential);
return AccountAuth(user, credential: credential);
}
Future<GuruUser> _loginGuruWithCredential(Credential credential) async {
return await GuruApi.instance.loginGuruWithCredential(credential: credential);
}
Future<GuruUser> _associateCredential(Credential credential) async {
return await GuruApi.instance.associateCredential(credential: credential);
}
Future<GuruUser> _requestGuruUser(Credential credential) async {
//MetaData是匿名请求或者当前没有任何 GuruUser Id走signIn接口
if (!accountDataStore.hasUid || credential.isAnonymous) {
Log.d("_loginGuruWithCredential!", tag: "Account");
return await _loginGuruWithCredential(credential);
} else {
Log.d("_associateCredential!");
//当前有 GuruUser id并且MetaData是三方登录Token走associate接口不管已有的SaasUser是不是三方登录
return await _associateCredential(credential);
}
}
Future _verifyOrReportAuthDevice(GuruUser guruUser) async {
final deviceTrack = await _buildDevice(guruUser);
final latestReportDeviceTimestamp =
await AppProperty.getInstance().getLatestReportDeviceTimestamp();
final elapsedInterval = DateTimeUtils.currentTimeInMillis() - latestReportDeviceTimestamp;
final isChanged = (elapsedInterval > DateTimeUtils.sixHourInMillis) || deviceTrack.isChanged;
final reportDevice = deviceTrack.device;
final deviceId = deviceTrack.device?.deviceId ?? "";
if (deviceId.isNotEmpty) {
GuruAnalytics.instance.setDeviceId(deviceId);
}
if (isChanged && reportDevice?.isValid == true && guruUser.isValid == true) {
final result = await GuruApi.instance.reportDevice(reportDevice!).then((_) {
return true;
}).catchError((error) {
Log.i("reportDevice error:$error", tag: "Account");
return false;
});
if (result) {
reportDevice.dumpDevice(msg: "REPORT DEVICE SUCCESS");
_updateDevice(reportDevice);
AppProperty.getInstance()
.setLatestReportDeviceTimestamp(DateTimeUtils.currentTimeInMillis());
AppProperty.getInstance().setAccountDevice(reportDevice);
}
}
}
Future _checkOrUploadAccountProfile(AccountProfile accountProfile) async {
bool upload = accountProfile.dirty;
String? changedCountryCode = DeviceUtils.buildLocaleInfo().countryCode.toLowerCase();
if (DartExt.isBlank(changedCountryCode) || accountProfile.countryCode == changedCountryCode) {
changedCountryCode = null;
} else {
upload = true;
}
Log.d(
"_checkOrUploadAccountProfile dirty:${accountProfile.dirty} upload:$upload $changedCountryCode",
tag: "Account");
if (upload) {
await modifyProfile(countryCode: changedCountryCode);
}
}
void refreshFcmToken() {
final saasUser = accountDataStore.user;
if (saasUser != null) {
_verifyOrReportAuthDevice(saasUser);
}
}
void _updateDevice(DeviceInfo device) {
accountDataStore.updateDeviceInfo(device);
}
Future _bindCredential(Credential credential) async {
accountDataStore.bindCredential(credential);
/// 这里匿名帐号是不会保存凭证的,因为匿名帐号的登陆凭证是自生成的
if (credential.authType != AuthType.anonymous) {
await AppProperty.getInstance().saveCredential(credential);
}
}
Future _unbindCredential(AuthType authType) async {
accountDataStore.unbindCredential(authType);
if (authType != AuthType.anonymous) {
await AppProperty.getInstance().deleteCredential(authType);
} else {
await AppProperty.getInstance().clearAnonymousSecretKey();
}
}
void _restoreCredentials(Map<AuthType, Credential> credentials) {
accountDataStore.updateCredentials(credentials);
}
Future _updateGuruUser(GuruUser guruUser) async {
accountDataStore.updateGuruUser(guruUser);
await AppProperty.getInstance().setAccountGuruUser(guruUser);
await GuruAnalytics.instance.setUserId(guruUser.uid);
}
void _updateFirebaseUser(User user) {
accountDataStore.updateFirebaseUser(user);
}
void _updateAccountProfile(AccountProfile accountProfile) {
accountDataStore.updateAccountProfile(accountProfile);
}
}