package app import ( "strings" "verification-platform-backend/internal/database" "verification-platform-backend/internal/model" "verification-platform-backend/internal/service" "verification-platform-backend/pkg/response" "github.com/gin-gonic/gin" ) func SetupInfoRoutes(r *gin.RouterGroup) { r.GET("/info", handleAppGetInfo) r.GET("/check-update", handleAppCheckUpdate) r.GET("/announcements", handleAppGetAnnouncements) } func handleAppGetInfo(c *gin.Context) { appKey := c.Param("appKey") var app model.Application if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil { response.Error(c, 404, "应用不存在") return } if service.GetApplicationDisabledStatus(app.ID) { response.Error(c, 403, "该应用已被禁用") return } response.Success(c, gin.H{ "id": app.ID, "name": app.Name, "description": app.Description, "icon_url": app.IconURL, "status": app.Status, "billing_type": app.BillingType, "login_policy": app.LoginPolicy, "max_devices": app.MaxDevices, "multi_open_mode": app.MultiOpenMode, "max_instances": app.MaxInstances, "enable_trial": app.EnableTrial, "trial_balance": app.TrialBalance, "heartbeat_interval": app.HeartbeatInterval, "heartbeat_timeout": app.HeartbeatTimeout, }) } func handleAppCheckUpdate(c *gin.Context) { appKey := c.Param("appKey") clientVersion := c.Query("version") var app model.Application if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil { response.Error(c, 404, "应用不存在") return } if service.GetApplicationDisabledStatus(app.ID) { response.Error(c, 403, "该应用已被禁用") return } // 查询该应用的所有已发布版本,按ID降序(创建顺序) var allVersions []model.Version if err := database.DB.Where("application_id = ? AND publish_status = ?", app.ID, "published"). Order("id DESC"). Preload("Files"). Find(&allVersions).Error; err != nil { response.Error(c, 500, "获取版本列表失败") return } // 没有任何版本 if len(allVersions) == 0 { response.Success(c, gin.H{ "has_update": false, "current_version": clientVersion, "latest_version": "", "download_url": "", "update_notes": "", "update_strategy": "", "update_method": "", "files": []interface{}{}, }) return } // 清理版本号:去除空格和前缀 v/V cleanVersion := func(v string) string { v = strings.TrimSpace(v) v = strings.TrimPrefix(v, "v") v = strings.TrimPrefix(v, "V") return v } // 如果没有传入版本号,直接返回最新版本(安装器模式,优先全量包) if clientVersion == "" { latestVersion := &allVersions[0] // 确定下载地址:无版本号时 download_url 优先指向全量包 downloadURL := latestVersion.FullPackagePath downloadSize := latestVersion.FullPackageSize downloadHash := latestVersion.FullPackageHash if downloadURL == "" { // 没有全量包,使用文件路径 downloadURL = latestVersion.FilePath downloadSize = latestVersion.FileSize downloadHash = latestVersion.FileHash } if latestVersion.CustomDownloadURL != "" { downloadURL = latestVersion.CustomDownloadURL } result := gin.H{ "has_update": true, "latest_version": latestVersion.Version, "download_url": downloadURL, "file_size": downloadSize, "file_hash": downloadHash, "entry_file": latestVersion.EntryFile, "entry_files": latestVersion.EntryFiles, "desktop_shortcut_files": latestVersion.DesktopShortcutFiles, "update_notes": latestVersion.Description, "update_strategy": latestVersion.UpdateStrategy, "update_type": latestVersion.UpdateType, "update_method": latestVersion.UpdateMethod, "changelog": latestVersion.Changelog, } // 同时返回补丁包信息(如果有),供客户端增量更新使用 if latestVersion.FilePath != "" && latestVersion.FilePath != downloadURL { result["patch_url"] = latestVersion.FilePath result["patch_size"] = latestVersion.FileSize result["patch_hash"] = latestVersion.FileHash } // 如果有完整包,也单独返回完整包信息 if latestVersion.FullPackagePath != "" { result["full_package_url"] = latestVersion.FullPackagePath result["full_package_size"] = latestVersion.FullPackageSize result["full_package_hash"] = latestVersion.FullPackageHash } response.Success(c, result) return } clientVersionClean := cleanVersion(clientVersion) // 找到客户端当前版本对应的版本记录 var clientVersionRecord *model.Version for i := range allVersions { if cleanVersion(allVersions[i].Version) == clientVersionClean { clientVersionRecord = &allVersions[i] break } } // 客户端版本不存在于服务器版本列表中,返回参数错误 if clientVersionRecord == nil { response.Error(c, 400, "版本号参数错误") return } // 判断客户端版本状态:检查是否有更高ID的强制更新版本 isSuperseded := false for i := range allVersions { v := &allVersions[i] // 存在ID更高(创建时间更晚)的强制更新版本 if v.ID > clientVersionRecord.ID && v.UpdateStrategy == "forced" { isSuperseded = true break } } // 如果被取代(有强制更新),必须更新 // 找到最新的强制更新版本 var latestVersion *model.Version var updateStrategy string if isSuperseded { // 找到ID最高的强制更新版本 for i := range allVersions { v := &allVersions[i] if v.ID > clientVersionRecord.ID && v.UpdateStrategy == "forced" { if latestVersion == nil || v.ID > latestVersion.ID { latestVersion = v } } } updateStrategy = "forced" } else { // 没有强制更新,检查是否有更高ID的版本(普通更新) // 最新版本就是ID最高的版本(已经是按ID降序排列,第一个就是最新) latestVersion = &allVersions[0] // 只有当最新版本ID大于客户端版本ID时才需要更新 if latestVersion.ID > clientVersionRecord.ID { updateStrategy = latestVersion.UpdateStrategy // optional 或 forced } else { // 已经是最新版本 response.Success(c, gin.H{ "has_update": false, "current_version": clientVersion, "latest_version": clientVersionRecord.Version, "download_url": "", "update_notes": "", "update_strategy": "", "update_method": "", "files": []interface{}{}, }) return } } // 确定下载地址 var downloadURL string var downloadSize int64 var downloadHash string var fullPackageURL string var fullPackageSize int64 var fullPackageHash string var isPatchUpdate bool // 保存完整包地址(如果有) if latestVersion.FullPackagePath != "" { fullPackageURL = latestVersion.FullPackagePath fullPackageSize = latestVersion.FullPackageSize fullPackageHash = latestVersion.FullPackageHash } if latestVersion.UpdateType == "patch" && latestVersion.BaseVersionID != nil { // 最新版本是补丁类型 // download_url 返回补丁包地址,full_package_url 返回完整包地址 downloadURL = latestVersion.FilePath downloadSize = latestVersion.FileSize downloadHash = latestVersion.FileHash isPatchUpdate = true } else { // 全量版本,download_url 返回文件路径 downloadURL = latestVersion.FilePath downloadSize = latestVersion.FileSize downloadHash = latestVersion.FileHash } // 自定义地址优先级最高(覆盖下载地址) if latestVersion.CustomDownloadURL != "" { downloadURL = latestVersion.CustomDownloadURL } // 构建返回结果 result := map[string]interface{}{ "has_update": true, "current_version": clientVersion, "latest_version": latestVersion.Version, "download_url": downloadURL, "file_size": downloadSize, "file_hash": downloadHash, "entry_file": latestVersion.EntryFile, "entry_files": latestVersion.EntryFiles, "desktop_shortcut_files": latestVersion.DesktopShortcutFiles, "update_notes": latestVersion.Description, "update_strategy": updateStrategy, "update_type": latestVersion.UpdateType, "update_method": latestVersion.UpdateMethod, "changelog": latestVersion.Changelog, } // 如果是补丁更新,添加补丁信息 if isPatchUpdate { result["is_patch"] = true result["patch_url"] = downloadURL result["patch_size"] = downloadSize result["patch_hash"] = downloadHash } // 如果有完整包,添加完整包信息作为备选 if fullPackageURL != "" { result["full_package_url"] = fullPackageURL result["full_package_size"] = fullPackageSize result["full_package_hash"] = fullPackageHash } response.Success(c, result) } func handleAppGetAnnouncements(c *gin.Context) { appKey := c.Param("appKey") var app model.Application if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil { response.Error(c, 404, "应用不存在") return } if service.GetApplicationDisabledStatus(app.ID) { response.Error(c, 403, "该应用已被禁用") return } var announcements []model.Announcement if err := database.DB.Where("application_id = ? AND status = ?", app.ID, "active").Order("is_top DESC, created_at DESC").Find(&announcements).Error; err != nil { response.Error(c, 500, "获取公告失败") return } result := make([]gin.H, len(announcements)) for i, a := range announcements { result[i] = gin.H{ "id": a.ID, "title": a.Title, "content": a.Content, "type": a.Type, "is_top": a.IsTop, "created_at": a.CreatedAt, } } response.Success(c, result) }