优化后台业务
This commit is contained in:
@@ -265,6 +265,7 @@ import { LISTING_FILTER_OPTIONS, type ListingFilterValue } from '@/pages/brand/c
|
||||
import { expandBrandFolderRecursive } from '@/shared/api/brand'
|
||||
import { getPywebviewApi, type UploadedJavaFile } from '@/shared/bridges/pywebview'
|
||||
import { getTaskPollIntervalMs } from '@/shared/task-progress-config'
|
||||
import { createCategorizedTimers } from '@/shared/utils/categorized-timers'
|
||||
import {
|
||||
addPriceTrackCandidate,
|
||||
completePriceTrackLoopChild,
|
||||
@@ -369,6 +370,17 @@ const taskDetails = ref<Record<number, string>>({})
|
||||
const taskSnapshots = ref<Record<number, PriceTrackTaskDetailVo>>({})
|
||||
const pollTimer = ref<number | null>(null)
|
||||
const pollingInFlight = ref(false)
|
||||
let disposed = false
|
||||
const timers = createCategorizedTimers('price-track')
|
||||
|
||||
function sleep(ms: number) {
|
||||
if (disposed) return Promise.resolve()
|
||||
return timers.sleep('queue-wait', ms)
|
||||
}
|
||||
|
||||
function clearSleepTimers() {
|
||||
timers.clearCategory('queue-wait')
|
||||
}
|
||||
|
||||
function uidForStorage() {
|
||||
return typeof window !== 'undefined' ? window.localStorage.getItem('uid') || '0' : '0'
|
||||
@@ -649,8 +661,8 @@ function onCountryDrop(toIndex: number) {
|
||||
}
|
||||
|
||||
function scheduleSaveCountryPref() {
|
||||
if (countryPrefSaveTimer) clearTimeout(countryPrefSaveTimer)
|
||||
countryPrefSaveTimer = setTimeout(() => {
|
||||
if (countryPrefSaveTimer) timers.clearTimer('preference-save', countryPrefSaveTimer)
|
||||
countryPrefSaveTimer = timers.setTimeout('preference-save', () => {
|
||||
countryPrefSaveTimer = null
|
||||
void persistCountryPref()
|
||||
}, 450)
|
||||
@@ -1043,6 +1055,7 @@ async function waitForTaskTerminal(taskId: number) {
|
||||
let transientErrorCount = 0
|
||||
const maxTransientErrors = 30
|
||||
while (true) {
|
||||
if (disposed) return 'STOPPED'
|
||||
try {
|
||||
const batch = await getPriceTrackTaskProgressBatch([taskId])
|
||||
if (transientErrorCount > 0) {
|
||||
@@ -1086,10 +1099,10 @@ async function waitForTaskTerminal(taskId: number) {
|
||||
if (transientErrorCount === 1 || transientErrorCount % 5 === 0) {
|
||||
ElMessage.warning(`任务 ${taskId} 运行中,后端服务暂时不可用,正在自动重试`)
|
||||
}
|
||||
await new Promise<void>((resolve) => { window.setTimeout(() => resolve(), getPollIntervalMs()) })
|
||||
await sleep(getPollIntervalMs())
|
||||
continue
|
||||
}
|
||||
await new Promise<void>((resolve) => { window.setTimeout(() => resolve(), getPollIntervalMs()) })
|
||||
await sleep(getPollIntervalMs())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1103,6 +1116,7 @@ function nextMatchedQueueItem() {
|
||||
}
|
||||
|
||||
async function processMatchedQueue() {
|
||||
if (disposed) return
|
||||
if (queueWorkerRunning.value) {
|
||||
return
|
||||
}
|
||||
@@ -1131,7 +1145,7 @@ async function processMatchedQueue() {
|
||||
let failedCount = 0
|
||||
try {
|
||||
let index = 0
|
||||
while (autoQueueEnabled.value) {
|
||||
while (!disposed && autoQueueEnabled.value) {
|
||||
const row = nextMatchedQueueItem()
|
||||
if (!row) {
|
||||
break
|
||||
@@ -1141,6 +1155,7 @@ async function processMatchedQueue() {
|
||||
let attempt = 0
|
||||
const taskVo = await (async () => {
|
||||
while (true) {
|
||||
if (disposed) throw new Error('component disposed')
|
||||
try {
|
||||
return await createPriceTrackTask({
|
||||
userId: 0,
|
||||
@@ -1156,7 +1171,7 @@ async function processMatchedQueue() {
|
||||
attempt += 1
|
||||
if (!transient || attempt >= 10) throw error
|
||||
queuePushResult.value = '后端服务暂时不可用,正在重试创建任务(' + attempt + '/10)...'
|
||||
await new Promise<void>((resolve) => { window.setTimeout(() => resolve(), getPollIntervalMs()) })
|
||||
await sleep(getPollIntervalMs())
|
||||
}
|
||||
}
|
||||
})()
|
||||
@@ -1197,6 +1212,7 @@ async function processMatchedQueue() {
|
||||
successCount += 1
|
||||
queuePushResult.value = '任务 ' + taskVo.taskId + ' 已完成'
|
||||
} catch (error) {
|
||||
if (disposed) break
|
||||
failedCount += 1
|
||||
queuePushResult.value = '第 ' + index + ' 条任务处理失败,已自动继续下一条:' + (error instanceof Error ? error.message : '未知错误')
|
||||
ElMessage.error(queuePushResult.value)
|
||||
@@ -1208,6 +1224,7 @@ async function processMatchedQueue() {
|
||||
ElMessage.success('店铺任务推送已完成:成功 ' + successCount + ' 条,失败 ' + failedCount + ' 条')
|
||||
}
|
||||
} catch (e) {
|
||||
if (disposed) return
|
||||
queuePushResult.value = e instanceof Error ? e.message : '推送异常'
|
||||
ElMessage.error(queuePushResult.value)
|
||||
} finally {
|
||||
@@ -1282,6 +1299,7 @@ async function stopLoopExecution() {
|
||||
}
|
||||
|
||||
async function runLoopExecution(loopId?: number) {
|
||||
if (disposed) return
|
||||
if (loopDispatching.value) return
|
||||
if (loopId && activeLoopRunId.value !== loopId) {
|
||||
activeLoopRunId.value = loopId
|
||||
@@ -1294,7 +1312,7 @@ async function runLoopExecution(loopId?: number) {
|
||||
}
|
||||
loopDispatching.value = true
|
||||
try {
|
||||
while (activeLoopRunId.value) {
|
||||
while (!disposed && activeLoopRunId.value) {
|
||||
const loop = await syncActiveLoopRun()
|
||||
if (!loop || loop.status === 'SUCCESS' || loop.status === 'FAILED' || loop.status === 'STOPPED') break
|
||||
|
||||
@@ -1306,6 +1324,7 @@ async function runLoopExecution(loopId?: number) {
|
||||
let completeAttempt = 0
|
||||
const updatedLoop = await (async () => {
|
||||
while (true) {
|
||||
if (disposed) throw new Error('component disposed')
|
||||
try {
|
||||
return await completePriceTrackLoopChild(loop.id, activeTaskId)
|
||||
} catch (error) {
|
||||
@@ -1314,7 +1333,7 @@ async function runLoopExecution(loopId?: number) {
|
||||
completeAttempt += 1
|
||||
if (!transient || completeAttempt >= 10) throw error
|
||||
queuePushResult.value = `循环任务 ${loop.id} 等待后端恢复后确认子任务完成(${completeAttempt}/10)...`
|
||||
await new Promise<void>((resolve) => { window.setTimeout(() => resolve(), getPollIntervalMs()) })
|
||||
await sleep(getPollIntervalMs())
|
||||
}
|
||||
}
|
||||
})()
|
||||
@@ -1332,6 +1351,7 @@ async function runLoopExecution(loopId?: number) {
|
||||
let dispatchAttempt = 0
|
||||
const dispatch = await (async () => {
|
||||
while (true) {
|
||||
if (disposed) throw new Error('component disposed')
|
||||
try {
|
||||
return await dispatchNextPriceTrackLoopRun(loop.id)
|
||||
} catch (error) {
|
||||
@@ -1340,7 +1360,7 @@ async function runLoopExecution(loopId?: number) {
|
||||
dispatchAttempt += 1
|
||||
if (!transient || dispatchAttempt >= 10) throw error
|
||||
queuePushResult.value = `循环任务 ${loop.id} 等待后端恢复后继续派发(${dispatchAttempt}/10)...`
|
||||
await new Promise<void>((resolve) => { window.setTimeout(() => resolve(), getPollIntervalMs()) })
|
||||
await sleep(getPollIntervalMs())
|
||||
}
|
||||
}
|
||||
})()
|
||||
@@ -1355,6 +1375,7 @@ async function runLoopExecution(loopId?: number) {
|
||||
let createAttempt = 0
|
||||
const taskVo = await (async () => {
|
||||
while (true) {
|
||||
if (disposed) throw new Error('component disposed')
|
||||
try {
|
||||
return await createPriceTrackTask(childTaskRequest)
|
||||
} catch (error) {
|
||||
@@ -1363,7 +1384,7 @@ async function runLoopExecution(loopId?: number) {
|
||||
createAttempt += 1
|
||||
if (!transient || createAttempt >= 10) throw error
|
||||
queuePushResult.value = `循环任务 ${loop.id} 等待后端恢复后重试创建子任务(${createAttempt}/10)...`
|
||||
await new Promise<void>((resolve) => { window.setTimeout(() => resolve(), getPollIntervalMs()) })
|
||||
await sleep(getPollIntervalMs())
|
||||
}
|
||||
}
|
||||
})()
|
||||
@@ -1483,25 +1504,27 @@ async function refreshTaskBatch() {
|
||||
}
|
||||
|
||||
function scheduleNextPoll(immediate = false) {
|
||||
if (disposed) return
|
||||
if (pollTimer.value) {
|
||||
if (!immediate) return
|
||||
window.clearTimeout(pollTimer.value)
|
||||
timers.clearTimer('task-poll', pollTimer.value)
|
||||
pollTimer.value = null
|
||||
}
|
||||
const run = async () => {
|
||||
pollTimer.value = null
|
||||
if (disposed) return
|
||||
if (pollingInFlight.value) { scheduleNextPoll(); return }
|
||||
if (!pollingTaskIds.value.length) return
|
||||
pollingInFlight.value = true
|
||||
try { await refreshTaskBatch() } finally { pollingInFlight.value = false }
|
||||
if (pollingTaskIds.value.length > 0) pollTimer.value = window.setTimeout(run, getPollIntervalMs())
|
||||
if (!disposed && pollingTaskIds.value.length > 0) pollTimer.value = timers.setTimeout('task-poll', run, getPollIntervalMs())
|
||||
}
|
||||
if (immediate) void run()
|
||||
else pollTimer.value = window.setTimeout(run, getPollIntervalMs())
|
||||
else pollTimer.value = timers.setTimeout('task-poll', run, getPollIntervalMs())
|
||||
}
|
||||
|
||||
function stopPolling() {
|
||||
if (pollTimer.value) { window.clearTimeout(pollTimer.value); pollTimer.value = null }
|
||||
if (pollTimer.value) { timers.clearTimer('task-poll', pollTimer.value); pollTimer.value = null }
|
||||
}
|
||||
|
||||
function addPollingTask(taskId: number) {
|
||||
@@ -1658,8 +1681,11 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
disposed = true
|
||||
stopPolling()
|
||||
if (countryPrefSaveTimer) clearTimeout(countryPrefSaveTimer)
|
||||
clearSleepTimers()
|
||||
timers.clearScope()
|
||||
if (countryPrefSaveTimer) timers.clearTimer('preference-save', countryPrefSaveTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user