From 8ada2ef67325166371234f62a5a5ee10cfa2432e Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Sun, 20 Sep 2026 18:54:40 +0800 Subject: [PATCH] fix(lastleg): bind merged return inventory to reusable target pallet --- .../service/ReturnInventoryService.java | 9 +++++++++ .../service/ReturnInventoryServiceTest.java | 12 +++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryService.java index d8b4bfa9f..62f63a88d 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryService.java @@ -58,6 +58,7 @@ public class ReturnInventoryService { @Resource private com.baiyue.lastleg.returnreceipt.mapper.ReturnReceiptMapper receiptMapper; @Resource private LastlegOrderMapper lastlegOrders; @Resource private LastlegOrderDetailMapper lastlegOrderDetails; + @Resource private com.baiyue.lastleg.palletops.gateway.PalletPhysicalGateway physicalPallets; @Transactional public ReturnInventoryCarton createReceiptCarton(ReturnOrder order, ReturnReceipt receipt, @@ -300,6 +301,14 @@ public class ReturnInventoryService { if (total <= 0) throw badRequest("至少填写一个大于 0 的 SKU 合箱数量"); long targetId = IdWorker.getId(); ReturnInventoryCarton target = new ReturnInventoryCarton(); + // Stable across transaction rollback/retry; a generated target ID would leak a new resource on every retry. + String palletBusinessKey = "RETURN-MERGE:" + hash(List.of(identity.operatorKey(), key, requestHash)); + var pallet = physicalPallets.ensureRelabelPallet(identity.overseasWarehouseId(), palletBusinessKey); + if (pallet == null || pallet.warehouseId() != identity.overseasWarehouseId() + || pallet.palletId() <= 0 || text(pallet.palletCode()) == null) { + throw conflict("合箱目标托盘资源无效,请核对后重试"); + } + target.setPalletCode(pallet.palletCode()); target.setId(targetId); target.setCustomerId(customerId); target.setOverseasWarehouseId(sources.get(0).getOverseasWarehouseId()); target.setCartonMark(numbers.next("RETURN_CARTON", target.getOverseasWarehouseId(), targetId)); target.setSourceBatchNo(String.join(",", consumedBatchNos)); target.setSourceReturnNo(String.join(",", consumedReturnNos)); target.setStatus(AVAILABLE); target.setSkuTypes(requested.size()); target.setTotalQuantity(total); target.setLength(request.length()); target.setWidth(request.width()); target.setHeight(request.height()); target.setWeight(request.weight()); target.setCreatedAt(now); target.setUpdatedAt(now); cartons.insert(target); diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryServiceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryServiceTest.java index e1adb845e..da1149160 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryServiceTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/returnreceipt/service/ReturnInventoryServiceTest.java @@ -50,6 +50,7 @@ class ReturnInventoryServiceTest { @Mock LastlegOrderNoGenerator numbers; @Mock ReturnOrderMapper orders; @Mock ReturnReceiptMapper receipts; + @Mock com.baiyue.lastleg.palletops.gateway.PalletPhysicalGateway physicalPallets; private ReturnInventoryService service; @BeforeEach @@ -64,6 +65,7 @@ class ReturnInventoryServiceTest { ReflectionTestUtils.setField(service, "numbers", numbers); ReflectionTestUtils.setField(service, "orders", orders); ReflectionTestUtils.setField(service, "receiptMapper", receipts); + ReflectionTestUtils.setField(service, "physicalPallets", physicalPallets); } @Test @@ -142,6 +144,8 @@ class ReturnInventoryServiceTest { when(cartons.lockMark("RC-SOURCE")).thenReturn(source); when(cartonItems.lockCarton(31L)).thenReturn(List.of(skuA, skuB)); when(numbers.next(anyString(), anyString(), anyLong())).thenReturn("RC-MERGED"); + when(physicalPallets.ensureRelabelPallet(org.mockito.ArgumentMatchers.eq(42L), anyString())) + .thenReturn(new com.baiyue.lastleg.palletops.gateway.PalletPhysicalGateway.PalletReference(91L, "PLT-MERGED", 42L, null, "EMPTY", null, 0)); when(balances.adjustCartonQuantity(anyString(), anyString(), anyString(), anyLong())).thenReturn(1); service.merge(new PdaWarehouseIdentity("operator-1", "操作员", 42L), @@ -158,6 +162,10 @@ class ReturnInventoryServiceTest { verify(cartons).insert(target.capture()); assertThat(target.getValue().getSourceBatchNo()).isEqualTo("RB-1"); assertThat(target.getValue().getSourceReturnNo()).isEqualTo("RT-1"); + assertThat(target.getValue().getPalletCode()).isEqualTo("PLT-MERGED"); + ArgumentCaptor businessKey = ArgumentCaptor.forClass(String.class); + verify(physicalPallets).ensureRelabelPallet(org.mockito.ArgumentMatchers.eq(42L), businessKey.capture()); + assertThat(businessKey.getValue()).matches("RETURN-MERGE:[0-9a-f]{64}"); } @Test @@ -190,6 +198,8 @@ class ReturnInventoryServiceTest { ReturnInventoryCarton locked = prepareConcurrentSource(); when(cartonItems.lockCarton(31L)).thenReturn(List.of(item(41L, "SKU-A", 2L))); when(numbers.next(anyString(), anyString(), anyLong())).thenReturn("RC-TARGET"); + when(physicalPallets.ensureRelabelPallet(org.mockito.ArgumentMatchers.eq(42L), anyString())) + .thenReturn(new com.baiyue.lastleg.palletops.gateway.PalletPhysicalGateway.PalletReference(92L, "PLT-TARGET", 42L, null, "EMPTY", null, 0)); when(balances.adjustCartonQuantity(anyString(), anyString(), anyString(), anyLong())).thenReturn(1); service.merge(new PdaWarehouseIdentity("operator-2", "操作员2", 42L), mergeRequest(2)); assertThat(locked.getTotalQuantity()).isZero(); @@ -224,7 +234,7 @@ class ReturnInventoryServiceTest { verify(cartons, never()).insert(any(ReturnInventoryCarton.class)); verify(cartonItems, never()).insert(any(ReturnInventoryCartonItem.class)); verify(operations, never()).updateById(any(ReturnInventoryOperation.class)); - verifyNoInteractions(orders, receipts, balances, numbers); + verifyNoInteractions(orders, receipts, balances, numbers, physicalPallets); } @Test