import 'dart:convert'; import 'dart:math' show min; import 'dart:async'; import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; import 'package:kaer_with_panels/app/common/app_config.dart'; import 'package:kaer_with_panels/app/model/enum/kr_request_type.dart'; import 'package:kaer_with_panels/app/services/kr_socket_service.dart'; import 'package:kaer_with_panels/app/utils/kr_secure_storage.dart'; import 'package:kaer_with_panels/app/services/kr_device_info_service.dart'; import 'package:kaer_with_panels/app/routes/app_pages.dart'; import 'package:kaer_with_panels/app/utils/kr_log_util.dart'; import '../services/api_service/kr_api.user.dart'; import '../services/singbox_imp/kr_sing_box_imp.dart'; import '../services/kr_site_config_service.dart'; import '../services/kr_subscribe_service.dart'; import '../utils/kr_event_bus.dart'; import '../../singbox/model/singbox_status.dart'; import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart'; import 'package:kaer_with_panels/app/services/api_service/kr_auth_api.dart'; import 'package:kaer_with_panels/app/services/kr_subscribe_service.dart'; import 'package:kaer_with_panels/app/utils/kr_common_util.dart'; import 'package:kaer_with_panels/app/utils/kr_device_util.dart'; import 'package:openinstall_flutter_plugin/openinstall_flutter_plugin.dart'; import 'dart:io' show Platform; class KRAppRunData { static final KRAppRunData _instance = KRAppRunData._internal(); static const String _keyUserInfo = 'USER_INFO'; static const bool inviteDebugMode = false; /// 登录token String? kr_token; /// 用户账号(使用响应式变量以便 UI 能监听变化) final Rx kr_account = Rx(null); /// 分享链接(使用响应式变量以便 UI 能监听变化) final Rx shareUrl = Rx(null); /// 分享链接(使用响应式变量以便 UI 能监听变化) final Rx kr_authType = Rx(null); /// 用户ID(使用响应式变量以便 UI 能监听变化) final Rx kr_userId = Rx(null); /// 用户邀请码(从用户信息接口获取) final RxString kr_referCode = ''.obs; /// 邀请人ID(谁邀请了当前用户) final RxInt kr_refererId = 0.obs; /// 用户余额 final RxInt kr_balance = 0.obs; /// 佣金 final RxInt kr_commission = 0.obs; /// 设备ID String? deviceId; /// 最后一次设备登录错误信息 String? kr_lastDeviceLoginError; /// 区号 String? kr_areaCode; // 需要被监听的属性,用 obs 包装 final kr_isLogin = false.obs; // 存储临时待绑定的邀请码(处理唤醒数据比登录完成早的情况) String? _kr_pendingInviteCode; KRAppRunData._internal(); factory KRAppRunData() => _instance; static KRAppRunData getInstance() { 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() { // 设备登录的账号格式为 "device_设备ID" return kr_authType.value != null && kr_authType.value == 'device'; } /// 🔧 修复2.1:验证Token格式是否有效 /// 检查Token是否符合JWT格式(header.payload.signature) /// 这能有效防止被污染的或过期的Token数据 bool _kr_isValidToken(String token) { try { // JWT格式检查: header.payload.signature (三段,每段用.分隔) final parts = token.split('.'); if (parts.length != 3) { KRLogUtil.kr_w('❌ Token格式无效:分段数不对 (${parts.length} != 3)', tag: 'AppRunData'); return false; } // 检查每一段是否为非空 for (var i = 0; i < parts.length; i++) { if (parts[i].isEmpty) { KRLogUtil.kr_w('❌ Token格式无效:第${i + 1}段为空', tag: 'AppRunData'); return false; } } // 尝试解码payload部分,验证是否是有效的base64和JSON String payload = parts[1]; // 手动添加必要的padding switch (payload.length % 4) { case 0: break; case 2: payload += '=='; break; case 3: payload += '='; break; default: KRLogUtil.kr_w('❌ Token payload长度无效', tag: 'AppRunData'); return false; } // 尝试解码和解析 try { final decodedBytes = base64.decode(payload); final decodedString = utf8.decode(decodedBytes); jsonDecode(decodedString); // 验证是否是有效JSON KRLogUtil.kr_i('✅ Token格式验证通过', tag: 'AppRunData'); return true; } catch (e) { KRLogUtil.kr_w('❌ Token payload无法解析: $e', tag: 'AppRunData'); return false; } } catch (e) { KRLogUtil.kr_e('Token验证异常: $e', tag: 'AppRunData'); return false; } } /// 从JWT token中解析userId int? _kr_parseUserIdFromToken(String token) { try { // JWT格式: header.payload.signature final parts = token.split('.'); if (parts.length != 3) { KRLogUtil.kr_e('JWT token格式错误', tag: 'AppRunData'); return null; } // 解码payload部分(base64) String payload = parts[1]; // 手动添加必要的padding(base64要求长度是4的倍数) switch (payload.length % 4) { case 0: break; // 不需要padding case 2: payload += '=='; break; case 3: payload += '='; break; default: KRLogUtil.kr_e('JWT payload长度无效', tag: 'AppRunData'); return null; } final decodedBytes = base64.decode(payload); final decodedString = utf8.decode(decodedBytes); // 解析JSON final Map payloadMap = jsonDecode(decodedString); // 获取UserId if (payloadMap.containsKey('UserId')) { final userId = payloadMap['UserId']; KRLogUtil.kr_i('从JWT解析出userId: $userId', tag: 'AppRunData'); return userId is int ? userId : int.tryParse(userId.toString()); } return null; } catch (e) { KRLogUtil.kr_e('解析JWT token失败: $e', tag: 'AppRunData'); return null; } } /// 保存用户信息 Future kr_saveUserInfo( String token, String account, ) async { KRLogUtil.kr_i('开始保存用户信息', tag: 'AppRunData'); try { // 从JWT token中解析userId kr_userId.value = _kr_parseUserIdFromToken(token); KRLogUtil.kr_i('从JWT解析userId: ${kr_userId.value}', tag: 'AppRunData'); final accountText = account.startsWith('device_') ? '9000${kr_userId}' : account; // 更新内存中的数据 kr_token = token; kr_account.value = accountText; final Map userInfo = { 'token': token, 'account': accountText, }; KRLogUtil.kr_i('准备保存用户信息到存储', tag: 'AppRunData'); await KRSecureStorage().kr_saveData( key: _keyUserInfo, value: jsonEncode(userInfo), ); // 验证保存是否成功 final savedData = await KRSecureStorage().kr_readData(key: _keyUserInfo); KRLogUtil.kr_i('用户信息-kr_readData$savedData', tag: 'AppRunData'); if (savedData == null || savedData.isEmpty) { KRLogUtil.kr_e('数据保存后无法读取,保存失败', tag: 'AppRunData'); kr_isLogin.value = false; return; } KRLogUtil.kr_i('用户信息保存成功,设置登录状态为true', tag: 'AppRunData'); // 只有在保存成功后才设置登录状态 kr_isLogin.value = true; KRLogUtil.kr_i('用户信息-kr_isLogin$kr_isLogin', tag: 'AppRunData'); KRLogUtil.kr_i('🔍 [AppRunData] 检查登录模式: account=$account', tag: 'AppRunData'); // 不等待userinfo接口返回 _fetchUserInfo(); } catch (e) { KRLogUtil.kr_e('保存用户信息失败: $e', tag: 'AppRunData'); // 如果出错,重置登录状态 kr_isLogin.value = false; rethrow; // 重新抛出异常,让调用者知道保存失败 } } /// 退出登录(其实是设备重新登录) Future 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'); } } catch (e) { KRLogUtil.kr_e('停止 VPN 服务失败: $e', tag: 'Logout'); } // 断开 Socket 连接 await _kr_disconnectSocket(); // 清理用户信息 kr_token = null; kr_account.value = null; kr_userId.value = null; kr_areaCode = null; // 删除存储的用户信息 await KRSecureStorage().kr_deleteData(key: _keyUserInfo); // 🔧 修复4: 清理订阅服务数据 - 防止未登录用户访问订阅 try { final subscribeService = Get.find(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'); } // 5️⃣ 执行设备登录 final success = await kr_checkAndPerformDeviceLogin(); if (!success) { // 设备登录失败 → 提示用户重试 HIDialog.show( message: '设备登录失败\n\n原因:${kr_lastDeviceLoginError ?? "未知错误"}\n\n请检查网络或重试', confirmText: '重试', preventBackDismiss: true, onConfirm: () async { await kr_loginOut(); // 递归重试 }, ); return; // 阻止跳首页 } // 等待一小段时间,确保登录状态已经更新 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'); } Get.offAllNamed(Routes.KR_HOME); }); } Future kr_loginOut_loading() async { KRCommonUtil.kr_showLoading(); // 先将登录状态设置为 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) { KRCommonUtil.kr_hideLoading(); KRLogUtil.kr_e('停止 VPN 服务失败: $e', tag: 'Logout'); } // 断开 Socket 连接 await _kr_disconnectSocket(); // 清理用户信息 kr_token = null; kr_account.value = null; kr_userId.value = null; kr_areaCode = null; // 删除存储的用户信息 await KRSecureStorage().kr_deleteData(key: _keyUserInfo); // 🔧 修复4: 清理订阅服务数据 - 防止未登录用户访问订阅 try { final subscribeService = Get.find(tag: 'KRSubscribeService'); if (subscribeService != null && subscribeService is KRSubscribeService) { KRLogUtil.kr_i('🧹 清理订阅服务数据...', tag: 'AppRunData'); await subscribeService.kr_logout(); KRLogUtil.kr_i('✅ 订阅服务数据已清理', tag: 'AppRunData'); } } catch (e) { KRCommonUtil.kr_hideLoading(); // 忽略异常:如果订阅服务未初始化或不可用,直接继续 KRLogUtil.kr_d('⚠️ 无法获取订阅服务,跳过清理: $e', tag: 'AppRunData'); } // 5️⃣ 执行设备登录 final success = await kr_checkAndPerformDeviceLogin(); if (!success) { KRCommonUtil.kr_hideLoading(); // 设备登录失败 → 提示用户重试 HIDialog.show( message: '设备登录失败\n\n原因:${kr_lastDeviceLoginError ?? "未知错误"}\n\n请检查网络或重试', confirmText: '重试', preventBackDismiss: true, onConfirm: () async { await kr_loginOut(); // 递归重试 }, ); return; // 阻止跳首页 } // 等待一小段时间,确保登录状态已经更新 await Future.delayed(const Duration(milliseconds: 300)); // 刷新订阅信息 KRLogUtil.kr_i('🔄 开始刷新订阅信息...', tag: 'DeviceManagement'); try { await KRSubscribeService().kr_refreshAll(); KRLogUtil.kr_i('✅ 订阅信息刷新成功', tag: 'DeviceManagement'); } catch (e) { KRCommonUtil.kr_hideLoading(); KRLogUtil.kr_e('订阅信息刷新失败: $e', tag: 'DeviceManagement'); } KRCommonUtil.kr_hideLoading(); Get.offAllNamed(Routes.KR_HOME); } /// 检查并执行设备登录 Future kr_checkAndPerformDeviceLogin() async { try { 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) { kr_lastDeviceLoginError = error.msg; print('❌ 设备登录失败: ${error.msg}'); KRLogUtil.kr_e('❌ 设备登录失败: ${error.msg}', tag: 'SplashController'); _logStepTiming('设备登录完成'); return false; }, (token) async { print('✅ 设备登录成功!Token: $token'); KRLogUtil.kr_i('✅ 设备登录成功', tag: 'SplashController'); final deviceId = KRDeviceInfoService().deviceId ?? 'unknown'; _logStepTiming('开始保存用户信息'); await kr_saveUserInfo( token, 'device_$deviceId', // 临时账号 ); _logStepTiming('保存用户信息完成'); kr_isLogin.value = true; print('✅ 已标记为登录状态'); // 静默邀请绑定 if (inviteDebugMode) { // Debug 模式下等待调试弹窗完成,避免页面跳转 await _kr_handleSilentInvitation(); } else { // 正式环境异步执行,不阻塞主流程 _kr_handleSilentInvitation(); } _logStepTiming('设备登录完成'); return true; }, ); } catch (e, stackTrace) { print('❌ 设备登录检查异常: $e'); print('📚 堆栈跟踪: $stackTrace'); kr_lastDeviceLoginError = e.toString(); KRLogUtil.kr_e('❌ 设备登录检查异常: $e', tag: 'SplashController'); return false; } } /// 初始化用户信息 Future kr_initializeUserInfo() async { KRLogUtil.kr_i('开始初始化用户信息', tag: 'AppRunData'); 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 userInfo = jsonDecode(userInfoString); kr_token = userInfo['token']; kr_account.value = userInfo['account']; // 从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'); // 🔧 修复2:验证token有效性和账号信息完整性 // 防止恢复被污染的或过期的数据 if (kr_token != null && kr_token!.isNotEmpty && _kr_isValidToken(kr_token!)) { // token格式验证通过(JWT格式检查) if (kr_account.value != null && kr_account.value!.isNotEmpty) { // 账号信息完整 KRLogUtil.kr_i('✅ Token和账号验证通过,设置登录状态为true', tag: 'AppRunData'); KRLogUtil.kr_i('📊 恢复账号: ${kr_account.value}', tag: 'AppRunData'); kr_isLogin.value = true; // 🔧 新增:恢复登录状态后也尝试检测一次静默邀请(重要:针对已安装后启动的情况) _kr_handleSilentInvitation(); } else { // 账号信息为空,清理旧数据 KRLogUtil.kr_w('⚠️ 账号信息为空,清理该条用户数据', tag: 'AppRunData'); await kr_loginOut(); } } else { // 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'); } await kr_loginOut(); } } catch (e) { KRLogUtil.kr_e('解析用户信息失败: $e', tag: 'AppRunData'); await kr_loginOut(); } } else { KRLogUtil.kr_i('未找到存储的用户信息,设置为未登录状态', tag: 'AppRunData'); kr_isLogin.value = false; } } catch (e) { KRLogUtil.kr_e('初始化用户信息过程出错: $e', tag: 'AppRunData'); kr_isLogin.value = false; } KRLogUtil.kr_i('用户信息初始化完成,登录状态: ${kr_isLogin.value}', tag: 'AppRunData'); } /// 建立 Socket 连接 /// /// ⚠️ 已遗弃:wsBaseUrl 不再使用 Future _kr_connectSocket(String userId) async { // 已遗弃,不再建立 Socket 连接 KRLogUtil.kr_i('Socket 连接已遗弃', tag: 'AppRunData'); // 如果需要实时消息,使用其他实现方式 } /// 处理接收到的消息 void _kr_handleMessage(Map message) { try { final String method = message['method'] as String; switch (method) { case 'kicked_device': KRLogUtil.kr_i('超出登录设备限制', tag: 'AppRunData'); kr_loginOut(); break; case 'kicked_admin': KRLogUtil.kr_i('强制退出', tag: 'AppRunData'); kr_loginOut(); break; case 'subscribe_update': KRLogUtil.kr_i('订阅信息已更新', tag: 'AppRunData'); // 发送订阅更新事件 KREventBus().kr_sendMessage(KRMessageType.kr_subscribe_update); break; default: KRLogUtil.kr_w('收到未知类型的消息: $message', tag: 'AppRunData'); } } catch (e) { KRLogUtil.kr_e('处理消息失败: $e', tag: 'AppRunData'); } } /// 处理连接状态变化 void _kr_handleConnectionState(bool isConnected) { KRLogUtil.kr_i('WebSocket 连接状态: ${isConnected ? "已连接" : "已断开"}', tag: 'AppRunData'); } /// 断开 Socket 连接 Future _kr_disconnectSocket() async { await KrSocketService.instance.disconnect(); } /// 获取用户详细信息(登录后调用) Future _fetchUserInfo() async { try { 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'); }, (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] id: ${userInfo.shareUrl}', tag: 'AppRunData'); // 保存到全局状态 kr_referCode.value = userInfo.referCode; kr_refererId.value = userInfo.refererId; kr_account.value = authType == 'device' ? '${userInfo.id}' : authIdentifier; kr_balance.value = userInfo.balance; kr_commission.value = userInfo.commission; shareUrl.value = userInfo.shareUrl; kr_authType.value = authType; 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_commission: ${shareUrl.value}', tag: 'AppRunData'); }, ); } catch (e, stackTrace) { KRLogUtil.kr_e('💥 [AppRunData] 获取用户信息异常: $e', tag: 'AppRunData'); KRLogUtil.kr_e('📚 [AppRunData] 错误堆栈: $stackTrace', tag: 'AppRunData'); } } /// 处理静默邀请 Future _kr_handleSilentInvitation() async { KRLogUtil.kr_i('🚀 开始处理静默邀请...', tag: 'AppRunData'); String? inviteCode; // 1. 先检查是否有之前通过唤醒/安装暂存的邀请码 if (_kr_pendingInviteCode != null && _kr_pendingInviteCode!.isNotEmpty) { inviteCode = _kr_pendingInviteCode; KRLogUtil.kr_i('📎 使用暂存的待绑定邀请码: $inviteCode', tag: 'AppRunData'); } // 2. 如果没有暂存码,尝试从平台环境获取 if (inviteCode == null || inviteCode!.isEmpty) { try { if (Platform.isMacOS || Platform.isWindows) { inviteCode = await KRDeviceUtil().kr_getDesktopInviteCode(); } else if (Platform.isAndroid || Platform.isIOS) { final Completer completer = Completer(); OpeninstallFlutterPlugin().install((data) async { final code = kr_parseInviteCodeFromData(data); KRLogUtil.kr_i('收到 OpenInstall 安装数据: $data, 解析出邀请码: $code', tag: 'AppRunData'); if (!completer.isCompleted) completer.complete(code); }); inviteCode = await completer.future .timeout(const Duration(seconds: 8), onTimeout: () => null); } } catch (e) { KRLogUtil.kr_e('获取静默邀请码异常: $e', tag: 'AppRunData'); } } if (inviteCode != null && inviteCode!.isNotEmpty) { KRLogUtil.kr_i('🔍 最终识别到邀请码: $inviteCode', tag: 'AppRunData'); if (inviteDebugMode) { // Debug 模式下弹出对话框确认 final bool isDesktop = Platform.isMacOS || Platform.isWindows; await HIDialog.show( title: isDesktop ? '调试:唤醒识别到邀请码' : '调试:邀请码绑定确认', message: isDesktop ? '桌面端识别到邀请码:$inviteCode\n是否进行绑定?' : '识别到邀请码:$inviteCode\n是否进行绑定?', confirmText: isDesktop ? '绑定' : '确认绑定', cancelText: isDesktop ? '跳过' : '取消', onConfirm: () async { await _kr_performInviteBinding(inviteCode!); _kr_pendingInviteCode = null; // 绑定后清除 }, onCancel: () { _kr_pendingInviteCode = null; // 取消也清除,避免重复弹窗 }, ); } else { // 正式环境静默绑定 await _kr_performInviteBinding(inviteCode!); _kr_pendingInviteCode = null; // 绑定后清除 } } else { KRLogUtil.kr_i('⚠️ 未识别到有效的邀请码,跳外静默绑定', tag: 'AppRunData'); } } /// 执行邀请码绑定请求 Future _kr_performInviteBinding(String inviteCode) async { KRLogUtil.kr_i('🚀 准备执行邀请码绑定: $inviteCode', tag: 'AppRunData'); final result = await KRUserApi().hi_inviteCode(inviteCode, isSilentInvite: true); result.fold( (error) => KRLogUtil.kr_w('❌ 邀请绑定失败: ${error.msg}', tag: 'AppRunData'), (_) => KRLogUtil.kr_i('✅ 邀请绑定成功', tag: 'AppRunData'), ); } /// 公开方法:直接处理 OpenInstall 返回的原始数据(用于唤醒等场景) Future kr_handleOpenInstallData(Map data) async { final code = kr_parseInviteCodeFromData(data); if (code != null && code.isNotEmpty) { KRLogUtil.kr_i('🔗 收到 OpenInstall 原始参数并触发解析: $code', tag: 'AppRunData'); // 暂存该邀请码 _kr_pendingInviteCode = code; // 如果当前已经是登录状态,则由于是唤醒(Hot Start)触发,直接按业务逻辑处理 if (kr_isLogin.value) { KRLogUtil.kr_i('✅ 用户已登录,立即处理唤醒绑定', tag: 'AppRunData'); if (inviteDebugMode) { await HIDialog.show( title: '调试:唤醒识别到邀请码', message: '唤醒数据解析到邀请码:$code\n是否进行绑定?', confirmText: '绑定', cancelText: '跳过', onConfirm: () async { await _kr_performInviteBinding(code); _kr_pendingInviteCode = null; }, onCancel: () => _kr_pendingInviteCode = null, ); } else { _kr_performInviteBinding(code).then((_) => _kr_pendingInviteCode = null); } } else { KRLogUtil.kr_i('⏳ 用户未登录,已暂存邀请码,等待登录完成后自动绑定', tag: 'AppRunData'); } } } /// 从 OpenInstall 数据中解析邀请码 String? kr_parseInviteCodeFromData(Map data) { try { if (data.containsKey('bindData')) { final bindDataStr = data['bindData'] as String?; if (bindDataStr != null && bindDataStr.isNotEmpty) { final Map bindData = jsonDecode(bindDataStr); return bindData['inviteCode']?.toString(); } } } catch (e) { KRLogUtil.kr_e('解析 OpenInstall 数据中邀请码失败: $e', tag: 'AppRunData'); } return null; } }