From 62407610cbafe0ca029c7663f0ef62f8b6596bf4 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Tue, 25 Aug 2026 17:54:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapper/PdaPalletOperationMapper.java | 2 +- .../mapper/PdaStocktakeItemMapper.java | 2 +- ...PalletOperationControllerContractTest.java | 10 ++++ .../PdaInventoryOperationContractTest.java | 9 ++++ .../phase4-overseas-pda-contract.test.mjs | 8 ++- .../phase4-overseas-pda.mjs | 51 +++++++++++++++---- 6 files changed, 68 insertions(+), 14 deletions(-) diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/palletops/mapper/PdaPalletOperationMapper.java b/lastleg-service/src/main/java/com/baiyue/lastleg/palletops/mapper/PdaPalletOperationMapper.java index 91be448d6..f28f4c05a 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/palletops/mapper/PdaPalletOperationMapper.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/palletops/mapper/PdaPalletOperationMapper.java @@ -24,7 +24,7 @@ public interface PdaPalletOperationMapper extends BaseMapper @Param("idempotencyKey") String idempotencyKey); @Select(CONTENT_SELECT + "WHERE b.overseas_warehouse_id=#{warehouseId} AND p.pallet_id=#{palletId} " + - "AND (p.available_packages+p.hold_packages+p.allocated_packages+p.picked_packages+p.receiving_packages)>0 " + + "AND (p.available_packages+p.hold_packages+p.allocated_packages+p.picked_packages+p.receiving_packages)>0 " + "ORDER BY b.customer_id,b.inbound_code,p.id") List content(@Param("warehouseId") String warehouseId, @Param("palletId") long palletId); diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/pda/stocktake/mapper/PdaStocktakeItemMapper.java b/lastleg-service/src/main/java/com/baiyue/lastleg/pda/stocktake/mapper/PdaStocktakeItemMapper.java index e74e61729..4347c6a68 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/pda/stocktake/mapper/PdaStocktakeItemMapper.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/pda/stocktake/mapper/PdaStocktakeItemMapper.java @@ -55,7 +55,7 @@ public interface PdaStocktakeItemMapper extends BaseMapper { int counted(@Param("taskId") long taskId); @Select("SELECT COUNT(*) FROM ll_pda_stocktake_item WHERE task_id=#{taskId} " + - "AND actual_packages IS NOT NULL AND difference_packages<>0") + "AND actual_packages IS NOT NULL AND difference_packages<>0") int differences(@Param("taskId") long taskId); @Select("SELECT id item_id,customer_id,balance_id,position_id,sku_code,product_name,inbound_code," + diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/palletops/controller/PdaPalletOperationControllerContractTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/palletops/controller/PdaPalletOperationControllerContractTest.java index 1f728cc1d..fc8416556 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/palletops/controller/PdaPalletOperationControllerContractTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/palletops/controller/PdaPalletOperationControllerContractTest.java @@ -1,5 +1,7 @@ package com.baiyue.lastleg.palletops.controller; +import com.baiyue.lastleg.palletops.mapper.PdaPalletOperationMapper; +import org.apache.ibatis.annotations.Select; import org.junit.jupiter.api.Test; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PostMapping; @@ -24,4 +26,12 @@ class PdaPalletOperationControllerContractTest { .flatMap(annotation -> Arrays.stream(annotation.value())).collect(Collectors.toSet()); assertEquals(Set.of("/pallets/resolve", "/assemble", "/disassemble", "/swap", "/unbind"), paths); } + + @Test void palletContentAnnotationUsesNativeGreaterThanOperator() throws Exception { + String sql = String.join("", PdaPalletOperationMapper.class.getMethod("content", String.class, long.class) + .getAnnotation(Select.class).value()); + assertTrue(sql.contains("receiving_packages)>0")); + assertFalse(sql.contains("<")); + assertFalse(sql.contains(">")); + } } diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/pda/PdaInventoryOperationContractTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/pda/PdaInventoryOperationContractTest.java index eaef73a37..b67a31d65 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/pda/PdaInventoryOperationContractTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/pda/PdaInventoryOperationContractTest.java @@ -4,6 +4,8 @@ import com.baiyue.lastleg.pda.move.controller.PdaInventoryMoveController; import com.baiyue.lastleg.pda.move.dto.PdaInventoryMoveRequest; import com.baiyue.lastleg.pda.stocktake.controller.PdaStocktakeController; import com.baiyue.lastleg.pda.stocktake.dto.PdaStocktakeRequests; +import com.baiyue.lastleg.pda.stocktake.mapper.PdaStocktakeItemMapper; +import org.apache.ibatis.annotations.Select; import org.junit.jupiter.api.Test; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.RequestMapping; @@ -38,4 +40,11 @@ class PdaInventoryOperationContractTest { assertThat(fields).doesNotContainAnyElementsOf(forbidden); } } + + @Test + void stocktakeAnnotationSqlUsesNativeComparisonOperators() throws Exception { + String sql = String.join("", PdaStocktakeItemMapper.class.getMethod("differences", long.class) + .getAnnotation(Select.class).value()); + assertThat(sql).contains("difference_packages<>0").doesNotContain("<", ">"); + } } diff --git a/scripts/admin-test-env-modules/phase4-overseas-pda-contract.test.mjs b/scripts/admin-test-env-modules/phase4-overseas-pda-contract.test.mjs index 2f54f9709..d59239abe 100644 --- a/scripts/admin-test-env-modules/phase4-overseas-pda-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase4-overseas-pda-contract.test.mjs @@ -54,7 +54,13 @@ test('PDA module covers the container-number receiving contract and excludes out assert.match(source, /目的仓 .* 不得代替物理仓/) assert.match(source, /retained as acceptance evidence/) assert.match(source, /preferredProducer\?\.cartonMarks\?\.length/) - assert.match(source, /for \(const \[index, cartonMark\] of cartonMarks\.entries\(\)\)/) + assert.match(source, /const cartonCandidates =/) + assert.match(source, /candidateExcluded = notFound\(response\)/) + assert.match(source, /if \(candidateExcluded\) continue/) + assert.match(source, /const fixturesConserved = cartonFixtures\.length === expectedPending/) + assert.match(source, /OVERSEAS实装箱唛与待收箱数守恒/) + assert.match(source, /for \(const \[index, fixture\] of cartonFixtures\.entries\(\)\)/) + assert.doesNotMatch(source, /for \(const \[index, cartonMark\] of cartonMarks\.entries\(\)\)/) assert.match(source, /last-leg\/orders\?page=1&size=200&containerNo=/) assert.match(source, /phase4OverseasPda\.orderId = id\(order\.id\)/) assert.match(source, /phase4OverseasPda\.customerId = id\(order\.customerId\)/) diff --git a/scripts/admin-test-env-modules/phase4-overseas-pda.mjs b/scripts/admin-test-env-modules/phase4-overseas-pda.mjs index e04e67ca4..2fd9bf5c8 100644 --- a/scripts/admin-test-env-modules/phase4-overseas-pda.mjs +++ b/scripts/admin-test-env-modules/phase4-overseas-pda.mjs @@ -338,23 +338,52 @@ async function receiveFba(context, token, inbound, pallet, producer) { } async function receiveOverseas(context, token, inbounds, pallets, producerCartonMarks = []) { - const cartonMarks = [...new Set((Array.isArray(producerCartonMarks) ? producerCartonMarks : [producerCartonMarks]) + const cartonCandidates = [...new Set((Array.isArray(producerCartonMarks) ? producerCartonMarks : [producerCartonMarks]) .map(id).filter(nonBlank))] - if (!cartonMarks.length) { + if (!cartonCandidates.length) { blockerGap(context, 'OVERSEAS逐箱链', '缺少本轮PDA任务冻结箱唛,逐箱实收不能使用伪造箱号') return false } - for (const [index, cartonMark] of cartonMarks.entries()) { - const resolved = await pda(context, token, 'POST', `解析OVERSEAS冻结箱唛${index + 1}`, - '/api/pda/v1/last-leg/receiving-containers/current/cartons/resolve', { cartonMark }) - const inbound = inbounds.find((item) => item.inboundCode === resolved.data?.inboundCode) - const pallet = inbound ? pallets.get(inbound.inboundCode) : null - if (!success(resolved.response) || !inbound || !pallet) { - blockerGap(context, `OVERSEAS箱唛${cartonMark}归属`, - `冻结箱唛未解析到带真实托盘的本柜入仓码;inboundCode=${resolved.data?.inboundCode || 'missing'}`, - 'fixture-mismatch', resolved.response) + const expectedPending = inbounds.reduce((total, inbound) => total + Number(inbound.pendingPackages || 0), 0) + const cartonFixtures = [] + let lastResponse = null + for (const [index, cartonMark] of cartonCandidates.entries()) { + const response = await context.requestOverseas('POST', + '/api/pda/v1/last-leg/receiving-containers/current/cartons/resolve', { cartonMark }, {}, token) + lastResponse = response + const candidateExcluded = notFound(response) + context.addStep(MODULE, `筛选OVERSEAS实装箱唛候选${index + 1}`, response, + success(response) || candidateExcluded, + `${candidateExcluded ? 'candidateSelected=false' : 'candidateSelected=true'}; ${trace(response)}`, OPTIONS) + if (candidateExcluded) continue + if (!success(response)) { + blockerGap(context, `OVERSEAS箱唛候选${cartonMark}预检`, + '候选箱唛既非本柜冻结事实,也不是未被最终实装选中的A0404;禁止继续收货', + 'fixture-mismatch', response) return false } + const resolved = response.body?.data + const inbound = inbounds.find((item) => item.inboundCode === resolved?.inboundCode) + const pallet = inbound ? pallets.get(inbound.inboundCode) : null + if (!inbound || !pallet) { + blockerGap(context, `OVERSEAS箱唛${cartonMark}归属`, + `冻结箱唛未解析到带真实托盘的本柜入仓码;inboundCode=${resolved?.inboundCode || 'missing'}`, + 'fixture-mismatch', response) + return false + } + cartonFixtures.push({ cartonMark, pallet }) + } + const fixturesConserved = cartonFixtures.length === expectedPending + context.assert(MODULE, 'OVERSEAS实装箱唛与待收箱数守恒', lastResponse, fixturesConserved, + `resolvedFixtures=${cartonFixtures.length}, pendingPackages=${expectedPending}; ${trace(lastResponse)}`, OPTIONS) + if (!fixturesConserved) { + blockerGap(context, 'OVERSEAS逐箱冻结事实', + `候选池未完整覆盖本柜真实冻结箱唛;resolvedFixtures=${cartonFixtures.length}, pendingPackages=${expectedPending}`, + 'fixture-mismatch', lastResponse) + return false + } + for (const [index, fixture] of cartonFixtures.entries()) { + const { cartonMark, pallet } = fixture const body = { cartonMark, palletCode: pallet.palletCode, idempotencyKey: key(context, `overseas-carton-${index}`) } const first = await pda(context, token, 'POST', `OVERSEAS逐箱实收${index + 1}`,