refactor 6
This commit is contained in:
@@ -5,7 +5,6 @@ use crate::domain::models::{
|
|||||||
UpdateCandidate,
|
UpdateCandidate,
|
||||||
};
|
};
|
||||||
use crate::services::{essentials_service, settings_service, software_state_service};
|
use crate::services::{essentials_service, settings_service, software_state_service};
|
||||||
use crate::winget::Software;
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_settings(app: AppHandle) -> AppSettings {
|
pub fn get_settings(app: AppHandle) -> AppSettings {
|
||||||
@@ -32,16 +31,6 @@ pub async fn initialize_app(app: AppHandle) -> Result<bool, String> {
|
|||||||
software_state_service::initialize_app(app).await
|
software_state_service::initialize_app(app).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_installed_software(app: AppHandle) -> Vec<Software> {
|
|
||||||
software_state_service::get_installed_software(app).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_updates(app: AppHandle) -> Vec<Software> {
|
|
||||||
software_state_service::get_updates(app).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn get_dashboard_snapshot(app: AppHandle) -> DashboardSnapshot {
|
pub async fn get_dashboard_snapshot(app: AppHandle) -> DashboardSnapshot {
|
||||||
software_state_service::get_dashboard_snapshot(app).await
|
software_state_service::get_dashboard_snapshot(app).await
|
||||||
@@ -57,11 +46,6 @@ pub async fn get_update_candidates(app: AppHandle) -> Vec<UpdateCandidate> {
|
|||||||
software_state_service::get_update_candidates(app).await
|
software_state_service::get_update_candidates(app).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_software_info(app: AppHandle, id: String) -> Option<Software> {
|
|
||||||
software_state_service::get_software_info(app, id).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn get_software_icon(app: AppHandle, id: String, name: String) -> Option<String> {
|
pub async fn get_software_icon(app: AppHandle, id: String, name: String) -> Option<String> {
|
||||||
software_state_service::get_software_icon(app, id, name).await
|
software_state_service::get_software_icon(app, id, name).await
|
||||||
|
|||||||
@@ -51,13 +51,6 @@ pub struct SyncEssentialsResult {
|
|||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize)]
|
|
||||||
pub struct InstallProgress {
|
|
||||||
pub id: String,
|
|
||||||
pub status: String,
|
|
||||||
pub progress: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ResolvedPostInstall {
|
pub struct ResolvedPostInstall {
|
||||||
pub software: Software,
|
pub software: Software,
|
||||||
|
|||||||
@@ -22,13 +22,10 @@ pub fn run() {
|
|||||||
commands::app_commands::save_settings,
|
commands::app_commands::save_settings,
|
||||||
commands::app_commands::sync_essentials,
|
commands::app_commands::sync_essentials,
|
||||||
commands::app_commands::get_essentials,
|
commands::app_commands::get_essentials,
|
||||||
commands::app_commands::get_installed_software,
|
|
||||||
commands::app_commands::get_updates,
|
|
||||||
commands::app_commands::get_dashboard_snapshot,
|
commands::app_commands::get_dashboard_snapshot,
|
||||||
commands::app_commands::get_essentials_status,
|
commands::app_commands::get_essentials_status,
|
||||||
commands::app_commands::get_update_candidates,
|
commands::app_commands::get_update_candidates,
|
||||||
commands::app_commands::get_software_icon,
|
commands::app_commands::get_software_icon,
|
||||||
commands::app_commands::get_software_info,
|
|
||||||
tasks::install_queue::install_software,
|
tasks::install_queue::install_software,
|
||||||
commands::app_commands::get_logs_history
|
commands::app_commands::get_logs_history
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -4,9 +4,23 @@ use tauri::AppHandle;
|
|||||||
use crate::domain::models::{EssentialsRepo, SyncEssentialsResult};
|
use crate::domain::models::{EssentialsRepo, SyncEssentialsResult};
|
||||||
use crate::services::log_service::emit_log;
|
use crate::services::log_service::emit_log;
|
||||||
use crate::services::settings_service;
|
use crate::services::settings_service;
|
||||||
|
use crate::services::task_event_service::emit_task_event;
|
||||||
use crate::storage::{essentials_store, paths};
|
use crate::storage::{essentials_store, paths};
|
||||||
|
|
||||||
pub async fn sync_essentials(app: &AppHandle) -> Result<SyncEssentialsResult, String> {
|
pub async fn sync_essentials(app: &AppHandle) -> Result<SyncEssentialsResult, String> {
|
||||||
|
emit_task_event(
|
||||||
|
app,
|
||||||
|
"sync-essentials",
|
||||||
|
"sync-essentials",
|
||||||
|
"sync_essentials",
|
||||||
|
"running",
|
||||||
|
"starting",
|
||||||
|
0.0,
|
||||||
|
None,
|
||||||
|
Some("Starting essentials sync".to_string()),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
let settings = settings_service::get_settings(app);
|
let settings = settings_service::get_settings(app);
|
||||||
let url = format!("{}/setup-essentials.json", settings.repo_url.trim_end_matches('/'));
|
let url = format!("{}/setup-essentials.json", settings.repo_url.trim_end_matches('/'));
|
||||||
let cache_path = paths::get_essentials_path(app);
|
let cache_path = paths::get_essentials_path(app);
|
||||||
@@ -42,6 +56,20 @@ pub async fn sync_essentials(app: &AppHandle) -> Result<SyncEssentialsResult, St
|
|||||||
status: "updated".to_string(),
|
status: "updated".to_string(),
|
||||||
message: "清单同步成功".to_string(),
|
message: "清单同步成功".to_string(),
|
||||||
})
|
})
|
||||||
|
.inspect(|_| {
|
||||||
|
emit_task_event(
|
||||||
|
app,
|
||||||
|
"sync-essentials",
|
||||||
|
"sync-essentials",
|
||||||
|
"sync_essentials",
|
||||||
|
"completed",
|
||||||
|
"updated",
|
||||||
|
1.0,
|
||||||
|
None,
|
||||||
|
Some("Essentials list updated successfully".to_string()),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
emit_log(
|
emit_log(
|
||||||
app,
|
app,
|
||||||
@@ -50,11 +78,35 @@ pub async fn sync_essentials(app: &AppHandle) -> Result<SyncEssentialsResult, St
|
|||||||
"Invalid JSON format from repository. Expected { version, essentials }.",
|
"Invalid JSON format from repository. Expected { version, essentials }.",
|
||||||
"error",
|
"error",
|
||||||
);
|
);
|
||||||
|
emit_task_event(
|
||||||
|
app,
|
||||||
|
"sync-essentials",
|
||||||
|
"sync-essentials",
|
||||||
|
"sync_essentials",
|
||||||
|
"failed",
|
||||||
|
"invalid_json",
|
||||||
|
1.0,
|
||||||
|
None,
|
||||||
|
Some("Invalid JSON format".to_string()),
|
||||||
|
None,
|
||||||
|
);
|
||||||
Err("Invalid JSON format".to_string())
|
Err("Invalid JSON format".to_string())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let err_msg = format!("HTTP Error: {}", response.status());
|
let err_msg = format!("HTTP Error: {}", response.status());
|
||||||
emit_log(app, "sync-essentials", "Error", &err_msg, "error");
|
emit_log(app, "sync-essentials", "Error", &err_msg, "error");
|
||||||
|
emit_task_event(
|
||||||
|
app,
|
||||||
|
"sync-essentials",
|
||||||
|
"sync-essentials",
|
||||||
|
"sync_essentials",
|
||||||
|
"failed",
|
||||||
|
"http_error",
|
||||||
|
1.0,
|
||||||
|
None,
|
||||||
|
Some(err_msg.clone()),
|
||||||
|
None,
|
||||||
|
);
|
||||||
Err(err_msg)
|
Err(err_msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,9 +123,35 @@ pub async fn sync_essentials(app: &AppHandle) -> Result<SyncEssentialsResult, St
|
|||||||
status: "cache_used".to_string(),
|
status: "cache_used".to_string(),
|
||||||
message: "网络不可用,已继续使用本地缓存".to_string(),
|
message: "网络不可用,已继续使用本地缓存".to_string(),
|
||||||
})
|
})
|
||||||
|
.inspect(|_| {
|
||||||
|
emit_task_event(
|
||||||
|
app,
|
||||||
|
"sync-essentials",
|
||||||
|
"sync-essentials",
|
||||||
|
"sync_essentials",
|
||||||
|
"completed",
|
||||||
|
"cache_used",
|
||||||
|
1.0,
|
||||||
|
None,
|
||||||
|
Some("Network unavailable, used local cache".to_string()),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
let err_msg = format!("Network issue: {}", e);
|
let err_msg = format!("Network issue: {}", e);
|
||||||
emit_log(app, "sync-essentials", "Error", &err_msg, "error");
|
emit_log(app, "sync-essentials", "Error", &err_msg, "error");
|
||||||
|
emit_task_event(
|
||||||
|
app,
|
||||||
|
"sync-essentials",
|
||||||
|
"sync-essentials",
|
||||||
|
"sync_essentials",
|
||||||
|
"failed",
|
||||||
|
"network_error",
|
||||||
|
1.0,
|
||||||
|
None,
|
||||||
|
Some(err_msg.clone()),
|
||||||
|
None,
|
||||||
|
);
|
||||||
Err(err_msg)
|
Err(err_msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ pub mod log_service;
|
|||||||
pub mod reconcile_service;
|
pub mod reconcile_service;
|
||||||
pub mod settings_service;
|
pub mod settings_service;
|
||||||
pub mod software_state_service;
|
pub mod software_state_service;
|
||||||
|
pub mod task_event_service;
|
||||||
|
|||||||
@@ -3,31 +3,54 @@ use tauri::AppHandle;
|
|||||||
use crate::domain::models::{DashboardSnapshot, EssentialsStatusItem, UpdateCandidate};
|
use crate::domain::models::{DashboardSnapshot, EssentialsStatusItem, UpdateCandidate};
|
||||||
use crate::providers::winget_client;
|
use crate::providers::winget_client;
|
||||||
use crate::services::{essentials_service, reconcile_service};
|
use crate::services::{essentials_service, reconcile_service};
|
||||||
use crate::winget::Software;
|
use crate::services::task_event_service::emit_task_event;
|
||||||
|
|
||||||
pub async fn initialize_app(app: AppHandle) -> Result<bool, String> {
|
pub async fn initialize_app(app: AppHandle) -> Result<bool, String> {
|
||||||
|
emit_task_event(
|
||||||
|
&app,
|
||||||
|
"env-check",
|
||||||
|
"env-check",
|
||||||
|
"initialize_app",
|
||||||
|
"running",
|
||||||
|
"checking_environment",
|
||||||
|
0.0,
|
||||||
|
None,
|
||||||
|
Some("Checking WinGet environment".to_string()),
|
||||||
|
None,
|
||||||
|
);
|
||||||
let app_clone = app.clone();
|
let app_clone = app.clone();
|
||||||
tokio::task::spawn_blocking(move || winget_client::ensure_environment_ready(&app_clone).map(|_| true))
|
let result = tokio::task::spawn_blocking(move || winget_client::ensure_environment_ready(&app_clone).map(|_| true))
|
||||||
.await
|
.await
|
||||||
.unwrap_or(Err("Initialization Task Panicked".to_string()))
|
.unwrap_or(Err("Initialization Task Panicked".to_string()));
|
||||||
|
|
||||||
|
match &result {
|
||||||
|
Ok(_) => emit_task_event(
|
||||||
|
&app,
|
||||||
|
"env-check",
|
||||||
|
"env-check",
|
||||||
|
"initialize_app",
|
||||||
|
"completed",
|
||||||
|
"ready",
|
||||||
|
1.0,
|
||||||
|
None,
|
||||||
|
Some("WinGet environment ready".to_string()),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
Err(err) => emit_task_event(
|
||||||
|
&app,
|
||||||
|
"env-check",
|
||||||
|
"env-check",
|
||||||
|
"initialize_app",
|
||||||
|
"failed",
|
||||||
|
"error",
|
||||||
|
1.0,
|
||||||
|
None,
|
||||||
|
Some(err.clone()),
|
||||||
|
None,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_installed_software(app: AppHandle) -> Vec<Software> {
|
result
|
||||||
tokio::task::spawn_blocking(move || winget_client::list_installed_packages(&app))
|
|
||||||
.await
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_updates(app: AppHandle) -> Vec<Software> {
|
|
||||||
tokio::task::spawn_blocking(move || winget_client::list_upgrade_candidates(&app))
|
|
||||||
.await
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_software_info(app: AppHandle, id: String) -> Option<Software> {
|
|
||||||
tokio::task::spawn_blocking(move || winget_client::get_package_by_id(&app, &id))
|
|
||||||
.await
|
|
||||||
.unwrap_or(None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_software_icon(app: AppHandle, id: String, name: String) -> Option<String> {
|
pub async fn get_software_icon(app: AppHandle, id: String, name: String) -> Option<String> {
|
||||||
|
|||||||
32
src-tauri/src/services/task_event_service.rs
Normal file
32
src-tauri/src/services/task_event_service.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
use tauri::{AppHandle, Emitter};
|
||||||
|
|
||||||
|
use crate::domain::models::TaskEventPayload;
|
||||||
|
use crate::winget::Software;
|
||||||
|
|
||||||
|
pub fn emit_task_event(
|
||||||
|
handle: &AppHandle,
|
||||||
|
task_id: &str,
|
||||||
|
software_id: &str,
|
||||||
|
task_type: &str,
|
||||||
|
status: &str,
|
||||||
|
stage: &str,
|
||||||
|
progress: f32,
|
||||||
|
target_version: Option<String>,
|
||||||
|
message: Option<String>,
|
||||||
|
software_info: Option<Software>,
|
||||||
|
) {
|
||||||
|
let _ = handle.emit(
|
||||||
|
"task-event",
|
||||||
|
TaskEventPayload {
|
||||||
|
task_id: task_id.to_string(),
|
||||||
|
software_id: software_id.to_string(),
|
||||||
|
task_type: task_type.to_string(),
|
||||||
|
status: status.to_string(),
|
||||||
|
stage: stage.to_string(),
|
||||||
|
progress,
|
||||||
|
target_version,
|
||||||
|
message,
|
||||||
|
software_info,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -11,10 +11,11 @@ use tokio::sync::mpsc;
|
|||||||
use winreg::enums::*;
|
use winreg::enums::*;
|
||||||
use winreg::RegKey;
|
use winreg::RegKey;
|
||||||
|
|
||||||
use crate::domain::models::{InstallProgress, InstallTask, TaskEventPayload};
|
use crate::domain::models::{InstallTask, TaskEventPayload};
|
||||||
use crate::providers::winget_client;
|
use crate::providers::winget_client;
|
||||||
use crate::services::essentials_service;
|
use crate::services::essentials_service;
|
||||||
use crate::services::log_service::emit_log;
|
use crate::services::log_service::emit_log;
|
||||||
|
use crate::services::task_event_service;
|
||||||
use crate::winget::{PostInstallStep, Software};
|
use crate::winget::{PostInstallStep, Software};
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
@@ -411,14 +412,6 @@ fn spawn_install_stream_reader<R: Read + Send + 'static>(
|
|||||||
let mut is_progress = false;
|
let mut is_progress = false;
|
||||||
if let Some(caps) = perc_re.captures(clean_line) {
|
if let Some(caps) = perc_re.captures(clean_line) {
|
||||||
if let Ok(p_val) = caps[1].parse::<f32>() {
|
if let Ok(p_val) = caps[1].parse::<f32>() {
|
||||||
let _ = handle.emit(
|
|
||||||
"install-status",
|
|
||||||
InstallProgress {
|
|
||||||
id: task_id.clone(),
|
|
||||||
status: "installing".to_string(),
|
|
||||||
progress: p_val / 100.0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let _ = handle.emit(
|
let _ = handle.emit(
|
||||||
"task-event",
|
"task-event",
|
||||||
TaskEventPayload {
|
TaskEventPayload {
|
||||||
@@ -439,14 +432,6 @@ fn spawn_install_stream_reader<R: Read + Send + 'static>(
|
|||||||
let current = caps[1].parse::<f32>().unwrap_or(0.0);
|
let current = caps[1].parse::<f32>().unwrap_or(0.0);
|
||||||
let total = caps[2].parse::<f32>().unwrap_or(1.0);
|
let total = caps[2].parse::<f32>().unwrap_or(1.0);
|
||||||
if total > 0.0 {
|
if total > 0.0 {
|
||||||
let _ = handle.emit(
|
|
||||||
"install-status",
|
|
||||||
InstallProgress {
|
|
||||||
id: task_id.clone(),
|
|
||||||
status: "installing".to_string(),
|
|
||||||
progress: (current / total).min(1.0),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let _ = handle.emit(
|
let _ = handle.emit(
|
||||||
"task-event",
|
"task-event",
|
||||||
TaskEventPayload {
|
TaskEventPayload {
|
||||||
@@ -488,35 +473,17 @@ fn emit_task_event(
|
|||||||
message: Option<String>,
|
message: Option<String>,
|
||||||
software_info: Option<Software>,
|
software_info: Option<Software>,
|
||||||
) {
|
) {
|
||||||
let _ = handle.emit(
|
task_event_service::emit_task_event(
|
||||||
"task-event",
|
handle,
|
||||||
TaskEventPayload {
|
task_id,
|
||||||
task_id: task_id.to_string(),
|
software_id,
|
||||||
software_id: software_id.to_string(),
|
task_type,
|
||||||
task_type: task_type.to_string(),
|
status,
|
||||||
status: status.to_string(),
|
stage,
|
||||||
stage: stage.to_string(),
|
|
||||||
progress,
|
progress,
|
||||||
target_version: target_version.clone(),
|
target_version.clone(),
|
||||||
message,
|
message,
|
||||||
software_info,
|
software_info,
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let legacy_status = match status {
|
|
||||||
"queued" => "pending".to_string(),
|
|
||||||
"completed" => "success".to_string(),
|
|
||||||
"failed" => "error".to_string(),
|
|
||||||
_ => stage.to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let _ = handle.emit(
|
|
||||||
"install-status",
|
|
||||||
InstallProgress {
|
|
||||||
id: software_id.to_string(),
|
|
||||||
status: legacy_status,
|
|
||||||
progress,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export const useTaskRuntimeStore = defineStore('task-runtime', {
|
|||||||
this.taskRecords[payload.task_id] = taskRecord
|
this.taskRecords[payload.task_id] = taskRecord
|
||||||
|
|
||||||
if (payload.status === 'completed' || payload.status === 'failed') {
|
if (payload.status === 'completed' || payload.status === 'failed') {
|
||||||
if (payload.status === 'completed') {
|
if (payload.task_type === 'install' && payload.status === 'completed') {
|
||||||
const latestInfo = payload.software_info
|
const latestInfo = payload.software_info
|
||||||
if (latestInfo) {
|
if (latestInfo) {
|
||||||
const installedIndex = catalog.allSoftware.findIndex(s => s.id.toLowerCase() === payload.software_id.toLowerCase())
|
const installedIndex = catalog.allSoftware.findIndex(s => s.id.toLowerCase() === payload.software_id.toLowerCase())
|
||||||
@@ -154,8 +154,15 @@ export const useTaskRuntimeStore = defineStore('task-runtime', {
|
|||||||
delete this.taskRecords[payload.task_id]
|
delete this.taskRecords[payload.task_id]
|
||||||
}
|
}
|
||||||
}, 3000)
|
}, 3000)
|
||||||
|
} else if (payload.task_type !== 'install') {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.taskRecords[payload.task_id]?.status === payload.status) {
|
||||||
|
delete this.taskRecords[payload.task_id]
|
||||||
|
}
|
||||||
|
}, 1500)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (payload.task_type === 'install') {
|
||||||
const index = this.batchQueue.indexOf(payload.software_id)
|
const index = this.batchQueue.indexOf(payload.software_id)
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.batchQueue.splice(index, 1)
|
this.batchQueue.splice(index, 1)
|
||||||
@@ -164,11 +171,7 @@ export const useTaskRuntimeStore = defineStore('task-runtime', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
listen('install-status', () => {
|
|
||||||
// Compatibility event is still emitted by the backend, but task-runtime
|
|
||||||
// now derives runtime state from the richer task-event stream.
|
|
||||||
})
|
})
|
||||||
|
|
||||||
listen('log-event', (event: { payload: LogEntry }) => {
|
listen('log-event', (event: { payload: LogEntry }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user