diff --git a/lib/app/model/response/kr_node_list.dart b/lib/app/model/response/kr_node_list.dart index 8b57413..6b25e72 100755 --- a/lib/app/model/response/kr_node_list.dart +++ b/lib/app/model/response/kr_node_list.dart @@ -137,12 +137,12 @@ class KrNodeListItem { final protocols = json['protocols']?.toString() ?? ''; // 协议配置JSON // 🔧 打印原始节点 JSON(用于调试) - KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'NodeList'); - KRLogUtil.kr_i('📥 收到节点 API 原始数据:', tag: 'NodeList'); - KRLogUtil.kr_i('节点名称: ${json['name']}', tag: 'NodeList'); - KRLogUtil.kr_i('协议类型: ${json['protocol']}', tag: 'NodeList'); - KRLogUtil.kr_i('完整 JSON:', tag: 'NodeList'); - KRLogUtil.kr_i(jsonEncode(json), tag: 'NodeList'); + // KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'NodeList'); + // KRLogUtil.kr_i('📥 收到节点 API 原始数据:', tag: 'NodeList'); + // KRLogUtil.kr_i('节点名称: ${json['name']}', tag: 'NodeList'); + // KRLogUtil.kr_i('协议类型: ${json['protocol']}', tag: 'NodeList'); + // KRLogUtil.kr_i('完整 JSON:', tag: 'NodeList'); + // KRLogUtil.kr_i(jsonEncode(json), tag: 'NodeList'); // 🔧 如果有 protocols 字段,从中解析 cipher(但不覆盖顶层的 port) if (protocols.isNotEmpty) { diff --git a/lib/app/modules/kr_home/views/hi_animated_connect_button.dart b/lib/app/modules/kr_home/views/hi_animated_connect_button.dart index dc2f683..1e4fd51 100644 --- a/lib/app/modules/kr_home/views/hi_animated_connect_button.dart +++ b/lib/app/modules/kr_home/views/hi_animated_connect_button.dart @@ -1,4 +1,5 @@ import 'dart:math'; +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter/services.dart'; @@ -11,6 +12,9 @@ import 'package:kaer_with_panels/app/services/global_overlay_service.dart'; import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart'; import 'package:kaer_with_panels/singbox/model/singbox_status.dart'; +DateTime? _hiConnectBtnNextAllowedAt; +const Duration _hiConnectBtnDebounce = Duration(milliseconds: 800); + /// ✅ 按钮组件(带多层呼吸同心圆动画) class HIAnimatedConnectButton extends GetView { final VoidCallback? onTriggerSubscriptionAnimation; @@ -88,11 +92,16 @@ class HIAnimatedConnectButton extends GetView { child: InkWell( onTap: () { HapticFeedback.lightImpact(); - if (isSwitching) { - print( - '🔵 Switch UI 正在更新,切换中点击了按钮: status=${status.runtimeType}, isConnected=$isConnected, isSwitching=$isSwitching'); + final _now = DateTime.now(); + if (_hiConnectBtnNextAllowedAt != null && + _now.isBefore(_hiConnectBtnNextAllowedAt!)) { + _hiConnectBtnNextAllowedAt = + _now.add(_hiConnectBtnDebounce); return; } + _hiConnectBtnNextAllowedAt = + _now.add(_hiConnectBtnDebounce); + final current = controller .kr_subscribeService.kr_currentSubscribe.value; bool hasValidSubscription = false; diff --git a/lib/app/network/base_response.dart b/lib/app/network/base_response.dart index 1a479ff..78c7e02 100755 --- a/lib/app/network/base_response.dart +++ b/lib/app/network/base_response.dart @@ -40,13 +40,13 @@ class BaseResponse { body = jsonDecode(decrypted); if (kDebugMode) { - print('✅ 解密成功'); - print(''); - print('📦 解密后的完整数据:'); - // 格式化打印 JSON,方便调试 - final bodyStr = JsonEncoder.withIndent(' ').convert(body); - print(bodyStr); - print('═══════════════════════════════════════'); + // print('✅ 解密成功'); + // print(''); + // print('📦 解密后的完整数据:'); + // // 格式化打印 JSON,方便调试 + // final bodyStr = JsonEncoder.withIndent(' ').convert(body); + // print(bodyStr); + // print('═══════════════════════════════════════'); } KRLogUtil.kr_i('🔓 解密成功', tag: 'BaseResponse'); diff --git a/lib/app/network/http_util.dart b/lib/app/network/http_util.dart index e5127f3..acdad11 100755 --- a/lib/app/network/http_util.dart +++ b/lib/app/network/http_util.dart @@ -50,7 +50,7 @@ class HttpUtil { void initDio() { KRLogUtil.kr_i('🚀 HttpUtil.initDio() 开始初始化', tag: 'HttpUtil'); // 不使用 Loggy,改用自定义简洁拦截器 - _dio.interceptors.add(_KRSimpleHttpInterceptor()); + _dio.interceptors.add(_KRSimpleHttpInterceptor(_dio)); _dio.options.baseUrl = AppConfig.getInstance().baseUrl; // 设置连接超时时间 @@ -334,7 +334,11 @@ class HttpUtil { ? err.requestOptions.path : err.requestOptions.uri.path; msg = '${msg.isNotEmpty ? msg : 'unknown'} ($_pathOnly)'; - KRCommonUtil.kr_showToast('请求失败($_pathOnly)', timeout: 3500); + final _ua = + (err.requestOptions.extra['__unknown_attempts'] as int?) ?? 0; + if (_ua >= 2) { + KRCommonUtil.kr_showToast('请求失败($_pathOnly)', timeout: 3500); + } } return BaseResponse.fromJson( {'code': code, 'msg': msg, 'data': {}}); @@ -405,6 +409,8 @@ class MyInterceptor extends Interceptor { /// 自定义简洁 HTTP 拦截器(无边框符号) class _KRSimpleHttpInterceptor extends Interceptor { + final Dio _dio; + _KRSimpleHttpInterceptor(this._dio); static String? _lastPath; static int _lastTsMs = 0; @override @@ -546,14 +552,38 @@ class _KRSimpleHttpInterceptor extends Interceptor { final path = (err.requestOptions.path.isNotEmpty) ? err.requestOptions.path : err.requestOptions.uri.path; - final now = DateTime.now().millisecondsSinceEpoch; - if (!(_lastPath == path && (now - _lastTsMs) < 2000)) { - _lastPath = path; - _lastTsMs = now; - KRCommonUtil.kr_showToast('请求失败($path)', timeout: 3500); + int unknownAttempts = + (err.requestOptions.extra['__unknown_attempts'] as int?) ?? 0; + if (unknownAttempts < 2) { + err.requestOptions.extra['__unknown_attempts'] = unknownAttempts + 1; + final delayMs = unknownAttempts == 0 ? 300 : 700; + Future.delayed(Duration(milliseconds: delayMs)).then((_) async { + final start = DateTime.now(); + while (!KRSingBoxImp.instance.kr_isProxyReady && + DateTime.now().difference(start) < const Duration(seconds: 1)) { + await Future.delayed(const Duration(milliseconds: 100)); + } + try { + final Response r = + await _dio.fetch(err.requestOptions); + handler.resolve(r); + } catch (e) { + handler.next(e is DioException + ? e + : DioException(requestOptions: err.requestOptions, error: e)); + } + }); + return; + } else { + final now = DateTime.now().millisecondsSinceEpoch; + if (!(_lastPath == path && (now - _lastTsMs) < 2000)) { + _lastPath = path; + _lastTsMs = now; + KRCommonUtil.kr_showToast('请求失败($path)', timeout: 3500); + } + KRLogUtil.kr_e('请求失败($path)', + tag: 'HttpUtil', error: err, stackTrace: err.stackTrace); } - KRLogUtil.kr_e('请求失败($path)', - tag: 'HttpUtil', error: err, stackTrace: err.stackTrace); } handler.next(err); } diff --git a/说明文档.md b/说明文档.md deleted file mode 100644 index e66c3a8..0000000 --- a/说明文档.md +++ /dev/null @@ -1,140 +0,0 @@ -# Windows 构建项目说明文档 - -## 🎯 项目概述 - -本项目是一个 Flutter Windows 应用程序,已成功配置完整的 Windows 构建流程。 - -## ✅ 构建状态 - -**当前状态**: ✅ **构建成功** - -- **Debug 构建**: ✓ 成功 (282.5s) -- **Release 构建**: ✓ 成功 (27s) -- **构建环境**: Windows Server + Visual Studio 2022 Enterprise - -## 🏗️ 构建流程 - -### 1. 环境准备 -- ✅ Flutter 3.24.5 已安装 -- ✅ Visual Studio 2022 Enterprise 已配置 -- ✅ NuGet 6.14.0 已安装(通过 Chocolatey) -- ✅ Windows 长路径支持已启用 - -### 2. 构建步骤 -1. **代码检出** - 从 Gitea 仓库获取代码 -2. **依赖安装** - 安装 Flutter 依赖包 -3. **代码生成** - 运行 build_runner 生成代码 -4. **Windows 构建** - 构建 Debug 和 Release 版本 -5. **产物上传** - 上传构建产物到 Gitea Actions - -### 3. 构建输出 -``` -Debug: build/windows/x64/runner/Debug/hostexecutor.exe -Release: build/windows/x64/runner/Release/hostexecutor.exe -``` - -## 🔧 关键修复记录 - -### 1. NuGet 安装问题 -**问题**: SSL/TLS 安全通道错误 -**解决方案**: -- 使用 Chocolatey 安装 NuGet -- 命令: `choco install nuget.commandline -y` - -### 2. Flutter 路径配置 -**问题**: Flutter 命令未找到 -**解决方案**: -- 添加 Flutter 到 PATH: `C:\flutter\bin` -- 在每个构建步骤中显式设置 PATH - -### 3. 长路径问题 -**问题**: Windows 路径长度限制 -**解决方案**: -- 启用 Windows 长路径支持 -- 注册表设置: `LongPathsEnabled = 1` - -### 4. 构建产物路径 -**问题**: 上传路径配置错误 -**解决方案**: -- 修正路径: `build/windows/x64/runner/Debug/` -- 原错误路径: `build/windows/runner/Debug/` - -## 📁 项目结构 - -``` -/Users/Apple/vpn/hi-client/ -├── .gitea/workflows/ # Gitea Actions 工作流配置 -├── lib/ # Flutter 源代码 -│ ├── app/ # 应用程序代码 -│ ├── core/ # 核心功能 -│ └── singbox/ # SingBox 相关 -├── windows/ # Windows 平台配置 -├── libcore/ # 核心库(子模块) -└── build/ # 构建输出(运行时生成) -``` - -## 🚀 快速开始 - -### 本地构建 -```bash -# 安装 Flutter 依赖 -flutter pub get - -# 生成代码 -dart run build_runner build --delete-conflicting-outputs - -# 构建 Windows Debug -flutter build windows - -# 构建 Windows Release -flutter build windows --release -``` - -### 使用脚本 -```bash -# 运行 Windows 构建修复脚本 -./fix_windows_build.ps1 - -# 安装 NuGet(如果需要) -./install_nuget_simple.bat -``` - -## 📋 注意事项 - -### 1. 构建环境要求 -- Windows 10/11 或 Windows Server -- Visual Studio 2022(包含 C++ 开发工具) -- Flutter 3.24.5+ -- NuGet CLI - -### 2. 常见问题 -- **CMake 警告**: 可忽略,不影响构建 -- **WebView2 警告**: 类型转换警告,不影响功能 -- **路径问题**: 确保使用正确的 x64 路径 - -### 3. 性能优化 -- Debug 构建约 4.7 分钟 -- Release 构建约 27 秒 -- 建议使用 Release 版本进行分发 - -## 🔍 调试工具 - -### 构建日志分析 -查看 `构建日志分析.md` 文件获取详细的构建日志分析和故障排除指南。 - -### 连接状态调试 -使用 `debug_connection_status.dart` 工具检查应用连接状态。 - -## 📞 支持 - -如遇到构建问题,请检查: -1. 环境配置是否正确 -2. 依赖是否完整安装 -3. 查看构建日志获取具体错误信息 -4. 参考本说明文档的修复记录 - ---- - -**最后更新**: $(date) -**构建状态**: ✅ 成功 -**文档版本**: 1.0 \ No newline at end of file