From ef9f253f479762343d89c13b18beb7c9e924baf2 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Mon, 24 Aug 2026 15:25:34 +0800 Subject: [PATCH] feat(lastleg): expose inbound PDA work instructions --- .../InboundPalletAssignmentController.java | 9 ++++ .../inboundplan/pda/dto/PdaResponses.java | 14 ++++++- .../service/InboundPdaWorkflowService.java | 23 +++++++++- ...InboundPalletAssignmentControllerTest.java | 42 +++++++++++++++++++ .../InboundPdaWorkflowServiceTest.java | 17 +++++++- 5 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/controller/InboundPalletAssignmentControllerTest.java diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/controller/InboundPalletAssignmentController.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/controller/InboundPalletAssignmentController.java index eb33d0f29..387334dae 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/controller/InboundPalletAssignmentController.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/controller/InboundPalletAssignmentController.java @@ -26,6 +26,7 @@ public class InboundPalletAssignmentController { @PathVariable String planNo, @PathVariable long planPalletId, @RequestParam(required = false) String customerId, @Valid @RequestBody PdaRequests.Assign request) { + requireOperate(user); service.assign(scope(user, customerId), planNo, planPalletId, request); return ApiResponse.success(null); } @@ -40,4 +41,12 @@ public class InboundPalletAssignmentController { } return String.valueOf(user.tenantId()); } + + private void requireOperate(AuthenticatedUser user) { + if (user == null || user.tenantId() == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + if (user.permissions() == null || !user.permissions().contains(InboundPdaController.OPERATE_PERMISSION)) { + throw new ResponseStatusException(HttpStatus.FORBIDDEN, + InboundPdaController.OPERATE_PERMISSION + " permission is required"); + } + } } diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/dto/PdaResponses.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/dto/PdaResponses.java index fa261c613..4abcdd0fd 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/dto/PdaResponses.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/dto/PdaResponses.java @@ -10,7 +10,19 @@ public final class PdaResponses { public record Task(long taskId, String taskNo, String planNo, String status, LocalDateTime pushedAt, LocalDateTime startedAt, LocalDateTime completedAt, List unloadPhotoFileIds, LocalDateTime unloadPhotoUploadedAt, - LocalDateTime estimatedEmptyReturnAt) {} + LocalDateTime estimatedEmptyReturnAt, + List pallets, List allocations) {} + + public record Pallet(long planPalletId, String palletNo, String destinationWarehouseId, + String destinationWarehouseCode, String destinationWarehouseName, + long warehousePalletId, long warehouseLocationId, + String assignmentStatus, int actualPackageCount) {} + + public record Allocation(long allocationId, long lineId, long planPalletId, + long orderId, String inboundCode, String fbaWarehouseCode, + int requiredPackageCount, int plannedPackageCount, + int countedPackageCount, int pickedPackageCount, + String status, int version) {} public record UnloadPhotoFile(long fileId, String fileName, String contentType, long sizeBytes) {} diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowService.java index 4c8b09a2a..9a8a4daec 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowService.java @@ -629,9 +629,30 @@ public class InboundPdaWorkflowService { } private PdaResponses.Task task(InboundPdaTask task, String planNo) { + List planPallets = repository.pallets(task.getPlanId()); + List planAllocations = repository.allocations(task.getPlanId()); + Map lines = repository.lines(task.getPlanId()).stream() + .collect(java.util.stream.Collectors.toMap(InboundPlanLine::getId, value -> value)); + List pallets = planPallets.stream().map(value -> new PdaResponses.Pallet( + value.getId(), value.getPalletNo(), value.getDestinationWarehouseId(), + value.getDestinationWarehouseCode(), value.getDestinationWarehouseName(), + Objects.requireNonNullElse(value.getWarehousePalletId(), 0L), + Objects.requireNonNullElse(value.getWarehouseLocationId(), 0L), + value.getAssignmentStatus(), InboundPdaWorkflowService.value(value.getActualPackageCount()))).toList(); + List allocations = planAllocations.stream().map(value -> { + InboundPlanLine line = lines.get(value.getLineId()); + if (line == null) throw conflict("Inbound allocation line is missing"); + return new PdaResponses.Allocation(value.getId(), value.getLineId(), value.getPalletId(), + line.getOrderId(), line.getInboundCode(), line.getFbaWarehouseCode(), + InboundPdaWorkflowService.value(line.getRequiredPackageCount()), + InboundPdaWorkflowService.value(value.getPlannedPackageCount()), + InboundPdaWorkflowService.value(value.getCountedPackageCount()), + InboundPdaWorkflowService.value(value.getPickedPackageCount()), value.getStatus(), + InboundPdaWorkflowService.value(value.getVersion())); + }).toList(); return new PdaResponses.Task(task.getId(), task.getTaskNo(), planNo, task.getStatus(), task.getPushedAt(), task.getStartedAt(), task.getCompletedAt(), unloadPhotoFileIds(task), - task.getUnloadPhotoUploadedAt(), task.getEstimatedEmptyReturnAt()); + task.getUnloadPhotoUploadedAt(), task.getEstimatedEmptyReturnAt(), pallets, allocations); } private List unloadPhotoFileIds(InboundPdaTask task) { diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/controller/InboundPalletAssignmentControllerTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/controller/InboundPalletAssignmentControllerTest.java new file mode 100644 index 000000000..b00867007 --- /dev/null +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/controller/InboundPalletAssignmentControllerTest.java @@ -0,0 +1,42 @@ +package com.baiyue.lastleg.inboundplan.pda.controller; + +import com.baiyue.common.security.AuthenticatedUser; +import com.baiyue.lastleg.inboundplan.pda.dto.PdaRequests; +import com.baiyue.lastleg.inboundplan.pda.service.InboundPdaWorkflowService; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.server.ResponseStatusException; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +class InboundPalletAssignmentControllerTest { + private final InboundPdaWorkflowService service = mock(InboundPdaWorkflowService.class); + private final InboundPalletAssignmentController controller = controller(); + private final PdaRequests.Assign request = new PdaRequests.Assign(1L, 2L, 3L, "assign-key"); + + @Test + void assignmentRequiresPdaOperatePermissionAndExplicitPlatformCustomer() { + assertThrows(ResponseStatusException.class, + () -> controller.assign(user(Set.of()), "PLAN-1", 4L, "100", request)); + assertThrows(ResponseStatusException.class, + () -> controller.assign(user(Set.of(InboundPdaController.OPERATE_PERMISSION)), "PLAN-1", 4L, null, request)); + + controller.assign(user(Set.of(InboundPdaController.OPERATE_PERMISSION)), "PLAN-1", 4L, "100", request); + + verify(service).assign("100", "PLAN-1", 4L, request); + } + + private AuthenticatedUser user(Set permissions) { + return new AuthenticatedUser(7L, 0L, 0L, 8L, "operator", "PLATFORM", "PDA", permissions); + } + + private InboundPalletAssignmentController controller() { + InboundPalletAssignmentController value = new InboundPalletAssignmentController(); + ReflectionTestUtils.setField(value, "service", service); + return value; + } +} diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowServiceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowServiceTest.java index e1353af91..27e13436b 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowServiceTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/service/InboundPdaWorkflowServiceTest.java @@ -95,6 +95,21 @@ class InboundPdaWorkflowServiceTest { verify(repository, never()).insert(any(InboundPdaTask.class)); } + @Test + void taskReturnsPalletAndAllocationWorkInstructions() { + when(repository.task("100", plan.getId())).thenReturn(task); + when(repository.pallets(plan.getId())).thenReturn(List.of(targetPallet)); + when(repository.allocations(plan.getId())).thenReturn(List.of(target)); + when(repository.lines(plan.getId())).thenReturn(List.of(targetLine)); + + var result = service.task("100", "LIP1"); + + assertEquals(targetPallet.getId(), result.pallets().get(0).planPalletId()); + assertEquals(target.getId(), result.allocations().get(0).allocationId()); + assertEquals("RK-A", result.allocations().get(0).inboundCode()); + assertEquals(3, result.allocations().get(0).plannedPackageCount()); + } + @Test void assignmentReservesWarehouseResourcesAndPersistsTheLock() { targetPallet.setDestinationWarehouseId("88"); @@ -427,7 +442,7 @@ class InboundPdaWorkflowServiceTest { assertEquals("COMPLETED", result.status()); verify(stockReceipts, never()).stockIn(any(), any(), any(), any(), any(), any(), any()); - verify(repository, never()).allocations(plan.getId()); + verify(repository, never()).update(task); } @Test