fix(container): hide provider framework errors

This commit is contained in:
2026-09-09 01:20:39 +08:00
parent 64340036a3
commit e643e43828
2 changed files with 20 additions and 1 deletions
@@ -108,6 +108,17 @@ describe('CarrierScheduleQueryPanel loading states', () => {
expect(getPortSchedule).not.toHaveBeenCalled()
})
it('does not expose a circuit-breaker exception name stored by an earlier scheduler attempt', async () => {
const failed = { ...queryView.query, lastErrorCode: 'PROVIDER_CALL_FAILED', lastErrorMessage: 'NoFallbackAvailableException', resultPortScheduleId: null }
vi.mocked(listCarrierScheduleQueries).mockResolvedValue({ records: [{ ...queryView, query: failed }], page: 1, size: 200, total: 1, pages: 1 })
vi.mocked(getCarrierScheduleQuery).mockResolvedValue({ query: { ...queryView, query: failed }, nodes: [], exceptions: [], histories: [] })
const wrapper = mount(CarrierScheduleQueryPanel, { props: { containerBookingId: '3' }, global: { plugins: [ElementPlus, pinia] } })
await flushPromises()
expect(wrapper.text()).toContain('轨迹供应商暂时不可用,请稍后重试或人工补录节点。')
expect(wrapper.text()).not.toContain('NoFallbackAvailableException')
})
it('renders canonical node location, actual time, source and empty-return stop facts from detail DTO', async () => {
vi.mocked(listCarrierScheduleQueries).mockResolvedValue({ records: [{ ...queryView, query: { ...queryView.query, status: 'STOPPED', latestActualNode: 'EMPTY_RETURNED', latestActualAt: '2026-08-20T17:45:00', stoppedAt: '2026-08-20T18:00:00', stopReason: '已还柜', resultPortScheduleId: null }, expired: false }], page: 1, size: 200, total: 1, pages: 1 })
vi.mocked(getCarrierScheduleQuery).mockResolvedValue({
@@ -91,6 +91,14 @@ function nodeLocation(item?: CarrierScheduleNodeFact | null) { return item ? [it
function exceptionLocation(item: CarrierScheduleExceptionFact) { return [item.locationCode, item.locationName].filter(Boolean).join(' · ') || '—' }
function nodeTimeText(item: CarrierScheduleNodeFact) { return item.actualAt ? `实际 ${formatTime(item.actualAt)}` : item.estimatedAt ? `预计 ${formatTime(item.estimatedAt)}` : `观测 ${formatTime(item.observedAt)}` }
function exceptionText(value: CarrierScheduleExceptionFact['exceptionCode']) { return ({ CUSTOMS_HOLD: '海关扣留', TERMINAL_HOLD: '码头扣留', DOCUMENT_HOLD: '单证扣留', UNKNOWN_HOLD: '其他扣留' } as Record<string, string>)[value] || value }
function providerErrorText(value?: string | null) {
const message = nonBlank(value)
if (!message) return '自动查询暂不可用,需要人工补充可靠结果。'
if (/(?:NoFallbackAvailableException|FeignException|ConnectException|SocketTimeoutException)/i.test(message)) {
return '轨迹供应商暂时不可用,请稍后重试或人工补录节点。'
}
return message
}
function historyText(action: string) { return ({ AUTO_CREATE: '国内出仓自动建任务', CREATE: '人工建立查询任务', NODE_OBSERVED: '记录标准节点', PROVIDER_QUERY: '船司查询', MANUAL_NODE: '人工补录标准节点', STOP: '停止查询' } as Record<string, string>)[action] || action }
function warehouseEventText(value: string) { return ({ STATUS_UPDATE: '状态更新', PUSH_PRELOAD: '推送预装清单', CONFIRM_PRELOAD: '确认预装清单', TRANSPORT_UPDATE: '更新运输信息', PDA_CLAIM: '领取 PDA 任务', PDA_BATCH: '提交拣货结果', PDA_COMPLETE_LOADING: '完成装柜' } as Record<string, string>)[value] || value || '状态更新' }
function nonBlank(value: unknown) { return typeof value === 'string' ? value.trim() : '' }
@@ -250,7 +258,7 @@ watch(() => manualForm.actualAt, (actualAt) => {
<LoadErrorState v-if="error" compact title="船节点查询状态加载失败" :message="error.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="error" /></template></LoadErrorState>
<div v-else-if="loading" class="loading-state"><el-skeleton :rows="1" animated /></div>
<template v-else>
<el-alert v-if="query?.lastErrorMessage || query?.status === 'MANUAL_REQUIRED'" type="error" :closable="false" show-icon :title="query?.lastErrorMessage || query?.manualReason || '自动查询暂不可用,需要人工补充可靠结果。'" />
<el-alert v-if="query?.lastErrorMessage || query?.status === 'MANUAL_REQUIRED'" type="error" :closable="false" show-icon :title="providerErrorText(query?.lastErrorMessage || query?.manualReason)" />
<el-alert v-if="query?.status === 'STOPPED'" type="info" :closable="false" show-icon :title="query.stopReason || '船节点查询已停止。'" />
<el-alert v-if="queryDetailError" type="warning" :closable="false" show-icon title="标准节点与变更历史暂不可用"><template #default><ApiErrorMeta :details="queryDetailError" /></template></el-alert>
<el-alert v-if="scheduleError" type="warning" :closable="false" show-icon title="关联船期结果暂不可用"><template #default><ApiErrorMeta :details="scheduleError" /></template></el-alert>