59 lines
3.8 KiB
TypeScript
59 lines
3.8 KiB
TypeScript
import type { Task } from './types'
|
||
import { label, phaseText, quantityText, unitText } from './state'
|
||
|
||
export interface CollectionControlEvidence {
|
||
collectionMode?: string | null
|
||
accountPaused?: boolean
|
||
blockingReason?: string | null
|
||
}
|
||
|
||
export function collectionControlState(task: Task | null | undefined, evidence: CollectionControlEvidence = {}) {
|
||
if (task && ['SUCCEEDED', 'FAILED', 'DEAD', 'ABORTED'].includes(task.taskStatus)) return null
|
||
const reasons = [task?.blockingReason, evidence.blockingReason]
|
||
const text = evidence.collectionMode === 'OFF' || reasons.includes('COLLECTION_OFF') ? '采集已关闭'
|
||
: evidence.accountPaused || reasons.includes('ACCOUNT_PAUSED') ? '已暂停'
|
||
: reasons.includes('EXECUTION_DISABLED') ? '执行未启用' : null
|
||
if (!text) return null
|
||
// A control switch alone cannot establish that an already running attempt has stopped.
|
||
const running = Boolean(task && ['RUNNING', 'APPLYING'].includes(task.taskStatus))
|
||
return { text, running, summary: running ? `${text === '采集已关闭' ? '已关闭' : text} · 执行中` : text }
|
||
}
|
||
|
||
export function isNormalContinuation(task: Task | null | undefined) {
|
||
const attempt = task?.latestAttempt
|
||
return task?.taskStatus === 'RETRY_WAIT' && attempt?.status === 'YIELDED'
|
||
&& task.latestAttemptNo != null && task.latestAttemptNo > 0
|
||
&& attempt.attemptNo === task.latestAttemptNo && task.attemptCount === task.latestAttemptNo
|
||
&& !attempt.errorCode && !attempt.failureSummary
|
||
}
|
||
|
||
export function taskStateText(task: Task | null | undefined, evidence: CollectionControlEvidence = {}) {
|
||
if (!task) return '尚无任务'
|
||
const control = collectionControlState(task, evidence)
|
||
if (control) return control.summary
|
||
if (isNormalContinuation(task)) return '本批已完成,等待续采'
|
||
const states: Record<string, string> = { WAITING_SOURCE: '等待数据准备', WAITING_UPSTREAM: '等待数据准备', READY: '等待执行', QUEUED: '等待执行', WAITING: '等待执行', RUNNING: '正在执行', RETRY_WAIT: '等待重试', YIELDED: '本批已完成,等待续采', DEAD: '已停止,需处理', SUCCEEDED: '执行完成', FAILED: '执行失败' }
|
||
return states[task.taskStatus] ?? label(task.taskStatus)
|
||
}
|
||
export function taskProgressText(task: Task | null | undefined) {
|
||
if (!task) return '尚无任务'
|
||
if (['WAITING_SOURCE', 'WAITING_UPSTREAM'].includes(task.taskStatus)) return '尚未开始,等待数据准备'
|
||
if (task.attemptCount === 0 && ['READY', 'QUEUED', 'WAITING'].includes(task.taskStatus)) return '尚未开始,等待执行'
|
||
const progress = task.progress
|
||
if (!progress?.phase) return task.taskStatus === 'SUCCEEDED' ? '本任务执行完成' : '当前进度尚未提供'
|
||
const phase = phaseText(progress.phase)
|
||
if (progress.completed == null || !progress.unit || progress.unit === 'NONE') return phase
|
||
const amount = `${quantityText(progress.completed)}${unitText(progress.unit)}`
|
||
if (!progress.totalKnown || progress.total == null) return `${phase},已处理 ${amount},总量尚未确定`
|
||
const total = Number(progress.total), completed = Number(progress.completed)
|
||
const percent = Number.isFinite(total) && Number.isFinite(completed) && total > 0 && completed >= 0 && completed <= total ? `(${Math.floor(completed / total * 1000) / 10}%,当前阶段)` : ''
|
||
return `${phase},已处理 ${quantityText(progress.completed)} / ${quantityText(progress.total)}${unitText(progress.unit)}${percent}`
|
||
}
|
||
export function taskCompletionText(task: Task | null | undefined) {
|
||
if (!task) return '—'
|
||
if (task.resultPublicationState === 'PUBLISHED' || task.resultPublicationState === 'REUSED') return '结果已可用'
|
||
if (task.taskStatus === 'SUCCEEDED') return '执行完成,结果尚未发布'
|
||
if (['DEAD', 'FAILED', 'ABORTED'].includes(task.taskStatus)) return '已停止,需先处理异常'
|
||
return '完成时间暂无法预计'
|
||
}
|