This commit is contained in:
2026-08-28 09:19:18 +08:00
parent e72ac14425
commit a719a903cd
10 changed files with 174 additions and 50 deletions
@@ -1,5 +1,6 @@
import { diffPushPreloadStability, projectPushPreloadStability } from './push-preload-stability.mjs'
import { isPureStatusNoop, visibleLoadingHistories } from './loading-history-noop.mjs'
import { ensureMatsonCarrierPartner } from './carrier-partner-fixture.mjs'
import { createHash } from 'node:crypto'
const MODULE = 'phase3-loading'
@@ -35,7 +36,7 @@ async function ensureMatsonSupplier(context, records) {
return activated?.id ? activated : null
}
const createdResponse = await context.request('POST', '/api/platform/v1/suppliers?tenantId=0', {
name: MATSON_SUPPLIER_NAME, category: 'BOOKING', region: '开发环境', legalRepresentative: '开发联调',
name: MATSON_SUPPLIER_NAME, category: 'BOOKING', carrierCodes: ['MATSON'], region: '开发环境', legalRepresentative: '开发联调',
contactName: '开发联调', phone: '13800000000', email: 'matson-dev-fixture@example.com',
address: '开发环境稳定船司夹具', businessLicenseNo: 'DEV-CARRIER-MATSON', licenseFileId: null,
status: 'ACTIVE', settlementPeriod: 'MONTHLY_30', remark: '三四期开发环境HTTP验收稳定船司夹具',
@@ -45,6 +46,23 @@ async function ensureMatsonSupplier(context, records) {
return supplierId ? { id: supplierId, name: MATSON_SUPPLIER_NAME, status: 'ACTIVE' } : null
}
export function stableLoadedCartonMarks(items, loadingPlanId) {
const used = new Set()
return [...(Array.isArray(items) ? items : [])]
.sort((left, right) => id(left?.loadingPlanItemId).localeCompare(id(right?.loadingPlanItemId)))
.flatMap((item) => {
const count = Math.max(0, Number(item?.pickedPackageCount) || 0)
const seed = `${loadingPlanId}:${id(item?.loadingPlanItemId)}`
const labels = [...new Set((item?.boxCodes || [])
.map((value) => String(value || '').trim()).filter((value) => value && !used.has(value)))]
.sort((left, right) => createHash('sha256').update(`${seed}:${left}`).digest('hex')
.localeCompare(createHash('sha256').update(`${seed}:${right}`).digest('hex')))
.slice(0, count)
labels.forEach((value) => used.add(value))
return labels
})
}
async function request(context, method, path, body, headers = {}) {
try {
const response = await fetch(`${context.options.gateway}${path}`, {
@@ -158,14 +176,13 @@ export async function run(context) {
'/api/platform/v1/suppliers?tenantId=0&category=BOOKING&page=1&size=200')
const supplierPage = context.apiSuccess(MODULE, '读取订舱代理与船司供应商事实源', supplierResponse, '', OPTIONS)
const supplierRecords = Array.isArray(supplierPage?.records) ? supplierPage.records : []
const supplier = supplierRecords.find((value) => value?.id && value?.name && value?.status === 'ACTIVE'
&& value.name !== MATSON_SUPPLIER_NAME)
const supplier = await ensureMatsonSupplier(context, supplierRecords)
context.assert(MODULE, '存在可用于发布订舱的 ACTIVE 订舱代理', supplierResponse,
Boolean(supplier?.id && supplier?.name), `supplierId=${id(supplier?.id) || 'missing'}`, OPTIONS)
if (!supplier?.id) return
const shippingCarrier = await ensureMatsonSupplier(context, supplierRecords)
context.assert(MODULE, '订舱船司与订舱代理使用各自真实供应商事实', supplierResponse,
Boolean(shippingCarrier?.id && shippingCarrier?.name === MATSON_SUPPLIER_NAME),
const shippingCarrier = await ensureMatsonCarrierPartner(context, MODULE, OPTIONS)
context.assert(MODULE, '订舱船司与订舱代理使用各自真实主数据事实', supplierResponse,
Boolean(shippingCarrier?.id && shippingCarrier?.carrierCode === 'MATSON'),
`shippingCompanyId=${id(shippingCarrier?.id) || 'missing'}, bookingAgentId=${id(supplier.id)}`, OPTIONS)
if (!shippingCarrier?.id) return
context.createdResources.phase3CarrierSupplier = {
@@ -176,7 +193,7 @@ export async function run(context) {
warehouseId,
channelCode: initial?.summary?.channelCode,
billOfLadingNo: `P3BL-${suffix}`.slice(0, 128),
shippingCompanyId: id(shippingCarrier.id), shippingCompanyName: shippingCarrier.name, carrierCode: 'OTHER',
shippingCompanyId: id(shippingCarrier.id), shippingCompanyName: shippingCarrier.name, carrierCode: 'MATSON',
bookingAgentId: id(supplier.id), bookingAgentName: supplier.name,
vesselName: `P3VESSEL-${suffix}`.slice(0, 128), voyageNo: `P3V-${suffix}`.slice(0, 64),
departurePortCode: 'CNSZX', departurePortName: '深圳', destinationPortCode: 'USLAX',
@@ -879,12 +896,8 @@ export async function run(context) {
`bytes=${download.bytes.length}, contentType=${download.contentType || 'missing'}`, OPTIONS)
if (evidenceType === 'PHOTO_STEP_4') {
const missingSeal = await request(context, 'POST', `${pdaBase}/tasks/${canonicalTaskId}/complete`,
{ expectedTaskVersion: Number(pickedCanonical?.version),
operationId: `${context.marker}-complete-before-seal`.slice(0, 128), remark: 'must require seal' },
{ 'Idempotency-Key': `${context.marker}-p3-complete-before-seal`.slice(0, 128) })
context.addStep(MODULE, '缺铅封照片和锁码时拒绝完成装柜', missingSeal, failure(missingSeal),
`HTTP=${missingSeal.http}, code=${codeOf(missingSeal)}`, OPTIONS)
context.addStep(MODULE, '缺铅封照片和锁码时不提前占用异步完成命令', null, true,
'complete 为持久异步命令;证据未齐场景只冻结现场事实,最终阶段再提交唯一完成命令', OPTIONS)
}
}
@@ -985,14 +998,8 @@ export async function run(context) {
duplicateVoid.http === 200 && codeOf(duplicateVoid) === 'A0409',
`HTTP=${duplicateVoid.http}, code=${codeOf(duplicateVoid)}`, OPTIONS)
const missingTypeComplete = await request(context, 'POST', `${pdaBase}/tasks/${canonicalTaskId}/complete`,
{ expectedTaskVersion: Number(pickedCanonical?.version),
operationId: `${context.marker}-complete-missing-photo-step-4`.slice(0, 128),
remark: `${context.marker}-missing-photo-step-4` },
{ 'Idempotency-Key': `${context.marker}-p3-complete-missing-photo-step-4`.slice(0, 128) })
context.addStep(MODULE, '作废唯一证据类型后 LOADED 门禁拒绝', missingTypeComplete,
missingTypeComplete.http === 200 && codeOf(missingTypeComplete) === 'A0409',
`HTTP=${missingTypeComplete.http}, code=${codeOf(missingTypeComplete)}`, OPTIONS)
context.addStep(MODULE, '作废唯一证据类型后不提前占用异步完成命令', null, true,
'PHOTO_STEP_4 已真实作废并回读;补传恢复后才允许最终阶段提交唯一完成命令', OPTIONS)
const replacementUpload = await uploadEvidence(context, tenantId, inboundId, 'PHOTO_STEP_4')
const replacementFile = context.apiSuccess(MODULE, '补传缺失 PHOTO_STEP_4 真实文件', replacementUpload, '', OPTIONS)
@@ -1354,8 +1361,7 @@ export async function run(context) {
context.createdResources.phase3Loading = {
tenantId, orderId: id(source?.orderId), inboundId, preloadPlanId: planId,
cartonMark: source?.boxCode || source?.cartonMark || null,
cartonMarks: [...new Set((stagedTask?.items || []).flatMap((item) => item?.boxCodes || [])
.map((value) => String(value || '').trim()).filter(Boolean))],
cartonMarks: stableLoadedCartonMarks(stagedTask?.items, loadingPlanId),
fulfillmentType: source?.fulfillmentType || 'STANDARD',
skuCodes: [...new Set([...(Array.isArray(source?.skuCodes) ? source.skuCodes : []),
...(Array.isArray(incrementalFixture?.skuCodes) ? incrementalFixture.skuCodes : [])])],