# Conflicts:
#	config/nacos/cn/event-bridge-service.yml
#	config/nacos/overseas/lastleg-service.yml
#	deploy/sql/头程数据库统一增量-MySQL8.sql
#	event-bridge-service/src/main/resources/application.yml
#	iam/src/main/resources/db/lastleg-permissions.sql
#	lastleg-service/AGENTS.md
#	lastleg-service/src/main/java/com/baiyue/lastleg/BaiyueLastlegApplication.java
#	lastleg-service/src/main/java/com/baiyue/lastleg/controller/LastlegOrderController.java
#	lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/controller/InboundPlanController.java
#	lastleg-service/src/main/java/com/baiyue/lastleg/inventory/command/controller/InventoryCommandController.java
#	lastleg-service/src/main/java/com/baiyue/lastleg/inventory/query/controller/InventoryQueryController.java
#	lastleg-service/src/main/java/com/baiyue/lastleg/procurement/controller/PlatformOverseasProcurementController.java
#	lastleg-service/src/main/java/com/baiyue/lastleg/procurement/service/OverseasProcurementService.java
#	lastleg-service/src/main/java/com/baiyue/lastleg/relabel/controller/RelabelApplicationController.java
#	lastleg-service/src/main/java/com/baiyue/lastleg/trucking/controller/PlatformUsTruckOrderController.java
#	lastleg-service/src/main/java/com/baiyue/lastleg/trucking/service/UsTruckOrderService.java
#	lastleg-service/src/main/resources/application.yml
#	lastleg-service/src/main/resources/db/lastleg.sql
#	scripts/db/incremental.sql
#	warehouse-overseas-service/src/main/java/com/baiyue/warehouse/controller/PlatformWarehouseController.java
#	warehouse-overseas-service/src/main/java/com/baiyue/warehouse/controller/PlatformWarehouseResourceController.java
This commit is contained in:
2026-08-20 18:14:55 +08:00
27 changed files with 415 additions and 19 deletions
@@ -10,6 +10,7 @@ import com.baiyue.lastleg.inboundplan.pda.entity.InboundPalletMerge;
import com.baiyue.lastleg.inboundplan.pda.entity.InboundPdaOperation;
import com.baiyue.lastleg.inboundplan.pda.entity.InboundPdaTask;
import com.baiyue.lastleg.inboundplan.pda.gateway.WarehouseResourceGateway;
import com.baiyue.lastleg.inboundplan.pda.gateway.UnloadPhotoFileGateway;
import com.baiyue.lastleg.inboundplan.pda.repository.PdaWorkflowRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
@@ -20,6 +21,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.mock.web.MockMultipartFile;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@@ -33,6 +35,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -44,6 +47,7 @@ class InboundPdaWorkflowServiceTest {
@Mock WarehouseResourceGateway warehouse;
@Mock InboundStockReceiptService stockReceipts;
@Mock InboundUnloadCompletedOutboxService unloadCompletedOutbox;
@Mock UnloadPhotoFileGateway unloadPhotoFiles;
private InboundPdaWorkflowService service;
private InboundPlan plan;
@@ -64,6 +68,7 @@ class InboundPdaWorkflowServiceTest {
ReflectionTestUtils.setField(service, "warehouse", warehouse);
ReflectionTestUtils.setField(service, "stockReceipts", stockReceipts);
ReflectionTestUtils.setField(service, "unloadCompletedOutbox", unloadCompletedOutbox);
ReflectionTestUtils.setField(service, "unloadPhotoFiles", unloadPhotoFiles);
ReflectionTestUtils.setField(service, "sourceObjectMapper", new ObjectMapper().findAndRegisterModules());
service.initializeObjectMapper();
plan = plan();
@@ -76,7 +81,7 @@ class InboundPdaWorkflowServiceTest {
target = allocation(31L, targetLine.getId(), targetPallet.getId(), 3, 3, 0);
source = allocation(32L, sourceLine.getId(), sourcePallet.getId(), 2, 2, 2);
otherWarehouse = allocation(33L, sourceLine.getId(), otherWarehousePallet.getId(), 2, 2, 2);
when(repository.lockPlan("100", "LIP1")).thenReturn(plan);
lenient().when(repository.lockPlan("100", "LIP1")).thenReturn(plan);
}
@Test
@@ -326,6 +331,8 @@ class InboundPdaWorkflowServiceTest {
assertEquals(List.of(101L, 103L), result.unloadPhotoFileIds());
assertEquals(result.unloadPhotoUploadedAt().plusDays(2), result.estimatedEmptyReturnAt());
verify(unloadPhotoFiles).validate("100", task.getId(), 101L);
verify(unloadPhotoFiles).validate("100", task.getId(), 103L);
verify(repository).update(task);
ArgumentCaptor<InboundPdaOperation> operation = ArgumentCaptor.forClass(InboundPdaOperation.class);
verify(repository).insert(operation.capture());
@@ -344,6 +351,54 @@ class InboundPdaWorkflowServiceTest {
verify(repository, never()).update(task);
}
@Test
void uploadUnloadPhotosRejectsAFileOwnedByAnotherBusinessTask() {
activeTask();
org.mockito.Mockito.doThrow(new IllegalArgumentException("ownership mismatch"))
.when(unloadPhotoFiles).validate("100", task.getId(), 101L);
assertThrows(ResponseStatusException.class, () -> service.uploadUnloadPhotos("100", "LIP1",
new PdaRequests.UnloadPhotos(List.of(101L), "unload-photo-wrong-owner", "7", "worker")));
verify(repository, never()).update(task);
}
@Test
void uploadUnloadPhotoFileBindsTheFileToTheActivePdaTask() {
activeTask();
var file = new MockMultipartFile("file", "unload.jpg", "image/jpeg", new byte[] {1, 2, 3});
when(unloadPhotoFiles.upload("100", task.getId(), 7L, file))
.thenReturn(new UnloadPhotoFileGateway.UploadedFile(101L, "unload.jpg", "image/jpeg", 3L));
var result = service.uploadUnloadPhotoFile("100", "LIP1", 7L, file);
assertEquals(101L, result.fileId());
assertEquals("unload.jpg", result.fileName());
assertEquals("image/jpeg", result.contentType());
assertEquals(3L, result.sizeBytes());
verify(unloadPhotoFiles).upload("100", task.getId(), 7L, file);
}
@Test
void uploadUnloadPhotoFileRejectsEmptyFileBeforeCallingFileService() {
var file = new MockMultipartFile("file", "unload.jpg", "image/jpeg", new byte[0]);
assertThrows(ResponseStatusException.class,
() -> service.uploadUnloadPhotoFile("100", "LIP1", 7L, file));
verify(unloadPhotoFiles, never()).upload(any(), anyLong(), anyLong(), any());
}
@Test
void uploadUnloadPhotoFileRejectsNonImageBeforeCallingFileService() {
var file = new MockMultipartFile("file", "unload.pdf", "application/pdf", new byte[] {1});
assertThrows(ResponseStatusException.class,
() -> service.uploadUnloadPhotoFile("100", "LIP1", 7L, file));
verify(unloadPhotoFiles, never()).upload(any(), anyLong(), anyLong(), any());
}
@Test
void completesWhenMergedAllocationsPreserveExactLineTotals() {
activeTask();