feat: add scheduled automatic task matching
This commit is contained in:
@@ -58,7 +58,7 @@
|
||||
<span>✓</span>{{ step }}
|
||||
</li>
|
||||
</ol>
|
||||
<button class="m-result-secondary" @click="openOrders">后台匹配,去记录查看</button>
|
||||
<button class="m-result-secondary" @click="openOrders">稍后去记录查看</button>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
@@ -118,9 +118,11 @@
|
||||
const resultState = ref<ResultState>('matching');
|
||||
const failureMessage = ref('当前任务库存未分配成功,请稍后重试');
|
||||
const matchProgress = ref(18);
|
||||
const matchRemainingSeconds = ref(0);
|
||||
const matchTotalSeconds = ref(0);
|
||||
const pollFinished = ref(false);
|
||||
let pollTimer: number | undefined;
|
||||
let progressTimer: number | undefined;
|
||||
let countdownTimer: number | undefined;
|
||||
let pollCount = 0;
|
||||
|
||||
const statCards = computed(() => [
|
||||
@@ -139,7 +141,10 @@
|
||||
});
|
||||
const matchStep = computed(() => (matchProgress.value < 35 ? 0 : matchProgress.value < 60 ? 1 : matchProgress.value < 85 ? 2 : 3));
|
||||
const matchSteps = ['正在匹配任务', '获取商品信息', '确认任务内容', '发放任务结果'];
|
||||
const matchingHint = computed(() => pollFinished.value ? '任务仍在匹配,可稍后前往记录查看' : '系统正在为您匹配任务,请稍等...');
|
||||
const matchingHint = computed(() => {
|
||||
if (matchRemainingSeconds.value > 0) return `系统正在派单... ${formatCountdown(matchRemainingSeconds.value)}`;
|
||||
return pollFinished.value ? '任务仍在匹配,可稍后前往记录查看' : '系统正在为您匹配任务,请稍等...';
|
||||
});
|
||||
const resultIcon = computed(() => resultState.value === 'success' ? mobileAssets.claimSuccess : resultState.value === 'failed' ? mobileAssets.claimFailed : mobileAssets.claimCancelled);
|
||||
const resultTitle = computed(() => resultState.value === 'success' ? '任务获取成功' : resultState.value === 'failed' ? '领取失败' : '已取消任务');
|
||||
const resultDescription = computed(() => resultState.value === 'success' ? '恭喜你成功领取任务' : resultState.value === 'failed' ? '本次任务领取未成功,请稍后再试' : '本次任务已取消,可重新领取');
|
||||
@@ -158,7 +163,10 @@
|
||||
]);
|
||||
halls.value = hallData || [];
|
||||
stats.value = statData;
|
||||
if (halls.value.length) await loadEligibilities();
|
||||
if (halls.value.length) {
|
||||
await loadEligibilities();
|
||||
await resumeMatchingOrder();
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = errorText(err);
|
||||
} finally {
|
||||
@@ -177,6 +185,21 @@
|
||||
eligibility.value = entries.find(([, value]) => value?.eligible)?.[1] || entries[0]?.[1];
|
||||
}
|
||||
|
||||
async function resumeMatchingOrder() {
|
||||
const pendingOrderID = Object.values(eligibilityByHall.value)
|
||||
.map((item: any) => Number(item?.pending_order_id || 0))
|
||||
.find((id) => id > 0);
|
||||
if (!pendingOrderID) return;
|
||||
try {
|
||||
const pendingOrder = await request<any>({ url: `/v1/task-orders/${pendingOrderID}` });
|
||||
if (pendingOrder?.status !== 'matching') return;
|
||||
openMatching();
|
||||
handleOrder(pendingOrder);
|
||||
} catch {
|
||||
// 保留页面上的“存在未完成订单”入口,避免详情请求失败阻断领取页加载。
|
||||
}
|
||||
}
|
||||
|
||||
async function claim() {
|
||||
if (claiming.value || !halls.value.length) return;
|
||||
claiming.value = true;
|
||||
@@ -224,15 +247,15 @@
|
||||
resultState.value = 'matching';
|
||||
showResult.value = true;
|
||||
matchProgress.value = 18;
|
||||
matchRemainingSeconds.value = 0;
|
||||
matchTotalSeconds.value = 0;
|
||||
pollCount = 0;
|
||||
progressTimer = window.setInterval(() => {
|
||||
matchProgress.value = Math.min(88, matchProgress.value + Math.ceil(Math.random() * 5));
|
||||
}, 700);
|
||||
}
|
||||
|
||||
function handleOrder(value: any) {
|
||||
order.value = value;
|
||||
if (value?.status === 'matching') {
|
||||
startCountdown(value);
|
||||
schedulePoll();
|
||||
} else if (['pending', 'processing', 'completed'].includes(value?.status)) {
|
||||
matchProgress.value = 100;
|
||||
@@ -250,6 +273,11 @@
|
||||
|
||||
function schedulePoll() {
|
||||
if (!order.value?.id) return;
|
||||
if (pollTimer) window.clearTimeout(pollTimer);
|
||||
const scheduledAt = Date.parse(String(order.value?.scheduled_match_at || ''));
|
||||
const wait = Number.isFinite(scheduledAt) && scheduledAt > Date.now()
|
||||
? scheduledAt - Date.now() + 300
|
||||
: 1200;
|
||||
pollTimer = window.setTimeout(async () => {
|
||||
pollCount += 1;
|
||||
try {
|
||||
@@ -264,14 +292,43 @@
|
||||
pollFinished.value = true;
|
||||
stopPolling();
|
||||
}
|
||||
}, 1200);
|
||||
}, wait);
|
||||
}
|
||||
|
||||
function startCountdown(value: any) {
|
||||
if (countdownTimer) window.clearInterval(countdownTimer);
|
||||
const scheduledAt = Date.parse(String(value?.scheduled_match_at || ''));
|
||||
if (!Number.isFinite(scheduledAt)) return;
|
||||
matchTotalSeconds.value = Math.max(1, Number(value?.dispatch_delay_seconds || 0));
|
||||
const update = () => {
|
||||
matchRemainingSeconds.value = Math.max(0, Math.ceil((scheduledAt - Date.now()) / 1000));
|
||||
const elapsed = Math.max(0, matchTotalSeconds.value - matchRemainingSeconds.value);
|
||||
matchProgress.value = Math.min(88, Math.round(18 + elapsed / matchTotalSeconds.value * 70));
|
||||
if (matchRemainingSeconds.value === 0 && countdownTimer) {
|
||||
window.clearInterval(countdownTimer);
|
||||
countdownTimer = undefined;
|
||||
}
|
||||
};
|
||||
update();
|
||||
if (matchRemainingSeconds.value > 0) countdownTimer = window.setInterval(update, 1000);
|
||||
}
|
||||
|
||||
function formatCountdown(totalSeconds: number) {
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
return hours > 0
|
||||
? [hours, minutes, seconds].map((item) => String(item).padStart(2, '0')).join(':')
|
||||
: [minutes, seconds].map((item) => String(item).padStart(2, '0')).join(':');
|
||||
}
|
||||
|
||||
function stopPolling(clearProgress = true) {
|
||||
if (pollTimer) window.clearTimeout(pollTimer);
|
||||
if (clearProgress && progressTimer) window.clearInterval(progressTimer);
|
||||
if (clearProgress && countdownTimer) window.clearInterval(countdownTimer);
|
||||
pollTimer = undefined;
|
||||
if (clearProgress) progressTimer = undefined;
|
||||
if (clearProgress) {
|
||||
countdownTimer = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function closeResult() {
|
||||
|
||||
Reference in New Issue
Block a user