diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/pda/service/DispatchPdaWorkflowService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/pda/service/DispatchPdaWorkflowService.java index 30fc798ed..2ecbd0cbb 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/pda/service/DispatchPdaWorkflowService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/pda/service/DispatchPdaWorkflowService.java @@ -77,22 +77,29 @@ public class DispatchPdaWorkflowService { DispatchPdaRequests.Pick request) { DispatchOrder order = requireWarehouseOrder(warehouseId, dispatchNo); String customerId = order.getCustomerId(); + DispatchPdaTask task = tasks.lockByDispatch(customerId, order.getId()); + if (task == null) throw conflict("Dispatch PDA task is not available for picking"); + if (!warehouseId.equals(task.getWarehouseId())) throw conflict("PDA pick warehouse does not match task warehouse"); + DispatchPdaOperation replay = replay(customerId, "PICK", request.idempotencyKey(), request); + if (replay != null) { + DispatchPdaResponses.Picked result = read(replay, DispatchPdaResponses.Picked.class); + if (!task.getId().equals(replay.getTaskId()) || result.dispatchItemId() != itemId) { + throw conflict("Idempotency key was reused with a different dispatch pick request"); + } + return result; + } requirePdaStatus(order); requireVehicle(order); items.bindReceivedPallets(customerId, order.getId()); - DispatchPdaTask task = tasks.lockByDispatch(customerId, order.getId()); - if (task == null || !("PDA_PENDING".equals(task.getStatus()) || "PICKING".equals(task.getStatus()))) { + if (!("PDA_PENDING".equals(task.getStatus()) || "PICKING".equals(task.getStatus()))) { throw conflict("Dispatch PDA task is not available for picking"); } - if (!warehouseId.equals(task.getWarehouseId())) throw conflict("PDA pick warehouse does not match task warehouse"); bindOperator(task, operatorId, operatorName); DispatchItem item = items.selectById(itemId); if (item == null || !customerId.equals(item.getCustomerId()) || !order.getId().equals(item.getDispatchOrderId())) { throw notFound("Dispatch pallet item not found"); } if (!warehouseId.equals(item.getOverseasWarehouseId())) throw conflict("Pallet does not belong to selected warehouse"); - DispatchPdaOperation replay = replay(customerId, "PICK", request.idempotencyKey(), request); - if (replay != null) return read(replay, DispatchPdaResponses.Picked.class); InventoryPosition position = positions.selectById(request.positionId()); if (position == null || !customerId.equals(position.getCustomerId()) || item.getWarehousePalletId() == null || !item.getWarehousePalletId().equals(position.getPalletId())) { diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/pda/service/DispatchPdaWorkflowServiceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/pda/service/DispatchPdaWorkflowServiceTest.java index fb7e76694..7341c6269 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/pda/service/DispatchPdaWorkflowServiceTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/pda/service/DispatchPdaWorkflowServiceTest.java @@ -108,18 +108,16 @@ class DispatchPdaWorkflowServiceTest { } @Test - void exactPickRetryReturnsPersistedResultWithoutInventoryDeduction() throws Exception { - DispatchOrder order = order("100", "DSP-1", "WH-1", "PICKING"); + void exactPickRetryReturnsPersistedResultAfterTaskAutomaticallyCompleted() throws Exception { + DispatchOrder order = order("100", "DSP-1", "WH-1", "READY_TO_DEPART"); order.setVehiclePlate("CA-100"); - DispatchPdaTask task = task(11L, "100", "WH-1", "PICKING"); + DispatchPdaTask task = task(11L, "100", "WH-1", "COMPLETED"); task.setOperatorId("7"); - DispatchItem item = item(21L, "100", 5); DispatchPdaRequests.Pick request = new DispatchPdaRequests.Pick(31L, 2, 2, "pick-1", null); DispatchPdaResponses.Picked persisted = new DispatchPdaResponses.Picked(21L, 31L, 2, 2, false, false); DispatchPdaOperation replay = operation("PICK", request, persisted); when(orders.findGlobalByDispatchNo("DSP-1")).thenReturn(order); when(tasks.lockByDispatch("100", 1L)).thenReturn(task); - when(items.selectById(21L)).thenReturn(item); when(operations.findKey("100", "pick-1")).thenReturn(replay); DispatchPdaResponses.Picked result = service.pick("WH-1", "DSP-1", 21L, "7", "Picker", request); @@ -127,6 +125,7 @@ class DispatchPdaWorkflowServiceTest { assertEquals(2, result.packages()); verify(inventory, never()).allocate(any(), any(), any()); verify(picks, never()).insert((DispatchPdaPick) any()); + verify(items, never()).bindReceivedPallets(any(), any()); } @Test @@ -207,7 +206,7 @@ class DispatchPdaWorkflowServiceTest { private DispatchPdaOperation operation(String type, Object request, Object result) throws Exception { DispatchPdaOperation value = new DispatchPdaOperation(); - value.setOperationType(type); value.setStatus("COMPLETED"); + value.setTaskId(11L); value.setOperationType(type); value.setStatus("COMPLETED"); value.setRequestHash(HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256") .digest(objectMapper.writeValueAsString(request).getBytes(StandardCharsets.UTF_8)))); value.setResultJson(objectMapper.writeValueAsString(result));