This commit is contained in:
2026-10-08 19:09:30 +08:00
parent 2c0f55d9b7
commit ed99ecba1c
17 changed files with 157 additions and 106 deletions
@@ -501,8 +501,6 @@ public class ReturnReceiptService {
public PdaReturnReceiptResponses.Confirmed confirm(PdaWarehouseIdentity identity, PdaReturnReceiptRequests.Confirm request) {
String key = text(request.idempotencyKey());
String requestHash = hash(request);
ReturnReceipt replay = receipts.findKey(identity.operatorKey(), key);
if (replay != null) return replayReceipt(replay, requestHash);
String startedBy = norm(request.startedBy());
if (!Set.of("RETURN_NO", "SKU").contains(startedBy)) throw badRequest("startedBy 必须为 RETURN_NO 或 SKU");
String trackingNo = text(request.trackingNo());
@@ -513,8 +511,8 @@ public class ReturnReceiptService {
if (order == null || !String.valueOf(identity.overseasWarehouseId()).equals(order.getOverseasWarehouseId())) {
throw notFound("当前绑定仓没有该退货单");
}
// Waiting for the order lock may allow the same request to commit. Use a
// current read, not the repeatable-read snapshot established by findKey.
// Lock the order before any consistent read: a snapshot opened before
// waiting would hide the winner's cartons when rendering replay labels.
ReturnReceipt concurrentReplay = receipts.lockKey(identity.operatorKey(), key);
if (concurrentReplay != null) {
if (!requestHash.equals(concurrentReplay.getRequestHash())) throw conflict("幂等键已用于不同的签收请求");
@@ -366,8 +366,7 @@ class ReturnReceiptServiceTest {
var first = service.confirm(identity, request);
ArgumentCaptor<ReturnReceipt> savedReceipt = ArgumentCaptor.forClass(ReturnReceipt.class);
verify(receipts).insert(savedReceipt.capture());
when(receipts.findKey("operator-1", "key-final")).thenReturn(savedReceipt.getValue());
when(orders.selectById(1L)).thenReturn(order);
when(receipts.lockKey("operator-1", "key-final")).thenReturn(savedReceipt.getValue());
var replay = service.confirm(identity, request);
assertTrue(first.fullyMatched());
@@ -383,7 +382,6 @@ class ReturnReceiptServiceTest {
void rejectsOverReceiptBeforeCreatingReceiptInventoryOrBatch() {
ReturnOrder order = order("RT-1", "10", ReturnReceiptService.PARTIALLY_RECEIVED, 2, 1);
ReturnOrderItem line = item(11L, "SKU-A", 2, 1);
when(receipts.findKey("operator-1", "key-over")).thenReturn(null);
when(orders.lockNo("RT-1")).thenReturn(order);
when(orderItems.lockOrder(1L)).thenReturn(List.of(line));
@@ -398,7 +396,6 @@ class ReturnReceiptServiceTest {
@Test
void rejectsAnotherWarehousesOrderBeforeLockingSkuOrWritingInventory() {
ReturnOrder order = order("RT-1", "11", ReturnReceiptService.PENDING_RECEIPT, 2, 0);
when(receipts.findKey("operator-1", "key-cross")).thenReturn(null);
when(orders.lockNo("RT-1")).thenReturn(order);
ResponseStatusException error = assertThrows(ResponseStatusException.class, () -> service.confirm(identity,
@@ -419,7 +416,7 @@ class ReturnReceiptServiceTest {
service.confirm(identity, original);
ArgumentCaptor<ReturnReceipt> savedReceipt = ArgumentCaptor.forClass(ReturnReceipt.class);
verify(receipts).insert(savedReceipt.capture());
when(receipts.findKey("operator-1", "key-1")).thenReturn(savedReceipt.getValue());
when(receipts.lockKey("operator-1", "key-1")).thenReturn(savedReceipt.getValue());
ResponseStatusException error = assertThrows(ResponseStatusException.class, () -> service.confirm(identity,
confirm("RETURN_NO", "TRACK-CHANGED", "RC-1", "key-1",
@@ -431,7 +428,6 @@ class ReturnReceiptServiceTest {
}
private void stubNewReceipt(ReturnOrder order, List<ReturnOrderItem> items, String cartonMark) {
when(receipts.findKey(eq("operator-1"), anyString())).thenReturn(null);
when(orders.lockNo("RT-1")).thenReturn(order);
when(orderItems.lockOrder(1L)).thenReturn(items);
when(orderItems.listOrder(1L)).thenReturn(items);
@@ -455,6 +451,10 @@ class ReturnReceiptServiceTest {
var result = service.confirm(identity, request);
verify(receipts, never()).findKey(anyString(), anyString());
var reads = org.mockito.Mockito.inOrder(orders, receipts);
reads.verify(orders).lockNo("RT-1");
reads.verify(receipts).lockKey("operator-1", "concurrent-key");
assertEquals("RR-COMMITTED", result.receiptNo());
assertEquals("RB-COMMITTED", result.receiptBatchNo());
assertEquals(1, result.receivedQuantity());