From 8142887644e62c46d5988464a6a26b25b1bef805 Mon Sep 17 00:00:00 2001 From: lzx <393768033@qq.com> Date: Thu, 29 Jan 2026 10:44:29 +0800 Subject: [PATCH] lunux client --- lib/providers/client_provider.dart | 10 ++ lib/screens/main_screen.dart | 3 + lib/utils/path_utils.dart | 181 +++++++++++++++-------------- 3 files changed, 109 insertions(+), 85 deletions(-) diff --git a/lib/providers/client_provider.dart b/lib/providers/client_provider.dart index 0a93d67..881ff70 100644 --- a/lib/providers/client_provider.dart +++ b/lib/providers/client_provider.dart @@ -348,6 +348,16 @@ class ClientProvider with ChangeNotifier { await _initialize(); } + /// 优雅关闭客户端(用于退出应用) + Future shutdown() async { + _clientCore.stop(); + await _miningManager.stopMining(); + await _sustainMiner.stop(); + + _refreshTimer?.cancel(); + _refreshTimer = null; + } + /// 获取支持的挖矿软件列表(辅助方法) List _getMiningSofts(MiningConfig? miningConfig) { final miningSofts = []; diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 1c62a6b..7f7d401 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -423,6 +423,9 @@ class MainScreen extends StatelessWidget { ); if (confirmed == true) { + // 先优雅关闭客户端和挖矿进程,再退出程序 + final provider = Provider.of(context, listen: false); + await provider.shutdown(); exit(0); } } diff --git a/lib/utils/path_utils.dart b/lib/utils/path_utils.dart index 3bf333e..68f85f8 100644 --- a/lib/utils/path_utils.dart +++ b/lib/utils/path_utils.dart @@ -2,108 +2,119 @@ import 'dart:io'; import 'package:path/path.dart' as p; /// 路径工具类 - 获取应用根目录和 bin 目录 +/// +/// 约定: +/// - Windows 开发模式:`windows/bin` 目录下放置配置和数据文件 +/// - Windows 发布模式:可执行文件同级目录下放置 `bin` 目录 +/// - Linux 开发模式:项目根目录下放置 `bin` 目录 +/// - Linux 发布模式:可执行文件同级目录下放置 `bin` 目录 class PathUtils { static String? _cachedAppRoot; /// 获取应用根目录 - /// 在发布模式下,返回 .exe 文件所在目录 - /// 在开发模式下,返回 windows 目录(因为 bin 文件夹在 windows/bin 下) static String get appRoot { if (_cachedAppRoot != null) { return _cachedAppRoot!; } - + try { - // 尝试获取可执行文件所在目录 final exePath = Platform.resolvedExecutable; final exeDir = p.dirname(exePath); - - // 检查是否是开发模式(可执行文件在 build 目录下) - if (exeDir.contains('build') || exeDir.contains('windows\\build') || exeDir.contains('windows/build')) { - // 开发模式:bin 文件夹在 windows/bin 下 - // 从可执行文件路径向上查找 windows 目录 - var currentPath = exeDir; - var foundWindows = false; - - // 向上查找,直到找到包含 'windows' 且不是 'build\windows' 的目录 - while (currentPath.isNotEmpty) { - final dirName = p.basename(currentPath); - final parent = p.dirname(currentPath); - - // 如果当前目录名是 'windows',且不在 build 路径中,说明找到了项目根目录下的 windows 目录 - if (dirName == 'windows' && !currentPath.contains('build\\windows') && !currentPath.contains('build/windows')) { - // 检查这个 windows 目录下是否有 bin 文件夹 - final binPath = p.join(currentPath, 'bin'); - if (Directory(binPath).existsSync()) { - _cachedAppRoot = currentPath; - foundWindows = true; - break; - } - } - - if (parent == currentPath) break; // 到达根目录 - currentPath = parent; - } - - if (foundWindows) { - return _cachedAppRoot!; - } - - // 如果向上查找失败,尝试从当前工作目录查找 - final currentDir = Directory.current.path; - if (currentDir.contains('windows')) { - // 找到 windows 目录 - final parts = currentDir.split(RegExp(r'[\\/]')); - var windowsIndex = -1; - for (int i = 0; i < parts.length; i++) { - if (parts[i] == 'windows') { - windowsIndex = i; - break; - } - } - - if (windowsIndex >= 0) { - final windowsDir = parts.sublist(0, windowsIndex + 1).join(Platform.pathSeparator); - final binPath = p.join(windowsDir, 'bin'); - if (Directory(binPath).existsSync()) { - _cachedAppRoot = windowsDir; - return _cachedAppRoot!; - } - } - } + + if (Platform.isWindows) { + _cachedAppRoot = _resolveWindowsAppRoot(exeDir); + } else if (Platform.isLinux) { + _cachedAppRoot = _resolveLinuxAppRoot(exeDir); + } else { + // 其他平台暂时按当前工作目录 + bin 处理 + _cachedAppRoot = _resolveGenericAppRoot(exeDir); } - - // 发布模式:可执行文件和 bin 文件夹在同一目录 - _cachedAppRoot = exeDir; + return _cachedAppRoot!; - } catch (e) { - // 如果获取失败,尝试使用当前工作目录 - final currentDir = Directory.current.path; - if (currentDir.contains('windows')) { - // 找到 windows 目录 - final parts = currentDir.split(RegExp(r'[\\/]')); - var windowsIndex = -1; - for (int i = 0; i < parts.length; i++) { - if (parts[i] == 'windows') { - windowsIndex = i; - break; - } - } - - if (windowsIndex >= 0) { - final windowsDir = parts.sublist(0, windowsIndex + 1).join(Platform.pathSeparator); - final binPath = p.join(windowsDir, 'bin'); - if (Directory(binPath).existsSync()) { - _cachedAppRoot = windowsDir; - return _cachedAppRoot!; - } - } - } - _cachedAppRoot = currentDir; + } catch (_) { + // 兜底:使用当前工作目录 + _cachedAppRoot = Directory.current.path; return _cachedAppRoot!; } } + /// Windows 平台下解析根目录 + static String _resolveWindowsAppRoot(String exeDir) { + // 开发模式:可执行文件在 build 目录下,需要回溯到 windows 目录 + if (exeDir.contains('build') || exeDir.contains('windows\\build') || exeDir.contains('windows/build')) { + var currentPath = exeDir; + + while (currentPath.isNotEmpty) { + final dirName = p.basename(currentPath); + final parent = p.dirname(currentPath); + + if (dirName == 'windows' && + !currentPath.contains('build\\windows') && + !currentPath.contains('build/windows')) { + final binPath = p.join(currentPath, 'bin'); + if (Directory(binPath).existsSync()) { + return currentPath; + } + } + + if (parent == currentPath) break; + currentPath = parent; + } + + // 回退到通过当前工作目录查找 + final fromCwd = _findDirWithBinFromCwd('windows'); + if (fromCwd != null) return fromCwd; + } + + // 发布模式:可执行文件和 bin 文件夹在同一目录 + return exeDir; + } + + /// Linux 平台下解析根目录 + static String _resolveLinuxAppRoot(String exeDir) { + // 如果可执行文件目录下有 bin,优先使用(发布模式) + final binInExeDir = p.join(exeDir, 'bin'); + if (Directory(binInExeDir).existsSync()) { + return exeDir; + } + + // 开发模式:从当前工作目录向上查找包含 bin 的目录(项目根目录) + final fromCwd = _findDirWithBinFromCwd(null); + if (fromCwd != null) return fromCwd; + + // 再退回到 exeDir + return exeDir; + } + + /// 通用平台兜底逻辑:优先找当前工作目录上的 bin + static String _resolveGenericAppRoot(String exeDir) { + final fromCwd = _findDirWithBinFromCwd(null); + if (fromCwd != null) return fromCwd; + return exeDir; + } + + /// 从当前工作目录向上查找,直到找到满足条件的目录 + /// 如果 [expectDirName] 不为空,则必须匹配目录名,例如 'windows' + static String? _findDirWithBinFromCwd(String? expectDirName) { + var currentPath = Directory.current.path; + + while (currentPath.isNotEmpty) { + final dirName = p.basename(currentPath); + final binPath = p.join(currentPath, 'bin'); + + if ((expectDirName == null || dirName == expectDirName) && + Directory(binPath).existsSync()) { + return currentPath; + } + + final parent = p.dirname(currentPath); + if (parent == currentPath) break; + currentPath = parent; + } + + return null; + } + /// 获取 bin 目录路径 static String get binDir { final root = appRoot;