重构版本状态为动态计算

- 状态不再写入数据库,改为查询时动态计算
- 每个应用的最新版 = 有效, 旧版 = 已失效
- 移除后端 status 筛选参数
- 移除前端 status 筛选器
- 移除编辑页的状态手动编辑
- 移除创建版本时自动更新旧版本状态的逻辑
This commit is contained in:
2026-05-08 16:40:02 +08:00
parent e2ebb6e46e
commit 01ca9e3df1
3 changed files with 28 additions and 47 deletions
+27 -12
View File
@@ -43,7 +43,6 @@ func handleGetAllVersions(c *gin.Context) {
applicationIDFilter := c.Query("application_id") applicationIDFilter := c.Query("application_id")
updateStrategyFilter := c.Query("update_strategy") updateStrategyFilter := c.Query("update_strategy")
updateMethodFilter := c.Query("update_method") updateMethodFilter := c.Query("update_method")
statusFilter := c.Query("status")
searchFilter := c.Query("search") searchFilter := c.Query("search")
var total int64 var total int64
@@ -86,9 +85,6 @@ func handleGetAllVersions(c *gin.Context) {
if updateMethodFilter != "" { if updateMethodFilter != "" {
countQuery = countQuery.Where("update_method = ?", updateMethodFilter) countQuery = countQuery.Where("update_method = ?", updateMethodFilter)
} }
if statusFilter != "" {
countQuery = countQuery.Where("status = ?", statusFilter)
}
if searchFilter != "" { if searchFilter != "" {
searchLower := strings.ToLower(searchFilter) searchLower := strings.ToLower(searchFilter)
countQuery = countQuery.Where("LOWER(version) LIKE ? OR LOWER(description) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%") countQuery = countQuery.Where("LOWER(version) LIKE ? OR LOWER(description) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%")
@@ -105,9 +101,6 @@ func handleGetAllVersions(c *gin.Context) {
if updateMethodFilter != "" { if updateMethodFilter != "" {
query = query.Where("update_method = ?", updateMethodFilter) query = query.Where("update_method = ?", updateMethodFilter)
} }
if statusFilter != "" {
query = query.Where("status = ?", statusFilter)
}
if searchFilter != "" { if searchFilter != "" {
searchLower := strings.ToLower(searchFilter) searchLower := strings.ToLower(searchFilter)
query = query.Where("LOWER(version) LIKE ? OR LOWER(description) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%") query = query.Where("LOWER(version) LIKE ? OR LOWER(description) LIKE ?", "%"+searchLower+"%", "%"+searchLower+"%")
@@ -134,6 +127,17 @@ func handleGetAllVersions(c *gin.Context) {
log.Printf("[DEBUG] Version %d: ID=%d, ApplicationID=%d, Version=%s\n", i, v.ID, v.ApplicationID, v.Version) log.Printf("[DEBUG] Version %d: ID=%d, ApplicationID=%d, Version=%s\n", i, v.ID, v.ApplicationID, v.Version)
} }
latestVersionIDs := make(map[uint]uint)
var latestIDs []uint
database.DB.Model(&model.Version{}).
Where("application_id IN ?", appIDs).
Select("MAX(id)").
Group("application_id").
Pluck("MAX(id)", &latestIDs)
for _, id := range latestIDs {
latestVersionIDs[id] = id
}
type VersionWithAppName struct { type VersionWithAppName struct {
model.Version model.Version
ApplicationName string `json:"application_name"` ApplicationName string `json:"application_name"`
@@ -143,6 +147,11 @@ func handleGetAllVersions(c *gin.Context) {
for i, v := range versions { for i, v := range versions {
appName := appNameMap[v.ApplicationID] appName := appNameMap[v.ApplicationID]
log.Printf("[DEBUG] Mapping version %d: ApplicationID=%d -> AppName=%s\n", i, v.ApplicationID, appName) log.Printf("[DEBUG] Mapping version %d: ApplicationID=%d -> AppName=%s\n", i, v.ApplicationID, appName)
if _, isLatest := latestVersionIDs[v.ID]; isLatest {
v.Status = "active"
} else {
v.Status = "superseded"
}
result[i] = VersionWithAppName{ result[i] = VersionWithAppName{
Version: v, Version: v,
ApplicationName: appName, ApplicationName: appName,
@@ -178,6 +187,16 @@ func handleGetVersionByID(c *gin.Context) {
return return
} }
var latestID uint
database.DB.Model(&model.Version{}).
Where("application_id = ?", version.ApplicationID).
Select("MAX(id)").
Scan(&latestID)
computedStatus := "superseded"
if version.ID == latestID {
computedStatus = "active"
}
response.Success(c, gin.H{ response.Success(c, gin.H{
"id": version.ID, "id": version.ID,
"application_id": version.ApplicationID, "application_id": version.ApplicationID,
@@ -192,7 +211,7 @@ func handleGetVersionByID(c *gin.Context) {
"update_method": version.UpdateMethod, "update_method": version.UpdateMethod,
"min_version": version.MinVersion, "min_version": version.MinVersion,
"changelog": version.Changelog, "changelog": version.Changelog,
"status": version.Status, "status": computedStatus,
"base_version_id": version.BaseVersionID, "base_version_id": version.BaseVersionID,
"files": version.Files, "files": version.Files,
"created_at": version.CreatedAt, "created_at": version.CreatedAt,
@@ -271,10 +290,6 @@ func handleCreateVersionGlobal(c *gin.Context) {
return return
} }
database.DB.Model(&model.Version{}).
Where("application_id = ? AND id != ? AND status = ?", req.ApplicationID, version.ID, "active").
Update("status", "superseded")
userIDPtr := &userID userIDPtr := &userID
versionIDPtr := &version.ID versionIDPtr := &version.ID
service.CreateLog(service.LogParams{ service.CreateLog(service.LogParams{
@@ -45,7 +45,6 @@ const formData = ref({
description: '', description: '',
update_strategy: 'optional' as 'optional' | 'forced', update_strategy: 'optional' as 'optional' | 'forced',
update_method: 'manual' as 'manual' | 'hot' | 'full', update_method: 'manual' as 'manual' | 'hot' | 'full',
status: 'active' as string,
min_version: '', min_version: '',
changelog: '', changelog: '',
entry_file: '', entry_file: '',
@@ -92,7 +91,6 @@ async function fetchVersion() {
formData.value.description = ver.description || '' formData.value.description = ver.description || ''
formData.value.update_strategy = ver.update_strategy || 'optional' formData.value.update_strategy = ver.update_strategy || 'optional'
formData.value.update_method = ver.update_method || 'manual' formData.value.update_method = ver.update_method || 'manual'
formData.value.status = ver.status || 'active'
formData.value.base_version_id = ver.base_version_id ? String(ver.base_version_id) : '' formData.value.base_version_id = ver.base_version_id ? String(ver.base_version_id) : ''
formData.value.min_version = ver.min_version || '' formData.value.min_version = ver.min_version || ''
formData.value.changelog = ver.changelog || '' formData.value.changelog = ver.changelog || ''
@@ -181,7 +179,6 @@ async function handleSubmit() {
description: formData.value.description, description: formData.value.description,
update_strategy: formData.value.update_strategy, update_strategy: formData.value.update_strategy,
update_method: formData.value.update_method, update_method: formData.value.update_method,
status: formData.value.status,
min_version: formData.value.min_version, min_version: formData.value.min_version,
changelog: formData.value.changelog, changelog: formData.value.changelog,
entry_file: formData.value.entry_file, entry_file: formData.value.entry_file,
@@ -455,23 +452,6 @@ onMounted(() => {
</UiSelect> </UiSelect>
</div> </div>
<div class="space-y-2">
<UiLabel>{{ t('admin.versions.createForm.status') }}</UiLabel>
<UiSelect v-model="formData.status" :disabled="saving">
<UiSelectTrigger>
<UiSelectValue />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectItem value="active">
{{ t('admin.versions.status.active') }}
</UiSelectItem>
<UiSelectItem value="superseded">
{{ t('admin.versions.status.superseded') }}
</UiSelectItem>
</UiSelectContent>
</UiSelect>
</div>
<div class="space-y-2"> <div class="space-y-2">
<UiLabel for="changelog"> <UiLabel for="changelog">
{{ t('admin.versions.createForm.changelog') }} {{ t('admin.versions.createForm.changelog') }}
+1 -15
View File
@@ -31,7 +31,6 @@ const tableRef = ref()
const appFilter = ref<string>('') const appFilter = ref<string>('')
const strategyFilter = ref<string>('') const strategyFilter = ref<string>('')
const methodFilter = ref<string>('') const methodFilter = ref<string>('')
const statusFilter = ref<string>('')
const searchFilter = ref<string>('') const searchFilter = ref<string>('')
const currentPage = ref(1) const currentPage = ref(1)
const pageSize = ref(20) const pageSize = ref(20)
@@ -63,11 +62,6 @@ const methodOptions = computed(() => [
{ label: t('admin.versions.methods.full'), value: 'full' }, { label: t('admin.versions.methods.full'), value: 'full' },
]) ])
const statusOptions = computed(() => [
{ label: t('admin.versions.status.active'), value: 'active' },
{ label: t('admin.versions.status.superseded'), value: 'superseded' },
])
const totalSize = computed(() => { const totalSize = computed(() => {
return versions.value.reduce((sum, v) => sum + (v.file_size || 0), 0) return versions.value.reduce((sum, v) => sum + (v.file_size || 0), 0)
}) })
@@ -122,9 +116,6 @@ async function fetchVersions() {
if (methodFilter.value) { if (methodFilter.value) {
params.append('update_method', methodFilter.value) params.append('update_method', methodFilter.value)
} }
if (statusFilter.value) {
params.append('status', statusFilter.value)
}
if (searchFilter.value) { if (searchFilter.value) {
params.append('search', searchFilter.value) params.append('search', searchFilter.value)
} }
@@ -239,7 +230,7 @@ watch([currentPage, pageSize], () => {
fetchVersions() fetchVersions()
}) })
watch([appFilter, strategyFilter, methodFilter, statusFilter, searchFilter], () => { watch([appFilter, strategyFilter, methodFilter, searchFilter], () => {
currentPage.value = 1 currentPage.value = 1
fetchVersions() fetchVersions()
}) })
@@ -350,11 +341,6 @@ watch([appFilter, strategyFilter, methodFilter, statusFilter, searchFilter], ()
:title="t('admin.versions.method')" :title="t('admin.versions.method')"
:options="methodOptions" :options="methodOptions"
/> />
<SingleFilter
v-model="statusFilter"
:title="t('admin.versions.columns.status')"
:options="statusOptions"
/>
</template> </template>
</DataTable> </DataTable>
</UiCardContent> </UiCardContent>