docs(private): 完善私有化部署开发计划和设计规范
- 增加实时进度和交接规则,定义任务状态枚举和更新格式 - 创建任务进度台账,涵盖P0-P5阶段全部开发任务 - 补充部署仓库交付边界确认和进度审计规范 - 完善MySQL/Redis双模式支持,增加external/managed选项 - 增加离线部署、安全治理、可观测性等完整交付能力 - 更新仓库结构设计,增加secrets.env、observability、data目录 - 补充健康检查、诊断脚本、升级回滚、备份恢复详细要求 - 优化应用商店审核状态查询逻辑,增加手动刷新接口 - 修复小米和VIVO商店状态查询中的版本匹配逻辑错误 - 增加缓存键版本隔离,防止不同版本状态混淆 - 优化厂商API连通性检查和审核状态轮询机制
这个提交包含在:
父节点
b77ccc663a
当前提交
a1e4d5741b
@ -368,6 +368,13 @@ export const updateAdminApi = {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
refreshStoreReviewStatus(versionId: string) {
|
||||||
|
return updateClient.post<{ data: PreflightSubmitResultDto }>(
|
||||||
|
`/api/v1/updates/store/app/${versionId}/refresh-review-status`,
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
deleteAppVersion(versionId: string) {
|
deleteAppVersion(versionId: string) {
|
||||||
return updateClient.delete<{ data: null }>(`/api/v1/updates/app/${versionId}`)
|
return updateClient.delete<{ data: null }>(`/api/v1/updates/app/${versionId}`)
|
||||||
},
|
},
|
||||||
|
|||||||
@ -564,10 +564,10 @@
|
|||||||
<span v-else class="text-muted">—</span>
|
<span v-else class="text-muted">—</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="状态" width="120">
|
<el-table-column label="状态" width="160">
|
||||||
<template #default="{row}">
|
<template #default="{row}">
|
||||||
<el-tag :type="preflightTagType(row.reviewState)" size="small">
|
<el-tag :type="preflightTagType(row.reviewState)" size="small">
|
||||||
{{ preflightStateLabel(row.reviewState) }}
|
{{ row.nonCurrentRelease ? '已上线(非本次发布)' : preflightStateLabel(row.reviewState) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@ -697,6 +697,12 @@
|
|||||||
<el-empty v-else description="暂无商店状态数据" />
|
<el-empty v-else description="暂无商店状态数据" />
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
|
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
:loading="refreshingReviewStatus"
|
||||||
|
@click="handleRefreshReviewStatus()"
|
||||||
|
>刷新厂商状态</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="storeReviewDetailItems.some(i => isActiveState(i.state))"
|
v-if="storeReviewDetailItems.some(i => isActiveState(i.state))"
|
||||||
type="danger"
|
type="danger"
|
||||||
@ -1574,6 +1580,7 @@ type StoreReviewItem = {
|
|||||||
const storeReviewDetailItems = ref<StoreReviewItem[]>([])
|
const storeReviewDetailItems = ref<StoreReviewItem[]>([])
|
||||||
const storeReviewDetailLive = ref(false)
|
const storeReviewDetailLive = ref(false)
|
||||||
const cancellingReview = ref(false)
|
const cancellingReview = ref(false)
|
||||||
|
const refreshingReviewStatus = ref(false)
|
||||||
const retryingStores = ref<Set<string>>(new Set())
|
const retryingStores = ref<Set<string>>(new Set())
|
||||||
const showPublishSchedule = ref(false)
|
const showPublishSchedule = ref(false)
|
||||||
const publishScheduleType = ref<'IMMEDIATE' | 'SCHEDULED'>('IMMEDIATE')
|
const publishScheduleType = ref<'IMMEDIATE' | 'SCHEDULED'>('IMMEDIATE')
|
||||||
@ -1629,6 +1636,38 @@ async function handleCancelReview(storeType?: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleRefreshReviewStatus() {
|
||||||
|
if (!storeReviewDetailVersion.value) return
|
||||||
|
refreshingReviewStatus.value = true
|
||||||
|
try {
|
||||||
|
const res = await updateAdminApi.refreshStoreReviewStatus(storeReviewDetailVersion.value.id)
|
||||||
|
const result = res.data.data
|
||||||
|
if (result && result.stores) {
|
||||||
|
// Merge refreshed states into the detail items
|
||||||
|
for (const s of result.stores) {
|
||||||
|
const idx = storeReviewDetailItems.value.findIndex(i => i.store === s.storeType)
|
||||||
|
if (idx >= 0) {
|
||||||
|
// Re-parse the row's storeReviewStatus to get updated fields from DB
|
||||||
|
const row = appVersions.value.find(v => v.id === storeReviewDetailVersion.value!.id)
|
||||||
|
if (row) {
|
||||||
|
const parsed = parseStoreReview(row.storeReviewStatus)
|
||||||
|
const updated = parsed.find(p => p.store === s.storeType)
|
||||||
|
if (updated) {
|
||||||
|
storeReviewDetailItems.value[idx] = updated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await loadAppVersions()
|
||||||
|
ElMessage.success('厂商状态已刷新')
|
||||||
|
} catch {
|
||||||
|
ElMessage.error('刷新失败,请稍后重试')
|
||||||
|
} finally {
|
||||||
|
refreshingReviewStatus.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleRetryStore(storeType: string) {
|
async function handleRetryStore(storeType: string) {
|
||||||
if (!storeReviewDetailVersion.value) return
|
if (!storeReviewDetailVersion.value) return
|
||||||
retryingStores.value = new Set([...retryingStores.value, storeType])
|
retryingStores.value = new Set([...retryingStores.value, storeType])
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户