fix(lastleg): bind merged return inventory to reusable target pallet

This commit is contained in:
2026-09-20 18:54:40 +08:00
parent 417cbc6363
commit 8ada2ef673
2 changed files with 20 additions and 1 deletions
@@ -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);
@@ -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<String> 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