function text(value) { return value == null ? '' : String(value).trim() } const PLATFORM_CHANNELS = '/api/platform/v1/ocean-bookings/channels' export async function publishPlatformLogisticsChannels(context, originWarehouseId, module) { const warehouseId = text(originWarehouseId) const sourcePath = `${PLATFORM_CHANNELS}?originWarehouseId=${encodeURIComponent(warehouseId)}` const response = await context.request('GET', sourcePath) const data = context.apiSuccess(module, '按起运仓查询平台正式物流渠道', response, `originWarehouseId=${warehouseId}`, { scenario: 'ocean-booking-channel-catalog', deploymentBlocker: true }) const channels = (Array.isArray(data) ? data : []).flatMap((value) => { const channelCode = text(value?.channelCode) const channelName = text(value?.channelName) return channelCode && channelName ? [{ channelCode, channelName }] : [] }) const resource = { marker: context.marker, originWarehouseId: warehouseId, sourcePath, channels } context.createdResources.platformLogisticsChannels = resource context.assert(module, '平台渠道目录归属本轮起运仓且不含空渠道', response, Boolean(warehouseId) && channels.length > 0, `originWarehouseId=${warehouseId}, channels=${channels.map((value) => value.channelCode).join('|') || 'none'}`, { scenario: 'ocean-booking-channel-catalog', deploymentBlocker: true }) return resource } export async function resolveOceanBookingMaster(context, module) { const resources = context.createdResources || {} const candidates = [resources.phase3Loading, resources.phase3ContainerLoading, resources.orchestrationFixtures?.phase3Container, resources.inboundMeasurement, resources.orders].filter(Boolean) const source = candidates.find((item) => item.marker === context.marker) || candidates[0] const tenantId = text(source?.tenantId || source?.customerId) const inboundId = text(source?.inboundId) let summary = source?.summary || null if (tenantId && inboundId && typeof context.request === 'function') { const response = await context.request('GET', `/api/platform/inbounds/${encodeURIComponent(inboundId)}?tenantId=${encodeURIComponent(tenantId)}`) if (String(response?.body?.code) === '200') summary = response.body.data?.summary || summary } const master = { customerId: text(summary?.tenantId || tenantId), customerName: text(summary?.customerName || source?.customerName), channelCode: text(summary?.channelCode || source?.channelCode), channelName: text(summary?.channelName || source?.channelName), warehouseId: text(summary?.warehouseId || source?.warehouseId || source?.receivingWarehouseId), warehouseName: text(summary?.warehouseName || source?.warehouseName), } const missing = ['customerId', 'customerName', 'channelCode', 'channelName', 'warehouseId', 'warehouseName'] .filter((key) => !master[key]) if (missing.length) { context.addStep(module, '订舱 fixture 使用本轮正式客户/渠道/仓库主数据', null, false, `marker=${context.marker}, inboundId=${inboundId || 'missing'}, missing=${missing.join(',')}`, { scenario: 'ocean-booking-master-data', deploymentBlocker: true }) return null } return master } export function oceanBookingDraft(context, suffix, master, labels = {}) { const { channelName: _readOnlyChannelName, warehouseName: _readOnlyWarehouseName, ...writableMaster } = master const normalizedSuffix = text(suffix).replace(/[^A-Za-z0-9]/g, '').slice(-32) || 'BOOKING' const containerRequirements = labels.containerRequirements || [{ containerType: '40HC', containerKind: 'FCL', quantity: 1, capacityCbm: 76, maxWeightKg: 28000, }] return { ...writableMaster, billOfLadingNo: labels.billOfLadingNo || `BL-${suffix}`, carrierCode: labels.carrierCode || 'OTHER', shippingCompanyId: labels.shippingCompanyId ?? null, shippingCompanyName: labels.shippingCompanyId ? labels.shippingCompanyName ?? null : null, bookingAgentId: labels.bookingAgentId ?? null, bookingAgentName: labels.bookingAgentId ? labels.bookingAgentName ?? null : null, vesselName: labels.vesselName || `验收船-${suffix}`, voyageNo: labels.voyageNo || `V-${suffix}`, departurePortCode: 'CNSZX', departurePortName: '深圳', destinationPortCode: 'USLAX', destinationPortName: '洛杉矶', placeOfDelivery: 'LAX', serviceTerm: 'CY-CY', freightTerm: 'NINGBO', estimatedSailingAt: labels.estimatedSailingAt || '2026-09-01T08:00:00', estimatedArrivalAt: labels.estimatedArrivalAt || '2026-09-20T08:00:00', remark: context.marker, parties: [{ partyType: 'SHIPPER', partyName: `发货方-${suffix}` }, { partyType: 'CONSIGNEE', partyName: `收货方-${suffix}` }, { partyType: 'NOTIFY_PARTY', partyName: `通知方-${suffix}` }], containerRequirements, } } export function oceanBookingDraftWithoutCustomer(context, suffix, master, labels = {}) { const draft = oceanBookingDraft(context, suffix, master, labels) delete draft.customerId delete draft.customerName return draft }