细节调整及优化(26.8.3)

This commit is contained in:
zhongluofeng
2026-08-21 17:25:47 +08:00
parent 0a19b4b38a
commit 4dd60f42a1
72 changed files with 4779 additions and 1395 deletions
+49 -27
View File
@@ -129,11 +129,18 @@ async fn fetch_latest_release() -> Result<LatestRelease, String> {
/// 下载文件到 dest,期间通过 UPDATE_PROGRESS 事件上报进度
async fn download_with_progress(app: &AppHandle, url: &str, dest: &Path) -> Result<(), String> {
let client = reqwest::Client::new();
// 注意:reqwest 的 timeout 是"从连接到响应体读完"的总超时。大文件(如
// thing.exe 10+MB)在慢速网络下 30s 内读不完会被掐断流导致下载到一半失败
// (此前 bug:更新包总在 ~50% 处中断)。这里只限制连接建立 15s,
// 总超时放宽到 10 分钟兜底防止永久悬挂。
let client = reqwest::Client::builder()
.connect_timeout(std::time::Duration::from_secs(15))
.timeout(std::time::Duration::from_secs(600))
.build()
.map_err(|e| format!("创建 HTTP 客户端失败: {}", e))?;
let resp = client
.get(url)
.header("User-Agent", "thing-app")
.timeout(std::time::Duration::from_secs(30))
.send()
.await
.map_err(|e| format!("下载请求失败: {}", e))?;
@@ -324,34 +331,31 @@ pub async fn update_check(app: AppHandle) -> Result<UpdateCheckResult, String> {
})
}
/// 更新应用本体
/// 便携版:下载 thing_{v}_x64.exe → update.bat 覆盖重启;
/// 安装版:下载 thing_{v}_x64-setup.exe → 提权静默安装 /S。
/// 下载进度通过 UPDATE_PROGRESS 事件上报,调用方返回前会触发应用退出
/// 更新应用本体(安装阶段)。下载由前端下载模块完成,本命令接收已下载的
/// 安装包路径(便携版 thing_{v}_x64.exe / 安装版 thing_{v}_x64-setup.exe)。
/// 便携版:copy 到临时目录 → update.bat 覆盖重启;
/// 安装版:copy 到临时目录 → 提权静默安装 /S
/// 调用返回前会触发应用退出。
#[tauri::command]
#[specta::specta]
pub async fn update_install(app: AppHandle) -> Result<(), String> {
let latest = fetch_latest_release().await?;
pub async fn update_install(app: AppHandle, downloaded_path: String) -> Result<(), String> {
let src = PathBuf::from(&downloaded_path);
if !src.exists() {
return Err(format!("下载文件不存在: {}", downloaded_path));
}
let installed = is_installed_version();
let (target_name, target_url) = if installed {
latest
.assets
.iter()
.find(|a| a.name.ends_with("-setup.exe"))
.map(|a| (a.name.clone(), a.browser_download_url.clone()))
.ok_or("未在 release 中找到安装包 (setup.exe)".to_string())?
} else {
latest
.assets
.iter()
.find(|a| a.name.ends_with(".exe") && !a.name.contains("setup"))
.map(|a| (a.name.clone(), a.browser_download_url.clone()))
.ok_or("未在 release 中找到便携版程序 (thing.exe)".to_string())?
};
// copy 到临时目录:与 update.bat / 安装器解耦,随后即可删除下载目录中的源文件
let temp_dir = std::env::temp_dir().join("thing-update");
fs::create_dir_all(&temp_dir).map_err(|e| format!("创建临时目录失败: {}", e))?;
let dest = temp_dir.join(&target_name);
download_with_progress(&app, &target_url, &dest).await?;
let file_name = src
.file_name()
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_else(|| "thing_update.exe".into());
let dest = temp_dir.join(&file_name);
fs::copy(&src, &dest).map_err(|e| format!("复制安装包到临时目录失败: {}", e))?;
// 临时副本就绪后清理下载目录中的源文件(失败不影响更新流程)
let _ = fs::remove_file(&src);
let _ = app.emit(
UPDATE_PROGRESS,
UpdateProgress {
@@ -377,6 +381,24 @@ pub async fn update_install(app: AppHandle) -> Result<(), String> {
#[tauri::command]
#[specta::specta]
pub async fn update_thinghk(app: AppHandle) -> Result<(), String> {
let result = update_thinghk_inner(&app).await;
if let Err(ref e) = result {
// 失败时 emit error 阶段,避免前端进度卡在最后状态无提示
let _ = app.emit(
UPDATE_PROGRESS,
UpdateProgress {
stage: "error".into(),
percent: 0,
downloaded_bytes: 0,
total_bytes: None,
message: e.clone(),
},
);
}
result
}
async fn update_thinghk_inner(app: &AppHandle) -> Result<(), String> {
let latest = fetch_latest_release().await?;
let asset = latest
.assets
@@ -385,7 +407,7 @@ pub async fn update_thinghk(app: AppHandle) -> Result<(), String> {
.ok_or("未在 release 中找到 ThingHK 内核包".to_string())?;
// 停止监控内核(含提权模式的 /shutdown 兜底由前端先停模块),避免 exe 被占用
if let Some(monitor) = app.try_state::<crate::monitor_kernel::MonitorKernel>() {
monitor.stop_subscription(&app).await;
monitor.stop_subscription(app).await;
}
if let Some(pm) = app.try_state::<crate::process_manager::ProcessManager>() {
let _ = pm.stop("monitor");
@@ -395,7 +417,7 @@ pub async fn update_thinghk(app: AppHandle) -> Result<(), String> {
let temp_dir = std::env::temp_dir().join("thing-update");
fs::create_dir_all(&temp_dir).map_err(|e| format!("创建临时目录失败: {}", e))?;
let zip_path = temp_dir.join(&asset.name);
download_with_progress(&app, &asset.browser_download_url, &zip_path).await?;
download_with_progress(app, &asset.browser_download_url, &zip_path).await?;
let _ = app.emit(
UPDATE_PROGRESS,
UpdateProgress {