feat: 构建号按版本号(App_Ver)独立记录,避免回退旧版本号被 App Store 拒

将 versions.build_ver 单值改为 build_map(每个 App_Ver 独立记录已用到的最大
构建号)。App Store 的构建号唯一性按 MARKETING_VERSION 分别计算,改为单一全局
计数器时回退到打过包的旧版本号会重复(如 2.195.0 已到 .5,切 2.196.0 后再回退,
重置/沿用全局计数都会 < .5 而被拒)。

- config.py: 新增 _ensure_versions 迁移旧 build_ver → build_map;PUT /versions
  不再重置构建号,改版本号仅切换当前 App_Ver,并支持可选手动设置 build_ver;
  GET /versions 返回 build_map 及当前版本号对应的 build_ver
- build_service.py: App_Store 打包时在 build_map[app_ver] 自身上 +1 并回写
- ConfigView.vue: 打包设置页显示可编辑的 Build_Ver,修改 App_Ver 时自动切换为
  该版本号已记录的构建号
- 迁移 config.json;更新前后端相关测试

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-07-29 16:49:14 +09:00
co-authored by Claude Opus 4.8
parent b6e56f5f9a
commit 91e9b027e8
7 changed files with 168 additions and 34 deletions
+32 -4
View File
@@ -272,9 +272,17 @@
<h4 class="section-title">应用版本号</h4>
<div class="form-group">
<label>App_Ver应用版本</label>
<input type="text" v-model="versions.app_ver" placeholder="2.180.0">
<input type="text" v-model="versions.app_ver" placeholder="2.180.0" @input="onAppVerInput">
<div style="font-size: 12px; color: #999; margin-top: 4px;">格式主版本.次版本.修订号</div>
</div>
<div class="form-group">
<label>Build_Ver构建号</label>
<input type="text" v-model="versions.build_ver" placeholder="2.180.0.0">
<div style="font-size: 12px; color: #999; margin-top: 4px;">
四段数字前三位须与 App_Ver 一致修改 App_Ver 会自动切换为该版本号已记录的构建号
每个版本号的构建号独立递增App_Store 打包时自动 +1一般无需手动修改
</div>
</div>
<button class="btn btn-primary" style="width: auto; padding: 10px 32px; margin-bottom: 24px;" @click="saveVersions">保存版本号</button>
<template v-if="isAdmin">
@@ -606,6 +614,21 @@ const branches = ref([])
const newBranch = ref('')
const buildSettings = ref({})
const versions = ref({ app_ver: '', build_ver: '' })
const buildMap = ref({})
const applyVersions = (data) => {
buildMap.value = data.build_map || {}
versions.value = { app_ver: data.app_ver || '', build_ver: data.build_ver || '' }
}
// 修改 App_Ver 时,自动把 Build_Ver 切换为该版本号已记录的构建号(新版本号则为 .0)
const onAppVerInput = () => {
const appVer = (versions.value.app_ver || '').trim()
if (/^\d+\.\d+\.\d+$/.test(appVer)) {
const n = Number(buildMap.value[appVer] || 0)
versions.value.build_ver = `${appVer}.${n}`
}
}
const uploadConfig = ref({ mode: 'oss', oss: {}, webdav: {}, dingtalk: {} })
const jsonContent = ref('{}')
@@ -641,7 +664,7 @@ const loadData = async () => {
authFetch('/api/config/versions'),
])
if (appsRes.ok) apps.value = await appsRes.json()
if (versionsRes.ok) versions.value = await versionsRes.json()
if (versionsRes.ok) applyVersions(await versionsRes.json())
return
}
@@ -663,7 +686,7 @@ const loadData = async () => {
buildSettings.value = await buildRes.json()
uploadConfig.value = await uploadRes.json()
jsonContent.value = JSON.stringify(await configRes.json(), null, 2)
versions.value = await versionsRes.json()
applyVersions(await versionsRes.json())
if (usersRes.ok) users.value = await usersRes.json()
}
@@ -1114,10 +1137,15 @@ const saveVersions = async () => {
const res = await authFetch('/api/config/versions', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ app_ver: versions.value.app_ver }),
body: JSON.stringify({
app_ver: versions.value.app_ver,
build_ver: versions.value.build_ver,
}),
})
if (res.ok) {
const data = await res.json()
// 回写该版本号最新的构建号,保持本地 build_map 与服务端一致
buildMap.value = { ...buildMap.value, [data.app_ver]: Number((data.build_ver.split('.')[3]) || 0) }
versions.value = { app_ver: data.app_ver, build_ver: data.build_ver }
alert('版本号已保存')
} else {