fix(selfop): distinguish preparation progress from warehouse tasks

This commit is contained in:
2026-10-13 12:50:29 +08:00
parent 9dcd991ddb
commit 6bdaff3fdf
7 changed files with 186 additions and 25 deletions
@@ -0,0 +1,35 @@
import { mount } from '@vue/test-utils'
import { describe, expect, it } from 'vitest'
import TaskExecutionFacts from './TaskExecutionFacts.vue'
import type { Task } from '../types'
import { hasActiveTask, label } from '../state'
const waiting: Task = { taskNo: 'LOCAL-A', warehouseCode: 'WH-A', taskType: 'BACKFILL', dataFamily: 'OUTBOUND', taskStatus: 'WAITING_SOURCE', applyState: 'NOT_STARTED', attemptCount: 0, latestAttemptNo: null, resultPublicationState: 'NOT_PUBLISHED', nextRetryAt: null, allowedActions: [], actionBlockReason: 'TASK_NOT_RETRYABLE', progress: null, queryFrom: null, queryUntil: null }
describe('collection execution facts', () => {
it('explains why a waiting warehouse has no attempt times or quality counts and keeps polling active', () => {
const wrapper = mount(TaskExecutionFacts, { props: { task: waiting, scopeLabel: '本仓任务' } })
expect(wrapper.text()).toContain('本仓任务尚未开始执行尝试')
expect(wrapper.text()).toContain('正在等待前面的数据准备任务完成')
expect(wrapper.text()).toContain('尚未生成校验摘要')
expect(wrapper.text()).not.toContain('耗时 0 秒')
expect(label(waiting.taskStatus)).toBe('等待来源数据')
expect(hasActiveTask([waiting])).toBe(true)
wrapper.unmount()
})
it('renders real upstream attempt times and preserves zero counts separately from missing counts', () => {
const upstream: Task = { ...waiting, taskNo: 'SHARED-A', taskStatus: 'RUNNING', attemptCount: 1, progress: { validationState: 'VALIDATING', invalidCount: 0, unknownCount: null, pendingReviewCount: 2, countUnit: 'ORDER', summaryAsOf: '2026-10-13T01:02:00Z' }, latestAttempt: { attemptNo: 1, status: 'FETCHING', phase: 'DETAIL', startedAt: '2026-10-13T01:00:00Z', finishedAt: null, progressUpdatedAt: '2026-10-13T01:02:00Z', heartbeatAt: '2026-10-13T01:02:00Z', executionHealth: 'HEALTHY', elapsedSeconds: 120, errorCode: null, failureSummary: null } }
const wrapper = mount(TaskExecutionFacts, { props: { task: upstream, scopeLabel: '账号共享上游' } })
expect(wrapper.text()).toContain('2026/10/13 09:00:00')
expect(wrapper.text()).toContain('结束 尚未结束')
expect(wrapper.text()).toContain('耗时 120 秒')
expect(wrapper.text()).toContain('心跳 2026/10/13 09:02:00 · 正常')
expect(wrapper.text()).toContain('异常 0,未知 —,待核对 2(单)')
expect(wrapper.text()).not.toContain('尚未生成校验摘要')
wrapper.unmount()
})
it('does not describe an unavailable recorded attempt as never started', () => {
const wrapper = mount(TaskExecutionFacts, { props: { task: { ...waiting, attemptCount: 1, taskStatus: 'DEAD' }, scopeLabel: '本仓任务' } })
expect(wrapper.text()).toContain('尝试详情尚不可用')
expect(wrapper.text()).not.toContain('尚未开始执行尝试')
wrapper.unmount()
})
})
@@ -0,0 +1,20 @@
<script setup lang="ts">
import { computed } from 'vue'
import type { Task } from '../types'
import { beijingTime, label, quantityText, unitText } from '../state'
const props = defineProps<{ task: Task; scopeLabel: string }>()
const quality = computed(() => props.task.progress)
const hasQuality = computed(() => [quality.value?.validationState, quality.value?.invalidCount, quality.value?.unknownCount, quality.value?.pendingReviewCount, quality.value?.summaryAsOf].some(value => value != null))
</script>
<template>
<div class="task-execution-facts" :aria-label="`${scopeLabel}执行信息`">
<p v-if="task.latestAttempt">尝试开始 {{ beijingTime(task.latestAttempt.startedAt) }};结束 {{ task.latestAttempt.finishedAt ? beijingTime(task.latestAttempt.finishedAt) : ['FETCHING', 'READY', 'VALIDATED', 'BUILDING'].includes(task.latestAttempt.status) ? '尚未结束' : '未记录' }};耗时 {{ quantityText(task.latestAttempt.elapsedSeconds) }} 秒;心跳 {{ beijingTime(task.latestAttempt.heartbeatAt) }} · {{ label(task.latestAttempt.executionHealth) }}。</p>
<p v-else-if="task.attemptCount === 0">{{ scopeLabel }}尚未开始执行尝试,开始、结束、耗时和心跳尚未产生。<template v-if="task.taskStatus === 'WAITING_SOURCE'">正在等待前面的数据准备任务完成。</template></p>
<p v-else>{{ scopeLabel }}尝试详情尚不可用,暂无法显示开始、结束、耗时和心跳。</p>
<p v-if="hasQuality">数据质量:{{ label(quality?.validationState) }};异常 {{ quantityText(quality?.invalidCount) }},未知 {{ quantityText(quality?.unknownCount) }},待核对 {{ quantityText(quality?.pendingReviewCount) }}({{ unitText(quality?.countUnit) }});摘要时间 {{ beijingTime(quality?.summaryAsOf) }}。未计算的数量不默认为零。</p>
<p v-else>数据质量:尚未生成校验摘要,异常、未知及待核对数量尚未计算。</p>
</div>
</template>
<style scoped>
p { margin: var(--admin-space-3) 0; overflow-wrap: anywhere; }
</style>