328 lines
10 KiB
Go
328 lines
10 KiB
Go
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,
|
|
"create_start_menu": latestVersion.CreateStartMenu,
|
|
"auto_run": latestVersion.AutoRun,
|
|
"default_install_dir": latestVersion.DefaultInstallDir,
|
|
"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,
|
|
"create_start_menu": latestVersion.CreateStartMenu,
|
|
"auto_run": latestVersion.AutoRun,
|
|
"default_install_dir": latestVersion.DefaultInstallDir,
|
|
"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)
|
|
}
|