2026-01-22 15:14:27 +08:00
|
|
|
|
import 'dart:async';
|
|
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
|
|
import 'package:logging/logging.dart';
|
|
|
|
|
|
import '../core/client_core.dart';
|
|
|
|
|
|
import '../core/system_info.dart';
|
|
|
|
|
|
import '../core/mining_manager.dart';
|
|
|
|
|
|
import '../core/sustain_miner.dart';
|
|
|
|
|
|
import '../core/database.dart';
|
|
|
|
|
|
import '../core/mining_task_info.dart';
|
|
|
|
|
|
import '../services/config_service.dart';
|
|
|
|
|
|
import '../services/update_service.dart';
|
|
|
|
|
|
import '../models/client_status.dart';
|
|
|
|
|
|
|
|
|
|
|
|
/// 客户端状态管理 Provider
|
|
|
|
|
|
class ClientProvider with ChangeNotifier {
|
|
|
|
|
|
final Logger _logger = Logger('ClientProvider');
|
|
|
|
|
|
final ClientCore _clientCore = ClientCore();
|
|
|
|
|
|
final SystemInfoService _systemInfo = SystemInfoService();
|
|
|
|
|
|
final MiningManager _miningManager = MiningManager();
|
|
|
|
|
|
final SustainMiner _sustainMiner = SustainMiner();
|
|
|
|
|
|
final DatabaseService _database = DatabaseService();
|
|
|
|
|
|
final ConfigService _configService = ConfigService();
|
|
|
|
|
|
final UpdateService _updateService = UpdateService();
|
|
|
|
|
|
|
|
|
|
|
|
Timer? _refreshTimer;
|
|
|
|
|
|
ClientInfo? _clientInfo;
|
|
|
|
|
|
bool _isInitialized = false;
|
|
|
|
|
|
bool _isLoading = false;
|
|
|
|
|
|
MiningTaskInfo? _currentMiningTask;
|
|
|
|
|
|
|
|
|
|
|
|
// 服务器地址(从配置文件读取)
|
|
|
|
|
|
String? _serverUrl;
|
|
|
|
|
|
|
|
|
|
|
|
// 版本信息
|
|
|
|
|
|
String? _remoteVersion;
|
|
|
|
|
|
bool _hasUpdate = false;
|
|
|
|
|
|
|
|
|
|
|
|
ClientProvider() {
|
|
|
|
|
|
_initialize();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 初始化客户端
|
|
|
|
|
|
Future<void> _initialize() async {
|
|
|
|
|
|
_isLoading = true;
|
|
|
|
|
|
notifyListeners();
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 初始化数据库
|
|
|
|
|
|
await _database.initialize();
|
|
|
|
|
|
|
|
|
|
|
|
// 加载系统信息(先加载简单的文件读取,延迟执行 Process 调用)
|
|
|
|
|
|
final version = await _systemInfo.getVersion();
|
|
|
|
|
|
final auth = await _systemInfo.getAuth();
|
|
|
|
|
|
|
|
|
|
|
|
// 检查权限
|
|
|
|
|
|
final hasPermission = await _systemInfo.checkAdminPermission();
|
|
|
|
|
|
if (!hasPermission) {
|
|
|
|
|
|
_logger.warning('未检测到管理员权限');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载挖矿配置
|
|
|
|
|
|
final miningConfig = await _configService.parseMiningConfig();
|
|
|
|
|
|
|
|
|
|
|
|
// 从配置文件读取服务器地址
|
|
|
|
|
|
_serverUrl = miningConfig?.serverUrl ?? '18.183.240.108:2345';
|
|
|
|
|
|
|
|
|
|
|
|
// 检查版本更新(如果有配置 update_url)
|
|
|
|
|
|
if (miningConfig?.updateUrl != null) {
|
|
|
|
|
|
_checkForUpdates(miningConfig!.updateUrl!, version);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取机器码和GPU信息(延迟执行,避免启动时崩溃)
|
|
|
|
|
|
// 只在启动时获取一次,如果失败就显示空列表
|
|
|
|
|
|
String machineCode = '正在获取...';
|
|
|
|
|
|
List<GPUInfo> gpus = [];
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 延迟3秒后获取,避免启动时崩溃
|
|
|
|
|
|
await Future.delayed(const Duration(seconds: 3));
|
|
|
|
|
|
_logger.info('开始获取系统信息...');
|
|
|
|
|
|
|
|
|
|
|
|
machineCode = await _systemInfo.getMachineCode();
|
|
|
|
|
|
gpus = await _systemInfo.getGPUInfo();
|
|
|
|
|
|
|
|
|
|
|
|
_logger.info('系统信息获取完成: MAC=$machineCode, GPUs=${gpus.length}');
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
_logger.warning('获取系统信息失败: $e');
|
|
|
|
|
|
// 获取失败时使用默认值
|
|
|
|
|
|
machineCode = '获取失败';
|
|
|
|
|
|
gpus = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 准备GPU信息(转换为服务器需要的格式)
|
|
|
|
|
|
final gpusMap = <String, dynamic>{};
|
|
|
|
|
|
for (var gpu in gpus) {
|
|
|
|
|
|
gpusMap[gpu.index.toString()] = gpu.toJson();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取支持的挖矿软件列表
|
|
|
|
|
|
final miningSofts = _getMiningSofts(miningConfig);
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化客户端核心(先连接,但不发送身份认证)
|
|
|
|
|
|
final initialized = await _clientCore.initialize(
|
|
|
|
|
|
serverUrl: _serverUrl ?? '18.183.240.108:2345',
|
|
|
|
|
|
auth: auth,
|
|
|
|
|
|
machineCode: machineCode, // 此时可能还是"正在获取...",需要等待真实值
|
|
|
|
|
|
gpusInfo: gpusMap,
|
|
|
|
|
|
miningSofts: miningSofts,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (!initialized) {
|
|
|
|
|
|
_logger.severe('客户端初始化失败');
|
|
|
|
|
|
// 即使初始化失败,也创建 ClientInfo 以便显示基本信息
|
|
|
|
|
|
_clientInfo = ClientInfo(
|
|
|
|
|
|
version: version,
|
|
|
|
|
|
auth: auth,
|
|
|
|
|
|
gpus: gpus,
|
|
|
|
|
|
machineCode: machineCode,
|
|
|
|
|
|
status: ClientStatus.offline,
|
|
|
|
|
|
miningInfo: null,
|
|
|
|
|
|
);
|
|
|
|
|
|
_isInitialized = true;
|
|
|
|
|
|
_isLoading = false;
|
|
|
|
|
|
notifyListeners();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置回调
|
|
|
|
|
|
_clientCore.onStatusChanged = _onStatusChanged;
|
|
|
|
|
|
_clientCore.onMiningTaskChanged = _onMiningTaskChanged;
|
|
|
|
|
|
|
|
|
|
|
|
// 等待机器码获取完成后,发送身份认证消息(使用 身份信息::硬盘身份码 格式)
|
|
|
|
|
|
if (machineCode != '正在获取...' && machineCode != '获取失败') {
|
|
|
|
|
|
// 更新客户端核心的机器码
|
|
|
|
|
|
_clientCore.updateMachineCode(machineCode, gpusMap, miningSofts);
|
|
|
|
|
|
// 发送身份认证消息
|
|
|
|
|
|
_clientCore.sendMachineCode();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复未完成的挖矿任务
|
|
|
|
|
|
final unfinishedTask = await _database.loadUnfinishedTask();
|
|
|
|
|
|
if (unfinishedTask != null) {
|
|
|
|
|
|
if (miningConfig != null) {
|
|
|
|
|
|
await _miningManager.startMining(unfinishedTask, miningConfig);
|
|
|
|
|
|
_currentMiningTask = unfinishedTask;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载持续挖矿配置并启动(恢复任务之后)
|
|
|
|
|
|
if (miningConfig != null) {
|
|
|
|
|
|
final loaded = await _sustainMiner.loadConfig(miningConfig);
|
|
|
|
|
|
if (loaded && _currentMiningTask == null) {
|
|
|
|
|
|
await _sustainMiner.start();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新客户端信息
|
|
|
|
|
|
_clientInfo = ClientInfo(
|
|
|
|
|
|
version: version,
|
|
|
|
|
|
auth: auth,
|
|
|
|
|
|
gpus: gpus.map((gpu) => GPUInfo(
|
|
|
|
|
|
index: gpu.index,
|
|
|
|
|
|
brand: gpu.brand,
|
|
|
|
|
|
model: gpu.model,
|
|
|
|
|
|
memory: gpu.memory,
|
|
|
|
|
|
)).toList(),
|
|
|
|
|
|
machineCode: machineCode,
|
2026-01-23 16:11:20 +08:00
|
|
|
|
status: _getInitialStatus(),
|
|
|
|
|
|
miningInfo: _getMiningInfo(),
|
2026-01-22 15:14:27 +08:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
_isInitialized = true;
|
|
|
|
|
|
_startAutoRefresh();
|
|
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
|
|
_logger.severe('初始化失败: $e', e, stackTrace);
|
|
|
|
|
|
// 即使初始化失败,也创建基本的 ClientInfo 以便显示错误信息
|
|
|
|
|
|
try {
|
|
|
|
|
|
final version = await _systemInfo.getVersion();
|
|
|
|
|
|
final auth = await _systemInfo.getAuth();
|
|
|
|
|
|
_clientInfo = ClientInfo(
|
|
|
|
|
|
version: version,
|
|
|
|
|
|
auth: auth,
|
|
|
|
|
|
gpus: [],
|
|
|
|
|
|
machineCode: '初始化失败',
|
|
|
|
|
|
status: ClientStatus.offline,
|
|
|
|
|
|
miningInfo: null,
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch (e2) {
|
|
|
|
|
|
_logger.severe('创建基本 ClientInfo 也失败: $e2');
|
|
|
|
|
|
// 如果连基本信息都获取不到,至少创建一个空的信息对象
|
|
|
|
|
|
_clientInfo = ClientInfo(
|
|
|
|
|
|
version: '未知',
|
|
|
|
|
|
auth: '未知',
|
|
|
|
|
|
gpus: [],
|
|
|
|
|
|
machineCode: '错误',
|
|
|
|
|
|
status: ClientStatus.offline,
|
|
|
|
|
|
miningInfo: null,
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
_isLoading = false;
|
|
|
|
|
|
notifyListeners();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 状态变化回调
|
|
|
|
|
|
void _onStatusChanged(ClientStatus status) {
|
|
|
|
|
|
if (_clientInfo != null) {
|
2026-01-23 16:11:20 +08:00
|
|
|
|
// 判断当前状态:优先显示挖矿状态
|
|
|
|
|
|
ClientStatus newStatus;
|
|
|
|
|
|
if (_miningManager.isMining) {
|
|
|
|
|
|
// 如果有租约任务,显示挖矿中;否则显示持续挖矿中
|
|
|
|
|
|
newStatus = _currentMiningTask != null
|
|
|
|
|
|
? ClientStatus.mining
|
|
|
|
|
|
: (_sustainMiner.isRunning ? ClientStatus.sustainingMining : ClientStatus.mining);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
newStatus = status;
|
|
|
|
|
|
}
|
2026-01-22 15:14:27 +08:00
|
|
|
|
|
|
|
|
|
|
_clientInfo = ClientInfo(
|
|
|
|
|
|
version: _clientInfo!.version,
|
|
|
|
|
|
auth: _clientInfo!.auth,
|
|
|
|
|
|
gpus: _clientInfo!.gpus,
|
|
|
|
|
|
machineCode: _clientInfo!.machineCode,
|
|
|
|
|
|
status: newStatus,
|
2026-01-23 16:11:20 +08:00
|
|
|
|
miningInfo: _getMiningInfo(),
|
2026-01-22 15:14:27 +08:00
|
|
|
|
);
|
|
|
|
|
|
notifyListeners();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 挖矿任务变化回调
|
|
|
|
|
|
void _onMiningTaskChanged(MiningTaskInfo? task) async {
|
|
|
|
|
|
_currentMiningTask = task;
|
|
|
|
|
|
|
|
|
|
|
|
if (task != null) {
|
|
|
|
|
|
// 暂停持续挖矿
|
|
|
|
|
|
await _sustainMiner.pause();
|
|
|
|
|
|
|
|
|
|
|
|
// 启动挖矿
|
|
|
|
|
|
final miningConfig = await _configService.parseMiningConfig();
|
|
|
|
|
|
if (miningConfig != null) {
|
|
|
|
|
|
await _miningManager.startMining(task, miningConfig);
|
|
|
|
|
|
await _database.insertMiningTask(task);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 停止挖矿
|
|
|
|
|
|
await _miningManager.stopMining();
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复持续挖矿
|
|
|
|
|
|
await _sustainMiner.resume();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-23 16:11:20 +08:00
|
|
|
|
// 更新状态
|
2026-01-22 15:14:27 +08:00
|
|
|
|
_onStatusChanged(_clientCore.isConnected ? ClientStatus.online : ClientStatus.offline);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 开始自动刷新
|
|
|
|
|
|
void _startAutoRefresh() {
|
|
|
|
|
|
_refreshTimer?.cancel();
|
|
|
|
|
|
_refreshTimer = Timer.periodic(const Duration(seconds: 5), (_) {
|
|
|
|
|
|
refresh();
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 手动刷新(不重新获取GPU信息,只更新状态)
|
|
|
|
|
|
Future<void> refresh() async {
|
|
|
|
|
|
if (!_isInitialized || _clientInfo == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 只更新状态,不重新获取GPU信息(GPU信息只在启动时获取一次)
|
2026-01-23 16:11:20 +08:00
|
|
|
|
ClientStatus status;
|
|
|
|
|
|
if (_miningManager.isMining) {
|
|
|
|
|
|
// 如果有租约任务,显示挖矿中;否则显示持续挖矿中
|
|
|
|
|
|
status = _currentMiningTask != null
|
|
|
|
|
|
? ClientStatus.mining
|
|
|
|
|
|
: (_sustainMiner.isRunning ? ClientStatus.sustainingMining : ClientStatus.mining);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
status = _clientCore.isConnected ? ClientStatus.online : ClientStatus.offline;
|
|
|
|
|
|
}
|
2026-01-22 15:14:27 +08:00
|
|
|
|
|
|
|
|
|
|
_clientInfo = ClientInfo(
|
|
|
|
|
|
version: _clientInfo!.version,
|
|
|
|
|
|
auth: _clientInfo!.auth,
|
|
|
|
|
|
gpus: _clientInfo!.gpus, // 使用已有的GPU信息,不重新获取
|
|
|
|
|
|
machineCode: _clientInfo!.machineCode,
|
|
|
|
|
|
status: status,
|
2026-01-23 16:11:20 +08:00
|
|
|
|
miningInfo: _getMiningInfo(),
|
2026-01-22 15:14:27 +08:00
|
|
|
|
);
|
|
|
|
|
|
notifyListeners();
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
_logger.warning('刷新失败: $e');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-23 16:11:20 +08:00
|
|
|
|
/// 获取初始状态
|
|
|
|
|
|
ClientStatus _getInitialStatus() {
|
|
|
|
|
|
if (_miningManager.isMining) {
|
|
|
|
|
|
return _currentMiningTask != null
|
|
|
|
|
|
? ClientStatus.mining
|
|
|
|
|
|
: (_sustainMiner.isRunning ? ClientStatus.sustainingMining : ClientStatus.mining);
|
|
|
|
|
|
}
|
|
|
|
|
|
return _clientCore.isConnected ? ClientStatus.online : ClientStatus.offline;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 获取挖矿信息(支持租约挖矿和持续挖矿)
|
|
|
|
|
|
MiningInfo? _getMiningInfo() {
|
|
|
|
|
|
if (_currentMiningTask != null) {
|
|
|
|
|
|
// 租约挖矿任务
|
|
|
|
|
|
return MiningInfo(
|
|
|
|
|
|
coin: _currentMiningTask!.coin,
|
|
|
|
|
|
algo: _currentMiningTask!.algo,
|
|
|
|
|
|
pool: _currentMiningTask!.pool,
|
|
|
|
|
|
poolUrl: _currentMiningTask!.poolUrl,
|
|
|
|
|
|
walletAddress: _currentMiningTask!.walletAddress,
|
|
|
|
|
|
workerId: _currentMiningTask!.workerId,
|
|
|
|
|
|
pid: null,
|
|
|
|
|
|
miner: _currentMiningTask!.miner,
|
|
|
|
|
|
endTimestamp: _currentMiningTask!.endTimestamp,
|
|
|
|
|
|
);
|
|
|
|
|
|
} else if (_miningManager.isMining && _sustainMiner.isRunning) {
|
|
|
|
|
|
// 持续挖矿任务
|
|
|
|
|
|
final sustainTask = _miningManager.currentTask;
|
|
|
|
|
|
if (sustainTask != null) {
|
|
|
|
|
|
return MiningInfo(
|
|
|
|
|
|
coin: sustainTask.coin,
|
|
|
|
|
|
algo: sustainTask.algo,
|
|
|
|
|
|
pool: sustainTask.pool,
|
|
|
|
|
|
poolUrl: sustainTask.poolUrl,
|
|
|
|
|
|
walletAddress: sustainTask.walletAddress,
|
|
|
|
|
|
workerId: sustainTask.workerId,
|
|
|
|
|
|
pid: null,
|
|
|
|
|
|
miner: sustainTask.miner,
|
|
|
|
|
|
endTimestamp: sustainTask.endTimestamp,
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 15:14:27 +08:00
|
|
|
|
/// 重启客户端
|
|
|
|
|
|
Future<void> restart() async {
|
|
|
|
|
|
_clientCore.stop();
|
|
|
|
|
|
await _miningManager.stopMining();
|
|
|
|
|
|
await _sustainMiner.stop();
|
|
|
|
|
|
|
|
|
|
|
|
_isInitialized = false;
|
|
|
|
|
|
await _initialize();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 获取支持的挖矿软件列表(辅助方法)
|
|
|
|
|
|
List<String> _getMiningSofts(MiningConfig? miningConfig) {
|
|
|
|
|
|
final miningSofts = <String>[];
|
|
|
|
|
|
if (miningConfig?.lolMinerPath != null && miningConfig!.lolMinerPath!.isNotEmpty) {
|
|
|
|
|
|
miningSofts.add('lolminer');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (miningConfig?.rigelPath != null && miningConfig!.rigelPath!.isNotEmpty) {
|
|
|
|
|
|
miningSofts.add('rigel');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (miningConfig?.bzMinerPath != null && miningConfig!.bzMinerPath!.isNotEmpty) {
|
|
|
|
|
|
miningSofts.add('bzminer');
|
|
|
|
|
|
}
|
|
|
|
|
|
return miningSofts;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 获取日志流
|
|
|
|
|
|
Stream<String> get logStream => _clientCore.logStream;
|
|
|
|
|
|
|
|
|
|
|
|
ClientInfo? get clientInfo => _clientInfo;
|
|
|
|
|
|
bool get isLoading => _isLoading;
|
|
|
|
|
|
bool get isInitialized => _isInitialized;
|
|
|
|
|
|
bool get hasUpdate => _hasUpdate;
|
|
|
|
|
|
String? get remoteVersion => _remoteVersion;
|
|
|
|
|
|
|
|
|
|
|
|
/// 检查版本更新
|
|
|
|
|
|
Future<void> _checkForUpdates(String updateUrl, String localVersion) async {
|
|
|
|
|
|
try {
|
|
|
|
|
|
final remoteInfo = await _updateService.checkVersion(updateUrl);
|
|
|
|
|
|
if (remoteInfo != null) {
|
|
|
|
|
|
_remoteVersion = remoteInfo.version;
|
|
|
|
|
|
_hasUpdate = _updateService.isNewerVersion(remoteInfo.version, localVersion);
|
|
|
|
|
|
if (_hasUpdate) {
|
|
|
|
|
|
_logger.info('发现新版本: $localVersion -> ${remoteInfo.version}');
|
|
|
|
|
|
}
|
|
|
|
|
|
notifyListeners();
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
_logger.warning('检查更新失败: $e');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 执行更新
|
|
|
|
|
|
Future<bool> performUpdate() async {
|
|
|
|
|
|
if (!_hasUpdate || _remoteVersion == null) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
final miningConfig = await _configService.parseMiningConfig();
|
|
|
|
|
|
if (miningConfig?.updateUrl == null) {
|
|
|
|
|
|
_logger.warning('更新URL未配置');
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取下载URL
|
|
|
|
|
|
final remoteInfo = await _updateService.checkVersion(miningConfig!.updateUrl!);
|
|
|
|
|
|
if (remoteInfo == null || remoteInfo.downloadUrl.isEmpty) {
|
|
|
|
|
|
_logger.warning('无法获取下载URL');
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 下载并更新
|
|
|
|
|
|
final success = await _updateService.downloadAndUpdate(remoteInfo.downloadUrl);
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
_logger.info('更新下载完成,将在下次启动时应用');
|
|
|
|
|
|
}
|
|
|
|
|
|
return success;
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
_logger.severe('执行更新失败: $e');
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
void dispose() {
|
|
|
|
|
|
_refreshTimer?.cancel();
|
|
|
|
|
|
_clientCore.stop();
|
|
|
|
|
|
_database.close();
|
|
|
|
|
|
super.dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|