diff --git a/firstleg/src/main/resources/openapi/firstleg-v1.yaml b/firstleg/src/main/resources/openapi/firstleg-v1.yaml index 322a8d38f..f84c6d7d7 100644 --- a/firstleg/src/main/resources/openapi/firstleg-v1.yaml +++ b/firstleg/src/main/resources/openapi/firstleg-v1.yaml @@ -773,7 +773,7 @@ paths: properties: code: {type: string} message: {type: string} - data: {$ref: '#/components/schemas/WeightPalletSnapshotPage'} + data: {$ref: '#/components/schemas/WeightPalletOptionPage'} '403': {description: 无入库测量查看或称重编辑权限} /api/platform/v1/first-leg/inbounds/{inboundId}/weight-results: post: @@ -1297,7 +1297,7 @@ components: palletType: {type: string} capacityCbm: {type: number, format: double, minimum: 0.0001} tareWeightKg: {type: number, format: double, minimum: 0, multipleOf: 0.001} - WeightPalletSnapshotPage: + WeightPalletOptionPage: type: object required: [records, page, size, total, pages] properties: diff --git a/firstleg/src/test/java/com/baiyue/firstleg/controller/PlatformInboundMeasurementControllerContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/controller/PlatformInboundMeasurementControllerContractTest.java index 67cf80c29..747a962f5 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/controller/PlatformInboundMeasurementControllerContractTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/controller/PlatformInboundMeasurementControllerContractTest.java @@ -30,7 +30,7 @@ class PlatformInboundMeasurementControllerContractTest { .contains("name: keyword") .contains("maximum: 100") .contains("maxItems: 100") - .contains("data: {$ref: '#/components/schemas/WeightPalletSnapshotPage'}"); + .contains("data: {$ref: '#/components/schemas/WeightPalletOptionPage'}"); } @Test diff --git a/scripts/admin-test-env-modules/inbound-measurement.mjs b/scripts/admin-test-env-modules/inbound-measurement.mjs index c6ebf4f85..f83dd6933 100644 --- a/scripts/admin-test-env-modules/inbound-measurement.mjs +++ b/scripts/admin-test-env-modules/inbound-measurement.mjs @@ -1,7 +1,8 @@ const MODULE = 'inbound-measurement' const SCENARIO = 'mojia-carton-measurement' const OPTIONS = { scenario: SCENARIO, deploymentBlocker: true } -const FIXTURE_PALLET_CAPACITY_CBM = 1.2 +const REUSABLE_PALLET_CODES = Object.freeze(['TP-001', 'YW-TP-002']) +const reusablePalletsByWarehouse = new Map() const WEIGHT_IMAGE = Uint8Array.from([137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, 0, 0, 0, 13, 73, 68, 65, 84, 8, 215, 99, 248, 207, 192, 240, 31, 0, 5, 0, 1, 255, 137, 153, 61, 29, 0, 0, 0, 0, @@ -27,6 +28,33 @@ function validPdf(response) { && response.bytes?.length > 4 && new TextDecoder().decode(response.bytes.slice(0, 4)) === '%PDF' } +async function reusablePallets(context, warehouseId) { + const cacheKey = asId(warehouseId) + if (reusablePalletsByWarehouse.has(cacheKey)) return reusablePalletsByWarehouse.get(cacheKey) + + const pallets = [] + for (const palletCode of REUSABLE_PALLET_CODES) { + const response = await context.request('GET', + `/api/platform/v1/pallets?page=1&size=20&tenantId=0&warehouseId=${encodeURIComponent(warehouseId)}&palletCode=${encodeURIComponent(palletCode)}`) + const page = context.apiSuccess(MODULE, `查询原有可复用托盘 ${palletCode}`, response, + `warehouseId=${warehouseId}`, OPTIONS) + const exact = (page?.records || []).find((item) => asId(item?.warehouseId) === cacheKey + && String(item?.palletCode || '') === palletCode && asId(item?.id)) + context.assert(MODULE, `原有托盘 ${palletCode} 属于当前仓库且可复用`, response, Boolean(exact), + `warehouseId=${warehouseId}, matched=${exact?.palletCode || 'missing'}`, OPTIONS) + if (!exact) return null + pallets.push({ id: asId(exact.id), code: palletCode }) + } + reusablePalletsByWarehouse.set(cacheKey, pallets) + return pallets +} + +function randomlyReusePalletIds(pallets, palletCount, randomValue) { + const seed = Number.isInteger(Number(randomValue)) ? Number(randomValue) : Date.now() + return Array.from({ length: palletCount }, (_, index) => + pallets[(seed >>> (index % 31)) % pallets.length].id) +} + async function waitForCompletedMeasurement(context, measurementPath, attempts = 30) { let response = null let measurement = null @@ -45,16 +73,15 @@ async function provisionWarehouseResources(context, { inboundId, warehouseId, pa const suffix = `${String(context.marker).replace(/\D/g, '').slice(-8)}${String(inboundId).slice(-6)}` const resources = [] - const palletIds = [] + const candidates = await reusablePallets(context, warehouseId) + if (!candidates) return null + const palletIds = randomlyReusePalletIds(candidates, palletCount, + context.context?.waitSplitVariant?.randomValue) for (let index = 1; index <= palletCount; index += 1) { - 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}-PALLET-${index}`, - }) - const palletId = asId(context.apiSuccess(MODULE, `创建称重托盘主数据 ${index}/${palletCount}`, - palletResponse, `warehouseId=${warehouseId}`, OPTIONS)) - if (!palletId) return null - palletIds.push(palletId) + const palletId = palletIds[index - 1] + context.assert(MODULE, `随机复用原有称重托盘 ${index}/${palletCount}`, null, + REUSABLE_PALLET_CODES.includes(candidates.find((item) => item.id === palletId)?.code), + `warehouseId=${warehouseId}, palletId=${palletId}`, OPTIONS) const resourceSuffix = `${suffix}${String(index).padStart(2, '0')}` let locationId = null if (context.context?.phase3LoadingRequireSharedOverflow !== true) { @@ -74,7 +101,7 @@ async function provisionWarehouseResources(context, { inboundId, warehouseId, pa context.createdResources.inboundWarehouseResources ??= {} context.createdResources.inboundWarehouseResources[inboundId] = { marker: context.marker, tenantId: '0', warehouseId: asId(warehouseId), palletCount, - palletCapacityCbm: FIXTURE_PALLET_CAPACITY_CBM, resources, + reusablePalletCodes: REUSABLE_PALLET_CODES, resources, } return { palletId: palletIds[0], palletIds, palletCount, resources } } diff --git a/scripts/admin-test-env-modules/it-test-wait-split-bulk-contract.test.mjs b/scripts/admin-test-env-modules/it-test-wait-split-bulk-contract.test.mjs index 608494f7f..56f67e3fe 100644 --- a/scripts/admin-test-env-modules/it-test-wait-split-bulk-contract.test.mjs +++ b/scripts/admin-test-env-modules/it-test-wait-split-bulk-contract.test.mjs @@ -26,6 +26,15 @@ test('随机线路只固定义乌仓并从客户真实可计价候选选择', () assert.doesNotMatch(runner, /channel-name|channel-code/) }) +test('称重只随机复用义乌仓原有两个托盘且禁止按订单新增', () => { + assert.match(measurement, /REUSABLE_PALLET_CODES = Object\.freeze\(\['TP-001', 'YW-TP-002'\]\)/) + assert.match(measurement, /context\.request\('GET',[\s\S]*\/api\/platform\/v1\/pallets\?page=1&size=20/) + assert.match(measurement, /randomlyReusePalletIds\(candidates, palletCount/) + assert.match(measurement, /随机复用原有称重托盘/) + assert.doesNotMatch(measurement, /context\.request\('POST', '\/api\/platform\/v1\/pallets/) + assert.doesNotMatch(measurement, /创建称重托盘主数据/) +}) + test('八千票执行器逐票记录且失败后继续', () => { assert.match(runner, /positiveInteger\(values\.get\('count'\), 8000/) assert.match(runner, /positiveInteger\(values\.get\('box-max'\), 300/)