diff --git a/backend/internal/model/models.go b/backend/internal/model/models.go index f846d8e..69fb165 100644 --- a/backend/internal/model/models.go +++ b/backend/internal/model/models.go @@ -232,12 +232,14 @@ type Version struct { Description string `gorm:"type:text" json:"description"` Changelog string `gorm:"type:text" json:"changelog"` Status string `gorm:"size:20;default:active" json:"status"` + BaseVersionID *uint `json:"base_version_id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` Application Application `gorm:"foreignKey:ApplicationID" json:"application,omitempty"` Files []VersionFile `gorm:"foreignKey:VersionID" json:"files,omitempty"` + BaseVersion *Version `gorm:"foreignKey:BaseVersionID" json:"base_version,omitempty"` } // VersionFile 版本文件模型 diff --git a/backend/internal/router/admin/versions.go b/backend/internal/router/admin/versions.go index c1bcf0c..21d0334 100644 --- a/backend/internal/router/admin/versions.go +++ b/backend/internal/router/admin/versions.go @@ -43,6 +43,7 @@ func handleGetAllVersions(c *gin.Context) { applicationIDFilter := c.Query("application_id") updateStrategyFilter := c.Query("update_strategy") updateMethodFilter := c.Query("update_method") + statusFilter := c.Query("status") searchFilter := c.Query("search") var total int64 @@ -85,6 +86,9 @@ func handleGetAllVersions(c *gin.Context) { if updateMethodFilter != "" { countQuery = countQuery.Where("update_method = ?", updateMethodFilter) } + if statusFilter != "" { + countQuery = countQuery.Where("status = ?", statusFilter) + } if searchFilter != "" { searchLower := strings.ToLower(searchFilter) countQuery = countQuery.Where("LOWER(version) LIKE ? OR LOWER(description) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%") @@ -101,6 +105,9 @@ func handleGetAllVersions(c *gin.Context) { if updateMethodFilter != "" { query = query.Where("update_method = ?", updateMethodFilter) } + if statusFilter != "" { + query = query.Where("status = ?", statusFilter) + } if searchFilter != "" { searchLower := strings.ToLower(searchFilter) query = query.Where("LOWER(version) LIKE ? OR LOWER(description) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%") @@ -186,6 +193,7 @@ func handleGetVersionByID(c *gin.Context) { "min_version": version.MinVersion, "changelog": version.Changelog, "status": version.Status, + "base_version_id": version.BaseVersionID, "files": version.Files, "created_at": version.CreatedAt, "updated_at": version.UpdatedAt, @@ -205,6 +213,7 @@ type CreateVersionRequest struct { MinVersion string `json:"min_version"` Description string `json:"description"` Changelog string `json:"changelog"` + BaseVersionID *uint `json:"base_version_id"` } func handleCreateVersionGlobal(c *gin.Context) { @@ -247,6 +256,7 @@ func handleCreateVersionGlobal(c *gin.Context) { Description: req.Description, Changelog: req.Changelog, Status: "active", + BaseVersionID: req.BaseVersionID, } if version.UpdateStrategy == "" { @@ -261,6 +271,10 @@ func handleCreateVersionGlobal(c *gin.Context) { return } + database.DB.Model(&model.Version{}). + Where("application_id = ? AND id != ? AND status = ?", req.ApplicationID, version.ID, "active"). + Update("status", "superseded") + userIDPtr := &userID versionIDPtr := &version.ID service.CreateLog(service.LogParams{ diff --git a/frontend/src/pages/admin/versions/[id].vue b/frontend/src/pages/admin/versions/[id].vue index 8ba87e9..0d33585 100644 --- a/frontend/src/pages/admin/versions/[id].vue +++ b/frontend/src/pages/admin/versions/[id].vue @@ -1,6 +1,6 @@ diff --git a/frontend/src/plugins/i18n/en.json b/frontend/src/plugins/i18n/en.json index 179b740..ffa6680 100644 --- a/frontend/src/plugins/i18n/en.json +++ b/frontend/src/plugins/i18n/en.json @@ -1418,6 +1418,7 @@ "fileSize": "File Size", "strategy": "Strategy", "method": "Method", + "status": "Status", "createdAt": "Created At", "actions": "Actions" }, @@ -1427,7 +1428,12 @@ }, "methods": { "manual": "Manual", - "auto": "Auto" + "hot": "Hot", + "full": "Full" + }, + "status": { + "active": "Active", + "superseded": "Superseded" }, "createForm": { "title": "Create Version", @@ -1461,6 +1467,12 @@ "entryFilePlaceholder": "Select entry file (optional)", "forcedUpdate": "Force Update", "forcedUpdateDesc": "When enabled, users must update to this version to continue using", + "updateMethod": "Update Method", + "baseVersion": "Base Version", + "baseVersionPlaceholder": "Select base version (patch mode)", + "baseVersionDesc": "Select a base version to only upload changed files for incremental patch update", + "noBaseVersion": "None (Full version)", + "status": "Status", "autoUpdate": "Auto Update", "autoUpdateDesc": "When enabled, the application will automatically download and install updates", "changelog": "Changelog", @@ -1474,6 +1486,7 @@ "previewFileCount": "File Count", "previewEntryFile": "Entry File", "previewForcedUpdate": "Force Update", + "previewUpdateMethod": "Update Method", "previewAutoUpdate": "Auto Update", "changelogPreview": "Changelog Preview", "noChangelog": "No changelog", diff --git a/frontend/src/plugins/i18n/zh.json b/frontend/src/plugins/i18n/zh.json index c7de8cb..626f497 100644 --- a/frontend/src/plugins/i18n/zh.json +++ b/frontend/src/plugins/i18n/zh.json @@ -1385,6 +1385,7 @@ "fileSize": "文件大小", "strategy": "更新策略", "method": "更新方式", + "status": "状态", "createdAt": "创建时间", "actions": "操作" }, @@ -1394,7 +1395,12 @@ }, "methods": { "manual": "手动更新", - "auto": "自动更新" + "hot": "热更新", + "full": "完整更新" + }, + "status": { + "active": "有效", + "superseded": "已失效" }, "createForm": { "title": "创建版本", @@ -1428,6 +1434,12 @@ "entryFilePlaceholder": "选择入口文件(可选)", "forcedUpdate": "强制更新", "forcedUpdateDesc": "启用后用户必须更新到此版本才能继续使用", + "updateMethod": "更新方式", + "baseVersion": "基础版本", + "baseVersionPlaceholder": "选择基础版本(补丁模式)", + "baseVersionDesc": "选择基础版本后,只会上传变更的文件,用于增量补丁更新", + "noBaseVersion": "无(完整版本)", + "status": "状态", "autoUpdate": "自动更新", "autoUpdateDesc": "启用后应用将自动下载并安装更新", "changelog": "更新日志", @@ -1441,6 +1453,7 @@ "previewFileCount": "文件数量", "previewEntryFile": "入口文件", "previewForcedUpdate": "强制更新", + "previewUpdateMethod": "更新方式", "previewAutoUpdate": "自动更新", "changelogPreview": "更新日志预览", "noChangelog": "暂无更新日志",