diff --git a/tenant-platform/src/services/storeReviewRealtime.ts b/tenant-platform/src/services/storeReviewRealtime.ts index 3d8218e..5d193ac 100644 --- a/tenant-platform/src/services/storeReviewRealtime.ts +++ b/tenant-platform/src/services/storeReviewRealtime.ts @@ -39,6 +39,9 @@ let imClient: ImClient | null = null let activeAppKey = '' let storeReviewHandler: ((event: StoreReviewRefreshEvent) => void) | null = null let serviceActivationHandler: ((event: ServiceActivationRefreshEvent) => void) | null = null +// Tracks when a message was last received over the IM WebSocket (any type). +// Used by callers to decide whether IM is healthy enough to skip HTTP polling. +let lastImMessageAt = 0 function sdkWsUrl() { return import.meta.env.VITE_IM_WS_URL ?? '' @@ -114,6 +117,7 @@ async function ensureConnection(appKey: string) { const wsUrl = sdkWsUrl() || undefined const clientInstance = new ImClient({ tokenSupplier: () => platformToken, wsUrl }) clientInstance.on('message', (message) => { + lastImMessageAt = Date.now() const event = parseAnyEvent(message) if (!event || event.appKey !== activeAppKey) return if (event.event === 'store_review_update' && storeReviewHandler) { @@ -131,6 +135,14 @@ async function ensureConnection(appKey: string) { imClient = clientInstance } +/** + * Returns true if the IM WebSocket received a message within the last 90 seconds, + * meaning the connection is healthy and polling can be skipped. + */ +export function isStoreReviewRealtimeConnected(): boolean { + return imClient !== null && Date.now() - lastImMessageAt < 90_000 +} + export async function connectStoreReviewRealtime(appKey: string, onEvent: (event: StoreReviewRefreshEvent) => void) { if (!appKey) return storeReviewHandler = onEvent diff --git a/tenant-platform/src/views/update/VersionManagementView.vue b/tenant-platform/src/views/update/VersionManagementView.vue index 7cc79f8..2451899 100644 --- a/tenant-platform/src/views/update/VersionManagementView.vue +++ b/tenant-platform/src/views/update/VersionManagementView.vue @@ -946,6 +946,7 @@ import { connectStoreReviewRealtime, disconnectStoreReviewRealtime, notifyStoreReviewRefresh, + isStoreReviewRealtimeConnected, type StoreReviewRefreshEvent, } from '@/services/storeReviewRealtime' import huaweiGuideImage from '@/assets/update-store/huawei/01.png' @@ -1684,6 +1685,9 @@ function openStoreReviewDetail(row: AppVersion) { function startDialogPoll() { stopDialogPoll() + // IM WebSocket delivers real-time store_review_update events; only fall back to + // HTTP polling when IM has been silent for over 90 s (disconnected or starting up). + const interval = isStoreReviewRealtimeConnected() ? 30_000 : 3_000 storeReviewDialogPollTimer = setInterval(() => { if (!showStoreReviewDetail.value || !storeReviewDetailVersion.value) { stopDialogPoll() @@ -1697,7 +1701,7 @@ function startDialogPoll() { } else { stopDialogPoll() } - }, 3000) + }, interval) } function stopDialogPoll() {