feat: 1.调整项目初始化,不用等待site/config接口,仅在hi_user_info中调用;将site_config_service拆成loadDomain和获取配置,在splash中只做loadDomain

2. 优化接口拦截的解密日志
3. 调整获取订阅到kr_home,与源版一致
4. 修复闪连抖动问题,增加套餐有效期判断
This commit is contained in:
2026-01-13 18:07:11 -08:00
parent 1d2a89a80a
commit ccf9a76de9
22 changed files with 737 additions and 1379 deletions
File diff suppressed because it is too large Load Diff
+147 -126
View File
@@ -52,9 +52,6 @@ class KRAppRunData {
/// 佣金
final RxInt kr_commission = 0.obs;
/// 登录类型
KRLoginType? kr_loginType;
/// 设备ID
String? deviceId;
@@ -72,6 +69,14 @@ class KRAppRunData {
return _instance;
}
DateTime _stepStartTime = DateTime.now();
void _logStepTiming(String stepName) {
final now = DateTime.now();
final stepMs = now.difference(_stepStartTime).inMilliseconds;
KRLogUtil.kr_i('[SPLASH_TIMING] ⏱️ $stepName 耗时: ${stepMs}ms',
tag: 'AppRunData');
_stepStartTime = now;
}
/// 判断是否是设备登录(游客模式)
bool isDeviceLogin() {
@@ -87,7 +92,8 @@ class KRAppRunData {
// JWT格式检查: header.payload.signature (三段,每段用.分隔)
final parts = token.split('.');
if (parts.length != 3) {
KRLogUtil.kr_w('❌ Token格式无效:分段数不对 (${parts.length} != 3)', tag: 'AppRunData');
KRLogUtil.kr_w('❌ Token格式无效:分段数不对 (${parts.length} != 3)',
tag: 'AppRunData');
return false;
}
@@ -185,8 +191,7 @@ class KRAppRunData {
Future<void> kr_saveUserInfo(
String token,
String account,
KRLoginType loginType,
String? areaCode) async {
) async {
KRLogUtil.kr_i('开始保存用户信息', tag: 'AppRunData');
try {
@@ -197,19 +202,15 @@ class KRAppRunData {
// 更新内存中的数据
kr_token = token;
kr_account.value = accountText;
kr_loginType = loginType;
kr_areaCode = areaCode;
final Map<String, dynamic> userInfo = {
'token': token,
'account': accountText,
'loginType': loginType.value,
'areaCode': areaCode ?? "",
};
KRLogUtil.kr_i('准备保存用户信息到存储', tag: 'AppRunData');
await KRSecureStorage().kr_saveData(
key: _keyUserInfo,
value: jsonEncode(userInfo),
@@ -230,11 +231,11 @@ class KRAppRunData {
kr_isLogin.value = true;
KRLogUtil.kr_i('用户信息-kr_isLogin$kr_isLogin', tag: 'AppRunData');
// 🔧 非游客模式下,调用用户信息接口获取 refer_code 等信息
KRLogUtil.kr_i('🔍 [AppRunData] 检查登录模式: account=$account', tag: 'AppRunData');
KRLogUtil.kr_i('🔍 [AppRunData] 检查登录模式: account=$account',
tag: 'AppRunData');
await _fetchUserInfo();
// 不等待userinfo接口返回
_fetchUserInfo();
} catch (e) {
KRLogUtil.kr_e('保存用户信息失败: $e', tag: 'AppRunData');
// 如果出错,重置登录状态
@@ -246,88 +247,89 @@ class KRAppRunData {
/// 退出登录(其实是设备重新登录)
Future<void> kr_loginOut() async {
HIDialog.show(
message: '当前登录已过期,请重新登录',
preventBackDismiss: true,
confirmText: '确定',
autoClose: false,
onConfirm: () async{
// 先将登录状态设置为 false,防止重连
kr_isLogin.value = false;
KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'AppRunData');
KRLogUtil.kr_i('开始重新进行设备登录', tag: 'AppRunData');
KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'AppRunData');
// === 停止 VPN 服务 ===
try {
// 检查 SingBox 服务状态并停止
if (KRSingBoxImp.instance.kr_status.value is SingboxStarted) {
await KRSingBoxImp.instance.kr_stop();
KRLogUtil.kr_i('VPN 服务已停止', tag: 'Logout');
message: '当前登录已过期,请重新登录',
preventBackDismiss: true,
confirmText: '确定',
autoClose: false,
onConfirm: () async {
// 先将登录状态设置为 false,防止重连
kr_isLogin.value = false;
KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━',
tag: 'AppRunData');
KRLogUtil.kr_i('开始重新进行设备登录', tag: 'AppRunData');
KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━',
tag: 'AppRunData');
// === 停止 VPN 服务 ===
try {
// 检查 SingBox 服务状态并停止
if (KRSingBoxImp.instance.kr_status.value is SingboxStarted) {
await KRSingBoxImp.instance.kr_stop();
KRLogUtil.kr_i('VPN 服务已停止', tag: 'Logout');
}
} catch (e) {
KRLogUtil.kr_e('停止 VPN 服务失败: $e', tag: 'Logout');
}
} catch (e) {
KRLogUtil.kr_e('停止 VPN 服务失败: $e', tag: 'Logout');
}
// 断开 Socket 连接
await _kr_disconnectSocket();
// 断开 Socket 连接
await _kr_disconnectSocket();
// 清理用户信息
kr_token = null;
kr_account.value = null;
kr_userId.value = null;
kr_loginType = null;
kr_areaCode = null;
// 清理用户信息
kr_token = null;
kr_account.value = null;
kr_userId.value = null;
kr_areaCode = null;
// 删除存储的用户信息
await KRSecureStorage().kr_deleteData(key: _keyUserInfo);
// 删除存储的用户信息
await KRSecureStorage().kr_deleteData(key: _keyUserInfo);
// 🔧 修复4: 清理订阅服务数据 - 防止未登录用户访问订阅
try {
final subscribeService = Get.find<dynamic>(tag: 'KRSubscribeService');
if (subscribeService != null && subscribeService is KRSubscribeService) {
KRLogUtil.kr_i('🧹 清理订阅服务数据...', tag: 'AppRunData');
await subscribeService.kr_logout();
KRLogUtil.kr_i('✅ 订阅服务数据已清理', tag: 'AppRunData');
// 🔧 修复4: 清理订阅服务数据 - 防止未登录用户访问订阅
try {
final subscribeService =
Get.find<dynamic>(tag: 'KRSubscribeService');
if (subscribeService != null &&
subscribeService is KRSubscribeService) {
KRLogUtil.kr_i('🧹 清理订阅服务数据...', tag: 'AppRunData');
await subscribeService.kr_logout();
KRLogUtil.kr_i('✅ 订阅服务数据已清理', tag: 'AppRunData');
}
} catch (e) {
// 忽略异常:如果订阅服务未初始化或不可用,直接继续
KRLogUtil.kr_d('⚠️ 无法获取订阅服务,跳过清理: $e', tag: 'AppRunData');
}
} catch (e) {
// 忽略异常:如果订阅服务未初始化或不可用,直接继续
KRLogUtil.kr_d('⚠️ 无法获取订阅服务,跳过清理: $e', tag: 'AppRunData');
}
// 5️⃣ 执行设备登录
final success = await kr_checkAndPerformDeviceLogin();
// 5️⃣ 执行设备登录
final success = await kr_checkAndPerformDeviceLogin();
if (!success) {
// 设备登录失败 → 提示用户重试
HIDialog.show(
message: '设备登录失败,请检查网络或重试',
confirmText: '重试',
preventBackDismiss: true,
onConfirm: () async {
await kr_loginOut(); // 递归重试
},
);
return; // 阻止跳首页
}
if (!success) {
// 设备登录失败 → 提示用户重试
HIDialog.show(
message: '设备登录失败,请检查网络或重试',
confirmText: '重试',
preventBackDismiss: true,
onConfirm: () async {
await kr_loginOut(); // 递归重试
},
);
return; // 阻止跳首页
}
// 等待一小段时间,确保登录状态已经更新
await Future.delayed(const Duration(milliseconds: 300));
// 等待一小段时间,确保登录状态已经更新
await Future.delayed(const Duration(milliseconds: 300));
// 刷新订阅信息
KRLogUtil.kr_i('🔄 开始刷新订阅信息...', tag: 'DeviceManagement');
try {
await KRSubscribeService().kr_refreshAll();
KRLogUtil.kr_i('✅ 订阅信息刷新成功', tag: 'DeviceManagement');
} catch (e) {
KRLogUtil.kr_e('订阅信息刷新失败: $e', tag: 'DeviceManagement');
}
// 刷新订阅信息
KRLogUtil.kr_i('🔄 开始刷新订阅信息...', tag: 'DeviceManagement');
try {
await KRSubscribeService().kr_refreshAll();
KRLogUtil.kr_i('✅ 订阅信息刷新成功', tag: 'DeviceManagement');
} catch (e) {
KRLogUtil.kr_e('订阅信息刷新失败: $e', tag: 'DeviceManagement');
}
Get.offAllNamed(Routes.KR_HOME);
}
);
Get.offAllNamed(Routes.KR_HOME);
});
}
Future<void> kr_loginOut_loading() async{
Future<void> kr_loginOut_loading() async {
KRCommonUtil.kr_showLoading();
// 先将登录状态设置为 false,防止重连
kr_isLogin.value = false;
@@ -353,13 +355,11 @@ class KRAppRunData {
kr_token = null;
kr_account.value = null;
kr_userId.value = null;
kr_loginType = null;
kr_areaCode = null;
// 删除存储的用户信息
await KRSecureStorage().kr_deleteData(key: _keyUserInfo);
// 🔧 修复4: 清理订阅服务数据 - 防止未登录用户访问订阅
try {
final subscribeService = Get.find<dynamic>(tag: 'KRSubscribeService');
@@ -413,35 +413,41 @@ class KRAppRunData {
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
print('🔍 开始执行设备登录...');
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
_logStepTiming('设备登录开始');
// 初始化设备信息服务
await KRDeviceInfoService().initialize();
_logStepTiming('初始化设备信息完成');
KRLogUtil.kr_i('🔐 开始执行设备登录', tag: 'AppRunData');
// 执行设备登录
_logStepTiming('开始设备登录请求');
final authApi = KRAuthApi();
final result = await authApi.kr_deviceLogin();
_logStepTiming('设备登录请求完成');
return await result.fold(
(error) {
(error) {
print('❌ 设备登录失败: ${error.msg}');
KRLogUtil.kr_e('❌ 设备登录失败: ${error.msg}', tag: 'SplashController');
_logStepTiming('设备登录完成');
return false;
},
(token) async {
(token) async {
print('✅ 设备登录成功!Token: $token');
KRLogUtil.kr_i('✅ 设备登录成功', tag: 'SplashController');
final deviceId = KRDeviceInfoService().deviceId ?? 'unknown';
_logStepTiming('开始保存用户信息');
await kr_saveUserInfo(
token,
'device_$deviceId', // 临时账号
KRLoginType.kr_email,
null, // 设备登录无需区号
);
_logStepTiming('保存用户信息完成');
kr_isLogin.value = true;
print('✅ 已标记为登录状态');
_logStepTiming('设备登录完成');
return true;
},
);
@@ -456,37 +462,34 @@ class KRAppRunData {
/// 初始化用户信息
Future<void> kr_initializeUserInfo() async {
KRLogUtil.kr_i('开始初始化用户信息', tag: 'AppRunData');
try {
try {
deviceId = KRDeviceInfoService().deviceId ?? 'unknown';
final String? userInfoString =
await KRSecureStorage().kr_readData(key: _keyUserInfo);
if (userInfoString != null && userInfoString.isNotEmpty) {
KRLogUtil.kr_i('找到存储的用户信息,开始解析', tag: 'AppRunData');
try {
final Map<String, dynamic> userInfo = jsonDecode(userInfoString);
kr_token = userInfo['token'];
kr_account.value = userInfo['account'];
final loginTypeValue = userInfo['loginType'];
kr_loginType = KRLoginType.values.firstWhere(
(e) => e.value == loginTypeValue,
orElse: () => KRLoginType.kr_telephone,
);
kr_areaCode = userInfo['areaCode'] ?? "";
// 从token中解析userId
if (kr_token != null && kr_token!.isNotEmpty) {
kr_userId.value = _kr_parseUserIdFromToken(kr_token!);
}
KRLogUtil.kr_i('解析用户信息成功: token=${kr_token != null}, account=${kr_account.value}', tag: 'AppRunData');
KRLogUtil.kr_i(
'解析用户信息成功: token=${kr_token != null}, account=${kr_account.value}',
tag: 'AppRunData');
// 🔧 修复2:验证token有效性和账号信息完整性
// 防止恢复被污染的或过期的数据
if (kr_token != null && kr_token!.isNotEmpty && _kr_isValidToken(kr_token!)) {
if (kr_token != null &&
kr_token!.isNotEmpty &&
_kr_isValidToken(kr_token!)) {
// token格式验证通过(JWT格式检查)
if (kr_account.value != null && kr_account.value!.isNotEmpty) {
// 账号信息完整
@@ -502,7 +505,9 @@ class KRAppRunData {
// Token无效或格式错误,清理旧数据
KRLogUtil.kr_w('⚠️ Token验证失败或格式错误,清理该条用户数据', tag: 'AppRunData');
if (kr_token != null && kr_token!.isNotEmpty) {
KRLogUtil.kr_w(' ❌ 可能的原因:Token已过期或被污染,格式: ${kr_token!.substring(0, min(30, kr_token!.length))}...', tag: 'AppRunData');
KRLogUtil.kr_w(
' ❌ 可能的原因:Token已过期或被污染,格式: ${kr_token!.substring(0, min(30, kr_token!.length))}...',
tag: 'AppRunData');
}
await kr_loginOut();
}
@@ -518,7 +523,7 @@ class KRAppRunData {
KRLogUtil.kr_e('初始化用户信息过程出错: $e', tag: 'AppRunData');
kr_isLogin.value = false;
}
KRLogUtil.kr_i('用户信息初始化完成,登录状态: ${kr_isLogin.value}', tag: 'AppRunData');
}
@@ -558,7 +563,8 @@ class KRAppRunData {
/// 处理连接状态变化
void _kr_handleConnectionState(bool isConnected) {
KRLogUtil.kr_i('WebSocket 连接状态: ${isConnected ? "已连接" : "已断开"}', tag: 'AppRunData');
KRLogUtil.kr_i('WebSocket 连接状态: ${isConnected ? "已连接" : "已断开"}',
tag: 'AppRunData');
}
/// 断开 Socket 连接
@@ -569,42 +575,57 @@ class KRAppRunData {
/// 获取用户详细信息(登录后调用)
Future<void> _fetchUserInfo() async {
try {
KRLogUtil.kr_i('📞 [AppRunData] 开始调用用户信息接口 /v1/public/user/info ...', tag: 'AppRunData');
KRLogUtil.kr_i('🔐 [AppRunData] 当前 Token: ${kr_token ?? "null"}', tag: 'AppRunData');
KRLogUtil.kr_i('📞 [AppRunData] 开始调用用户信息接口 /v1/public/user/info ...',
tag: 'AppRunData');
KRLogUtil.kr_i('🔐 [AppRunData] 当前 Token: ${kr_token ?? "null"}',
tag: 'AppRunData');
final result = await KRUserApi.kr_getUserInfo();
result.fold(
(error) {
KRLogUtil.kr_e('❌ [AppRunData] 获取用户信息失败: ${error.msg} (code: ${error.code})', tag: 'AppRunData');
(error) {
KRLogUtil.kr_e(
'❌ [AppRunData] 获取用户信息失败: ${error.msg} (code: ${error.code})',
tag: 'AppRunData');
},
(userInfo) {
final authType = userInfo.authMethods.isNotEmpty
? userInfo.authMethods[0].authType
: null;
final authIdentifier = userInfo.authMethods.isNotEmpty
? userInfo.authMethods[0].authIdentifier
: null;
(userInfo) {
final authType = userInfo.authMethods.isNotEmpty
? userInfo.authMethods[0].authType
: null;
final authIdentifier = userInfo.authMethods.isNotEmpty
? userInfo.authMethods[0].authIdentifier
: null;
KRLogUtil.kr_i('✅ [AppRunData] 获取用户信息成功', tag: 'AppRunData');
KRLogUtil.kr_i('📋 [AppRunData] refer_code: "${userInfo.referCode}"', tag: 'AppRunData');
KRLogUtil.kr_i('💰 [AppRunData] balance: ${userInfo.balance}', tag: 'AppRunData');
KRLogUtil.kr_i('💵 [AppRunData] commission: ${userInfo.commission}', tag: 'AppRunData');
KRLogUtil.kr_i('📧 [AppRunData] 登录类型: ${authType}', tag: 'AppRunData');
KRLogUtil.kr_i('📧 [AppRunData] email: ${authIdentifier}', tag: 'AppRunData');
KRLogUtil.kr_i('🆔 [AppRunData] id: ${userInfo.id}', tag: 'AppRunData');
KRLogUtil.kr_i('📋 [AppRunData] refer_code: "${userInfo.referCode}"',
tag: 'AppRunData');
KRLogUtil.kr_i('💰 [AppRunData] balance: ${userInfo.balance}',
tag: 'AppRunData');
KRLogUtil.kr_i('💵 [AppRunData] commission: ${userInfo.commission}',
tag: 'AppRunData');
KRLogUtil.kr_i('📧 [AppRunData] 登录类型: ${authType}',
tag: 'AppRunData');
KRLogUtil.kr_i('📧 [AppRunData] email: ${authIdentifier}',
tag: 'AppRunData');
KRLogUtil.kr_i('🆔 [AppRunData] id: ${userInfo.id}',
tag: 'AppRunData');
// 保存到全局状态
kr_referCode.value = userInfo.referCode;
kr_refererId.value = userInfo.refererId;
kr_account.value = authType == 'device' ? '9000${userInfo.id}' : authIdentifier;
kr_account.value =
authType == 'device' ? '9000${userInfo.id}' : authIdentifier;
kr_balance.value = userInfo.balance;
kr_commission.value = userInfo.commission;
KRLogUtil.kr_i('💾 [AppRunData] 用户信息已保存到全局状态:', tag: 'AppRunData');
KRLogUtil.kr_i(' - kr_referCode: "${kr_referCode.value}"', tag: 'AppRunData');
KRLogUtil.kr_i(' - kr_refererId: ${kr_refererId.value}', tag: 'AppRunData');
KRLogUtil.kr_i(' - kr_balance: ${kr_balance.value}', tag: 'AppRunData');
KRLogUtil.kr_i(' - kr_commission: ${kr_commission.value}', tag: 'AppRunData');
KRLogUtil.kr_i(' - kr_referCode: "${kr_referCode.value}"',
tag: 'AppRunData');
KRLogUtil.kr_i(' - kr_refererId: ${kr_refererId.value}',
tag: 'AppRunData');
KRLogUtil.kr_i(' - kr_balance: ${kr_balance.value}',
tag: 'AppRunData');
KRLogUtil.kr_i(' - kr_commission: ${kr_commission.value}',
tag: 'AppRunData');
},
);
} catch (e, stackTrace) {