From 17ea51b58329adfb44380c7c90b00eb412a835a9 Mon Sep 17 00:00:00 2001 From: Rust Date: Wed, 22 Oct 2025 21:49:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=89=A9=E5=B1=95=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E5=8D=8F=E8=AE=AE=E5=AD=97=E6=AE=B5=E5=B9=B6=E4=B8=94?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=82=80=E8=AF=B7=E7=A0=81=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/app/common/app_run_data.dart | 60 +++++++++- lib/app/model/business/kr_outbound_item.dart | 35 +++++- lib/app/model/response/kr_node_list.dart | 38 +++++- lib/app/model/response/kr_user_info.dart | 18 ++- .../controllers/kr_invite_controller.dart | 23 +++- lib/app/services/api_service/api.dart | 3 + lib/app/services/api_service/kr_api.user.dart | 32 ++--- .../services/singbox_imp/kr_sing_box_imp.dart | 110 ++++++++++++------ 8 files changed, 252 insertions(+), 67 deletions(-) diff --git a/lib/app/common/app_run_data.dart b/lib/app/common/app_run_data.dart index 2e2996f..ebd3687 100755 --- a/lib/app/common/app_run_data.dart +++ b/lib/app/common/app_run_data.dart @@ -30,6 +30,15 @@ class KRAppRunData { /// 用户ID(使用响应式变量以便 UI 能监听变化) final Rx kr_userId = Rx(null); + /// 用户邀请码(从用户信息接口获取) + final RxString kr_referCode = ''.obs; + + /// 用户余额 + final RxInt kr_balance = 0.obs; + + /// 佣金 + final RxInt kr_commission = 0.obs; + /// 登录类型 KRLoginType? kr_loginType; @@ -146,9 +155,17 @@ class KRAppRunData { // 只有在保存成功后才设置登录状态 kr_isLogin.value = true; - // 设备登录模式不再调用用户信息接口 - // Socket 连接将在需要时建立 - KRLogUtil.kr_i('用户信息已保存,跳过用户信息接口调用', tag: 'AppRunData'); + // 🔧 非游客模式下,调用用户信息接口获取 refer_code 等信息 + KRLogUtil.kr_i('🔍 [AppRunData] 检查登录模式: account=$account', tag: 'AppRunData'); + final isDevice = isDeviceLogin(); + KRLogUtil.kr_i('🔍 [AppRunData] isDeviceLogin: $isDevice', tag: 'AppRunData'); + + if (!isDevice) { + KRLogUtil.kr_i('✅ [AppRunData] 正常登录模式,开始获取用户详细信息', tag: 'AppRunData'); + await _fetchUserInfo(); + } else { + KRLogUtil.kr_i('⏭️ [AppRunData] 设备登录模式,跳过用户信息接口调用', tag: 'AppRunData'); + } } catch (e) { KRLogUtil.kr_e('保存用户信息失败: $e', tag: 'AppRunData'); @@ -298,4 +315,41 @@ class KRAppRunData { 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) { + 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] email: ${userInfo.email}', tag: 'AppRunData'); + KRLogUtil.kr_i('🆔 [AppRunData] id: ${userInfo.id}', tag: 'AppRunData'); + + // 保存到全局状态 + kr_referCode.value = userInfo.referCode; + kr_balance.value = userInfo.balance; + kr_commission.value = userInfo.commission; + + KRLogUtil.kr_i('💾 [AppRunData] 用户信息已保存到全局状态:', tag: 'AppRunData'); + KRLogUtil.kr_i(' - kr_referCode: "${kr_referCode.value}"', tag: 'AppRunData'); + KRLogUtil.kr_i(' - kr_balance: ${kr_balance.value}', tag: 'AppRunData'); + KRLogUtil.kr_i(' - kr_commission: ${kr_commission.value}', tag: 'AppRunData'); + }, + ); + } catch (e, stackTrace) { + KRLogUtil.kr_e('💥 [AppRunData] 获取用户信息异常: $e', tag: 'AppRunData'); + KRLogUtil.kr_e('📚 [AppRunData] 错误堆栈: $stackTrace', tag: 'AppRunData'); + } + } } diff --git a/lib/app/model/business/kr_outbound_item.dart b/lib/app/model/business/kr_outbound_item.dart index 63131b2..3f9114d 100755 --- a/lib/app/model/business/kr_outbound_item.dart +++ b/lib/app/model/business/kr_outbound_item.dart @@ -41,19 +41,24 @@ class KROutboundItem { tag = nodeListItem.name; // 设置标签 serverAddr = nodeListItem.serverAddr; // 设置服务器地址 - // 将 config 字符串转换为 Map city = nodeListItem.city; // 设置城市 country = nodeListItem.country; // 设置国家 - // 安全解析 config 字段 - // 新API格式:config为空,直接使用节点字段构建配置 - // 旧API格式:config包含JSON配置 - if (nodeListItem.config.isEmpty) { + // 🔧 优先使用直接字段构建配置(新API格式) + // 判断条件:如果有 port 和 serverAddr,说明是新格式 + if (nodeListItem.port > 0 && nodeListItem.serverAddr.isNotEmpty) { print('ℹ️ 节点 ${nodeListItem.name} 使用直接字段构建配置'); _buildConfigFromFields(nodeListItem); return; } + // 兜底:尝试解析 config 字段(旧API格式) + if (nodeListItem.config.isEmpty) { + print('❌ 节点 ${nodeListItem.name} 缺少配置信息(无port或config)'); + config = {}; + return; + } + late Map json; try { json = jsonDecode(nodeListItem.config) as Map; @@ -228,17 +233,31 @@ class KROutboundItem { /// 直接从节点字段构建配置(新API格式) void _buildConfigFromFields(KrNodeListItem nodeListItem) { + print('🔧 开始构建节点配置 - 协议: ${nodeListItem.protocol}, 名称: ${nodeListItem.name}'); + print('📋 节点详细信息:'); + print(' - serverAddr: ${nodeListItem.serverAddr}'); + print(' - port: ${nodeListItem.port}'); + print(' - uuid: ${nodeListItem.uuid}'); + print(' - method: ${nodeListItem.method}'); + switch (nodeListItem.protocol) { case "shadowsocks": + // 优先使用 protocols 解析出来的 cipher,其次是 method 字段,最后才是默认值 + String finalMethod = nodeListItem.method.isNotEmpty + ? nodeListItem.method + : "2022-blake3-aes-256-gcm"; + config = { "type": "shadowsocks", "tag": nodeListItem.name, "server": nodeListItem.serverAddr, "server_port": nodeListItem.port, - "method": "chacha20-ietf-poly1305", // 默认加密方法 + "method": finalMethod, "password": nodeListItem.uuid }; print('✅ Shadowsocks 节点配置构建成功: ${nodeListItem.name}'); + print('📄 使用加密方法: $finalMethod'); + print('📄 完整配置: $config'); break; case "vless": config = { @@ -258,6 +277,7 @@ class KROutboundItem { } }; print('✅ VLESS 节点配置构建成功: ${nodeListItem.name}'); + print('📄 完整配置: $config'); break; case "vmess": config = { @@ -276,6 +296,7 @@ class KROutboundItem { } }; print('✅ VMess 节点配置构建成功: ${nodeListItem.name}'); + print('📄 完整配置: $config'); break; case "trojan": config = { @@ -292,6 +313,7 @@ class KROutboundItem { } }; print('✅ Trojan 节点配置构建成功: ${nodeListItem.name}'); + print('📄 完整配置: $config'); break; case "hysteria2": config = { @@ -310,6 +332,7 @@ class KROutboundItem { } }; print('✅ Hysteria2 节点配置构建成功: ${nodeListItem.name}'); + print('📄 完整配置: $config'); break; default: print('⚠️ 不支持的协议类型: ${nodeListItem.protocol}'); diff --git a/lib/app/model/response/kr_node_list.dart b/lib/app/model/response/kr_node_list.dart index a8c6f54..a1740d3 100755 --- a/lib/app/model/response/kr_node_list.dart +++ b/lib/app/model/response/kr_node_list.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'package:kaer_with_panels/app/utils/kr_log_util.dart'; class KRNodeList { @@ -54,7 +55,9 @@ class KrNodeListItem { final String relayMode; final String relayNode; final String serverAddr; - final int port; // 新增:端口字段 + final int port; // 端口字段 + final String method; // 加密方法(用于Shadowsocks等) + final String protocols; // 协议配置JSON字符串(新API格式) final int speedLimit; final List tags; final int traffic; @@ -81,6 +84,8 @@ class KrNodeListItem { this.relayNode = '', required this.serverAddr, this.port = 0, // 默认值 + this.method = '', // 默认空字符串 + this.protocols = '', // 默认空字符串 required this.speedLimit, required this.tags, required this.traffic, @@ -102,10 +107,33 @@ class KrNodeListItem { factory KrNodeListItem.fromJson(Map json) { try { // 支持新旧两种API格式 - // 新格式: address, port + // 最新格式: protocols 字段包含协议配置数组 + // 新格式: address, port, method(直接字段) // 旧格式: server_addr, config 中包含 port final serverAddr = json['address']?.toString() ?? json['server_addr']?.toString() ?? ''; - final port = _parseIntSafely(json['port']); + int port = _parseIntSafely(json['port']); + String method = json['method']?.toString() ?? ''; // 加密方法(Shadowsocks等) + final protocols = json['protocols']?.toString() ?? ''; // 协议配置JSON + + // 🔧 如果有 protocols 字段,从中解析 port 和 cipher + if (protocols.isNotEmpty) { + try { + final protocolsList = jsonDecode(protocols) as List; + if (protocolsList.isNotEmpty) { + final firstProtocol = protocolsList[0] as Map; + // 优先使用 protocols 中的配置 + if (firstProtocol['port'] != null) { + port = _parseIntSafely(firstProtocol['port']); + } + if (firstProtocol['cipher'] != null && firstProtocol['cipher'].toString().isNotEmpty) { + method = firstProtocol['cipher'].toString(); + } + KRLogUtil.kr_i('从 protocols 解析: port=$port, cipher=$method', tag: 'NodeList'); + } + } catch (e) { + KRLogUtil.kr_w('解析 protocols 字段失败: $e', tag: 'NodeList'); + } + } return KrNodeListItem( id: _parseIntSafely(json['id']), @@ -116,6 +144,8 @@ class KrNodeListItem { relayNode: json['relay_node']?.toString() ?? '', serverAddr: serverAddr, port: port, + method: method, + protocols: protocols, speedLimit: _parseIntSafely(json['speed_limit']), tags: _parseStringList(json['tags']), traffic: _parseIntSafely(json['traffic']), @@ -142,6 +172,8 @@ class KrNodeListItem { protocol: '', serverAddr: '', port: 0, + method: '', + protocols: '', speedLimit: 0, tags: [], traffic: 0, diff --git a/lib/app/model/response/kr_user_info.dart b/lib/app/model/response/kr_user_info.dart index 434fd92..a999073 100755 --- a/lib/app/model/response/kr_user_info.dart +++ b/lib/app/model/response/kr_user_info.dart @@ -6,7 +6,11 @@ class KRUserInfo { final String avatar; final String areaCode; final String telephone; - final int balance; + final int balance; + final int commission; + final int referralPercentage; + final bool onlyFirstPurchase; + final int giftAmount; KRUserInfo({ required this.id, @@ -16,7 +20,11 @@ class KRUserInfo { this.avatar = '', this.areaCode = '', this.telephone = '', - this.balance = 0 + this.balance = 0, + this.commission = 0, + this.referralPercentage = 0, + this.onlyFirstPurchase = false, + this.giftAmount = 0, }); factory KRUserInfo.fromJson(Map json) { @@ -28,7 +36,11 @@ class KRUserInfo { avatar: json['avatar'] ?? '', areaCode: json['area_code'] ?? '', telephone: json['telephone'] ?? '', - balance: json['balance'] ?? 0, + balance: json['balance'] ?? 0, + commission: json['commission'] ?? 0, + referralPercentage: json['referral_percentage'] ?? 0, + onlyFirstPurchase: json['only_first_purchase'] ?? false, + giftAmount: json['gift_amount'] ?? 0, ); } } diff --git a/lib/app/modules/kr_invite/controllers/kr_invite_controller.dart b/lib/app/modules/kr_invite/controllers/kr_invite_controller.dart index 63a1a91..c9c9574 100755 --- a/lib/app/modules/kr_invite/controllers/kr_invite_controller.dart +++ b/lib/app/modules/kr_invite/controllers/kr_invite_controller.dart @@ -3,6 +3,7 @@ import 'package:kaer_with_panels/app/common/app_config.dart'; import 'package:kaer_with_panels/app/common/app_run_data.dart'; import 'package:kaer_with_panels/app/services/api_service/kr_api.user.dart'; import 'package:kaer_with_panels/app/utils/kr_common_util.dart'; +import 'package:kaer_with_panels/app/utils/kr_log_util.dart'; import 'package:kaer_with_panels/app/localization/app_translations.dart'; import 'package:kaer_with_panels/app/routes/app_pages.dart'; import 'package:flutter/services.dart'; @@ -72,15 +73,31 @@ class KRInviteController extends GetxController { } // ⚠️ 已废弃:新版本后端不再提供 kr_getUserInfo 接口 - // 邀请码现在使用 AppConfig 中的固定值,等待新接口实现 + // 从 AppRunData 获取邀请码(登录后自动获取) Future _kr_fetchUserInfo() async { try { kr_isLoading.value = true; - // 使用 AppConfig 中的固定邀请码 - kr_referCode.value = AppConfig.kr_userReferCode; + KRLogUtil.kr_i('🔍 [InviteController] 开始获取邀请码...', tag: 'InviteController'); + + // 从 AppRunData 获取用户邀请码(已在登录时获取) + final appData = KRAppRunData.getInstance(); + KRLogUtil.kr_i('📊 [InviteController] AppRunData 状态:', tag: 'InviteController'); + KRLogUtil.kr_i(' - kr_isLogin: ${appData.kr_isLogin.value}', tag: 'InviteController'); + KRLogUtil.kr_i(' - kr_account: ${appData.kr_account.value}', tag: 'InviteController'); + KRLogUtil.kr_i(' - kr_referCode: ${appData.kr_referCode.value}', tag: 'InviteController'); + KRLogUtil.kr_i(' - kr_balance: ${appData.kr_balance.value}', tag: 'InviteController'); + KRLogUtil.kr_i(' - kr_commission: ${appData.kr_commission.value}', tag: 'InviteController'); + + kr_referCode.value = appData.kr_referCode.value; + KRLogUtil.kr_i('📋 [InviteController] 获取到邀请码: "${kr_referCode.value}"', tag: 'InviteController'); + + if (kr_referCode.value.isEmpty) { + KRLogUtil.kr_w('⚠️ [InviteController] 邀请码为空!', tag: 'InviteController'); + } } catch (e) { + KRLogUtil.kr_e('❌ [InviteController] 获取邀请码失败: $e', tag: 'InviteController'); KRCommonUtil.kr_showToast(e.toString()); } finally { kr_isLoading.value = false; diff --git a/lib/app/services/api_service/api.dart b/lib/app/services/api_service/api.dart index aaaefc4..c1dc9ea 100755 --- a/lib/app/services/api_service/api.dart +++ b/lib/app/services/api_service/api.dart @@ -101,4 +101,7 @@ abstract class Api { /// 获取可用支付方式(公开接口) static const String kr_getPublicPaymentMethods = "/v1/public/payment/methods"; + + /// 获取用户信息(用于获取邀请码等) + static const String kr_getUserInfo = "/v1/public/user/info"; } diff --git a/lib/app/services/api_service/kr_api.user.dart b/lib/app/services/api_service/kr_api.user.dart index cffd47a..1a7d100 100755 --- a/lib/app/services/api_service/kr_api.user.dart +++ b/lib/app/services/api_service/kr_api.user.dart @@ -83,22 +83,22 @@ class KRUserApi { return right(baseResponse.model); } - // ⚠️ 已废弃:新版本后端不再提供此接口 - // Future> kr_getUserInfo() async { - // final Map data = {}; - // BaseResponse baseResponse = - // await HttpUtil.getInstance().request( - // Api.kr_getUserInfo, - // data, - // method: HttpMethod.GET, - // isShowLoading: false, - // ); - // if (!baseResponse.isSuccess) { - // return left( - // HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); - // } - // return right(baseResponse.model); - // } + /// 获取用户信息(包含邀请码、余额、佣金等) + static Future> kr_getUserInfo() async { + final Map data = {}; + BaseResponse baseResponse = + await HttpUtil.getInstance().request( + Api.kr_getUserInfo, + data, + method: HttpMethod.GET, + isShowLoading: false, + ); + if (!baseResponse.isSuccess) { + return left( + HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); + } + return right(baseResponse.model); + } Future> kr_getAffiliateCount() async { final Map data = {}; diff --git a/lib/app/services/singbox_imp/kr_sing_box_imp.dart b/lib/app/services/singbox_imp/kr_sing_box_imp.dart index ccb5884..7f5ef5f 100755 --- a/lib/app/services/singbox_imp/kr_sing_box_imp.dart +++ b/lib/app/services/singbox_imp/kr_sing_box_imp.dart @@ -404,7 +404,27 @@ class KRSingBoxImp { void kr_saveOutbounds(List> outbounds) async { KRLogUtil.kr_i('💾 开始保存配置文件...', tag: 'SingBox'); KRLogUtil.kr_i('📊 出站节点数量: ${outbounds.length}', tag: 'SingBox'); - + + // 打印每个节点的详细配置 + for (int i = 0; i < outbounds.length; i++) { + final outbound = outbounds[i]; + KRLogUtil.kr_i('📋 节点[$i] 配置:', tag: 'SingBox'); + KRLogUtil.kr_i(' - type: ${outbound['type']}', tag: 'SingBox'); + KRLogUtil.kr_i(' - tag: ${outbound['tag']}', tag: 'SingBox'); + KRLogUtil.kr_i(' - server: ${outbound['server']}', tag: 'SingBox'); + KRLogUtil.kr_i(' - server_port: ${outbound['server_port']}', tag: 'SingBox'); + if (outbound['method'] != null) { + KRLogUtil.kr_i(' - method: ${outbound['method']}', tag: 'SingBox'); + } + if (outbound['password'] != null) { + KRLogUtil.kr_i(' - password: ${outbound['password']?.toString().substring(0, 8)}...', tag: 'SingBox'); + } + if (outbound['uuid'] != null) { + KRLogUtil.kr_i(' - uuid: ${outbound['uuid']?.toString().substring(0, 8)}...', tag: 'SingBox'); + } + KRLogUtil.kr_i(' - 完整配置: ${jsonEncode(outbound)}', tag: 'SingBox'); + } + kr_outbounds = outbounds; final map = {}; @@ -413,10 +433,10 @@ class KRSingBoxImp { final file = _file(kr_configName); final temp = _tempFile(kr_configName); final mapStr = jsonEncode(map); - + KRLogUtil.kr_i('📄 配置文件内容长度: ${mapStr.length}', tag: 'SingBox'); - KRLogUtil.kr_i('📄 配置文件前500字符: ${mapStr.substring(0, mapStr.length > 500 ? 500 : mapStr.length)}', tag: 'SingBox'); - + KRLogUtil.kr_i('📄 完整配置文件内容: $mapStr', tag: 'SingBox'); + await file.writeAsString(mapStr); await temp.writeAsString(mapStr); @@ -428,7 +448,7 @@ class KRSingBoxImp { .mapLeft((err) { KRLogUtil.kr_e('❌ 保存配置文件失败: $err', tag: 'SingBox'); }).run(); - + KRLogUtil.kr_i('✅ 配置文件保存完成', tag: 'SingBox'); } @@ -438,7 +458,7 @@ class KRSingBoxImp { KRLogUtil.kr_i('🚀 开始启动 SingBox...', tag: 'SingBox'); KRLogUtil.kr_i('📁 配置文件路径: $_cutPath', tag: 'SingBox'); KRLogUtil.kr_i('📝 配置名称: $kr_configName', tag: 'SingBox'); - + // 检查配置文件是否存在 final configFile = File(_cutPath); if (await configFile.exists()) { @@ -448,7 +468,7 @@ class KRSingBoxImp { } else { KRLogUtil.kr_w('⚠️ 配置文件不存在: $_cutPath', tag: 'SingBox'); } - + await kr_singBox.start(_cutPath, kr_configName, false).map( (r) { KRLogUtil.kr_i('✅ SingBox 启动成功', tag: 'SingBox'); @@ -525,35 +545,59 @@ class KRSingBoxImp { //// 设置出站模式 Future kr_updateConnectionType(KRConnectionType newType) async { - if (kr_connectionType.value == newType) { - return; - } + try { + KRLogUtil.kr_i('🔄 开始更新连接类型...', tag: 'SingBox'); + KRLogUtil.kr_i('📊 当前类型: ${kr_connectionType.value}', tag: 'SingBox'); + KRLogUtil.kr_i('📊 新类型: $newType', tag: 'SingBox'); - kr_connectionType.value = newType; + if (kr_connectionType.value == newType) { + KRLogUtil.kr_i('⚠️ 连接类型相同,无需更新', tag: 'SingBox'); + return; + } - final oOption = _getConfigOption(); + kr_connectionType.value = newType; - var mode = ""; - switch (newType) { - case KRConnectionType.global: - mode = "other"; - break; - case KRConnectionType.rule: - mode = KRCountryUtil.kr_getCurrentCountryCode(); - break; - // case KRConnectionType.direct: - // mode = "direct"; - // break; - } - oOption["region"] = mode; - final op = SingboxConfigOption.fromJson(oOption); + final oOption = _getConfigOption(); - await kr_singBox.changeOptions(op) - ..map((r) {}).mapLeft((err) { - KRLogUtil.kr_e('更新连接类型失败: $err'); - }).run(); - if (kr_status.value == SingboxStarted()) { - await kr_restart(); + var mode = ""; + switch (newType) { + case KRConnectionType.global: + mode = "other"; + KRLogUtil.kr_i('🌍 切换到全局代理模式', tag: 'SingBox'); + break; + case KRConnectionType.rule: + mode = KRCountryUtil.kr_getCurrentCountryCode(); + KRLogUtil.kr_i('🎯 切换到规则代理模式: $mode', tag: 'SingBox'); + break; + // case KRConnectionType.direct: + // mode = "direct"; + // break; + } + oOption["region"] = mode; + KRLogUtil.kr_i('📝 更新 region 配置: $mode', tag: 'SingBox'); + + final op = SingboxConfigOption.fromJson(oOption); + KRLogUtil.kr_i('📄 配置选项: ${oOption.toString()}', tag: 'SingBox'); + + await kr_singBox.changeOptions(op) + ..map((r) { + KRLogUtil.kr_i('✅ 连接类型更新成功', tag: 'SingBox'); + }).mapLeft((err) { + KRLogUtil.kr_e('❌ 更新连接类型失败: $err', tag: 'SingBox'); + throw err; + }).run(); + + if (kr_status.value == SingboxStarted()) { + KRLogUtil.kr_i('🔄 VPN已启动,准备重启以应用新配置...', tag: 'SingBox'); + await kr_restart(); + KRLogUtil.kr_i('✅ VPN重启完成', tag: 'SingBox'); + } else { + KRLogUtil.kr_i('ℹ️ VPN未启动,配置已更新', tag: 'SingBox'); + } + } catch (e, stackTrace) { + KRLogUtil.kr_e('💥 更新连接类型异常: $e', tag: 'SingBox'); + KRLogUtil.kr_e('📚 错误堆栈: $stackTrace', tag: 'SingBox'); + rethrow; } } @@ -598,7 +642,7 @@ class KRSingBoxImp { void kr_selectOutbound(String tag) { KRLogUtil.kr_i('🎯 开始选择出站节点: $tag', tag: 'SingBox'); KRLogUtil.kr_i('📊 当前活动组数量: ${kr_activeGroups.length}', tag: 'SingBox'); - + // 打印所有活动组信息 for (int i = 0; i < kr_activeGroups.length; i++) { final group = kr_activeGroups[i];