|
...
|
...
|
@@ -8,27 +8,26 @@ import 'package:doublefeel_flutter/app/routes/app_pages.dart'; |
|
|
|
import 'package:doublefeel_flutter/core/config/app_environment_config.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/constants/app_const.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/logging/app_logger.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/services/app_config_service.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
|
|
|
|
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
|
|
|
|
import 'package:doublefeel_flutter/pigeon/wechat_api.g.dart';
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/util/app_toast.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/network/api/user_api.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/network/api/vip_api.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/platform/pigeon_api_facade.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/result/app_result.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/services/app_config_service.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/services/loading_service.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/services/user_state_service.dart';
|
|
|
|
import 'package:doublefeel_flutter/core/util/app_toast.dart';
|
|
|
|
import 'package:doublefeel_flutter/data/local/local_storage.dart';
|
|
|
|
import 'package:doublefeel_flutter/data/local/user_account_storage.dart';
|
|
|
|
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
|
|
|
|
import 'package:doublefeel_flutter/data/models/local/user_preferences.dart';
|
|
|
|
import 'package:doublefeel_flutter/data/models/user/user_models.dart';
|
|
|
|
import 'package:doublefeel_flutter/data/models/vip/vip_info.dart';
|
|
|
|
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
|
|
|
|
import 'package:doublefeel_flutter/pigeon/wechat_api.g.dart';
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
import 'package:flutter_timezone/flutter_timezone.dart';
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
|
|
|
|
class LoginController extends GetxController {
|
|
|
|
final UserApi _userApi = Get.find<UserApi>();
|
|
...
|
...
|
@@ -370,21 +369,39 @@ class LoginController extends GetxController { |
|
|
|
}
|
|
|
|
|
|
|
|
isLoggingIn.value = true;
|
|
|
|
final loginStopwatch = Stopwatch()..start();
|
|
|
|
_logLoginTiming('phone_login.start');
|
|
|
|
try {
|
|
|
|
await LoadingService.instance.run(() async {
|
|
|
|
final loginRes = await _userApi.login(
|
|
|
|
telephone: cleanPhone,
|
|
|
|
verifyCode: codeInput.value,
|
|
|
|
final loginRes = await _measureLoginStep(
|
|
|
|
'phone_login.api',
|
|
|
|
() => _userApi.login(
|
|
|
|
telephone: cleanPhone,
|
|
|
|
verifyCode: codeInput.value,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
if (loginRes is AppSuccess<LoginResponse>) {
|
|
|
|
await _handleLoginSuccess(loginRes.data);
|
|
|
|
await Get.find<LocalStorage>().setLastLoginMethod('phone');
|
|
|
|
await environmentConfig.updateHideChooseRegion(true);
|
|
|
|
await _measureLoginStep(
|
|
|
|
'phone_login.handle_success',
|
|
|
|
() => _handleLoginSuccess(loginRes.data),
|
|
|
|
);
|
|
|
|
await _measureLoginStep(
|
|
|
|
'phone_login.persist_login_method',
|
|
|
|
() => Get.find<LocalStorage>().setLastLoginMethod('phone'),
|
|
|
|
);
|
|
|
|
await _measureLoginStep(
|
|
|
|
'phone_login.persist_region_choice',
|
|
|
|
() => environmentConfig.updateHideChooseRegion(true),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} finally {
|
|
|
|
isLoggingIn.value = false;
|
|
|
|
loginStopwatch.stop();
|
|
|
|
_logLoginTiming(
|
|
|
|
'phone_login.finish total_ms=${loginStopwatch.elapsedMilliseconds}',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
...
|
...
|
@@ -585,7 +602,10 @@ class LoginController extends GetxController { |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
final regRes = await _userApi.register(registerData);
|
|
|
|
final regRes = await _measureLoginStep(
|
|
|
|
'login_success.register',
|
|
|
|
() => _userApi.register(registerData),
|
|
|
|
);
|
|
|
|
if (regRes is AppSuccess<RegisterResponse>) {
|
|
|
|
accessToken = regRes.data.accessToken;
|
|
|
|
} else {
|
|
...
|
...
|
@@ -595,12 +615,21 @@ class LoginController extends GetxController { |
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch user info and VIP info
|
|
|
|
final userInfoResFuture = _userApi.getUserInfo(accessToken: accessToken);
|
|
|
|
final userInfoResFuture = _measureLoginStep(
|
|
|
|
'login_success.get_user_info',
|
|
|
|
() => _userApi.getUserInfo(accessToken: accessToken),
|
|
|
|
);
|
|
|
|
final vipInfoResFuture = isRegister
|
|
|
|
? Future.value(null)
|
|
|
|
: _vipApi.getVipInfo(accessToken: accessToken);
|
|
|
|
: _measureLoginStep(
|
|
|
|
'login_success.get_vip_info',
|
|
|
|
() => _vipApi.getVipInfo(accessToken: accessToken),
|
|
|
|
);
|
|
|
|
|
|
|
|
final results = await Future.wait([userInfoResFuture, vipInfoResFuture]);
|
|
|
|
final results = await _measureLoginStep(
|
|
|
|
'login_success.wait_user_and_vip',
|
|
|
|
() => Future.wait([userInfoResFuture, vipInfoResFuture]),
|
|
|
|
);
|
|
|
|
final userInfoRes = results[0];
|
|
|
|
final vipInfoRes = results[1];
|
|
|
|
|
|
...
|
...
|
@@ -621,51 +650,36 @@ class LoginController extends GetxController { |
|
|
|
vip = vipInfoRes.data;
|
|
|
|
}
|
|
|
|
|
|
|
|
await _userPrefs.updateFromLogin(
|
|
|
|
accessToken: accessToken,
|
|
|
|
me: me,
|
|
|
|
partner: partner,
|
|
|
|
vip: vip,
|
|
|
|
await _measureLoginStep(
|
|
|
|
'login_success.persist_user_preferences',
|
|
|
|
() => _userPrefs.updateFromLogin(
|
|
|
|
accessToken: accessToken,
|
|
|
|
me: me,
|
|
|
|
partner: partner,
|
|
|
|
vip: vip,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (Get.isRegistered<AppEnvironmentConfig>()) {
|
|
|
|
var env = Get.find<AppEnvironmentConfig>();
|
|
|
|
unawaited(
|
|
|
|
AppPlatformHostApi()
|
|
|
|
.updateLoginInfo(
|
|
|
|
jsonEncode(UserPreferences(
|
|
|
|
accessToken: accessToken,
|
|
|
|
meUserInfo: me,
|
|
|
|
partnerUserInfo: partner,
|
|
|
|
vipInfo: vip != null
|
|
|
|
? UserPreferencesVipInfo.fromVipInfo(vip)
|
|
|
|
: null,
|
|
|
|
)),
|
|
|
|
env.serverBaseUrl,
|
|
|
|
)
|
|
|
|
.catchError((Object error, StackTrace stackTrace) {
|
|
|
|
AppLogger.e('Native login info sync failed', error, stackTrace);
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} on Exception catch (e) {
|
|
|
|
AppLogger.e(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
await _userStateService.onLogin();
|
|
|
|
await _measureLoginStep(
|
|
|
|
'login_success.user_state_on_login',
|
|
|
|
_userStateService.onLogin,
|
|
|
|
);
|
|
|
|
if (isRegister) {
|
|
|
|
ta.track('success_regist');
|
|
|
|
}
|
|
|
|
|
|
|
|
// 用户登录成功即代表同意了协议,持久化到本地供冷启动时 SDK 初始化判断使用
|
|
|
|
await Get.find<LocalStorage>().setTermsAgreed(true);
|
|
|
|
await _measureLoginStep(
|
|
|
|
'login_success.persist_terms_agreed',
|
|
|
|
() => Get.find<LocalStorage>().setTermsAgreed(true),
|
|
|
|
);
|
|
|
|
|
|
|
|
// 根据引导完成状态决定跳转目标
|
|
|
|
final userId = me.id ?? 0;
|
|
|
|
if (_userAccount.hasCompletedOnboarding(userId) ||
|
|
|
|
me.isPassNoviceGuide == 1) {
|
|
|
|
// 该账号已完成引导,直接进主页
|
|
|
|
_logLoginTiming('login_success.route_home');
|
|
|
|
Get.offAllNamed(AppRoutes.home);
|
|
|
|
} else {
|
|
|
|
// 新用户或未完成引导,进入引导页(支持断点续做)
|
|
...
|
...
|
@@ -679,4 +693,30 @@ class LoginController extends GetxController { |
|
|
|
// userInfoRes 失败,外层 finally 会重置 isLoggingIn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<T> _measureLoginStep<T>(
|
|
|
|
String name,
|
|
|
|
Future<T> Function() operation,
|
|
|
|
) async {
|
|
|
|
if (!environmentConfig.isDebug) return operation();
|
|
|
|
final stopwatch = Stopwatch()..start();
|
|
|
|
AppLogger.i('[LoginTiming] $name.start');
|
|
|
|
try {
|
|
|
|
return await operation();
|
|
|
|
} catch (error, stackTrace) {
|
|
|
|
AppLogger.e('[LoginTiming] $name.error', error, stackTrace);
|
|
|
|
rethrow;
|
|
|
|
} finally {
|
|
|
|
stopwatch.stop();
|
|
|
|
AppLogger.i(
|
|
|
|
'[LoginTiming] $name.finish elapsed_ms=${stopwatch.elapsedMilliseconds}',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _logLoginTiming(String message) {
|
|
|
|
if (environmentConfig.isDebug) {
|
|
|
|
AppLogger.i('[LoginTiming] $message');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} |
...
|
...
|
|