feat: 节点测速调整

This commit is contained in:
2025-11-26 19:23:15 -08:00
parent 909020654f
commit b059d01556
6 changed files with 513 additions and 740 deletions
@@ -124,10 +124,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
// 闪连状态存储键
static const String _quickConnectKey = 'kr_quick_connect_enabled';
// 国家内节点重选相关属性
final RxString currentSelectedCountry = ''.obs;
final int countryReselectionLatencyThreshold = 3000; // 延迟阈值(毫秒)
// 添加一个方法来切换状态
void toggleQuickConnect(bool? value) async {
// 只有当传入的值不为 null 时才更新状态
@@ -395,10 +391,10 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
_kr_addStatusSyncCheck();
if (AppConfig().kr_is_daytime == true) {
Future.delayed(const Duration(seconds: 1), () {
Future.delayed(const Duration(seconds: 5), () {
KRUpdateUtil().kr_checkUpdate();
Future.delayed(const Duration(seconds: 1), () {
Future.delayed(const Duration(seconds: 5), () {
// 不做语言切换
// KRLanguageSwitchDialog.kr_show();
});
@@ -552,7 +548,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
if (isValidLogin) {
kr_currentViewStatus.value = KRHomeViewsStatus.kr_loggedIn;
KRLogUtil.kr_i('登录状态变化:设置为已登录', tag: 'HomeController');
KRAnnouncementService().kr_checkAnnouncement();
// 订阅服务已在 splash 页面初始化,此处无需重复初始化
@@ -590,9 +586,9 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
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');
@@ -692,13 +688,15 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
ever(KRSingBoxImp.instance.kr_status, (status) {
KRLogUtil.kr_i('🔄 连接状态变化: $status', tag: 'HomeController');
KRLogUtil.kr_i('📊 当前状态类型: ${status.runtimeType}', tag: 'HomeController');
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;
@@ -750,12 +748,14 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
break;
case SingboxStopping():
KRLogUtil.kr_i('🟠 状态: 正在停止', tag: 'HomeController');
// 取消连接超时处理
_cancelConnectionTimeout();
kr_connectText.value = AppTranslations.kr_home.disconnecting;
kr_isConnected.value = false;
kr_currentSpeed.value = "--";
break;
}
// 强制更新UI
update();
});
@@ -839,7 +839,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
if (currentStatus is SingboxStarting || currentStatus is SingboxStopping) {
KRLogUtil.kr_i('🔄 正在切换中,忽略本次操作 (当前状态: $currentStatus)', tag: 'HomeController');
if (kDebugMode) {
}
}
return;
}
@@ -848,12 +848,11 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
// 开启连接
KRLogUtil.kr_i('🔄 开始连接...', tag: 'HomeController');
if (kDebugMode) {
}
}
await KRSingBoxImp.instance.kr_start();
KRLogUtil.kr_i('✅ 连接命令已发送', tag: 'HomeController');
if (kDebugMode) {
}
}
// 🔧 修复: 等待状态更新,最多3秒
await _waitForStatus(SingboxStarted, maxSeconds: 3);
@@ -861,7 +860,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
// 关闭连接
KRLogUtil.kr_i('🛑 开始断开连接...', tag: 'HomeController');
if (kDebugMode) {
}
}
await KRSingBoxImp.instance.kr_stop().timeout(
const Duration(seconds: 10),
onTimeout: () {
@@ -871,7 +870,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
);
KRLogUtil.kr_i('✅ 断开命令已发送', tag: 'HomeController');
if (kDebugMode) {
}
}
// 🔧 修复: 等待状态更新,最多2秒
await _waitForStatus(SingboxStopped, maxSeconds: 2);
@@ -879,13 +878,13 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
} catch (e) {
KRLogUtil.kr_e('❌ 切换失败: $e', tag: 'HomeController');
if (kDebugMode) {
}
}
// 发生错误时强制同步状态
kr_forceSyncConnectionStatus();
}
if (kDebugMode) {
}
}
}
/// 🔧 等待状态达到预期值
@@ -1096,14 +1095,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
return node.urlTestDelay.value < 65535 && node.urlTestDelay.value > 0;
}
/// 设置当前选择的国家(由hi_node_list_controller调用)
void setCurrentSelectedCountry(String country) {
currentSelectedCountry.value = country;
KRLogUtil.kr_i('🌍 设置当前选择国家: $country', tag: 'HomeController');
}
/// 更新自动模式延迟
void _kr_updateAutoLatency(dynamic element) {
for (var subElement in element.items) {
@@ -1137,11 +1128,36 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
}
}
// 🔒 节点切换状态锁和节流控制
bool _isSwitchingNode = false;
DateTime? _lastSwitchTime;
static const Duration _switchThrottleDuration = Duration(milliseconds: 2000); // 2秒节流
/// 🔧 修复:统一的节点切换方法(包含UI同步和后台操作等待)
/// 执行节点切换,包括UI更新和后台操作的完整同步
/// 返回 true 表示切换成功,false 表示失败
Future<bool> kr_performNodeSwitch(String tag) async {
try {
// 🔒 状态锁:防止并发切换
if (_isSwitchingNode) {
KRLogUtil.kr_w('⚠️ 节点切换正在进行中,忽略重复请求', tag: 'HomeController');
KRCommonUtil.kr_showToast('请等待当前节点切换完成');
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. 保存原节点,以备失败恢复
@@ -1155,6 +1171,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
if (!kr_isConnected.value) {
KRLogUtil.kr_i('📴 VPN未连接,只更新UI变量: $tag', tag: 'HomeController');
kr_cutSeletedTag.value = tag;
kr_updateConnectionInfo();
// kr_moveToSelectedNode();
// 🔧 修复:保存节点选择以便VPN启动时应用
@@ -1191,29 +1208,29 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
KRLogUtil.kr_i('💾 保存新节点选择: $tag', tag: 'HomeController');
await KRSecureStorage().kr_saveData(key: 'SELECTED_NODE_TAG', value: tag);
// 🔧 方案A增强:重启VPN连接以断开所有现有长连接
KRLogUtil.kr_i('🔄 [增强] 停止VPN连接以断开现有连接...', tag: 'HomeController');
await KRSingBoxImp.instance.kr_stop(); // 先停止VPN
// 🔧 方案A优化:重启VPN连接以断开所有现有长连接
KRLogUtil.kr_i('🔄 [优化] 停止VPN连接以断开现有连接...', tag: 'HomeController');
await KRSingBoxImp.instance.kr_stop(); // 先停止VPN(已跳过DNS恢复)
// 🚀 方案A增强:增加等待时间,确保所有连接完全释放
KRLogUtil.kr_i('⏳ [增强] 等待VPN完全停止(1500ms,确保旧连接全部断开...', tag: 'HomeController');
await Future.delayed(const Duration(milliseconds: 1500)); // 从800ms增加到1500ms
// 🚀 优化:减少等待时间(DNS操作已优化,无需过长等待)
KRLogUtil.kr_i('⏳ [优化] 等待VPN完全停止(800ms...', tag: 'HomeController');
await Future.delayed(const Duration(milliseconds: 800)); // 从1500ms减少到800ms
KRLogUtil.kr_i('🔄 [增强] 启动VPN并应用新节点: $tag', tag: 'HomeController');
await KRSingBoxImp.instance.kr_start(); // 重新启动VPN,会自动使用新保存的节点
KRLogUtil.kr_i('🔄 [优化] 启动VPN并应用新节点: $tag', tag: 'HomeController');
await KRSingBoxImp.instance.kr_start(); // 重新启动VPN(已跳过DNS备份)
// 🚀 方案A增强:增加启动等待时间,确保新VPN完全建立
KRLogUtil.kr_i('⏳ [增强] 等待VPN完全启动(2500ms,确保新连接完全建立...', tag: 'HomeController');
await Future.delayed(const Duration(milliseconds: 2500)); // 从1500ms增加到2500ms
// 🚀 优化:减少等待时间(DNS操作已优化)
KRLogUtil.kr_i('⏳ [优化] 等待VPN完全启动(1200ms...', tag: 'HomeController');
await Future.delayed(const Duration(milliseconds: 1200)); // 从2500ms减少到1200ms
// 后台切换成功,更新UI
kr_cutSeletedTag.value = tag;
kr_updateConnectionInfo();
// kr_moveToSelectedNode();
kr_moveToSelectedNode();
// 🚀 方案A增强:增加验证等待时间,确保活动组完全更新
KRLogUtil.kr_i('⏳ [增强] 等待活动组更新(500ms...', tag: 'HomeController');
await Future.delayed(const Duration(milliseconds: 500)); // 从200ms增加到500ms
// 🚀 优化:减少验证等待时间
KRLogUtil.kr_i('⏳ [优化] 等待活动组更新(300ms...', tag: 'HomeController');
await Future.delayed(const Duration(milliseconds: 300)); // 从500ms减少到300ms
// 🚀 方案A增强:验证节点是否真正切换成功
KRLogUtil.kr_i('🔍 [增强] 验证节点切换是否成功...', tag: 'HomeController');
@@ -1221,7 +1238,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
// 刷新活动组信息
final activeGroups = KRSingBoxImp.instance.kr_activeGroups;
final selectGroup = activeGroups.firstWhere(
(group) => group.tag == 'select',
(group) => group.tag == 'select',
orElse: () => throw Exception('未找到 select 组'),
);
@@ -1271,6 +1288,8 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
KRCommonUtil.kr_showToast('节点切换异常,请重试');
return false;
} finally {
// 🔒 释放状态锁
_isSwitchingNode = false;
// 关闭加载状态
kr_isLatency.value = false;
KRLogUtil.kr_i('🔄 节点切换流程完成', tag: 'HomeController');
@@ -1308,8 +1327,8 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
}
// 2. 如果是 auto,优先使用 kr_cutSeletedTag(保存了实际选中的节点)
else if (kr_cutSeletedTag.value.isNotEmpty &&
kr_cutSeletedTag.value != 'auto' &&
kr_cutSeletedTag.value != 'select') {
kr_cutSeletedTag.value != 'auto' &&
kr_cutSeletedTag.value != 'select') {
// auto 模式下,使用保存的实际节点
actualTag = kr_cutSeletedTag.value;
KRLogUtil.kr_i('✅ 使用 auto 模式下的实际节点 (kr_cutSeletedTag): $actualTag', tag: 'getCurrentNodeCountry');
@@ -1344,7 +1363,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
print('[getCurrentNodeCountry] select 组选中的是 auto,查找 urltest 组');
// 如果 select 组选中的是 auto,从 urltest 组获取
final urlTestGroup = allGroups.firstWhere(
(group) => group.type == ProxyType.urltest,
(group) => group.type == ProxyType.urltest,
orElse: () => throw Exception('未找到 urltest 组'),
);
@@ -1366,7 +1385,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
// 活动组不为空,从活动组获取
// 从 SingBox 活动组中找到 "select" 选择器组
final selectGroup = KRSingBoxImp.instance.kr_activeGroups.firstWhere(
(group) => group.tag == 'select',
(group) => group.tag == 'select',
orElse: () => throw Exception('未找到 select 组'),
);
@@ -1420,88 +1439,41 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
}
/// 获取真实连接的节点信息(auto 模式下获取实际连接的节点
/// 获取真实连接的节点信息(统一为手动选择场景
Map<String, dynamic> kr_getRealConnectedNodeInfo() {
// 如果不是 auto 模式,也不是 country-auto 模式,直接返回当前选中的节点信息
if (kr_cutTag.value != 'auto' && !kr_cutTag.value.endsWith('-auto')) {
final node = kr_subscribeService.keyList[kr_cutSeletedTag.value];
return {
'nodeName': kr_cutSeletedTag.value,
'delay': node?.urlTestDelay.value ?? -2,
'country': node?.country ?? '',
};
}
// 处理 auto 模式(包括全局 auto 和 country-auto
print('当前活动组----${KRSingBoxImp.instance.kr_activeGroups.length}');
for (var group in KRSingBoxImp.instance.kr_activeGroups) {
print('当前活动组----$group}');
// 处理全局 auto 模式
if (kr_cutTag.value.endsWith('auto') && group.type == ProxyType.urltest && group.tag == 'auto') {
final selectedNode = group.selected;
final node = kr_subscribeService.keyList[selectedNode];
return {
'nodeName': selectedNode,
'delay': node?.urlTestDelay.value ?? -2,
'country': node?.country ?? '',
};
}
}
// 处理 country-auto 模式的备用方案(当 SingBox 组数据不可用时)
if (kr_cutTag.value.endsWith('-auto')) {
final countryCode = kr_cutTag.value.replaceAll('-auto', '');
KRLogUtil.kr_i('🔄 kr_getRealConnectedNodeInfo 使用备用方案获取 country-auto 信息: $countryCode', tag: 'HomeController');
final autoNodeInfo = kr_getCountryAutoSelectedNode(countryCode);
if (autoNodeInfo != null) {
return {
'nodeName': autoNodeInfo['tag'],
'delay': autoNodeInfo['delay'] ?? -2,
'country': autoNodeInfo['country'] ?? countryCode,
};
}
}
// 如果没有找到 urltest 组,返回默认值
String actualTag = kr_cutSeletedTag == 'auto' ? kr_cutSeletedTag.value : kr_cutTag.value;
final node = kr_subscribeService.keyList[actualTag];
return {
'nodeName': kr_cutTag.value,
'delay': -2,
'country': '',
'delay': node?.urlTestDelay.value ?? -2,
'country': node?.country ?? '',
};
}
/// 获取真实连接的节点名称
String kr_getRealConnectedNodeName() {
final info = kr_getRealConnectedNodeInfo();
return info['nodeName'] as String;
}
/// 获取真实连接的节点延迟
int kr_getRealConnectedNodeDelay() {
final info = kr_getRealConnectedNodeInfo();
return info['delay'] as int;
}
/// 获取真实连接的节点国家
String kr_getRealConnectedNodeCountry() {
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(info['country']);
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';
}
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';
// }
}
@@ -1840,10 +1812,10 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
Future<void> 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');
@@ -1857,15 +1829,15 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
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;
@@ -2127,16 +2099,16 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
/// 连接超时处理
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((_) {
// 重置状态
@@ -2146,7 +2118,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
kr_currentNodeLatency.value = -2;
kr_resetConnectionInfo();
update();
KRLogUtil.kr_i('✅ 连接超时处理完成', tag: 'HomeController');
}).catchError((e) {
KRLogUtil.kr_e('❌ 连接超时处理失败: $e', tag: 'HomeController');
@@ -2154,7 +2126,7 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
}
});
}
void _cancelConnectionTimeout() {
_connectionTimeoutTimer?.cancel();
_connectionTimeoutTimer = null;
@@ -2197,209 +2169,33 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
});
}
/// 获取国家-auto组的当前选中子节点信息
/// 获取国家选中子节点信息(已不使用自动选择逻辑)
Map<String, dynamic>? kr_getCountryAutoSelectedNode(String countryCode) {
try {
final activeGroups = KRSingBoxImp.instance.kr_activeGroups;
final allGroups = KRSingBoxImp.instance.kr_allGroups;
final autoGroupTag = '${countryCode}-auto';
KRLogUtil.kr_i('🔍 开始获取国家-auto选中节点: countryCode=$countryCode, autoGroupTag=$autoGroupTag', tag: 'HomeController');
KRLogUtil.kr_i('📊 活跃组数量: ${activeGroups.length}, 所有组数量: ${allGroups.length}', tag: 'HomeController');
// 优先检查活跃组
if (activeGroups.isNotEmpty) {
KRLogUtil.kr_i('✅ 活跃组不为空,优先检查活跃组', tag: 'HomeController');
for (var group in activeGroups) {
KRLogUtil.kr_i('🔄 检查活跃组: tag=${group.tag}, type=${group.type}, selected=${group.selected}', tag: 'HomeController');
if (group.tag == autoGroupTag && group.type == ProxyType.urltest) {
KRLogUtil.kr_i('✅ 在活跃组中找到匹配的urltest组: $autoGroupTag', tag: 'HomeController');
return _kr_extractSelectedNodeInfo(group, countryCode);
}
}
}
// 如果活跃组中没有,检查所有组
if (allGroups.isNotEmpty) {
KRLogUtil.kr_i('🔍 活跃组中未找到,检查所有组', tag: 'HomeController');
for (var group in allGroups) {
KRLogUtil.kr_i('🔄 检查所有组: tag=${group.tag}, type=${group.type}, selected=${group.selected}', tag: 'HomeController');
if (group.tag == autoGroupTag && group.type == ProxyType.urltest) {
KRLogUtil.kr_i('✅ 在所有组中找到匹配的urltest组: $autoGroupTag', tag: 'HomeController');
return _kr_extractSelectedNodeInfo(group, countryCode);
}
}
}
// 如果组数据都为空,使用备用方案:从订阅服务中找该国家最快的节点
KRLogUtil.kr_i('🔄 组数据为空,使用备用方案从订阅服务获取最快节点', tag: 'HomeController');
return _kr_getFastestNodeFromSubscribeService(countryCode);
} catch (e) {
KRLogUtil.kr_e('💥 获取国家-auto选中节点异常: $e', tag: 'HomeController');
}
return null;
}
/// 从订阅服务获取该国家最快节点的备用方案
Map<String, dynamic>? _kr_getFastestNodeFromSubscribeService(String countryCode) {
try {
KRLogUtil.kr_i('🔄 使用订阅服务查找国家最快节点: $countryCode', tag: 'HomeController');
// 从订阅服务中获取该国家的所有节点
final allNodes = kr_subscribeService.allList.where((node) =>
node.country == countryCode && !node.tag.endsWith('-auto')
).toList();
KRLogUtil.kr_i('📊 找到 ${allNodes.length} 个该国家的普通节点', tag: 'HomeController');
if (allNodes.isEmpty) {
KRLogUtil.kr_w('⚠️ 未找到该国家的任何普通节点: $countryCode', tag: 'HomeController');
return null;
}
// 找出延迟最小的节点(排除0和超时)
KROutboundItem? fastestNode;
int fastestDelay = 999999;
for (var node in allNodes) {
final delay = node.urlTestDelay.value;
KRLogUtil.kr_w('🔍 检查节点 ${node.tag} 的延迟: $delay', tag: 'HomeController');
if (delay > 0 && delay < fastestDelay) {
fastestDelay = delay;
fastestNode = node;
}
}
if (fastestNode != null) {
KRLogUtil.kr_i('✅ 找到最快节点: ${fastestNode.tag}, 延迟: ${fastestDelay}ms', tag: 'HomeController');
return {
'tag': fastestNode.tag,
'delay': fastestDelay,
'country': countryCode,
};
} else {
KRLogUtil.kr_w('⚠️ 该国家的节点都没有有效延迟数据', tag: 'HomeController');
return null;
}
} catch (e) {
KRLogUtil.kr_e('💥 获取订阅服务最快节点异常: $e', tag: 'HomeController');
return null;
}
}
/// 提取选中节点信息的辅助方法
Map<String, dynamic>? _kr_extractSelectedNodeInfo(dynamic group, String countryCode) {
try {
// 获取当前选中的节点
final selectedNode = group.selected;
KRLogUtil.kr_i('🎯 当前选中节点: $selectedNode', tag: 'HomeController');
KRLogUtil.kr_i('📋 组内项目数量: ${group.items.length}', tag: 'HomeController');
if (selectedNode != null && selectedNode.isNotEmpty) {
KRLogUtil.kr_i('✅ 选中节点有效,开始查找节点详情', tag: 'HomeController');
// 打印所有节点信息用于调试
for (var item in group.items) {
KRLogUtil.kr_i('📄 组内节点: tag=${item.tag}, delay=${item.urlTestDelay}', tag: 'HomeController');
}
// 在组内查找选中的节点
try {
final selectedItem = group.items.firstWhere(
(item) => item.tag == selectedNode,
);
KRLogUtil.kr_i('🎉 成功找到选中节点: tag=${selectedItem.tag}, delay=${selectedItem.urlTestDelay}', tag: 'HomeController');
return {
'tag': selectedNode,
'delay': selectedItem.urlTestDelay,
'country': countryCode,
};
} catch (e) {
KRLogUtil.kr_e('❌ 在组内未找到选中节点: $selectedNode', tag: 'HomeController');
return null;
}
} else {
KRLogUtil.kr_w('⚠️ 选中节点为空或无效', tag: 'HomeController');
return null;
}
} catch (e) {
KRLogUtil.kr_e('💥 提取选中节点信息异常: $e', tag: 'HomeController');
return null;
}
}
/// 获取全局 auto 组的当前选中子节点信息
Map<String, dynamic>? kr_getGlobalAutoSelectedNode() {
try {
final activeGroups = KRSingBoxImp.instance.kr_activeGroups;
KRLogUtil.kr_i('🌍 开始获取全局auto选中节点信息', tag: 'HomeController');
KRLogUtil.kr_i('📊 活跃组数量: ${activeGroups.length}', tag: 'HomeController');
for (var group in activeGroups) {
KRLogUtil.kr_i('🔄 检查全局组: tag=${group.tag}, type=${group.type}, selected=${group.selected}', tag: 'HomeController');
KRLogUtil.kr_i('📋 全局组内项目数量: ${group.items.length}', tag: 'HomeController');
if (group.tag == 'auto' && group.type == ProxyType.urltest) {
KRLogUtil.kr_i('✅ 找到全局auto urltest组', tag: 'HomeController');
// 获取当前选中的节点
final selectedNode = group.selected;
KRLogUtil.kr_i('🎯 全局auto当前选中节点: $selectedNode', tag: 'HomeController');
if (selectedNode != null && selectedNode.isNotEmpty) {
KRLogUtil.kr_i('✅ 全局auto选中节点有效,开始查找详情', tag: 'HomeController');
// 打印所有节点信息用于调试
for (var item in group.items) {
KRLogUtil.kr_i('📄 全局auto组内节点: tag=${item.tag}, delay=${item.urlTestDelay}', tag: 'HomeController');
}
// 在组内查找选中的节点
try {
final selectedItem = group.items.firstWhere(
(item) => item.tag == selectedNode,
);
KRLogUtil.kr_i('🎉 成功找到全局auto选中节点: tag=${selectedItem.tag}, delay=${selectedItem.urlTestDelay}', tag: 'HomeController');
return {
'tag': selectedNode,
'delay': selectedItem.urlTestDelay,
'country': '',
};
} catch (e) {
KRLogUtil.kr_e('❌ 在全局auto组内未找到选中节点: $selectedNode', tag: 'HomeController');
return null;
}
} else {
KRLogUtil.kr_w('⚠️ 全局auto选中节点为空或无效', tag: 'HomeController');
}
}
}
KRLogUtil.kr_w('❌ 未找到全局auto组', tag: 'HomeController');
} catch (e) {
KRLogUtil.kr_e('💥 获取全局auto选中节点异常: $e', tag: 'HomeController');
}
/// 从订阅服务获取该国家最快节点的备用方案
Map<String, dynamic>? _kr_getFastestNodeFromSubscribeService(String countryCode) {
return null;
}
/// 提取选中节点信息的辅助方法
Map<String, dynamic>? _kr_extractSelectedNodeInfo(dynamic group, String countryCode) {
return null;
}
// 已移除:全局 auto 选中节点信息获取(统一为手动选择模式)
/// 获取指定国家的所有真实节点延迟列表
List<Map<String, dynamic>> kr_getCountryRealNodeDelays(String countryCode) {
final delays = <Map<String, dynamic>>[];
try {
// 从订阅服务中获取该国家的节点列表
final countryNodes = kr_subscribeService.keyList.values
.where((item) => item.country == countryCode && !item.tag.endsWith('-auto'))
.where((item) => item.country == countryCode)
.toList();
for (final node in countryNodes) {
delays.add({
'tag': node.tag,
@@ -2407,13 +2203,13 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
'city': node.city,
});
}
// 按延迟排序
delays.sort((a, b) => a['delay'].compareTo(b['delay']));
} catch (e) {
KRLogUtil.kr_e('获取国家真实节点延迟列表失败: $e', tag: 'HomeController');
}
return delays;
}
@@ -2432,24 +2228,11 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
if (group.type == ProxyType.selector) {
KRLogUtil.kr_d('找到 selector 组: ${group.tag}, 选中: ${group.selected}', tag: 'HomeController');
// 如果是auto模式,从urltest组获取延迟
if (kr_cutTag.value.endsWith('auto')) {
for (var item in group.items) {
if (item.tag == "auto" && item.urlTestDelay != 0) {
kr_currentNodeLatency.value = item.urlTestDelay;
KRLogUtil.kr_i('✅ auto模式延迟值: ${item.urlTestDelay}ms', tag: 'HomeController');
return true;
}
}
}
// 手动选择模式
else {
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;
}
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;
}
}
}
@@ -34,7 +34,7 @@ class HIAnimatedConnectButton extends GetView<KRHomeController> {
print('🔵 Switch UI 更新: status=${status.runtimeType}, isConnected=$isConnected, isSwitching=$isSwitching');
final isShow = isConnected; // delay == -1 || isConnected;
final isShow = delay == -1 || isConnected;
final Color buttonColor = Theme.of(context).primaryColor;
final double screenWidth = Get.width;