This commit is contained in:
2026-08-14 18:50:07 +08:00
parent 9b36978448
commit 10d3390bb1
17 changed files with 461 additions and 61 deletions
@@ -1,3 +1,5 @@
import { publishPlatformLogisticsChannels } from './ocean-booking-fixture.mjs'
const MODULE = 'phase3-container-loading'
const OPTIONS = { scenario: 'phase3-container-smart-loading-pda', deploymentBlocker: true }
const CONTAINERS = '/api/platform/v1/containers'
@@ -46,6 +48,32 @@ export function selectContainerLoadingFixture(context) {
return candidates.find((candidate) => candidate.marker === context.marker) || candidates[0]
}
export function selectPlatformChannelIsolationPair(catalog, marker, originWarehouseId, primaryChannelCode) {
const catalogMarker = id(catalog?.marker)
const catalogWarehouseId = id(catalog?.originWarehouseId)
const sourcePath = id(catalog?.sourcePath)
if (catalogMarker !== id(marker) || catalogWarehouseId !== id(originWarehouseId)
|| !sourcePath.startsWith('/api/platform/')) {
return { pair: null, reason: 'BLOCKED_FIXTURE: missing current-marker platform channel catalog for origin warehouse' }
}
const channels = []
const seen = new Set()
for (const value of Array.isArray(catalog?.channels) ? catalog.channels : []) {
const channelCode = id(value?.channelCode).trim()
const channelName = id(value?.channelName).trim()
const status = id(value?.status).trim().toUpperCase()
if (!channelCode || !channelName || (status && status !== 'ACTIVE') || seen.has(channelCode)) continue
seen.add(channelCode)
channels.push({ channelCode, channelName })
}
const primary = channels.find((value) => value.channelCode === id(primaryChannelCode))
const foreign = channels.find((value) => value.channelCode !== id(primaryChannelCode))
if (!primary || !foreign) {
return { pair: null, reason: `BLOCKED_FIXTURE: ACTIVE platform channels=${channels.length}, distinct pair unavailable` }
}
return { pair: { primary, foreign }, reason: null }
}
export async function run(context) {
const source = selectContainerLoadingFixture(context)
const tenantId = id(source?.tenantId)
@@ -70,11 +98,19 @@ export async function run(context) {
const suffix = context.marker.replace(/[^A-Za-z0-9]/g, '').slice(-16)
const channelCode = id(inbound?.summary?.channelCode || source?.channelCode)
const channelName = inbound?.summary?.channelName || source?.channelName || channelCode
const warehouseName = inbound?.summary?.warehouseName || source?.warehouseName
if (!channelCode || !warehouseName) return
const channelCatalog = await publishPlatformLogisticsChannels(context, warehouseId, MODULE)
const selectedChannel = channelCatalog.channels.find((value) => value.channelCode === channelCode)
context.assert(MODULE, '入库渠道存在于当前起运仓 ACTIVE 平台渠道目录', inboundResponse,
Boolean(selectedChannel),
`originWarehouseId=${warehouseId}, channelCode=${channelCode}, catalog=${channelCatalog.channels.map((value) => value.channelCode).join('|') || 'none'}`,
OPTIONS)
if (!selectedChannel) return
const channelName = selectedChannel.channelName
const bookingDraft = {
customerId: tenantId, customerName: inbound?.summary?.customerName || `验收客户-${tenantId}`,
channelCode: channelCode || 'FBA', channelName, warehouseId, warehouseName,
channelCode, warehouseId, warehouseName,
shippingCompanyId: null, shippingCompanyName: `验收船公司-${suffix}`,
bookingAgentId: null, bookingAgentName: `验收代理-${suffix}`,
departurePortCode: 'CNSZX', departurePortName: '深圳',
@@ -87,7 +123,6 @@ export async function run(context) {
}
context.assert(MODULE, '订舱渠道与仓库快照完整', inboundResponse, Boolean(channelCode && channelName && warehouseName),
`channelCode=${channelCode || 'missing'}, warehouseId=${warehouseId}, warehouseName=${warehouseName || 'missing'}`, OPTIONS)
if (!channelCode || !warehouseName) return
const bookingCreate = await request(context, 'POST', '/api/platform/v1/ocean-bookings',
{ expectedVersion: 0, data: bookingDraft }, { 'Idempotency-Key': `${context.marker}-loading-booking-create` })
const bookingCreated = context.apiSuccess(MODULE, '创建含渠道仓库快照的隔离订舱', bookingCreate, '', OPTIONS)
@@ -118,35 +153,42 @@ export async function run(context) {
`candidateFound=${Boolean(candidate)}, available=${candidate?.availablePackageCount ?? 'missing'}`, OPTIONS)
if (!candidate?.inboundDetailId) return
const foreignChannelCode = `${channelCode}-OTHER`.slice(0, 64)
const foreignBookingCreate = await request(context, 'POST', '/api/platform/v1/ocean-bookings', {
expectedVersion: 0,
data: { ...bookingDraft, channelCode: foreignChannelCode, channelName: `异渠道-${suffix}`, remark: `${context.marker}-other-channel` },
}, { 'Idempotency-Key': `${context.marker}-other-channel-booking-create`.slice(0, 128) })
const foreignBookingCreated = context.apiSuccess(MODULE, '创建异渠道隔离订舱', foreignBookingCreate, '', OPTIONS)
const foreignBookingId = id(foreignBookingCreated?.oceanBooking?.id)
if (!foreignBookingId) return
const foreignSubmit = await request(context, 'POST', `/api/platform/v1/ocean-bookings/${foreignBookingId}/submit`, {
expectedVersion: Number(foreignBookingCreated?.oceanBooking?.version), reason: context.marker,
}, { 'Idempotency-Key': `${context.marker}-other-channel-booking-submit`.slice(0, 128) })
const foreignSubmitted = context.apiSuccess(MODULE, '提交异渠道隔离订舱', foreignSubmit, '', OPTIONS)
const foreignConfirm = await request(context, 'POST', `/api/platform/v1/ocean-bookings/${foreignBookingId}/confirm`, {
expectedVersion: Number(foreignSubmitted?.oceanBooking?.version), reason: context.marker,
}, { 'Idempotency-Key': `${context.marker}-other-channel-booking-confirm`.slice(0, 128) })
const foreignConfirmed = context.apiSuccess(MODULE, '确认异渠道订舱并自动生成柜次', foreignConfirm, '', OPTIONS)
const foreignContainerId = id(foreignConfirmed?.containerAllocations?.[0]?.containerBookingId)
if (!foreignContainerId) return
const foreignPlanResponse = await request(context, 'POST', `${SMART}/plans`, {
bookingId: foreignContainerId, remark: `${context.marker}-other-channel`,
})
const foreignPlan = context.apiSuccess(MODULE, '创建异渠道智能装柜计划', foreignPlanResponse, '', OPTIONS)
const foreignAssignResponse = await request(context, 'POST', `${SMART}/plans/${foreignPlan?.id}/assignments`, {
inboundId, inboundDetailId: id(candidate.inboundDetailId), packageCount: 1,
expectedVersion: Number(foreignPlan?.version), operationId: `${context.marker}-other-channel-assign`.slice(0, 128),
})
context.addStep(MODULE, '异渠道入库单拒绝装入当前柜次', foreignAssignResponse,
foreignAssignResponse.http === 200 && ['A0422', '422'].includes(codeOf(foreignAssignResponse)),
`HTTP=${foreignAssignResponse.http}, code=${codeOf(foreignAssignResponse)}`, OPTIONS)
const channelIsolation = selectPlatformChannelIsolationPair(
context.createdResources?.platformLogisticsChannels, context.marker, warehouseId, channelCode)
if (!channelIsolation.pair) {
context.addStep(MODULE, '两个真实有效渠道的柜次隔离 fixture', null, false,
`${channelIsolation.reason}; originWarehouseId=${warehouseId}, primaryChannelCode=${channelCode}`, OPTIONS)
} else {
const foreignChannel = channelIsolation.pair.foreign
const foreignBookingCreate = await request(context, 'POST', '/api/platform/v1/ocean-bookings', {
expectedVersion: 0,
data: { ...bookingDraft, channelCode: foreignChannel.channelCode, remark: `${context.marker}-other-channel` },
}, { 'Idempotency-Key': `${context.marker}-other-channel-booking-create`.slice(0, 128) })
const foreignBookingCreated = context.apiSuccess(MODULE, '创建真实异渠道隔离订舱', foreignBookingCreate, '', OPTIONS)
const foreignBookingId = id(foreignBookingCreated?.oceanBooking?.id)
if (!foreignBookingId) return
const foreignSubmit = await request(context, 'POST', `/api/platform/v1/ocean-bookings/${foreignBookingId}/submit`, {
expectedVersion: Number(foreignBookingCreated?.oceanBooking?.version), reason: context.marker,
}, { 'Idempotency-Key': `${context.marker}-other-channel-booking-submit`.slice(0, 128) })
const foreignSubmitted = context.apiSuccess(MODULE, '提交真实异渠道隔离订舱', foreignSubmit, '', OPTIONS)
const foreignConfirm = await request(context, 'POST', `/api/platform/v1/ocean-bookings/${foreignBookingId}/confirm`, {
expectedVersion: Number(foreignSubmitted?.oceanBooking?.version), reason: context.marker,
}, { 'Idempotency-Key': `${context.marker}-other-channel-booking-confirm`.slice(0, 128) })
const foreignConfirmed = context.apiSuccess(MODULE, '确认真实异渠道订舱并自动生成柜次', foreignConfirm, '', OPTIONS)
const foreignContainerId = id(foreignConfirmed?.containerAllocations?.[0]?.containerBookingId)
if (!foreignContainerId) return
const foreignPlanResponse = await request(context, 'POST', `${SMART}/plans`, {
bookingId: foreignContainerId, remark: `${context.marker}-other-channel`,
})
const foreignPlan = context.apiSuccess(MODULE, '创建真实异渠道智能装柜计划', foreignPlanResponse, '', OPTIONS)
const foreignAssignResponse = await request(context, 'POST', `${SMART}/plans/${foreignPlan?.id}/assignments`, {
inboundId, inboundDetailId: id(candidate.inboundDetailId), packageCount: 1,
expectedVersion: Number(foreignPlan?.version), operationId: `${context.marker}-other-channel-assign`.slice(0, 128),
})
context.addStep(MODULE, '两个真实有效渠道之间拒绝跨渠道装柜', foreignAssignResponse,
foreignAssignResponse.http === 200 && ['A0422', '422'].includes(codeOf(foreignAssignResponse)),
`primary=${channelCode}, foreign=${foreignChannel.channelCode}, HTTP=${foreignAssignResponse.http}, code=${codeOf(foreignAssignResponse)}`, OPTIONS)
}
const containerDetailResponse = await context.request('GET', `${CONTAINERS}/${containerId}`)
const containerDetail = context.apiSuccess(MODULE, '回查货柜详情', containerDetailResponse, '', OPTIONS)