From 36deeb218ff7ae466388289738f8d82be70f8920 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Sun, 17 May 2026 12:15:06 +0800 Subject: [PATCH] remove manual review button; store review is now fully automated via polling Co-Authored-By: Claude Sonnet 4.6 --- tenant-platform/src/api/update.ts | 4 +- .../views/update/VersionManagementView.vue | 70 +++++++++++++++++-- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/tenant-platform/src/api/update.ts b/tenant-platform/src/api/update.ts index d7b5913..362018f 100644 --- a/tenant-platform/src/api/update.ts +++ b/tenant-platform/src/api/update.ts @@ -323,10 +323,10 @@ export const updateAdminApi = { ) }, - updateStoreReview(versionId: string, storeType: StoreType, state: StoreReviewState) { + updateStoreReview(versionId: string, storeType: StoreType, state: StoreReviewState, reason?: string) { return updateClient.post<{ data: AppVersion }>( `/api/v1/updates/store/app/${versionId}/review`, - { storeType, state }, + { storeType, state, ...(reason ? { reason } : {}) }, ) }, diff --git a/tenant-platform/src/views/update/VersionManagementView.vue b/tenant-platform/src/views/update/VersionManagementView.vue index 7cb6684..22136ba 100644 --- a/tenant-platform/src/views/update/VersionManagementView.vue +++ b/tenant-platform/src/views/update/VersionManagementView.vue @@ -537,7 +537,7 @@ - +
@@ -617,6 +617,7 @@ @click="handleRetryStore(item.store)" >重新提交
+
@@ -638,7 +639,7 @@ plain @click="showPublishSchedule = true" >修改发布计划 - 关闭 + 关闭
@@ -1049,6 +1050,8 @@ const operationLogs = ref<{ }[]>([]) const loadingOperationLogs = ref(false) let storeReviewReloadTimer: ReturnType | null = null +let storeReviewAutoRefreshTimer: ReturnType | null = null +let storeReviewDialogPollTimer: ReturnType | null = null const hasGraySelectCallback = computed(() => Boolean(publishConfigForm.value.graySelectCallbackUrl.trim())) const hasGrayDirectorySyncCallback = computed(() => Boolean(publishConfigForm.value.grayDirectorySyncCallbackUrl.trim())) @@ -1566,6 +1569,32 @@ function openStoreReviewDetail(row: AppVersion) { storeReviewDetailItems.value = parseStoreReview(row.storeReviewStatus) storeReviewDetailLive.value = false showStoreReviewDetail.value = true + startDialogPoll() +} + +function startDialogPoll() { + stopDialogPoll() + storeReviewDialogPollTimer = setInterval(() => { + if (!showStoreReviewDetail.value || !storeReviewDetailVersion.value) { + stopDialogPoll() + return + } + const hasActive = storeReviewDetailItems.value.some(i => + i.state === 'PENDING' || i.state === 'SUBMITTING' || i.state === 'UNDER_REVIEW' + ) + if (hasActive) { + void loadAppVersions() + } else { + stopDialogPoll() + } + }, 3000) +} + +function stopDialogPoll() { + if (storeReviewDialogPollTimer) { + clearInterval(storeReviewDialogPollTimer) + storeReviewDialogPollTimer = null + } } async function confirmSubmitToStores() { @@ -2325,16 +2354,22 @@ onMounted(() => { } } - // Only show toast for non-trivial state transitions + // Toast: show for all state/stage changes except PENDING (initial queue) const state = (event.reviewState || '').toUpperCase() + const stage = (event.stage || '').toUpperCase() if (state && state !== 'PENDING') { + // Use stage description for SUBMITTING transitions (排队→上传→审核中) + let stageHint = '' + if (state === 'SUBMITTING') { + stageHint = stage === 'QUEUED' ? '排队中' : stage === 'SUBMITTING' ? '正在上传' : '' + } ElMessage({ message: event.reviewReason - ? `${storeName} → ${stateName}:${event.reviewReason}` - : `${storeName} → ${stateName}`, + ? `${storeName} → ${stageHint || stateName}:${event.reviewReason}` + : `${storeName} → ${stageHint || stateName}`, type: state === 'REJECTED' || state === 'WITHDRAWN' ? 'error' : state === 'APPROVED' ? 'success' : 'info', - duration: 4000, + duration: state === 'SUBMITTING' ? 2000 : 4000, }) } @@ -2344,6 +2379,24 @@ onMounted(() => { console.warn('[tenant-platform] store review realtime unavailable', error) } }) + + // Fallback: poll every 30s while any store is in an active state so WebSocket + // drop-outs don't leave the UI stale (e.g. a 20-min upload where WS reconnects). + storeReviewAutoRefreshTimer = setInterval(() => { + const hasActive = appVersions.value.some(v => { + if (!v.storeReviewStatus) return false + try { + const m = JSON.parse(v.storeReviewStatus) as Record + return Object.values(m).some(entry => { + const state = typeof entry === 'string' ? entry : (entry as Record)?.state as string + return state === 'PENDING' || state === 'SUBMITTING' || state === 'UNDER_REVIEW' + }) + } catch { return false } + }) + if (hasActive) { + void loadAppVersions() + } + }, 30_000) }) onBeforeUnmount(() => { @@ -2353,6 +2406,11 @@ onBeforeUnmount(() => { clearTimeout(storeReviewReloadTimer) storeReviewReloadTimer = null } + if (storeReviewAutoRefreshTimer) { + clearInterval(storeReviewAutoRefreshTimer) + storeReviewAutoRefreshTimer = null + } + stopDialogPoll() })