export const COMPLETION_PHASE = Object.freeze({ READY: 'READY', SUBMITTING: 'SUBMITTING', RECONCILING: 'RECONCILING', PROCESSING: 'PROCESSING', FAILED: 'FAILED', COMPLETED: 'COMPLETED' }) const PROCESSING_STATUSES = new Set(['COMPLETING', 'PROCESSING']) const COMPLETED_STATUSES = new Set(['COMPLETED']) const FAILED_STATUSES = new Set(['COMPLETION_FAILED']) function normalizedStatus(value) { return String(value || '').trim().toUpperCase() } /** * Resolve already-normalized task/command facts into a UI phase. DTO field * extraction deliberately stays outside this module until the backend schema * is frozen. */ export function resolveCompletionPhase({ taskStatus, commandStatus, submitting = false, reconciling = false } = {}) { if (reconciling) return COMPLETION_PHASE.RECONCILING if (submitting) return COMPLETION_PHASE.SUBMITTING const statuses = [taskStatus, commandStatus].map(normalizedStatus) if (statuses.some((status) => COMPLETED_STATUSES.has(status))) return COMPLETION_PHASE.COMPLETED if (statuses.some((status) => FAILED_STATUSES.has(status))) return COMPLETION_PHASE.FAILED if (statuses.some((status) => PROCESSING_STATUSES.has(status))) return COMPLETION_PHASE.PROCESSING return COMPLETION_PHASE.READY } export function completionPresentation(phase, failureStage = '') { if (phase === COMPLETION_PHASE.SUBMITTING) return { label: '正在提交', action: '', blocked: true } if (phase === COMPLETION_PHASE.RECONCILING) return { label: '正在确认提交结果', action: '', blocked: true } if (phase === COMPLETION_PHASE.PROCESSING) return { label: '完成处理中', action: '', blocked: true } if (phase === COMPLETION_PHASE.COMPLETED) return { label: '装柜已完成', action: '', blocked: true } if (phase === COMPLETION_PHASE.FAILED) { return { label: failureStage ? `完成失败:${failureStage}` : '完成失败', action: '重试完成', blocked: true, retryable: true } } return { label: '待确认装柜', action: '确认装柜完成', blocked: false } } export function shouldReconcileCompletion(error = {}) { const code = normalizedStatus(error.code || error.errCode) return Boolean(error.network || error.timeout || code === 'TIMEOUT' || code === 'REQUEST_TIMEOUT') } /** * A retry keeps the original operationId and the exact accepted request facts. * expectedTaskVersion participates in requestHash and must not be refreshed. */ export function refreshCompletionAttempt(previousAttempt = {}, latestFacts = {}) { if (!previousAttempt.operationId) throw new Error('缺少原完成操作标识,不能安全重试') const expectedTaskVersion = previousAttempt.expectedTaskVersion if (expectedTaskVersion === null || expectedTaskVersion === undefined) throw new Error('缺少服务端最新任务版本,不能安全重试') return { ...previousAttempt, ...latestFacts, operationId: previousAttempt.operationId, expectedTaskVersion: Number(expectedTaskVersion) } }