优化后台业务

This commit is contained in:
super
2026-04-30 11:36:58 +08:00
parent 9c16c9c589
commit c6ecfb5b77
48 changed files with 1211 additions and 2391 deletions

View File

@@ -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>