import 'dart:async'; import 'dart:io'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'package:get/get.dart'; import 'package:kaer_with_panels/app/common/app_run_data.dart'; import 'package:kaer_with_panels/app/services/kr_subscribe_service.dart'; import '../../../../singbox/model/singbox_proxy_type.dart'; import '../../../../singbox/model/singbox_status.dart'; import '../../../common/app_config.dart'; import '../../../localization/app_translations.dart'; import '../../../localization/kr_language_utils.dart'; import '../../../model/business/kr_group_outbound_list.dart'; import '../../../model/business/kr_outbound_item.dart'; import '../../../utils/kr_event_bus.dart'; import '../../../utils/kr_update_util.dart'; import '../../../widgets/dialogs/kr_dialog.dart'; import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart'; import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart'; import 'package:kaer_with_panels/app/routes/app_pages.dart'; import '../models/kr_home_views_status.dart'; import 'package:kaer_with_panels/app/model/response/kr_user_available_subscribe.dart'; import 'package:kaer_with_panels/app/utils/kr_log_util.dart'; import 'package:kaer_with_panels/app/utils/kr_common_util.dart'; import 'package:kaer_with_panels/app/utils/kr_secure_storage.dart'; import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart'; import 'package:flutter/foundation.dart'; import 'package:kaer_with_panels/app/utils/kr_init_log_collector.dart'; // 🔧 新增:导入日志收集器 import 'package:kaer_with_panels/app/utils/kr_latency_tester.dart'; // 🔧 新增:导入真实延迟测试工具 class KRHomeController extends GetxController with WidgetsBindingObserver { // 🔧 新增:日志收集器实例 final _initLog = KRInitLogCollector(); /// 订阅服务 final KRSubscribeService kr_subscribeService = KRSubscribeService(); /// 当前视图状态,登录状态 final Rx kr_currentViewStatus = KRHomeViewsStatus.kr_notLoggedIn.obs; /// 当前列表视图状态 final kr_currentListStatus = KRHomeViewsListStatus.kr_loading.obs; /// 连接字符串 final kr_connectText = AppTranslations.kr_home.disconnected.obs; // 当前节点名称 final kr_currentNodeName = 'auto'.obs; /// 当前连接速率 final RxString kr_currentSpeed = "--".obs; // 当前节点延迟 final kr_currentNodeLatency = (-2).obs; // 是否已连接 final kr_isConnected = false.obs; // 是否显示延迟 final kr_isLatency = false.obs; /// 默认 var kr_cutTag = ''.obs; var kr_cutSeletedTag = ''.obs; var kr_coutryText = ''.obs; var kr_selectedCountryTag = 'auto'.obs; void kr_setSelectedCountryTag(String country) { kr_selectedCountryTag.value = country; kr_coutryText.value = country; KRSecureStorage().kr_saveData(key: 'SELECTED_COUNTRY_TAG', value: country); } /// 当前连接信息 final RxString kr_currentIp = AppTranslations.kr_home.disconnected.obs; final RxString kr_currentProtocol = AppTranslations.kr_home.disconnected.obs; final RxString kr_connectionTime = '00:00:00'.obs; // 连接计时器 Timer? _kr_connectionTimer; // 🔧 新增:EventBus 监听器 Worker(用于清理) Worker? _eventBusWorker; int _kr_connectionSeconds = 0; // 🔧 保守修复:添加状态变化时间追踪,用于检测状态卡住 DateTime? _lastStatusChangeTime; Timer? _statusWatchdogTimer; // 当前选中的组 final Rx kr_currentGroup = Rx(null); // 添加是否用户正在移动地图的标志 final kr_isUserMoving = false.obs; // 添加最后的地图中心点 final kr_lastMapCenter = 0.obs; // 为"闪连"Checkbox添加一个响应式变量,默认为 false final isQuickConnectEnabled = false.obs; // 存储实例 final KRSecureStorage _storage = KRSecureStorage(); // 闪连状态存储键 static const String _quickConnectKey = 'kr_quick_connect_enabled'; // 添加一个方法来切换状态 void toggleQuickConnect(bool? value) async { // 只有当传入的值不为 null 时才更新状态 if (value != null) { isQuickConnectEnabled.value = value; // 保存闪连状态到本地存储 await _saveQuickConnectStatus(value); KRLogUtil.kr_i('闪连状态已更新: $value', tag: 'QuickConnect'); } } // 保存闪连状态到本地存储 Future _saveQuickConnectStatus(bool enabled) async { try { await _storage.kr_saveBool(key: _quickConnectKey, value: enabled); KRLogUtil.kr_i('闪连状态已保存到本地存储: $enabled', tag: 'QuickConnect'); } catch (e) { KRLogUtil.kr_e('保存闪连状态失败: $e', tag: 'QuickConnect'); } } // 从本地存储加载闪连状态 Future _loadQuickConnectStatus() async { try { final enabled = await _storage.kr_getBool(key: _quickConnectKey); if (enabled != null) { isQuickConnectEnabled.value = enabled; KRLogUtil.kr_i('从本地存储加载闪连状态: $enabled', tag: 'QuickConnect'); } else { // 如果没有保存过状态,使用默认值false isQuickConnectEnabled.value = false; KRLogUtil.kr_i('未找到保存的闪连状态,使用默认值: false', tag: 'QuickConnect'); } } catch (e) { KRLogUtil.kr_e('加载闪连状态失败: $e', tag: 'QuickConnect'); // 出错时使用默认值 isQuickConnectEnabled.value = false; } } // 获取当前闪连状态 bool get isQuickConnectActive => isQuickConnectEnabled.value; /// 检查闪连自动连接启动条件 Future _checkQuickConnectAutoStart() async { try { KRLogUtil.kr_i('检查闪连自动连接启动条件', tag: 'QuickConnect'); // 检查闪连是否开启 final enabled = await _storage.kr_getBool(key: _quickConnectKey); if (enabled == null || enabled == false) { KRLogUtil.kr_i('闪连功能未开启,跳过自动连接', tag: 'QuickConnect'); return; } // 检查用户是否已登录 if (!KRAppRunData.getInstance().kr_isLogin.value) { KRLogUtil.kr_i('用户未登录,跳过自动连接', tag: 'QuickConnect'); return; } // 检查当前是否已连接 if (kr_isConnected.value) { KRLogUtil.kr_i('当前已连接,同步线路信息', tag: 'QuickConnect'); await _restoreSelectedNode(); kr_updateConnectionInfo(); return; } KRLogUtil.kr_i('满足闪连自动连接条件,开始自动连接', tag: 'QuickConnect'); await _autoConnect(); } catch (e) { KRLogUtil.kr_e('检查闪连自动连接启动条件时发生错误: $e', tag: 'QuickConnect'); } } /// 执行自动连接 Future _autoConnect() async { try { KRLogUtil.kr_i('开始执行闪连自动连接', tag: 'QuickConnect'); // 防止重复操作 if (KRSingBoxImp.instance.kr_status.value == SingboxStarted()) { KRLogUtil.kr_w('连接操作正在进行中,跳过自动连接', tag: 'QuickConnect'); return; } final current = kr_subscribeService.kr_currentSubscribe.value; if (!KRAppRunData.getInstance().kr_isLogin.value || current == null) { KRLogUtil.kr_w('未登录或无可用订阅,阻止自动连接', tag: 'QuickConnect'); return; } bool isExpired = false; if (current.expireTime.isNotEmpty) { try { isExpired = DateTime.parse(current.expireTime).isBefore(DateTime.now()); } catch (_) {} } if (isExpired) { KRLogUtil.kr_w('订阅不可用(过期或流量耗尽),阻止自动连接', tag: 'QuickConnect'); return; } await _kr_prepareCountrySelectionBeforeStart(); final selectedAfter = await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG'); KRLogUtil.kr_i( '准备后 SELECTED_NODE_TAG_autoConnect: ${selectedAfter ?? ''}', tag: 'HomeController'); KRLogUtil.kr_i( '准备后 kr_currentNodeName_autoConnect: ${kr_currentNodeName.value}', tag: 'HomeController'); KRLogUtil.kr_i('准备后 kr_cutTag_autoConnect: ${kr_cutTag.value}', tag: 'HomeController'); KRLogUtil.kr_i( '准备后 kr_cutSeletedTag_autoConnect: ${kr_cutSeletedTag.value}', tag: 'HomeController'); await kr_performNodeSwitch(selectedAfter!); await KRSingBoxImp.instance.kr_start(); KRLogUtil.kr_i('闪连自动连接执行完成', tag: 'QuickConnect'); } catch (e) { KRLogUtil.kr_e('闪连自动连接失败: $e', tag: 'QuickConnect'); } } /// 手动触发闪连状态变更(公共方法) Future triggerQuickConnect() async { try { // 切换闪连状态 bool newStatus = !isQuickConnectEnabled.value; await _storage.kr_saveBool(key: _quickConnectKey, value: newStatus); isQuickConnectEnabled.value = newStatus; KRLogUtil.kr_i('手动切换闪连状态: ${newStatus ? "开启" : "关闭"}', tag: 'QuickConnect'); } catch (e) { KRLogUtil.kr_e('切换闪连状态失败: $e', tag: 'QuickConnect'); } } @override void onInit() async { super.onInit(); // 加载闪连状态 _loadQuickConnectStatus(); // 加载已选择的国家 await _loadSelectedCountryTag(); // 绑定订阅状态 _bindSubscribeStatus(); /// 登录处理 _kr_initLoginStatus(); // 绑定连接状态 _bindConnectionStatus(); // 注册应用生命周期监听 WidgetsBinding.instance.addObserver(this); // 🔧 新增:恢复上次选择的节点显示 _restoreSelectedNode(); // 延迟同步连接状态,确保状态正确 Future.delayed(const Duration(milliseconds: 500), () { kr_forceSyncConnectionStatus(); }); // 🔧 Android 15 新增:5秒后再次强制更新高度,兜底保护 // 🔧 保守修复: 启动状态监控定时器,每 30 秒检查一次状态 _startStatusWatchdog(); } /// 🔧 新增:恢复上次选择的节点显示 Future _restoreSelectedNode() async { try { // 从 Hive 读取保存的节点 final savedNode = await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG'); if (savedNode != null && savedNode.isNotEmpty) { KRLogUtil.kr_i('📖 恢复上次选择的节点显示: $savedNode', tag: 'HomeController'); // 更新 UI 显示 kr_currentNodeName.value = savedNode; kr_cutTag.value = savedNode; kr_cutSeletedTag.value = savedNode; // 如果是国家节点,更新国家文本 if (savedNode != 'auto') { kr_coutryText.value = savedNode; } KRLogUtil.kr_i('✅ 节点显示已恢复: $savedNode', tag: 'HomeController'); } else { KRLogUtil.kr_i('ℹ️ 没有保存的节点,使用默认 auto', tag: 'HomeController'); } } catch (e) { KRLogUtil.kr_e('❌ 恢复节点显示失败: $e', tag: 'HomeController'); // 失败时保持默认值 'auto' } } Future _loadSelectedCountryTag() async { try { final v = await KRSecureStorage().kr_readData(key: 'SELECTED_COUNTRY_TAG'); if (v != null && v.isNotEmpty) { kr_selectedCountryTag.value = v; kr_coutryText.value = v; } else { kr_selectedCountryTag.value = 'auto'; kr_coutryText.value = 'auto'; } } catch (_) {} } void _kr_initLoginStatus() { _initLog.log('开始初始化登录状态处理', tag: 'Home'); KRLogUtil.kr_i('初始化登录状态', tag: 'HomeController'); // 🔧 Android 15 紧急修复:8秒超时,更快响应移动网络慢的情况 Timer(const Duration(seconds: 8), () { if (kr_currentListStatus.value == KRHomeViewsListStatus.kr_loading) { _initLog.logWarning('⏱️ 订阅加载超时(8秒保护),强制设置为无数据状态', tag: 'Subscribe'); _initLog.log('当前列表状态仍为: loading,触发超时保护', tag: 'Subscribe'); KRLogUtil.kr_w('⏱️ 订阅服务初始化超时(8秒),强制设置为无数据状态', tag: 'HomeController'); // 🔧 Android 15 优化:超时设置为 none 而非 error,避免底部面板显示错误界面 kr_currentListStatus.value = KRHomeViewsListStatus.kr_none; KRLogUtil.kr_i('✅ 已强制切换到默认视图', tag: 'HomeController'); } }); // 🔧 Android 15 新增:3秒警告,帮助诊断 Timer(const Duration(seconds: 3), () { if (kr_currentListStatus.value == KRHomeViewsListStatus.kr_loading) { _initLog.logWarning('⚠️ 订阅服务已加载3秒,网络可能较慢', tag: 'Subscribe'); KRLogUtil.kr_w('⚠️ 订阅服务初始化已耗时3秒,可能网络较慢', tag: 'HomeController'); } }); // 延迟初始化,确保所有异步操作完成 Future.delayed(const Duration(milliseconds: 100), () { _kr_validateAndSetLoginStatus(); }); // 注册登录状态监听器 ever(KRAppRunData().kr_isLogin, (isLoggedIn) { KRLogUtil.kr_i('登录状态变化: $isLoggedIn', tag: 'HomeController'); _kr_handleLoginStatusChange(isLoggedIn); }); // 添加状态同步检查 _kr_addStatusSyncCheck(); if (AppConfig().kr_is_daytime == true) { Future.delayed(const Duration(seconds: 5), () { KRUpdateUtil().kr_checkUpdate(); Future.delayed(const Duration(seconds: 5), () { // 不做语言切换 // KRLanguageSwitchDialog.kr_show(); }); }); } } /// 验证并设置登录状态 void _kr_validateAndSetLoginStatus() { try { _initLog.log('🔍 开始验证登录状态', tag: 'Home'); // 多重验证登录状态 final hasToken = KRAppRunData().kr_token != null && KRAppRunData().kr_token!.isNotEmpty; final isLoginFlag = KRAppRunData().kr_isLogin.value; final isValidLogin = hasToken && isLoginFlag; _initLog.log( '登录验证结果: hasToken=$hasToken, isLogin=$isLoginFlag, isValid=$isValidLogin', tag: 'Home'); KRLogUtil.kr_i( '登录状态验证: hasToken=$hasToken, isLogin=$isLoginFlag, isValid=$isValidLogin', tag: 'HomeController'); KRLogUtil.kr_i('Token内容: ${KRAppRunData().kr_token?.substring(0, 10)}...', tag: 'HomeController'); if (isValidLogin) { kr_currentViewStatus.value = KRHomeViewsStatus.kr_loggedIn; _initLog.logSuccess('用户已登录,准备加载订阅数据', tag: 'Home'); KRLogUtil.kr_i('设置为已登录状态', tag: 'HomeController'); _kr_ensureSubscribeServiceInitialized(); KRLogUtil.kr_i('订阅服务已在启动页初始化,跳过重复初始化', tag: 'HomeController'); } else { kr_currentViewStatus.value = KRHomeViewsStatus.kr_notLoggedIn; _initLog.logWarning('用户未登录,跳过订阅加载', tag: 'Home'); KRLogUtil.kr_i('设置为未登录状态', tag: 'HomeController'); } } catch (e) { _initLog.logError('登录状态验证失败', tag: 'Home', error: e); KRLogUtil.kr_e('登录状态验证失败: $e', tag: 'HomeController'); kr_currentViewStatus.value = KRHomeViewsStatus.kr_notLoggedIn; } } /// 确保订阅服务初始化 void _kr_ensureSubscribeServiceInitialized() { try { _initLog.logSeparator(); _initLog.log('📦 开始订阅服务初始化', tag: 'Subscribe'); // 🔧 修复1: 添加登录状态检查 - 只有已登录用户才能初始化订阅 final isLoggedIn = KRAppRunData().kr_isLogin.value; if (!isLoggedIn) { _initLog.logWarning('未登录用户,跳过订阅服务初始化', tag: 'Subscribe'); KRLogUtil.kr_w('⚠️ 未登录用户,不初始化订阅服务', tag: 'HomeController'); kr_currentListStatus.value = KRHomeViewsListStatus.kr_none; return; } // 检查订阅服务状态 final currentStatus = kr_subscribeService.kr_currentStatus.value; _initLog.log('当前订阅服务状态: $currentStatus', tag: 'Subscribe'); KRLogUtil.kr_i('订阅服务当前状态: $currentStatus', tag: 'HomeController'); if (currentStatus == KRSubscribeServiceStatus.kr_none || currentStatus == KRSubscribeServiceStatus.kr_error) { _initLog.log('订阅服务需要初始化,开始加载...', tag: 'Subscribe'); KRLogUtil.kr_i('订阅服务未初始化或错误,开始初始化', tag: 'HomeController'); // 设置加载状态 kr_currentListStatus.value = KRHomeViewsListStatus.kr_loading; _initLog.log('设置列表状态为: loading', tag: 'Subscribe'); final startTime = DateTime.now(); _initLog.log('开始调用 kr_refreshAll() 加载订阅数据', tag: 'Subscribe'); // 🔧 Android 15 增强:添加 10 秒超时保护 kr_subscribeService.kr_refreshAll().timeout( const Duration(seconds: 10), onTimeout: () { final elapsed = DateTime.now().difference(startTime).inMilliseconds; _initLog.logWarning('订阅服务初始化超时(10秒), 实际耗时: ${elapsed}ms', tag: 'Subscribe'); KRLogUtil.kr_w('⏱️ 订阅服务初始化超时(10秒)', tag: 'HomeController'); kr_currentListStatus.value = KRHomeViewsListStatus.kr_none; throw TimeoutException('订阅服务初始化超时'); }, ).then((_) async { _checkQuickConnectAutoStart(); final elapsed = DateTime.now().difference(startTime).inMilliseconds; _initLog.logSuccess('订阅服务初始化完成, 耗时: ${elapsed}ms', tag: 'Subscribe'); _initLog.log('最终列表状态: ${kr_currentListStatus.value}', tag: 'Subscribe'); // 🔧 新增:记录订阅数据详情 final subscribeList = kr_subscribeService.kr_availableSubscribes; _initLog.log('订阅列表数量: ${subscribeList.length}', tag: 'Subscribe'); if (subscribeList.isEmpty) { _initLog.logWarning('⚠️ 订阅列表为空!用户可能没有有效订阅', tag: 'Subscribe'); } else { for (int i = 0; i < subscribeList.length; i++) { final sub = subscribeList[i]; _initLog.log( '订阅[$i]: ID=${sub.id}, 名称=${sub.name}, 试用=${sub.isTryOut}', tag: 'Subscribe'); _initLog.log(' 过期时间: ${sub.expireTime}, 流量: ${sub.traffic}', tag: 'Subscribe'); } } // 记录当前选中的订阅 final currentSub = kr_subscribeService.kr_currentSubscribe.value; if (currentSub != null) { _initLog.log('当前选中订阅: ${currentSub.name} (ID: ${currentSub.id})', tag: 'Subscribe'); } else { _initLog.logWarning('⚠️ 没有选中任何订阅', tag: 'Subscribe'); } // 记录订阅服务的最终状态 final finalServiceStatus = kr_subscribeService.kr_currentStatus.value; _initLog.log('订阅服务最终状态: $finalServiceStatus', tag: 'Subscribe'); KRLogUtil.kr_i('✅ 订阅服务初始化完成', tag: 'HomeController'); // 🔧 关键修复:订阅服务初始化完成后,关闭日志文件 await _initLog.finalize(); // 成功后状态由服务自己控制,不在这里设置 }).catchError((error) async { final elapsed = DateTime.now().difference(startTime).inMilliseconds; _initLog.logError('订阅服务初始化失败, 耗时: ${elapsed}ms', tag: 'Subscribe', error: error); _initLog.log('设置列表状态为: none (失败)', tag: 'Subscribe'); KRLogUtil.kr_e('❌ 订阅服务初始化失败: $error', tag: 'HomeController'); // 🔧 关键修复:即使失败也要关闭日志文件 await _initLog.finalize(); // 🔧 Android 15 优化:失败时设置为 none 而非 error,允许用户手动重试 kr_currentListStatus.value = KRHomeViewsListStatus.kr_none; }); } else if (currentStatus == KRSubscribeServiceStatus.kr_loading) { KRLogUtil.kr_i('订阅服务正在初始化中', tag: 'HomeController'); kr_currentListStatus.value = KRHomeViewsListStatus.kr_loading; } else if (currentStatus == KRSubscribeServiceStatus.kr_success) { KRLogUtil.kr_i('订阅服务已成功初始化', tag: 'HomeController'); kr_currentListStatus.value = KRHomeViewsListStatus.kr_none; } } catch (e) { KRLogUtil.kr_e('❌ 确保订阅服务初始化异常: $e', tag: 'HomeController'); // 🔧 Android 15 优化:异常时设置为 none 而非 error kr_currentListStatus.value = KRHomeViewsListStatus.kr_none; } } /// 处理登录状态变化 void _kr_handleLoginStatusChange(bool isLoggedIn) { try { if (isLoggedIn) { // 再次验证登录状态的有效性 final isValidLogin = KRAppRunData().kr_token != null && KRAppRunData().kr_token!.isNotEmpty; if (isValidLogin) { kr_currentViewStatus.value = KRHomeViewsStatus.kr_loggedIn; KRLogUtil.kr_i('登录状态变化:设置为已登录', tag: 'HomeController'); // 订阅服务已在 splash 页面初始化,此处无需重复初始化 KRLogUtil.kr_i('订阅服务已在启动页初始化,跳过重复初始化', tag: 'HomeController'); } else { KRLogUtil.kr_w('登录状态为true但token为空,重置为未登录', tag: 'HomeController'); kr_currentViewStatus.value = KRHomeViewsStatus.kr_notLoggedIn; } } else { kr_currentViewStatus.value = KRHomeViewsStatus.kr_notLoggedIn; KRLogUtil.kr_i('登录状态变化:设置为未登录', tag: 'HomeController'); // 重置列表状态,防止出现无限高度 kr_currentListStatus.value = KRHomeViewsListStatus.kr_none; // 退出登录清理订阅服务 kr_subscribeService.kr_logout(); } } catch (e) { KRLogUtil.kr_e('处理登录状态变化失败: $e', tag: 'HomeController'); kr_currentViewStatus.value = KRHomeViewsStatus.kr_notLoggedIn; } } /// 添加状态同步检查 void _kr_addStatusSyncCheck() { // 在下一帧检查状态同步 WidgetsBinding.instance.addPostFrameCallback((_) { _kr_syncLoginStatus(); }); } /// 同步登录状态 void _kr_syncLoginStatus() { try { final currentLoginStatus = KRAppRunData().kr_isLogin.value; final currentViewStatus = kr_currentViewStatus.value; KRLogUtil.kr_i( '状态同步检查: login=$currentLoginStatus, view=$currentViewStatus', tag: 'HomeController'); // 检查状态是否一致 if (currentViewStatus == KRHomeViewsStatus.kr_loggedIn && !currentLoginStatus) { KRLogUtil.kr_w('状态不一致:视图显示已登录但实际未登录,修正状态', tag: 'HomeController'); kr_currentViewStatus.value = KRHomeViewsStatus.kr_notLoggedIn; } else if (currentViewStatus == KRHomeViewsStatus.kr_notLoggedIn && currentLoginStatus) { KRLogUtil.kr_w('状态不一致:视图显示未登录但实际已登录,修正状态', tag: 'HomeController'); _kr_validateAndSetLoginStatus(); } } catch (e) { KRLogUtil.kr_e('状态同步检查失败: $e', tag: 'HomeController'); } } /// 属性数据 void kr_refreshAll() { kr_subscribeService.kr_refreshAll(); } /// 绑定订阅状态 void _bindSubscribeStatus() { ever(kr_subscribeService.kr_currentStatus, (data) { KRLogUtil.kr_i('订阅服务状态变化: $data', tag: 'HomeController'); if (KRAppRunData.getInstance().kr_isLogin.value) { switch (data) { case KRSubscribeServiceStatus.kr_loading: KRLogUtil.kr_i('订阅服务加载中', tag: 'HomeController'); kr_currentListStatus.value = KRHomeViewsListStatus.kr_loading; break; case KRSubscribeServiceStatus.kr_error: KRLogUtil.kr_w('订阅服务错误', tag: 'HomeController'); kr_currentListStatus.value = KRHomeViewsListStatus.kr_error; // 不再自动重试,让用户手动刷新 break; case KRSubscribeServiceStatus.kr_success: KRLogUtil.kr_i('订阅服务成功', tag: 'HomeController'); kr_cutTag.value = ''; kr_cutSeletedTag.value = ''; kr_currentNodeName.value = ""; if (kr_currentListStatus.value != KRHomeViewsListStatus.kr_none) { kr_currentListStatus.value = KRHomeViewsListStatus.kr_none; } else { // kr_updateBottomPanelHeight(); } _kr_testLatencyWithoutVpn(); break; case KRSubscribeServiceStatus.kr_none: KRLogUtil.kr_i('订阅服务未初始化', tag: 'HomeController'); // 订阅服务已在 splash 页面初始化,如果状态为none可能是网络问题 if (kr_currentViewStatus.value == KRHomeViewsStatus.kr_loggedIn && kr_subscribeService.kr_availableSubscribes.isEmpty) { KRLogUtil.kr_i('订阅服务状态为none,可能是网络问题,已在启动页初始化', tag: 'HomeController'); } break; } } else { KRLogUtil.kr_i('用户未登录,忽略订阅状态变化', tag: 'HomeController'); } }); // 🔧 修复:保存 EventBus 监听器 Worker,以便在控制器销毁时清理 _eventBusWorker = KREventBus().kr_listenMessages( [KRMessageType.kr_payment, KRMessageType.kr_subscribe_update], _kr_handleMessage, ); if (kDebugMode) { print('✅ [HomeController] EventBus 监听器已注册'); } } /// 处理消息 void _kr_handleMessage(KRMessageData message) { print('✅ [HomeController] EventBus message.kr_type${message.kr_type}'); switch (message.kr_type) { case KRMessageType.kr_payment: kr_refreshAll(); break; case KRMessageType.kr_subscribe_update: // 处理订阅更新消息 // 显示提示框 KRDialog.show( title: AppTranslations.kr_home.subscriptionUpdated, message: AppTranslations.kr_home.subscriptionUpdatedMessage, confirmText: AppTranslations.kr_dialog.kr_confirm, cancelText: AppTranslations.kr_dialog.kr_cancel, onConfirm: () { kr_refreshAll(); }, onCancel: () => Get.back(), ); break; // TODO: Handle this case. } } /// 绑定连接状态 void _bindConnectionStatus() { // 添加更详细的状态监听 ever(KRSingBoxImp.instance.kr_status, (status) { KRLogUtil.kr_i('🔄 连接状态变化: $status', tag: 'HomeController'); KRLogUtil.kr_i('📊 当前状态类型: ${status.runtimeType}', tag: 'HomeController'); // 🔧 保守修复: 记录状态变化时间 _lastStatusChangeTime = DateTime.now(); switch (status) { case SingboxStopped(): KRLogUtil.kr_i('🔴 状态: 已停止', tag: 'HomeController'); kr_connectText.value = AppTranslations.kr_home.disconnected; kr_stopConnectionTimer(); kr_resetConnectionInfo(); // 取消连接超时处理 _cancelConnectionTimeout(); kr_currentSpeed.value = "--"; kr_isLatency.value = false; kr_isConnected.value = false; kr_currentNodeLatency.value = -2; // 强制刷新 isConnected 状态 kr_isConnected.refresh(); break; case SingboxStarting(): KRLogUtil.kr_i('🟡 状态: 正在启动', tag: 'HomeController'); kr_connectText.value = AppTranslations.kr_home.connecting; kr_currentSpeed.value = AppTranslations.kr_home.connecting; kr_currentNodeLatency.value = -1; kr_isConnected.value = false; // 修复:启动中应该为false // 启动连接超时处理 _startConnectionTimeout(); break; case SingboxStarted(): KRLogUtil.kr_i('🟢 状态: 已启动', tag: 'HomeController'); if (kDebugMode) {} // 取消连接超时处理 _cancelConnectionTimeout(); kr_connectText.value = AppTranslations.kr_home.connected; kr_startConnectionTimer(); kr_updateConnectionInfo(); kr_isLatency.value = false; kr_isConnected.value = true; // 🔧 关键修复:如果延迟还是-1(连接中状态),立即设置为0(已连接但延迟未知) if (kr_currentNodeLatency.value == -1) { if (kDebugMode) {} kr_currentNodeLatency.value = 0; kr_currentNodeLatency.refresh(); // 强制刷新延迟值 if (kDebugMode) {} } // 🔧 修复:立即尝试更新延迟值 _kr_updateLatencyOnConnected(); // 强制刷新 isConnected 状态 kr_isConnected.refresh(); // 强制更新UI update(); if (kDebugMode) {} break; case SingboxStopping(): KRLogUtil.kr_i('🟠 状态: 正在停止', tag: 'HomeController'); kr_connectText.value = AppTranslations.kr_home.disconnecting; kr_isConnected.value = false; kr_currentSpeed.value = "--"; break; } // 强制更新UI update(); }); // 添加活动组监听,确保状态同步 ever(KRSingBoxImp.instance.kr_activeGroups, (value) { KRLogUtil.kr_i('📡 活动组更新,数量: ${value.length}', tag: 'HomeController'); if (value.isEmpty) { KRLogUtil.kr_w('⚠️ 活动组为空', tag: 'HomeController'); // 🔧 修复:如果已连接但活动组为空,设置延迟为0而不是-1 if (kr_isConnected.value && kr_currentNodeLatency.value == -1) { KRLogUtil.kr_w('⚠️ 已连接但活动组为空,设置延迟为0', tag: 'HomeController'); kr_currentNodeLatency.value = 0; } return; } try { bool hasSelector = false; for (var element in value) { KRLogUtil.kr_i( '📋 处理组: ${element.tag}, 类型: ${element.type}, 选中: ${element.selected}', tag: 'HomeController'); if (element.type == ProxyType.selector) { hasSelector = true; _kr_handleSelectorProxy(element, value); } else if (element.type == ProxyType.urltest) { KRLogUtil.kr_d('URL测试代理选中: ${element.selected}', tag: 'HomeController'); } } // 🔧 修复:如果已连接但没有selector组,设置延迟为0 if (!hasSelector && kr_isConnected.value && kr_currentNodeLatency.value == -1) { KRLogUtil.kr_w('⚠️ 已连接但无selector组,设置延迟为0', tag: 'HomeController'); kr_currentNodeLatency.value = 0; } // 强制更新UI update(); } catch (e) { KRLogUtil.kr_e('处理活动组时发生错误: $e', tag: 'HomeController'); // 🔧 修复:发生错误时,如果已连接,设置延迟为0 if (kr_isConnected.value && kr_currentNodeLatency.value == -1) { KRLogUtil.kr_w('⚠️ 处理活动组错误,设置延迟为0', tag: 'HomeController'); kr_currentNodeLatency.value = 0; } } }); ever(KRSingBoxImp.instance.kr_allGroups, (value) { List updateTags = []; // 收集需要更新的标记ID for (var element in value) { for (var subElement in element.items) { var node = kr_subscribeService.keyList[subElement.tag]; if (node != null) { if (subElement.urlTestDelay != 0) { node.urlTestDelay.value = subElement.urlTestDelay; updateTags.add(subElement.tag); // 添加需要更新的标记ID } } } // 批量更新所有变化的标记 } if (updateTags.isNotEmpty) { kr_updateMarkers(updateTags); } }); } /// 🔧 重构: 参考 hiddify-app 的 toggleConnection 实现 Future kr_toggleSwitch(bool value) async { final currentStatus = KRSingBoxImp.instance.kr_status.value; KRLogUtil.kr_i( '🔵 toggleSwitch 被调用: value=$value, currentStatus=$currentStatus', tag: 'HomeController'); if (kDebugMode) {} // 🔧 保守修复: 检测状态是否卡住(超过5秒) if (currentStatus is SingboxStarting || currentStatus is SingboxStopping) { final now = DateTime.now(); if (_lastStatusChangeTime != null && now.difference(_lastStatusChangeTime!) > const Duration(seconds: 5)) { // 状态卡住,强制重置 KRLogUtil.kr_w('⚠️ 检测到状态卡住超过10秒 (当前: $currentStatus),执行强制重置', tag: 'HomeController'); await _forceResetState(); // 重置后重新尝试操作 KRLogUtil.kr_i('🔄 状态已重置,继续执行切换操作', tag: 'HomeController'); } else { KRLogUtil.kr_i('🔄 正在切换中,忽略本次操作 (当前状态: $currentStatus)', tag: 'HomeController'); if (kDebugMode) {} return; } } try { // 🔧 保守修复: 记录状态变化时间 _lastStatusChangeTime = DateTime.now(); if (value) { // 开启连接 KRLogUtil.kr_i('🔄 开始连接...', tag: 'HomeController'); if (kDebugMode) {} await _kr_prepareCountrySelectionBeforeStart(); final selectedAfter = await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG'); // KRLogUtil.kr_i('准备后 SELECTED_NODE_TAG: ${selectedAfter ?? ''}', // tag: 'HomeController'); // KRLogUtil.kr_i('准备后 kr_currentNodeName: ${kr_currentNodeName.value}', // tag: 'HomeController'); // KRLogUtil.kr_i('准备后 kr_cutTag: ${kr_cutTag.value}', // tag: 'HomeController'); // KRLogUtil.kr_i('准备后 kr_cutSeletedTag: ${kr_cutSeletedTag.value}', // tag: 'HomeController'); await kr_performNodeSwitch(selectedAfter!); await KRSingBoxImp.instance.kr_start(); KRLogUtil.kr_i('✅ 连接命令已发送', tag: 'HomeController'); if (kDebugMode) {} // 🔧 修复: 等待状态更新,最多3秒 await _waitForStatus(SingboxStatus.started().runtimeType, maxSeconds: 3); } else { // 关闭连接 KRLogUtil.kr_i('🛑 开始断开连接...', tag: 'HomeController'); if (kDebugMode) {} await KRSingBoxImp.instance.kr_stop().timeout( const Duration(seconds: 10), onTimeout: () { KRLogUtil.kr_e('⚠️ 停止操作超时', tag: 'HomeController'); throw TimeoutException('Stop operation timeout'); }, ); KRLogUtil.kr_i('✅ 断开命令已发送', tag: 'HomeController'); if (kDebugMode) {} // 🔧 保守修复: 等待状态更新,增加超时处理 final success = await _waitForStatus( SingboxStatus.stopped().runtimeType, maxSeconds: 3, ); if (!success) { // 停止超时,强制同步状态 KRLogUtil.kr_w('⚠️ VPN 停止超时(3秒),强制同步状态', tag: 'HomeController'); kr_forceSyncConnectionStatus(); } } } catch (e) { KRLogUtil.kr_e('❌ 切换失败: $e', tag: 'HomeController'); if (kDebugMode) {} // 发生错误时强制同步状态 kr_forceSyncConnectionStatus(); } if (kDebugMode) {} } /// 🔧 等待状态达到预期值 /// 🔧 保守修复: 返回 bool 表示是否成功达到预期状态 Future _waitForStatus(Type expectedType, {int maxSeconds = 3}) async { if (kDebugMode) {} final startTime = DateTime.now(); while (DateTime.now().difference(startTime).inSeconds < maxSeconds) { final currentStatus = KRSingBoxImp.instance.kr_status.value; if (kDebugMode) {} if (currentStatus.runtimeType == expectedType) { if (kDebugMode) {} // 强制同步确保 kr_isConnected 正确 kr_forceSyncConnectionStatus(); // 🔧 更新状态变化时间 _lastStatusChangeTime = DateTime.now(); return true; // 成功 } await Future.delayed(const Duration(milliseconds: 100)); } // 🔧 保守修复: 超时后记录详细日志 final finalStatus = KRSingBoxImp.instance.kr_status.value; KRLogUtil.kr_w( '⏱️ 等待状态超时: 期望=${expectedType.toString()}, 实际=${finalStatus.runtimeType}, 耗时=${maxSeconds}秒', tag: 'HomeController', ); if (kDebugMode) {} kr_forceSyncConnectionStatus(); return false; // 超时失败 } Future _kr_prepareCountrySelectionBeforeStart() async { try { // KRLogUtil.kr_i('开始准备国家选择', tag: 'CountrySelect'); final storedCountry = kr_selectedCountryTag.value; // KRLogUtil.kr_i('使用响应式 SELECTED_COUNTRY_TAG: $storedCountry', // tag: 'CountrySelect'); if (storedCountry == 'auto') { KRLogUtil.kr_w('当前为 auto,按全局最优节点进行选择', tag: 'CountrySelect'); final best = _kr_selectBestNodeTagGlobal(); if (best != null && best.isNotEmpty) { // KRLogUtil.kr_i('选中全局最优节点: $best', tag: 'CountrySelect'); await KRSecureStorage() .kr_saveData(key: 'SELECTED_NODE_TAG', value: best); kr_currentNodeName.value = best; kr_cutTag.value = best; kr_cutSeletedTag.value = best; kr_updateConnectionInfo(); return; } // KRLogUtil.kr_w('未找到可用的全局最优节点', tag: 'CountrySelect'); // KRLogUtil.kr_i('触发直接延迟测试以获取有效延迟', tag: 'CountrySelect'); await _kr_testLatencyWithoutVpn(); final bestAfterTest = _kr_selectBestNodeTagGlobal(); if (bestAfterTest != null && bestAfterTest.isNotEmpty) { // KRLogUtil.kr_i('延迟测试后选中全局最优节点: $bestAfterTest', tag: 'CountrySelect'); await KRSecureStorage() .kr_saveData(key: 'SELECTED_NODE_TAG', value: bestAfterTest); kr_currentNodeName.value = bestAfterTest; kr_cutTag.value = bestAfterTest; kr_cutSeletedTag.value = bestAfterTest; kr_updateConnectionInfo(); return; } String? fallback = kr_subscribeService.keyList.values .where((n) => n.tag != 'auto') .map((n) => n.tag) .cast() .firstWhere((t) => t != null && t.isNotEmpty, orElse: () => null); if (fallback != null && fallback.isNotEmpty) { KRLogUtil.kr_w('使用后备节点: $fallback', tag: 'CountrySelect'); await KRSecureStorage() .kr_saveData(key: 'SELECTED_NODE_TAG', value: fallback); final verifyB = await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG'); KRLogUtil.kr_i('写入后校验 SELECTED_NODE_TAG: $verifyB', tag: 'CountrySelect'); kr_currentNodeName.value = fallback; kr_cutTag.value = fallback; kr_cutSeletedTag.value = fallback; kr_updateConnectionInfo(); } else { KRLogUtil.kr_w('没有可用的后备节点', tag: 'CountrySelect'); } return; } final currentSelected = await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG'); KRLogUtil.kr_i('当前 SELECTED_NODE_TAG: ${currentSelected ?? ''}', tag: 'CountrySelect'); bool valid = false; if (currentSelected != null && currentSelected.isNotEmpty) { final node = kr_subscribeService.keyList[currentSelected]; if (node != null) { final d = node.urlTestDelay.value; if (node.country == storedCountry && d > 0 && d < 65535) { valid = true; } KRLogUtil.kr_i( '校验节点: tag=${node.tag}, country=${node.country}, delay=$d, valid=$valid', tag: 'CountrySelect'); } } if (!valid) { KRLogUtil.kr_w('当前节点无效或不匹配国家,将按国家选择最优节点: $storedCountry', tag: 'CountrySelect'); final bestInCountry = _kr_selectBestNodeTagInCountry(storedCountry); if (bestInCountry != null && bestInCountry.isNotEmpty) { KRLogUtil.kr_i('选中国家最优节点: $bestInCountry', tag: 'CountrySelect'); await KRSecureStorage() .kr_saveData(key: 'SELECTED_NODE_TAG', value: bestInCountry); final verify2 = await KRSecureStorage().kr_readData(key: 'SELECTED_NODE_TAG'); KRLogUtil.kr_i('写入后校验 SELECTED_NODE_TAG: $verify2', tag: 'CountrySelect'); kr_currentNodeName.value = bestInCountry; kr_cutTag.value = bestInCountry; kr_cutSeletedTag.value = bestInCountry; kr_updateConnectionInfo(); } else { KRLogUtil.kr_w('未找到国家范围内的可用节点: $storedCountry', tag: 'CountrySelect'); } } KRLogUtil.kr_i('国家选择准备完成', tag: 'CountrySelect'); } catch (_) {} } String? _kr_selectBestNodeTagGlobal() { try { String? bestTag; int bestDelay = 65535; for (final node in kr_subscribeService.keyList.values) { final tag = node.tag; final delay = node.urlTestDelay.value; if (tag == 'auto') continue; if (delay > 0 && delay < 65535) { if (delay < bestDelay) { bestDelay = delay; bestTag = tag; } } } return bestTag; } catch (_) { return null; } } String? _kr_selectBestNodeTagInCountry(String countryCode) { try { String? bestTag; int bestDelay = 65535; for (final node in kr_subscribeService.keyList.values) { final tag = node.tag; final delay = node.urlTestDelay.value; if (tag == 'auto') continue; if (node.country != countryCode) continue; if (delay > 0 && delay < 65535) { if (delay < bestDelay) { bestDelay = delay; bestTag = tag; } } } return bestTag; } catch (_) { return null; } } /// 处理选择器代理 void _kr_handleSelectorProxy(dynamic element, List allGroups) { try { KRLogUtil.kr_d( '处理选择器代理 - 内核选择: ${element.selected}, 用户选择: ${kr_cutTag.value}, 切换中: $_isSwitchingNode', tag: 'HomeController'); // 🔧 关键修复:如果正在切换节点中,不要用内核返回值覆盖 UI // 这是导致"跳两次"问题的根本原因 if (_isSwitchingNode) { KRLogUtil.kr_d('⏳ 节点切换进行中,跳过内核状态同步', tag: 'HomeController'); // 只更新延迟值,不更新选中状态 _kr_updateNodeLatency(element); return; } // 如果用户手动选择了节点(不是auto) if (kr_cutTag.value != "auto") { // 🔧 修复:用户手动选择时,不要用内核返回的 selected 覆盖用户选择 // 只有当内核返回的节点与用户选择一致时,才更新 kr_cutSeletedTag if (element.selected == kr_cutTag.value) { kr_cutSeletedTag.value = element.selected; } _kr_handleManualMode(element); return; } // 默认auto模式处理 - 此时可以用内核返回值更新 UI if (element.selected.isNotEmpty) { kr_cutSeletedTag.value = element.selected; } _kr_handleAutoMode(element, allGroups); } catch (e) { KRLogUtil.kr_e('处理选择器代理出错: $e', tag: 'HomeController'); } } /// 处理手动模式 void _kr_handleManualMode(dynamic element) { try { KRLogUtil.kr_d( '处理手动模式 - 内核选择: ${element.selected}, 用户选择: ${kr_cutTag.value}', tag: 'HomeController'); // 🔧 关键修复:不要用内核返回的 selected 覆盖用户手动选择 // 只有当内核返回的节点与用户选择一致时,才更新相关状态 // 这可以防止 UI 跳动 // 更新延迟值(这个始终需要更新) _kr_updateNodeLatency(element); // 只有当内核确认切换成功时,才更新 UI 状态 if (element.selected == kr_cutTag.value) { kr_cutSeletedTag.value = element.selected; kr_currentNodeName.value = kr_truncateText(element.selected, maxLength: 25); // kr_moveToSelectedNode(); KRLogUtil.kr_d('✅ 内核确认节点切换成功: ${element.selected}', tag: 'HomeController'); } else { // 内核返回的节点与用户选择不一致,保持用户选择的显示 KRLogUtil.kr_d('⏳ 等待内核切换到用户选择的节点: ${kr_cutTag.value}', tag: 'HomeController'); } } catch (e) { KRLogUtil.kr_e('处理手动模式出错: $e', tag: 'HomeController'); } } /// 更新节点延迟 void _kr_updateNodeLatency(dynamic element) { try { bool delayUpdated = false; for (var subElement in element.items) { if (subElement.tag == element.selected) { // 检查延迟是否有效 if (subElement.urlTestDelay != 0) { kr_currentNodeLatency.value = subElement.urlTestDelay; delayUpdated = true; } // 更新速度显示 // kr_updateSpeed(subElement.urlTestDelay); // // 停止动画 // _kr_speedAnimationController.reverse(); KRLogUtil.kr_d('更新节点延迟: ${subElement.urlTestDelay}', tag: 'HomeController'); break; } } // 🔧 修复:如果已连接但延迟未更新,设置为0 if (!delayUpdated && kr_isConnected.value && kr_currentNodeLatency.value == -1) { KRLogUtil.kr_w('⚠️ 已连接但延迟未更新,设置为0', tag: 'HomeController'); kr_currentNodeLatency.value = 0; } } catch (e) { KRLogUtil.kr_e('更新节点延迟出错: $e', tag: 'HomeController'); // 🔧 修复:发生错误时,根据连接状态设置合适的值 if (kr_isConnected.value) { kr_currentNodeLatency.value = 0; // 已连接但延迟未知 } else { kr_currentNodeLatency.value = -2; // 未连接 } kr_currentSpeed.value = "--"; // 停止动画 // _kr_speedAnimationController.reverse(); } } /// 处理自动模式 void _kr_handleAutoMode(dynamic element, List allGroups) { KRLogUtil.kr_d('处理自动模式 - 活动组: ${allGroups.toString()}', tag: 'HomeController'); // 更新auto模式的延迟 _kr_updateAutoLatency(element); // 查找并处理urltest类型的组 for (var item in allGroups) { if (item.type == ProxyType.urltest) { // 检查延迟是否有效(小于65535) if (item.selected != null && _kr_isValidLatency(item.selected)) { kr_cutSeletedTag.value = item.selected; kr_currentNodeName.value = kr_truncateText("${item.selected}(auto)", maxLength: 25); // kr_moveToSelectedNode(); kr_updateConnectionInfo(); break; } else { // 如果延迟无效,尝试选择延迟最小的节点 _kr_selectBestLatencyNode(item.items); break; } } } } /// 选择延迟最小的节点 void _kr_selectBestLatencyNode(List items) { int minDelay = 65535; String? bestNode = null; for (var item in items) { // 只考虑有效的延迟值(小于65535且大于0) if (item.urlTestDelay < minDelay && item.urlTestDelay < 65535 && item.urlTestDelay > 0) { minDelay = item.urlTestDelay; bestNode = item.tag; } } if (bestNode != null) { kr_cutSeletedTag.value = bestNode; kr_currentNodeName.value = kr_truncateText("${bestNode}(auto)", maxLength: 25); // kr_moveToSelectedNode(); kr_updateConnectionInfo(); } } /// 更新连接信息 void kr_updateConnectionInfo() { try { final selectedNode = kr_subscribeService.keyList[kr_cutSeletedTag.value]; if (selectedNode != null) { KRLogUtil.kr_d( '更新节点信息 - 协议: ${selectedNode.protocol}, IP: ${selectedNode.serverAddr}', tag: 'HomeController'); kr_currentProtocol.value = kr_truncateText(selectedNode.protocol, maxLength: 15); kr_currentIp.value = kr_truncateText(selectedNode.serverAddr, maxLength: 20); } else { KRLogUtil.kr_d('未找到选中的节点: ${kr_cutSeletedTag.value}', tag: 'HomeController'); kr_currentProtocol.value = "--"; kr_currentIp.value = "--"; } } catch (e) { KRLogUtil.kr_e('更新连接信息失败: $e', tag: 'HomeController'); kr_currentProtocol.value = "--"; kr_currentIp.value = "--"; } } /// 处理文本截断 String kr_truncateText(String text, {int maxLength = 20}) { if (text.length <= maxLength) return text; return '${text.substring(0, maxLength)}...'; } /// 检查延迟是否有效 bool _kr_isValidLatency(String? nodeTag) { if (nodeTag == null) return false; // 从keyList中获取节点信息 final node = kr_subscribeService.keyList[nodeTag]; if (node == null) return false; // 检查延迟是否有效(小于65535且大于0) return node.urlTestDelay.value < 65535 && node.urlTestDelay.value > 0; } /// 更新自动模式延迟 void _kr_updateAutoLatency(dynamic element) { for (var subElement in element.items) { if (subElement.tag == "auto") { if (subElement.urlTestDelay != 0) { kr_currentNodeLatency.value = subElement.urlTestDelay; } else { kr_currentNodeLatency.value = -2; // 当延迟为 0 时,设置为未连接状态 } break; } } } /// 切换列表状态 void kr_switchListStatus(KRHomeViewsListStatus status) { kr_currentListStatus.value = status; } // 切换订阅 Future kr_switchSubscribe( KRUserAvailableSubscribeItem subscribe) async { try { KRLogUtil.kr_i("kr_switchSubscribe", tag: "kr_switchSubscribe"); // 通知订阅服务切换订阅 await kr_subscribeService.kr_switchSubscribe(subscribe); } catch (e) { KRLogUtil.kr_e('切换订阅失败: $e', tag: 'HomeController'); rethrow; } } // 🔒 节点切换状态锁和节流控制 bool _isSwitchingNode = false; DateTime? _lastSwitchTime; static const Duration _switchThrottleDuration = Duration(milliseconds: 2000); // 2秒节流 /// 🔧 修复:统一的节点切换方法(包含UI同步和后台操作等待) /// 执行节点切换,包括UI更新和后台操作的完整同步 /// 返回 true 表示切换成功,false 表示失败 Future kr_performNodeSwitch(String tag) async { try { // 🔒 状态锁:防止并发切换 if (_isSwitchingNode) { KRLogUtil.kr_w('⚠️ 节点切换正在进行中,忽略重复请求', tag: 'HomeController'); KRCommonUtil.kr_showToast('请等待当前节点切换完成'); return false; } if (kr_cutTag.value == tag) { return false; } // 🔒 节流控制:2秒内的重复切换请求直接忽略 final now = DateTime.now(); if (_lastSwitchTime != null && now.difference(_lastSwitchTime!) < _switchThrottleDuration) { final remainingTime = _switchThrottleDuration.inMilliseconds - now.difference(_lastSwitchTime!).inMilliseconds; KRLogUtil.kr_w('⚠️ 切换过于频繁,请等待 ${remainingTime}ms', tag: 'HomeController'); KRCommonUtil.kr_showToast('切换过于频繁,请稍后再试'); return false; } // 🔒 设置状态锁和记录时间 _isSwitchingNode = true; _lastSwitchTime = now; KRLogUtil.kr_i('🔄 开始切换节点: $tag', tag: 'HomeController'); // 1. 保存原节点,以备失败恢复 final originalTag = kr_cutTag.value; // 2. 设置切换中状态 kr_cutTag.value = tag; kr_currentNodeName.value = tag; // 🔧 修复:更新节点名称 // 3. 如果VPN未连接,只更新UI变量即可 if (!kr_isConnected.value) { KRLogUtil.kr_i('📴 VPN未连接,只更新UI变量: $tag', tag: 'HomeController'); kr_cutSeletedTag.value = tag; kr_updateConnectionInfo(); // kr_moveToSelectedNode(); // 🔧 修复:保存节点选择以便VPN启动时应用 KRLogUtil.kr_i('💾 保存节点选择以便稍后应用: $tag', tag: 'HomeController'); KRSecureStorage() .kr_saveData(key: 'SELECTED_NODE_TAG', value: tag) .then((_) { KRLogUtil.kr_i('✅ 节点选择已保存: $tag', tag: 'HomeController'); }).catchError((e) { KRLogUtil.kr_e('❌ 保存节点选择失败: $e', tag: 'HomeController'); }); return true; } // 4. VPN已连接,使用热切换方式(selectOutbound)切换节点 try { KRLogUtil.kr_i('🔌 VPN已连接,使用热切换方式切换节点: $tag', tag: 'HomeController'); // 🔧 设置切换中状态,显示"正在连接" kr_currentNodeLatency.value = -1; kr_isLatency.value = true; // 显示加载动画 // 🔧 保存新节点选择 KRLogUtil.kr_i('💾 保存新节点选择: $tag', tag: 'HomeController'); await KRSecureStorage() .kr_saveData(key: 'SELECTED_NODE_TAG', value: tag); // 🚀 核心改进:使用 selectOutbound 进行热切换(参考 hiddify-app) // 优势:不重启VPN,保持连接状态,切换瞬间完成,VPN开关不闪烁 KRLogUtil.kr_i('🔄 [热切换] 调用 selectOutbound 切换节点...', tag: 'HomeController'); // 🔧 关键修复:确定正确的 selector 组 tag // selectOutbound(groupTag, outboundTag) - 第一个参数是组的tag,不是节点的tag final activeGroups = KRSingBoxImp.instance.kr_activeGroups; String selectorGroupTag = 'select'; // 默认值 // 查找 selector 类型的组 for (var group in activeGroups) { if (group.type == ProxyType.selector) { selectorGroupTag = group.tag; KRLogUtil.kr_i('🔍 找到 selector 组: $selectorGroupTag', tag: 'HomeController'); break; } } KRLogUtil.kr_i('📡 调用 selectOutbound("$selectorGroupTag", "$tag")', tag: 'HomeController'); // 调用 sing-box 的 selectOutbound API final result = await KRSingBoxImp.instance.kr_singBox .selectOutbound(selectorGroupTag, tag) .run(); // 处理切换结果 result.fold( (error) { // 切换失败 KRLogUtil.kr_e('❌ selectOutbound 调用失败: $error', tag: 'HomeController'); throw Exception('节点切换失败: $error'); }, (_) { // 切换成功 KRSingBoxImp.instance.kr_startNodeSelectionMonitor(tag); KRLogUtil.kr_i('✅ selectOutbound 调用成功', tag: 'HomeController'); }, ); // 后台切换成功,立即更新UI(乐观更新) kr_cutSeletedTag.value = tag; kr_updateConnectionInfo(); // kr_moveToSelectedNode(); // 🔧 短暂等待以确保内核状态同步(相比重启,等待时间大幅缩短) KRLogUtil.kr_i('⏳ [热切换] 等待内核状态同步(200ms)...', tag: 'HomeController'); await Future.delayed(const Duration(milliseconds: 200)); // 🔍 验证节点是否真正切换成功 KRLogUtil.kr_i('🔍 [验证] 检查节点切换结果...', tag: 'HomeController'); try { final updatedGroups = KRSingBoxImp.instance.kr_activeGroups; final selectGroup = updatedGroups.firstWhere( (group) => group.type == ProxyType.selector, orElse: () => throw Exception('未找到 selector 组'), ); KRLogUtil.kr_i( '📊 [验证] ${selectGroup.tag}组当前选中: ${selectGroup.selected}', tag: 'HomeController'); KRLogUtil.kr_i('📊 [验证] 目标节点: $tag', tag: 'HomeController'); if (selectGroup.selected != tag) { KRLogUtil.kr_w('⚠️ [验证] 节点选择验证失败,实际选中: ${selectGroup.selected}', tag: 'HomeController'); // 不抛出异常,但记录警告 } else { KRLogUtil.kr_i('✅ [验证] 节点选择验证成功!', tag: 'HomeController'); } } catch (e) { KRLogUtil.kr_w('⚠️ [验证] 节点验证过程出错: $e', tag: 'HomeController'); } // 更新延迟信息 _kr_updateLatencyOnConnected(); KRLogUtil.kr_i('✅ 节点热切换成功,VPN保持连接: $tag', tag: 'HomeController'); return true; } catch (switchError) { // 后台切换失败,恢复到原节点 KRLogUtil.kr_e('❌ 后台节点切换失败: $switchError', tag: 'HomeController'); // 恢复原状态 kr_cutTag.value = originalTag; kr_currentNodeName.value = originalTag; // 🔧 修复:同时恢复节点名称显示 kr_currentNodeLatency.value = -2; // 恢复为未连接状态 // 恢复原节点选择 try { await KRSecureStorage() .kr_saveData(key: 'SELECTED_NODE_TAG', value: originalTag); } catch (e) { KRLogUtil.kr_e('❌ 恢复节点选择失败: $e', tag: 'HomeController'); } // 显示错误提示给用户 KRCommonUtil.kr_showToast('节点切换失败,已恢复为: $originalTag'); return false; } } catch (e) { KRLogUtil.kr_e('❌ 节点切换异常: $e', tag: 'HomeController'); kr_isLatency.value = false; KRCommonUtil.kr_showToast('节点切换异常,请重试'); return false; } finally { // 🔒 释放状态锁 _isSwitchingNode = false; // 关闭加载状态 kr_isLatency.value = false; KRLogUtil.kr_i('🔄 节点切换流程完成', tag: 'HomeController'); } } /// 🔧 修复:简化的 kr_selectNode 方法 /// 现在只是委托给新的 kr_performNodeSwitch 方法 /// 为了保持向后兼容,保留此方法但改为调用新方法 Future kr_selectNode(String tag) async { return await kr_performNodeSwitch(tag); } /// 获取当前节点国家 /// 🔧 修复:优先使用 kr_cutSeletedTag,避免依赖可能为空的 kr_activeGroups /// 策略: /// 1. 如果 kr_cutTag 不是 auto,直接使用(用户手动选择的节点) /// 2. 如果是 auto,优先使用 kr_cutSeletedTag(保存了实际选中的节点) /// 3. 如果 kr_cutSeletedTag 也是 auto 或空,再尝试从 kr_activeGroups 获取 String kr_getCurrentNodeCountry() { // KRLogUtil.kr_i('========== 开始获取国家代码 ==========', // tag: 'getCurrentNodeCountry'); // KRLogUtil.kr_i('kr_cutTag: ${kr_cutTag.value}', // tag: 'getCurrentNodeCountry'); // KRLogUtil.kr_i('kr_cutSeletedTag: ${kr_cutSeletedTag.value}', // tag: 'getCurrentNodeCountry'); // KRLogUtil.kr_i('keyList 节点总数: ${kr_subscribeService.keyList.length}', // tag: 'getCurrentNodeCountry'); String actualTag; if (kr_selectedCountryTag.value == 'auto' && kr_cutSeletedTag.value.isNotEmpty) { actualTag = kr_cutSeletedTag.value; } // 🔧 优先策略: // 1. 如果 kr_cutTag 不是 auto,直接使用(用户手动选择的节点) else if (kr_cutTag.value != 'auto' && kr_cutTag.value != 'select' && kr_cutTag.value.isNotEmpty) { // 用户手动选择了具体节点 actualTag = kr_cutTag.value; // KRLogUtil.kr_i('✅ 使用手动选择的节点: $actualTag', tag: 'getCurrentNodeCountry'); } // 2. 如果是 auto,优先使用 kr_cutSeletedTag(保存了实际选中的节点) else if (kr_cutSeletedTag.value.isNotEmpty && kr_cutSeletedTag.value != 'auto' && kr_cutSeletedTag.value != 'select') { // auto 模式下,使用保存的实际节点 actualTag = kr_cutSeletedTag.value; // KRLogUtil.kr_i('✅ 使用 auto 模式下的实际节点 (kr_cutSeletedTag): $actualTag', // tag: 'getCurrentNodeCountry'); } // 3. 降级:尝试从活动组获取 else { try { // KRLogUtil.kr_i('⚠️ 尝试从活动组获取实际节点', tag: 'getCurrentNodeCountry'); // KRLogUtil.kr_i('活动组数量: ${KRSingBoxImp.instance.kr_activeGroups.length}', // tag: 'getCurrentNodeCountry'); // 🔧 修复:活动组为空时,尝试使用 allGroups if (KRSingBoxImp.instance.kr_activeGroups.isEmpty) { print('[getCurrentNodeCountry] ⚠️ 活动组为空,尝试使用 allGroups'); KRLogUtil.kr_w('⚠️ 活动组为空,尝试使用 allGroups', tag: 'getCurrentNodeCountry'); final allGroups = KRSingBoxImp.instance.kr_allGroups; print('[getCurrentNodeCountry] allGroups 数量: ${allGroups.length}'); if (allGroups.isEmpty) { print('[getCurrentNodeCountry] ❌ allGroups 也为空,返回空字符串'); KRLogUtil.kr_w('❌ allGroups 也为空,返回空字符串', tag: 'getCurrentNodeCountry'); return ''; } // 从 allGroups 中查找 select 组 final selectGroup = allGroups.firstWhere( (group) => group.tag == 'select', orElse: () => throw Exception('未找到 select 组'), ); print( '[getCurrentNodeCountry] selectGroup.selected: ${selectGroup.selected}'); if (selectGroup.selected.isEmpty || selectGroup.selected == 'auto' || selectGroup.selected == 'select') { print('[getCurrentNodeCountry] select 组选中的是 auto,查找 urltest 组'); // 如果 select 组选中的是 auto,从 urltest 组获取 final urlTestGroup = allGroups.firstWhere( (group) => group.type == ProxyType.urltest, orElse: () => throw Exception('未找到 urltest 组'), ); if (urlTestGroup.selected.isNotEmpty) { actualTag = urlTestGroup.selected; print( '[getCurrentNodeCountry] ✅ 从 allGroups 的 urltest 组获取节点: $actualTag'); KRLogUtil.kr_i('✅ 从 allGroups 的 urltest 组获取节点: $actualTag', tag: 'getCurrentNodeCountry'); } else { print('[getCurrentNodeCountry] ❌ urltest 组的 selected 也为空'); KRLogUtil.kr_w('❌ urltest 组的 selected 也为空', tag: 'getCurrentNodeCountry'); return ''; } } else { actualTag = selectGroup.selected; print( '[getCurrentNodeCountry] ✅ 从 allGroups 的 select 组获取节点: $actualTag'); KRLogUtil.kr_i('✅ 从 allGroups 的 select 组获取节点: $actualTag', tag: 'getCurrentNodeCountry'); } } else { // 活动组不为空,从活动组获取 // 从 SingBox 活动组中找到 "select" 选择器组 final selectGroup = KRSingBoxImp.instance.kr_activeGroups.firstWhere( (group) => group.tag == 'select', orElse: () => throw Exception('未找到 select 组'), ); if (selectGroup.selected.isEmpty) { KRLogUtil.kr_w('❌ select 组的 selected 为空', tag: 'getCurrentNodeCountry'); return ''; } actualTag = selectGroup.selected; KRLogUtil.kr_i('✅ 从活动组获取节点: $actualTag', tag: 'getCurrentNodeCountry'); } } catch (e) { KRLogUtil.kr_e('❌ 从活动组获取节点失败: $e', tag: 'getCurrentNodeCountry'); return ''; } } // 验证 actualTag 是否有效 if (actualTag.isEmpty) { KRLogUtil.kr_w('❌ 节点标签为空', tag: 'getCurrentNodeCountry'); return ''; } // 使用实际节点标签查找国家代码 KRLogUtil.kr_i('查找节点: $actualTag', tag: 'getCurrentNodeCountry'); final node = kr_subscribeService.keyList[actualTag]; if (node == null) { KRLogUtil.kr_e('❌ 节点未找到: $actualTag', tag: 'getCurrentNodeCountry'); KRLogUtil.kr_e('keyList 中的所有节点标签:', tag: 'getCurrentNodeCountry'); int count = 0; for (var key in kr_subscribeService.keyList.keys) { count++; KRLogUtil.kr_e( ' [$count] $key -> country: ${kr_subscribeService.keyList[key]?.country}', tag: 'getCurrentNodeCountry'); if (count >= 10) { KRLogUtil.kr_e( ' ... 还有 ${kr_subscribeService.keyList.length - 10} 个节点', tag: 'getCurrentNodeCountry'); break; } } return ''; } // KRLogUtil.kr_i('✅ 找到节点: $actualTag', tag: 'getCurrentNodeCountry'); // KRLogUtil.kr_i(' - city: ${node.city}', tag: 'getCurrentNodeCountry'); // KRLogUtil.kr_i(' - country: "${node.country}"', // tag: 'getCurrentNodeCountry'); // KRLogUtil.kr_i(' - country.isEmpty: ${node.country.isEmpty}', // tag: 'getCurrentNodeCountry'); // KRLogUtil.kr_i(' - country.length: ${node.country.length}', // tag: 'getCurrentNodeCountry'); // KRLogUtil.kr_i('========== 国家代码获取结束 ==========', // tag: 'getCurrentNodeCountry'); return node.country; } /// 获取真实连接的节点信息(统一为手动选择场景) Map kr_getRealConnectedNodeInfo() { String actualTag = kr_cutSeletedTag == 'auto' ? kr_cutSeletedTag.value : kr_cutTag.value; final node = kr_subscribeService.keyList[actualTag]; return { 'nodeName': kr_cutTag.value, 'delay': node?.urlTestDelay.value ?? -2, 'country': node?.country ?? '', }; } /// 获取真实连接的节点国家 String kr_getRealConnectedNodeCountry() { final country = kr_getCurrentNodeCountry(); if (country.isEmpty) return ''; return kr_getCountryFullName(country); // controller.kr_cutSeletedTag.value // final info = kr_getRealConnectedNodeInfo(); // final delay = info['delay'] as int; // 使用真实连接的节点延迟,而不是 kr_currentNodeLatency.value // final country1 = kr_getCurrentNodeCountry(); // print('country----$country1'); // print('kr_getRealConnectedNodeCountry - delay from info: $delay, country from info: ${info['country']}'); // final country = kr_getCountryFullName(country1); // if (delay == -2) { // return '--'; // } else if (delay == -1) { // return '${country} ${AppTranslations.kr_home.connecting}'; // } else if (delay == 0) { // return '${country} ${AppTranslations.kr_home.connected}'; // } else if (delay >= 3000) { // return '${country} ${AppTranslations.kr_home.timeout}'; // } else { // return '${country} ${delay}ms'; // } } // 格式化字节数 String kr_formatBytes(int bytes) { if (bytes < 1024) { return '$bytes B'; } else if (bytes < 1024 * 1024) { return '${(bytes / 1024).toStringAsFixed(1)} KB'; } else if (bytes < 1024 * 1024 * 1024) { return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; } else { return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB'; } } // 设置当前选中的组 void kr_setCurrentGroup(dynamic group) { try { KRLogUtil.kr_i('设置当前组: ${group.tag}', tag: 'HomeController'); kr_currentGroup.value = group; update(); // 通知 GetBuilder 更新 } catch (e) { KRLogUtil.kr_e('设置当前组失败: $e', tag: 'HomeController'); } } /// 获取国家全称 /// [countryCode] 国家代码(大小写不敏感) String kr_getCountryFullName(String countryCode) { final Map countryNames = { 'CN': '中国', 'HK': '香港', 'TW': '台湾', 'MO': '澳门', 'US': '美国', 'JP': '日本', 'KR': '韩国', 'SG': '新加坡', 'MY': '马来西亚', 'TH': '泰国', 'VN': '越南', 'ID': '印度尼西亚', 'PH': '菲律宾', 'IN': '印度', 'RU': '俄罗斯', 'GB': '英国', 'DE': '德国', 'FR': '法国', 'IT': '意大利', 'ES': '西班牙', 'NL': '荷兰', 'CH': '瑞士', 'SE': '瑞典', 'NO': '挪威', 'FI': '芬兰', 'DK': '丹麦', 'IE': '爱尔兰', 'AT': '奥地利', 'PT': '葡萄牙', 'PL': '波兰', 'UA': '乌克兰', 'CA': '加拿大', 'MX': '墨西哥', 'BR': '巴西', 'AR': '阿根廷', 'AU': '澳大利亚', 'NZ': '新西兰', 'ZA': '南非', 'AE': '阿拉伯联合酋长国', 'IL': '以色列', 'TR': '土耳其', }; final String code = countryCode.toUpperCase(); return countryNames[code] ?? 'Unknown Country'; } @override void onReady() { super.onReady(); } @override void onClose() { if (kDebugMode) { print('🧹 [HomeController] 开始清理资源...'); } // 清理 EventBus 监听器 _eventBusWorker?.dispose(); if (kDebugMode) { print('✅ [HomeController] EventBus 监听器已清理'); } // 清理连接计时器 _kr_connectionTimer?.cancel(); if (kDebugMode) { print('✅ [HomeController] 连接计时器已清理'); } // 🔧 保守修复: 清理状态监控定时器 _statusWatchdogTimer?.cancel(); _connectionTimeoutTimer?.cancel(); if (kDebugMode) { print('✅ [HomeController] 状态监控定时器已清理'); } if (kDebugMode) { print('✅ [HomeController] 资源清理完成'); } super.onClose(); } /// 应用生命周期状态变化处理 @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); KRLogUtil.kr_i('应用生命周期状态变化: $state', tag: 'QuickConnect'); switch (state) { case AppLifecycleState.resumed: // 应用从后台恢复到前台,同步连接状态 KRLogUtil.kr_i('应用恢复到前台,同步连接状态', tag: 'QuickConnect'); _onAppResumed(); break; case AppLifecycleState.paused: // 应用进入后台 KRLogUtil.kr_i('应用进入后台', tag: 'QuickConnect'); break; case AppLifecycleState.detached: // 应用被销毁 KRLogUtil.kr_i('应用被销毁', tag: 'QuickConnect'); break; case AppLifecycleState.inactive: // 应用处于非活动状态(如来电话时) KRLogUtil.kr_i('应用处于非活动状态', tag: 'QuickConnect'); break; case AppLifecycleState.hidden: // 应用被隐藏 KRLogUtil.kr_i('应用被隐藏', tag: 'QuickConnect'); break; } } /// 应用恢复到前台时的处理 void _onAppResumed() { // 同步连接状态,确保UI显示正确 Future.delayed(const Duration(milliseconds: 500), () { kr_forceSyncConnectionStatus(); }); } // 移动到选中节点 void kr_moveToSelectedNode() {} // 简化移动地图方法 void kr_moveToLocation() { try { // 🔧 关键修复:约束坐标到有效范围,防止超出地图边界 kr_isUserMoving.value = false; } catch (e) { KRLogUtil.kr_e('移动地图失败: $e', tag: 'HomeController'); } } // 添加一个方法来批量更新标记 void kr_updateMarkers(List tags) { // 使用Set来去重 final Set updateIds = tags.toSet(); // 一次性更新所有需要更新的标记 update(updateIds.toList()); // 延迟2秒后关闭加载状态 Future.delayed(const Duration(seconds: 1), () { kr_isLatency.value = false; }); } /// 刷新地图标记 void showMarkersMap() { KRLogUtil.kr_i('========== 刷新地图标记 ==========', tag: 'HomeController'); KRLogUtil.kr_i('当前选中节点: ${kr_cutSeletedTag.value}', tag: 'HomeController'); KRLogUtil.kr_i('可用节点数: ${kr_subscribeService.allList.length}', tag: 'HomeController'); KRLogUtil.kr_i('国家分组数: ${kr_subscribeService.countryOutboundList.length}', tag: 'HomeController'); // 手动触发地图标记更新 update(['map_markers']); KRLogUtil.kr_i('✅ 地图标记更新完成', tag: 'HomeController'); } /// 选择地图标记 void selectMarkerMap(int index) { try { if (index >= 0 && index < kr_subscribeService.allList.length) { // 重置所有节点的选中状态 for (var item in kr_subscribeService.allList) { item.selected = 0; } // 设置当前节点为选中状态 kr_subscribeService.allList[index].selected = 1; // 手动触发更新 update(['map_markers']); } } catch (e) { KRLogUtil.kr_e('选择地图标记失败: $e', tag: 'HomeController'); } } /// 手动触发 SingBox URL 测试(调试用) Future kr_manualUrlTest() async { try { KRLogUtil.kr_i('🔧 手动触发 SingBox URL 测试...', tag: 'HomeController'); // 直接调用 SingBox 的 URL 测试 await KRSingBoxImp.instance.kr_urlTest("auto"); // 等待测试完成 await Future.delayed(const Duration(seconds: 5)); // 检查结果 KRLogUtil.kr_i('📊 检查手动测试结果...', tag: 'HomeController'); final activeGroups = KRSingBoxImp.instance.kr_activeGroups; for (int i = 0; i < activeGroups.length; i++) { final group = activeGroups[i]; KRLogUtil.kr_i( '📋 活动组[$i]: tag=${group.tag}, type=${group.type}, selected=${group.selected}', tag: 'HomeController'); for (int j = 0; j < group.items.length; j++) { final item = group.items[j]; KRLogUtil.kr_i( ' └─ 节点[$j]: tag=${item.tag}, type=${item.type}, delay=${item.urlTestDelay}', tag: 'HomeController'); } } } catch (e) { KRLogUtil.kr_e('❌ 手动 URL 测试失败: $e', tag: 'HomeController'); } } /// 强制使用直接连接测试(绕过 SingBox URL 测试) Future kr_forceDirectTest() async { try { KRLogUtil.kr_i('🔧 强制使用直接连接测试...', tag: 'HomeController'); // 使用直接连接测试所有节点 await _kr_testLatencyWithoutVpn(); KRLogUtil.kr_i('✅ 直接连接测试完成', tag: 'HomeController'); } catch (e) { KRLogUtil.kr_e('❌ 直接连接测试失败: $e', tag: 'HomeController'); } } /// 测试延迟 Future kr_urlTest() async { kr_isLatency.value = true; try { KRLogUtil.kr_i('🧪 开始延迟测试...', tag: 'HomeController'); KRLogUtil.kr_i('📊 当前连接状态: ${kr_isConnected.value}', tag: 'HomeController'); if (kr_isConnected.value) { // 已连接状态:使用 SingBox 通过代理测试 KRLogUtil.kr_i('🔗 已连接状态 - 使用 SingBox 通过代理测试延迟', tag: 'HomeController'); await KRSingBoxImp.instance.kr_urlTest("select"); // 等待一段时间让 SingBox 完成测试 await Future.delayed(const Duration(seconds: 3)); // 再次检查活动组状态 KRLogUtil.kr_i('🔄 检查代理测试后的活动组状态...', tag: 'HomeController'); final activeGroups = KRSingBoxImp.instance.kr_activeGroups; for (int i = 0; i < activeGroups.length; i++) { final group = activeGroups[i]; KRLogUtil.kr_i( '📋 活动组[$i]: tag=${group.tag}, type=${group.type}, selected=${group.selected}', tag: 'HomeController'); for (int j = 0; j < group.items.length; j++) { final item = group.items[j]; KRLogUtil.kr_i( ' └─ 节点[$j]: tag=${item.tag}, type=${item.type}, delay=${item.urlTestDelay}', tag: 'HomeController'); } } } else { // 未连接状态:使用本机网络直接ping节点IP KRLogUtil.kr_i('🔌 未连接状态 - 使用本机网络直接ping节点IP测试延迟', tag: 'HomeController'); KRLogUtil.kr_i('🌐 这将绕过代理,直接使用本机网络连接节点', tag: 'HomeController'); await _kr_testLatencyWithoutVpn(); } } catch (e) { KRLogUtil.kr_e('❌ 延迟测试失败: $e', tag: 'HomeController'); } finally { // 延迟1秒后关闭加载状态 Future.delayed(const Duration(seconds: 1), () { kr_isLatency.value = false; }); } } /// 未连接状态下的真实延迟测试(TCP连接测试) Future _kr_testLatencyWithoutVpn() async { kr_isLatency.value = true; try { KRLogUtil.kr_i('🚀 开始真实延迟测试(按国家分两阶段)', tag: 'HomeController'); KRLogUtil.kr_i('📊 当前连接状态: ${kr_isConnected.value}', tag: 'HomeController'); final allNodes = kr_subscribeService.allList .where((item) => item.tag != 'auto') .toList(); KRLogUtil.kr_i('📋 可测试节点总数: ${allNodes.length}', tag: 'HomeController'); if (allNodes.isEmpty) { KRLogUtil.kr_w('⚠️ 没有可测试的节点', tag: 'HomeController'); return; } final Map> byCountry = {}; for (final node in allNodes) { final code = node.country; byCountry.putIfAbsent(code, () => []).add(node); } KRLogUtil.kr_i('🌎 国家数: ${byCountry.length}', tag: 'HomeController'); final Map tagToNode = { for (final n in allNodes) n.tag: n, }; final List> stage1 = []; final List stage1Tags = []; for (final entry in byCountry.entries) { final list = entry.value; if (list.isEmpty) continue; final node = list.first; try { String host = node.serverAddr; int port = 0; if (node.config.containsKey('server_port')) { port = node.config['server_port'] as int; } else if (node.config.containsKey('port')) { port = node.config['port'] as int; } if (host.isNotEmpty && port > 0) { stage1.add(MapEntry(node.tag, SocketAddress(host, port))); stage1Tags.add(node.tag); KRLogUtil.kr_i('阶段1加入: ${entry.key}/${node.tag} -> $host:$port', tag: 'HomeController'); } else { final n = tagToNode[node.tag]; if (n != null) n.urlTestDelay.value = 65535; KRLogUtil.kr_w('阶段1节点缺少地址或端口: ${node.tag}', tag: 'HomeController'); } } catch (e) { final n = tagToNode[node.tag]; if (n != null) n.urlTestDelay.value = 65535; KRLogUtil.kr_w('阶段1解析失败: ${node.tag}, $e', tag: 'HomeController'); } } if (stage1.isNotEmpty) { KRLogUtil.kr_i('🔌 阶段1测试国家首个节点: ${stage1.length}', tag: 'HomeController'); final results1 = await KRLatencyTester.testMultipleNodes( nodes: stage1, concurrency: 5, timeout: const Duration(seconds: 5), ); int s1ok = 0; for (final tag in stage1Tags) { final delay = results1[tag]; if (delay != null) { final node = tagToNode[tag]; if (node != null) { node.urlTestDelay.value = delay; s1ok++; } } } KRLogUtil.kr_i('✅ 阶段1完成: 成功 $s1ok/${stage1Tags.length}', tag: 'HomeController'); } final List> stage2 = []; final List stage2Tags = []; for (final entry in byCountry.entries) { final list = entry.value; if (list.length <= 1) continue; for (final node in list.skip(1)) { try { String host = node.serverAddr; int port = 0; if (node.config.containsKey('server_port')) { port = node.config['server_port'] as int; } else if (node.config.containsKey('port')) { port = node.config['port'] as int; } if (host.isNotEmpty && port > 0) { stage2.add(MapEntry(node.tag, SocketAddress(host, port))); stage2Tags.add(node.tag); } else { final n = tagToNode[node.tag]; if (n != null) n.urlTestDelay.value = 65535; } } catch (e) { final n = tagToNode[node.tag]; if (n != null) n.urlTestDelay.value = 65535; } } } if (stage2.isNotEmpty) { KRLogUtil.kr_i('🔌 阶段2测试剩余节点: ${stage2.length}', tag: 'HomeController'); final results2 = await KRLatencyTester.testMultipleNodes( nodes: stage2, concurrency: 10, timeout: const Duration(seconds: 5), ); int s2ok = 0; for (final tag in stage2Tags) { final delay = results2[tag]; if (delay != null) { final node = tagToNode[tag]; if (node != null) { node.urlTestDelay.value = delay; s2ok++; } } } KRLogUtil.kr_i('✅ 阶段2完成: 成功 $s2ok/${stage2Tags.length}', tag: 'HomeController'); } final tested = allNodes.where((n) => n.urlTestDelay.value < 65535).toList(); final notTested = allNodes.length - tested.length; KRLogUtil.kr_i('📊 总结: 可用 ${tested.length}, 不可用/失败 ${notTested}', tag: 'HomeController'); } catch (e) { KRLogUtil.kr_e('❌ 真实延迟测试过程出错: $e', tag: 'HomeController'); KRLogUtil.kr_e('❌ 错误堆栈: ${StackTrace.current}', tag: 'HomeController'); } finally { kr_isLatency.value = false; } } /// 开始连接计时 void kr_startConnectionTimer() { kr_stopConnectionTimer(); // _kr_connectionSeconds = 0; // _kr_connectionTimer = Timer.periodic(const Duration(seconds: 1), (timer) { // _kr_connectionSeconds++; // kr_connectionTime.value = kr_formatDuration(_kr_connectionSeconds); // KRLogUtil.kr_i(kr_connectText.value, tag: 'kr_startConnectionTimer'); // }); } /// 停止连接计时 void kr_stopConnectionTimer() { _kr_connectionTimer?.cancel(); _kr_connectionTimer = null; } /// 格式化时长 String kr_formatDuration(int seconds) { final hours = seconds ~/ 3600; final minutes = (seconds % 3600) ~/ 60; final remainingSeconds = seconds % 60; return '${hours.toString().padLeft(2, '0')}:' '${minutes.toString().padLeft(2, '0')}:' '${remainingSeconds.toString().padLeft(2, '0')}'; } /// 重置连接信息 void kr_resetConnectionInfo() { kr_currentIp.value = AppTranslations.kr_home.disconnected; kr_currentProtocol.value = AppTranslations.kr_home.disconnected; kr_currentSpeed.value = "--"; kr_connectionTime.value = '00:00:00'; _kr_connectionSeconds = 0; kr_currentNodeLatency.value = -2; // 设置为未连接状态 } /// 调试:打印所有节点的坐标信息 void kr_debugPrintNodeCoordinates() { KRLogUtil.kr_i('========== 节点坐标调试信息 ==========', tag: 'HomeController'); KRLogUtil.kr_i('节点总数: ${kr_subscribeService.allList.length}', tag: 'HomeController'); if (kr_subscribeService.allList.isEmpty) { KRLogUtil.kr_w('节点列表为空!请检查:', tag: 'HomeController'); KRLogUtil.kr_w('1. 是否已登录', tag: 'HomeController'); KRLogUtil.kr_w('2. 是否有订阅', tag: 'HomeController'); KRLogUtil.kr_w('3. 订阅是否已加载完成', tag: 'HomeController'); return; } int validNodes = 0; int invalidNodes = 0; for (int i = 0; i < kr_subscribeService.allList.length; i++) { final node = kr_subscribeService.allList[i]; if (node.latitude != 0.0 || node.longitude != 0.0) { validNodes++; if (i < 5) { // 只打印前5个有效节点 KRLogUtil.kr_i( '节点[$i] ${node.tag}: (${node.latitude}, ${node.longitude})', tag: 'HomeController'); } } else { invalidNodes++; if (i < 3) { // 只打印前3个无效节点 KRLogUtil.kr_w('节点[$i] ${node.tag}: 坐标为(0, 0) - 无效!', tag: 'HomeController'); } } } KRLogUtil.kr_i('有效节点: $validNodes', tag: 'HomeController'); KRLogUtil.kr_w('无效节点(坐标为0): $invalidNodes', tag: 'HomeController'); if (invalidNodes > 0) { KRLogUtil.kr_w('⚠️ 发现 $invalidNodes 个节点坐标为0,这些节点不会显示在地图上', tag: 'HomeController'); KRLogUtil.kr_w('可能原因:', tag: 'HomeController'); KRLogUtil.kr_w('1. 后端API未返回 latitude/longitude 字段', tag: 'HomeController'); KRLogUtil.kr_w('2. 后端数据库中节点坐标未配置', tag: 'HomeController'); } } /// 强制同步连接状态 void kr_forceSyncConnectionStatus() { try { KRLogUtil.kr_i('🔄 强制同步连接状态...', tag: 'HomeController'); final currentStatus = KRSingBoxImp.instance.kr_status.value; KRLogUtil.kr_i('📊 当前 SingBox 状态: $currentStatus', tag: 'HomeController'); // 根据当前状态强制更新UI switch (currentStatus) { case SingboxStopped(): kr_connectText.value = AppTranslations.kr_home.disconnected; kr_isConnected.value = false; kr_currentSpeed.value = "--"; kr_currentNodeLatency.value = -2; break; case SingboxStarting(): kr_connectText.value = AppTranslations.kr_home.connecting; kr_isConnected.value = false; kr_currentSpeed.value = AppTranslations.kr_home.connecting; kr_currentNodeLatency.value = -1; break; case SingboxStarted(): kr_connectText.value = AppTranslations.kr_home.connected; kr_isConnected.value = true; kr_startConnectionTimer(); kr_updateConnectionInfo(); // 🔧 修复:同步已启动状态时,尝试更新延迟值 if (!_kr_tryUpdateDelayFromActiveGroups()) { // 如果获取不到延迟值,设置为0(已连接但延迟未知) kr_currentNodeLatency.value = 0; KRLogUtil.kr_w('⚠️ 强制同步时无法获取延迟值,设置为0', tag: 'HomeController'); } break; case SingboxStopping(): kr_connectText.value = AppTranslations.kr_home.disconnecting; kr_isConnected.value = false; kr_currentSpeed.value = "--"; break; } // 强制更新UI update(); KRLogUtil.kr_i('✅ 连接状态同步完成', tag: 'HomeController'); } catch (e) { KRLogUtil.kr_e('❌ 强制同步连接状态失败: $e', tag: 'HomeController'); } } /// 🔧 保守修复: 强制重置状态(用于状态卡住时) Future _forceResetState() async { try { KRLogUtil.kr_w('🔄 开始强制重置状态...', tag: 'HomeController'); // 1. 尝试停止 SingBox try { await KRSingBoxImp.instance.kr_stop().timeout( const Duration(seconds: 5), onTimeout: () { KRLogUtil.kr_w('⚠️ 强制停止超时,继续重置', tag: 'HomeController'); }, ); } catch (e) { KRLogUtil.kr_w('⚠️ 强制停止失败: $e,继续重置', tag: 'HomeController'); } // 2. 等待状态稳定 await Future.delayed(const Duration(milliseconds: 500)); // 3. 强制重置所有 UI 状态为断开 kr_connectText.value = AppTranslations.kr_home.disconnected; KRSingBoxImp.instance.kr_status.value = SingboxStatus.stopped(); kr_isConnected.value = false; kr_currentSpeed.value = "--"; kr_currentNodeLatency.value = -2; kr_currentIp.value = "--"; kr_currentProtocol.value = "--"; kr_stopConnectionTimer(); kr_resetConnectionInfo(); // 4. 重置状态变化时间 _lastStatusChangeTime = null; // 5. 强制更新 UI update(); KRLogUtil.kr_i('✅ 强制重置状态完成', tag: 'HomeController'); } catch (e) { KRLogUtil.kr_e('❌ 强制重置状态失败: $e', tag: 'HomeController'); } } /// 🔧 保守修复: 启动状态监控定时器,定期检测状态是否卡住 void _startStatusWatchdog() { _statusWatchdogTimer?.cancel(); _statusWatchdogTimer = Timer.periodic(const Duration(seconds: 30), (timer) { final currentStatus = KRSingBoxImp.instance.kr_status.value; // 只检测中间状态(Starting/Stopping) if (currentStatus is SingboxStarting || currentStatus is SingboxStopping) { final now = DateTime.now(); if (_lastStatusChangeTime != null) { final duration = now.difference(_lastStatusChangeTime!); if (duration > const Duration(seconds: 5)) { // 状态卡住超过 5 秒 KRLogUtil.kr_w( '⚠️ [Watchdog] 检测到状态卡住: ${currentStatus.runtimeType}, 持续时间: ${duration.inSeconds}秒', tag: 'HomeController', ); // 自动触发强制重置 _forceResetState().then((_) {}); } } } }); KRLogUtil.kr_i('✅ 状态监控定时器已启动', tag: 'HomeController'); } /// 连接超时处理 Timer? _connectionTimeoutTimer; void _startConnectionTimeout() { _connectionTimeoutTimer?.cancel(); _connectionTimeoutTimer = Timer(const Duration(seconds: 30), () { KRLogUtil.kr_w('⏰ 连接超时,强制重置状态', tag: 'HomeController'); // 检查是否仍在连接中 if (KRSingBoxImp.instance.kr_status.value is SingboxStarting) { KRLogUtil.kr_w('🔄 连接超时,强制停止并重置', tag: 'HomeController'); // 强制停止连接 KRSingBoxImp.instance.kr_stop().then((_) { // 重置状态 kr_connectText.value = AppTranslations.kr_home.disconnected; kr_isConnected.value = false; kr_currentSpeed.value = "--"; kr_currentNodeLatency.value = -2; kr_resetConnectionInfo(); update(); KRLogUtil.kr_i('✅ 连接超时处理完成', tag: 'HomeController'); }).catchError((e) { KRLogUtil.kr_e('❌ 连接超时处理失败: $e', tag: 'HomeController'); }); } }); } void _cancelConnectionTimeout() { _connectionTimeoutTimer?.cancel(); _connectionTimeoutTimer = null; } /// 连接成功后更新延迟值 void _kr_updateLatencyOnConnected() { KRLogUtil.kr_i('🔧 尝试获取连接延迟值...', tag: 'HomeController'); // 立即尝试从活动组获取延迟 bool delayUpdated = _kr_tryUpdateDelayFromActiveGroups(); if (delayUpdated) { KRLogUtil.kr_i('✅ 延迟值已从活动组更新', tag: 'HomeController'); return; } // 如果立即获取失败,设置临时值并启动延迟重试 KRLogUtil.kr_w('⚠️ 活动组暂无延迟数据,设置临时值并启动重试', tag: 'HomeController'); kr_currentNodeLatency.value = 0; // 设置为0表示已连接但延迟未知 // 延迟500ms后重试(等待活动组数据到达) Future.delayed(const Duration(milliseconds: 500), () { if (_kr_tryUpdateDelayFromActiveGroups()) { KRLogUtil.kr_i('✅ 延迟重试成功,延迟值已更新', tag: 'HomeController'); return; } // 再次延迟1秒重试 Future.delayed(const Duration(seconds: 1), () { if (_kr_tryUpdateDelayFromActiveGroups()) { KRLogUtil.kr_i('✅ 第二次延迟重试成功', tag: 'HomeController'); return; } // 如果还是获取不到,保持为0(表示已连接但延迟未知) KRLogUtil.kr_w('⚠️ 多次重试后仍无法获取延迟值,保持为已连接状态', tag: 'HomeController'); kr_currentNodeLatency.value = 0; }); }); } /// 获取指定国家的所有真实节点延迟列表 List> kr_getCountryRealNodeDelays(String countryCode) { final delays = >[]; try { // 从订阅服务中获取该国家的节点列表 final countryNodes = kr_subscribeService.keyList.values .where((item) => item.country == countryCode) .toList(); for (final node in countryNodes) { delays.add({ 'tag': node.tag, 'delay': node.urlTestDelay.value, 'city': node.city, }); } // 按延迟排序 delays.sort((a, b) => a['delay'].compareTo(b['delay'])); } catch (e) { KRLogUtil.kr_e('获取国家真实节点延迟列表失败: $e', tag: 'HomeController'); } return delays; } /// 尝试从活动组更新延迟值 bool _kr_tryUpdateDelayFromActiveGroups() { try { final activeGroups = KRSingBoxImp.instance.kr_activeGroups; if (activeGroups.isEmpty) { KRLogUtil.kr_d('活动组为空', tag: 'HomeController'); return false; } // 查找 selector 类型的组 for (var group in activeGroups) { if (group.type == ProxyType.selector) { KRLogUtil.kr_d('找到 selector 组: ${group.tag}, 选中: ${group.selected}', tag: 'HomeController'); for (var item in group.items) { if (item.tag == kr_cutTag.value && item.urlTestDelay != 0) { kr_currentNodeLatency.value = item.urlTestDelay; KRLogUtil.kr_i('✅ 延迟值: ${item.urlTestDelay}ms', tag: 'HomeController'); return true; } } } } KRLogUtil.kr_d('未找到匹配的延迟数据', tag: 'HomeController'); return false; } catch (e) { KRLogUtil.kr_e('获取延迟值失败: $e', tag: 'HomeController'); return false; } } }