重构版本状态为动态计算

- 状态不再写入数据库,改为查询时动态计算
- 每个应用的最新版 = 有效, 旧版 = 已失效
- 移除后端 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")
updateStrategyFilter := c.Query("update_strategy")
updateMethodFilter := c.Query("update_method")
statusFilter := c.Query("status")
searchFilter := c.Query("search")
var total int64
@@ -86,9 +85,6 @@ 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+"%")
@@ -105,9 +101,6 @@ 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+"%")
@@ -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)
}
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 {
model.Version
ApplicationName string `json:"application_name"`
@@ -143,6 +147,11 @@ func handleGetAllVersions(c *gin.Context) {
for i, v := range versions {
appName := appNameMap[v.ApplicationID]
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{
Version: v,
ApplicationName: appName,
@@ -178,6 +187,16 @@ func handleGetVersionByID(c *gin.Context) {
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{
"id": version.ID,
"application_id": version.ApplicationID,
@@ -192,7 +211,7 @@ func handleGetVersionByID(c *gin.Context) {
"update_method": version.UpdateMethod,
"min_version": version.MinVersion,
"changelog": version.Changelog,
"status": version.Status,
"status": computedStatus,
"base_version_id": version.BaseVersionID,
"files": version.Files,
"created_at": version.CreatedAt,
@@ -271,10 +290,6 @@ 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{
@@ -45,7 +45,6 @@ const formData = ref({
description: '',
update_strategy: 'optional' as 'optional' | 'forced',
update_method: 'manual' as 'manual' | 'hot' | 'full',
status: 'active' as string,
min_version: '',
changelog: '',
entry_file: '',
@@ -92,7 +91,6 @@ async function fetchVersion() {
formData.value.description = ver.description || ''
formData.value.update_strategy = ver.update_strategy || 'optional'
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.min_version = ver.min_version || ''
formData.value.changelog = ver.changelog || ''
@@ -181,7 +179,6 @@ async function handleSubmit() {
description: formData.value.description,
update_strategy: formData.value.update_strategy,
update_method: formData.value.update_method,
status: formData.value.status,
min_version: formData.value.min_version,
changelog: formData.value.changelog,
entry_file: formData.value.entry_file,
@@ -455,23 +452,6 @@ onMounted(() => {
</UiSelect>
</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">
<UiLabel for="changelog">
{{ t('admin.versions.createForm.changelog') }}
+1 -15
View File
@@ -31,7 +31,6 @@ const tableRef = ref()
const appFilter = ref<string>('')
const strategyFilter = ref<string>('')
const methodFilter = ref<string>('')
const statusFilter = ref<string>('')
const searchFilter = ref<string>('')
const currentPage = ref(1)
const pageSize = ref(20)
@@ -63,11 +62,6 @@ const methodOptions = computed(() => [
{ 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(() => {
return versions.value.reduce((sum, v) => sum + (v.file_size || 0), 0)
})
@@ -122,9 +116,6 @@ async function fetchVersions() {
if (methodFilter.value) {
params.append('update_method', methodFilter.value)
}
if (statusFilter.value) {
params.append('status', statusFilter.value)
}
if (searchFilter.value) {
params.append('search', searchFilter.value)
}
@@ -239,7 +230,7 @@ watch([currentPage, pageSize], () => {
fetchVersions()
})
watch([appFilter, strategyFilter, methodFilter, statusFilter, searchFilter], () => {
watch([appFilter, strategyFilter, methodFilter, searchFilter], () => {
currentPage.value = 1
fetchVersions()
})
@@ -350,11 +341,6 @@ watch([appFilter, strategyFilter, methodFilter, statusFilter, searchFilter], ()
:title="t('admin.versions.method')"
:options="methodOptions"
/>
<SingleFilter
v-model="statusFilter"
:title="t('admin.versions.columns.status')"
:options="statusOptions"
/>
</template>
</DataTable>
</UiCardContent>