package main import ( "fmt" "log/slog" "time" "u-desk/internal/api" ) // ========== 版本更新管理接口 ========== // requireUpdateAPI 检查 updateAPI 是否已初始化 func (a *App) requireUpdateAPI() (*api.UpdateAPI, error) { a.mu.Lock() defer a.mu.Unlock() if a.updateAPI == nil { return nil, fmt.Errorf("更新功能正在初始化中") } return a.updateAPI, nil } // CheckUpdate 检查更新 func (a *App) CheckUpdate() (map[string]interface{}, error) { api, err := a.requireUpdateAPI() if err != nil { return nil, err } return api.CheckUpdate() } // GetCurrentVersion 获取当前版本号 func (a *App) GetCurrentVersion() (map[string]interface{}, error) { api, err := a.requireUpdateAPI() if err != nil { return nil, err } return api.GetCurrentVersion() } // GetUpdateConfig 获取更新配置 func (a *App) GetUpdateConfig() (map[string]interface{}, error) { api, err := a.requireUpdateAPI() if err != nil { return nil, err } return api.GetUpdateConfig() } // SetUpdateConfig 设置更新配置 func (a *App) SetUpdateConfig(autoCheckEnabled bool, checkIntervalMinutes int, checkURL string) (map[string]interface{}, error) { api, err := a.requireUpdateAPI() if err != nil { return nil, err } return api.SetUpdateConfig(autoCheckEnabled, checkIntervalMinutes, checkURL) } // DownloadUpdate 下载更新包 func (a *App) DownloadUpdate(downloadURL string) (map[string]interface{}, error) { api, err := a.requireUpdateAPI() if err != nil { return nil, err } return api.DownloadUpdate(downloadURL) } // InstallUpdate 安装更新包 func (a *App) InstallUpdate(installerPath string, autoRestart bool) (map[string]interface{}, error) { api, err := a.requireUpdateAPI() if err != nil { return nil, err } return api.InstallUpdate(installerPath, autoRestart) } // InstallUpdateWithHash 安装更新包(带哈希验证) func (a *App) InstallUpdateWithHash(installerPath string, autoRestart bool, expectedHash string, hashType string) (map[string]interface{}, error) { api, err := a.requireUpdateAPI() if err != nil { return nil, err } return api.InstallUpdateWithHash(installerPath, autoRestart, expectedHash, hashType) } // VerifyUpdateFile 验证更新文件哈希值 func (a *App) VerifyUpdateFile(filePath string, expectedHash string, hashType string) (map[string]interface{}, error) { api, err := a.requireUpdateAPI() if err != nil { return nil, err } return api.VerifyUpdateFile(filePath, expectedHash, hashType) } // startAutoUpdateCheck 启动自动更新检查(可重入:先停止旧定时器再按当前配置新建) func (a *App) startAutoUpdateCheck() { updateAPI, err := a.requireUpdateAPI() if err != nil { return } config, err := updateAPI.GetUpdateConfig() if err != nil { slog.Warn("获取更新配置失败", "error", err) return } success, ok := config["success"].(bool) if !ok || !success { return } configData, ok := config["data"].(map[string]interface{}) if !ok { return } autoCheckEnabled, ok := configData["auto_check_enabled"].(bool) if !ok || !autoCheckEnabled { // 关闭自动检查时停止存量定时器 a.stopUpdateTicker() return } interval, ok := configData["check_interval_minutes"].(int) if !ok || interval <= 0 { interval = 5 } a.mu.Lock() if a.updateCheckStop != nil { close(a.updateCheckStop) } if a.updateTicker != nil { a.updateTicker.Stop() } stop := make(chan struct{}) a.updateCheckStop = stop a.updateTicker = time.NewTicker(time.Duration(interval) * time.Minute) ticker := a.updateTicker a.mu.Unlock() go a.checkUpdate() go func() { for { select { case <-ticker.C: a.checkUpdate() case <-stop: return } } }() } // stopUpdateTicker 停止自动更新检查定时器及其 goroutine func (a *App) stopUpdateTicker() { a.mu.Lock() defer a.mu.Unlock() if a.updateCheckStop != nil { close(a.updateCheckStop) a.updateCheckStop = nil } if a.updateTicker != nil { a.updateTicker.Stop() a.updateTicker = nil } } // resetAutoUpdateCheck 重置自动更新检查(更新配置变更后调用,免重启生效) func (a *App) resetAutoUpdateCheck() { a.stopUpdateTicker() a.startAutoUpdateCheck() } // checkUpdate 执行更新检查 func (a *App) checkUpdate() { defer func() { if r := recover(); r != nil { slog.Error("自动检查更新发生错误", "error", r) } }() a.mu.Lock() api := a.updateAPI a.mu.Unlock() if api == nil { return } result, err := api.CheckUpdate() if err != nil { return } success, ok := result["success"].(bool) if !ok || !success { return } data, ok := result["data"].(map[string]interface{}) if !ok { return } hasUpdate, ok := data["has_update"].(bool) if ok && hasUpdate && a.mainWindow != nil { a.mainWindow.EmitEvent("update-available", data) } }