This commit is contained in:
2026-08-17 13:47:20 +08:00
parent 6739c83467
commit b914bb7532
6 changed files with 102 additions and 26 deletions
@@ -3,7 +3,8 @@ import { readFile } from 'node:fs/promises'
const MODULE = 'inbound-measurement'
const SCENARIO = 'mojia-carton-measurement'
const OPTIONS = { scenario: SCENARIO, deploymentBlocker: true }
const DEFAULT_PALLET_CAPACITY_CBM = 1.2
const WEIGHING_IMAGE_COUNT = 1
const FIXTURE_PALLET_CAPACITY_CBM = 1.2
function asId(value) { return value == null ? '' : String(value) }
function asNumber(value) { return Number(value) }
@@ -37,21 +38,21 @@ async function waitForCompletedMeasurement(context, measurementPath, attempts =
return { response, measurement, attempts }
}
async function provisionWarehouseResources(context, { inboundId, warehouseId, totalVolumeCbm }) {
const palletCount = Math.ceil(Number(totalVolumeCbm) / DEFAULT_PALLET_CAPACITY_CBM)
if (!warehouseId || !Number.isFinite(palletCount) || palletCount < 1) return false
async function provisionWarehouseResources(context, { inboundId, warehouseId, palletCount }) {
if (!warehouseId || !Number.isInteger(palletCount) || palletCount < 1) return null
const suffix = `${String(context.marker).replace(/\D/g, '').slice(-8)}${String(inboundId).slice(-6)}`
const palletResponse = await context.request('POST', '/api/platform/v1/pallets?tenantId=0', {
warehouseId, palletType: 'STANDARD', capacityCbm: FIXTURE_PALLET_CAPACITY_CBM,
weightKg: 8, color: '蓝色', remark: `${context.marker}-INBOUND-${inboundId}`,
})
const palletId = asId(context.apiSuccess(MODULE, '创建称重托盘主数据', palletResponse,
`warehouseId=${warehouseId}`, OPTIONS))
if (!palletId) return null
const resources = []
for (let index = 1; index <= palletCount; index += 1) {
const resourceSuffix = `${suffix}${String(index).padStart(2, '0')}`
const palletResponse = await context.request('POST', '/api/platform/v1/pallets?tenantId=0', {
warehouseId, palletType: 'STANDARD', capacityCbm: DEFAULT_PALLET_CAPACITY_CBM,
weightKg: 8, color: '蓝色', remark: `${context.marker}-INBOUND-${inboundId}`,
})
const palletId = asId(context.apiSuccess(MODULE, `创建测量占用托盘 ${index}/${palletCount}`,
palletResponse, `warehouseId=${warehouseId}`, OPTIONS))
let locationId = null
if (context.context?.phase3LoadingRequireSharedOverflow !== true) {
const locationResponse = await context.request('POST', '/api/platform/v1/locations?tenantId=0', {
@@ -63,16 +64,64 @@ async function provisionWarehouseResources(context, { inboundId, warehouseId, to
locationId = asId(context.apiSuccess(MODULE, `创建测量占用库位 ${index}/${palletCount}`,
locationResponse, `warehouseId=${warehouseId}`, OPTIONS))
}
if (!palletId || (context.context?.phase3LoadingRequireSharedOverflow !== true && !locationId)) return false
if (context.context?.phase3LoadingRequireSharedOverflow !== true && !locationId) return null
resources.push({ palletId, locationId })
}
context.createdResources.inboundWarehouseResources ??= {}
context.createdResources.inboundWarehouseResources[inboundId] = {
marker: context.marker, tenantId: '0', warehouseId: asId(warehouseId), palletCount,
palletCapacityCbm: DEFAULT_PALLET_CAPACITY_CBM, resources,
palletCapacityCbm: FIXTURE_PALLET_CAPACITY_CBM, resources,
}
return true
return { palletId, palletCount, resources }
}
async function createConfirmedWeightEvidence(context, { inboundId, tenantId, palletId }) {
const image = await readFile(new URL('../../../apps/pda-uniapp/static/c7.png', import.meta.url))
const uploadResponse = await context.requestMultipart('POST',
`/api/platform/v1/files/measurements/${tenantId}/${inboundId}`, {
content: image, type: 'image/png', name: `weight-${inboundId}.png`,
})
const upload = context.apiSuccess(MODULE, '上传称重图片并执行 OCR', uploadResponse,
`inboundId=${inboundId}`, OPTIONS)
const fileId = asId(upload?.fileId)
if (!fileId) return null
const createResponse = await context.request('POST',
`/api/platform/v1/first-leg/inbounds/${inboundId}/weight-results?${query(tenantId)}`, {
fileId, palletId, ocrProvider: 'ADMIN_ACCEPTANCE', ocrRawText: null,
ocrWeightValue: null, ocrWeightUnit: null, confidence: null,
})
const created = context.apiSuccess(MODULE, '登记称重图片与托盘快照', createResponse,
`fileId=${fileId}, palletId=${palletId}`, OPTIONS)
if (!created?.id) return null
const confirmResponse = await context.request('POST',
`/api/platform/v1/first-leg/inbounds/${inboundId}/weight-results/${created.id}/confirm?${query(tenantId)}`, {
version: Number(created.version), adjustedWeightValue: 100,
adjustedWeightUnit: 'KG', adjustmentReason: `${context.marker} 自动化人工确认称重`,
})
const confirmed = context.apiSuccess(MODULE, '人工确认称重图片净重', confirmResponse,
`resultId=${created.id}, palletId=${palletId}`, OPTIONS)
const netWeightKg = Number(confirmed?.netWeightKg)
if (confirmed?.recognitionStatus !== 'CONFIRMED' || !Number.isFinite(netWeightKg) || netWeightKg <= 0) {
return null
}
const workspaceResponse = await context.request('GET',
`/api/platform/v1/first-leg/inbounds/${inboundId}/measurement?${query(tenantId)}`)
const workspace = context.apiSuccess(MODULE, '回查已确认称重图片和货物总重量', workspaceResponse,
`resultId=${created.id}`, OPTIONS)
const confirmedRows = (workspace?.weightResults || [])
.filter((result) => result?.recognitionStatus === 'CONFIRMED')
const persisted = confirmedRows.length === WEIGHING_IMAGE_COUNT
&& asId(confirmedRows[0]?.id) === asId(created.id)
&& asId(confirmedRows[0]?.palletId) === palletId
&& closeTo(workspace?.actualGrossWeightKg, netWeightKg, 0.001)
context.assert(MODULE, '图片张数决定托数且净重汇总已落地', workspaceResponse, persisted,
`confirmedImages=${confirmedRows.length}, expectedPallets=${WEIGHING_IMAGE_COUNT}, netWeight=${netWeightKg}, actual=${workspace?.actualGrossWeightKg ?? 'missing'}`,
OPTIONS)
return persisted ? { count: confirmedRows.length, netWeightKg } : null
}
function findIsolatedInbound(context) {
@@ -364,17 +413,29 @@ export async function run(context) {
}
const warehouseResourcesReady = await provisionWarehouseResources(context, {
inboundId, warehouseId: asId(summary?.warehouseId), totalVolumeCbm: totalVolume,
inboundId, warehouseId: asId(summary?.warehouseId), palletCount: WEIGHING_IMAGE_COUNT,
})
context.assert(MODULE, '测量提交前已创建足量真实托盘和库位', draftResponse,
warehouseResourcesReady,
`warehouseId=${asId(summary?.warehouseId) || 'missing'}, palletCount=${Math.ceil(totalVolume / DEFAULT_PALLET_CAPACITY_CBM)}`,
context.assert(MODULE, '测量提交前按称重图片数准备真实库位', draftResponse,
Boolean(warehouseResourcesReady),
`warehouseId=${asId(summary?.warehouseId) || 'missing'}, palletCount=${WEIGHING_IMAGE_COUNT}`,
OPTIONS)
if (!warehouseResourcesReady) {
finishScenario(context, start)
return
}
const weightEvidence = await createConfirmedWeightEvidence(context, {
inboundId, tenantId, palletId: warehouseResourcesReady.palletId,
})
context.assert(MODULE, '测量提交前已通过正式接口确认称重图片', draftResponse,
Boolean(weightEvidence),
`confirmedImages=${weightEvidence?.count ?? 0}, expected=${WEIGHING_IMAGE_COUNT}`,
OPTIONS)
if (!weightEvidence) {
finishScenario(context, start)
return
}
const submitResponse = await context.request('POST',
`/api/platform/v1/first-leg/inbounds/${inboundId}/measurement/submit?${query(tenantId)}`,
{ measurementId: draft.measurementId, version: Number(draft.version) })
@@ -418,14 +479,16 @@ export async function run(context) {
finalMeasurement?.processStatus === 'WAIT_SPLIT' && finalMeasurement?.measureReviewStatus === 'APPROVED'
&& finalMeasurement?.measurementStatus === 'APPROVED'
&& Number(finalMeasurement?.actualPackageCount) === originalPackageCount
&& closeTo(finalMeasurement?.actualGrossWeightKg, weightEvidence.netWeightKg, 0.001)
&& closeTo(finalMeasurement?.actualVolumeCbm, totalVolume)
&& closeTo(finalMeasurement?.volumeWeightKg, volumeWeight),
`processStatus=${finalMeasurement?.processStatus ?? 'missing'}, measurementStatus=${finalMeasurement?.measurementStatus ?? 'missing'}, packages=${finalMeasurement?.actualPackageCount ?? 'missing'}`, OPTIONS)
`processStatus=${finalMeasurement?.processStatus ?? 'missing'}, measurementStatus=${finalMeasurement?.measurementStatus ?? 'missing'}, packages=${finalMeasurement?.actualPackageCount ?? 'missing'}, pallets=${weightEvidence.count}, cargoWeight=${finalMeasurement?.actualGrossWeightKg ?? 'missing'}`, OPTIONS)
context.createdResources.inboundMeasurement = {
marker: context.marker, tenantId, inboundId, measurementId: asId(draft.measurementId),
boxCode, finalStatus: finalMeasurement?.measurementStatus || null,
processStatus: finalMeasurement?.processStatus || null,
palletCount: weightEvidence.count, actualGrossWeightKg: weightEvidence.netWeightKg,
}
finishScenario(context, start)
}