优化
This commit is contained in:
+1
@@ -45,6 +45,7 @@ class InboundOrderControllerContractTest {
|
||||
.contains("/api/platform/inbounds:")
|
||||
.contains("/api/platform/inbounds/{inboundId}:")
|
||||
.contains("/api/platform/inbounds/{inboundId}/carton-label-pdf:")
|
||||
.contains("/api/platform/v1/first-leg/inbounds/{inboundId}/measurement/carton-scans:")
|
||||
.contains("required: false")
|
||||
.contains("InboundOrderSummary:")
|
||||
.contains("required: [id, tenantId]")
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package com.baiyue.firstleg.controller;
|
||||
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.firstleg.dto.CartonMeasurementCommand;
|
||||
import com.baiyue.firstleg.dto.CartonMeasurementResult;
|
||||
import com.baiyue.firstleg.service.CartonMeasurementService;
|
||||
import com.baiyue.firstleg.service.InboundMeasurementService;
|
||||
import com.baiyue.firstleg.service.MeasurementSubmissionService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.mock.web.MockPart;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
class PlatformCartonScanControllerTest {
|
||||
@Test
|
||||
void bindsJsonDataAndImageToTheFormalCartonMeasurementCommand() throws Exception {
|
||||
CartonMeasurementService cartonMeasurements = mock(CartonMeasurementService.class);
|
||||
PlatformInboundMeasurementController controller = new PlatformInboundMeasurementController();
|
||||
ReflectionTestUtils.setField(controller, "service", mock(InboundMeasurementService.class));
|
||||
ReflectionTestUtils.setField(controller, "submission", mock(MeasurementSubmissionService.class));
|
||||
ReflectionTestUtils.setField(controller, "cartonMeasurements", cartonMeasurements);
|
||||
AuthenticatedUser user = new AuthenticatedUser(90001L, 0L, 0L, null,
|
||||
"platform_admin", "PLATFORM", "WEB", Set.of("platform:firstleg:inbound:measure:edit"));
|
||||
MockMvc mvc = MockMvcBuilders.standaloneSetup(controller)
|
||||
.setCustomArgumentResolvers(authenticationPrincipal(user))
|
||||
.build();
|
||||
when(cartonMeasurements.upsertForInbound(eq(10001L), eq(90001L), eq(70001L), any()))
|
||||
.thenReturn(new CartonMeasurementResult(30001L, 1, 40001L, "RK-001#5-1", 1,
|
||||
new BigDecimal("60.1"), new BigDecimal("40.2"), new BigDecimal("30.3"),
|
||||
new BigDecimal("12.5"), 99001L, "SIM-01",
|
||||
LocalDateTime.parse("2026-08-07T10:30:00"), true));
|
||||
MockPart data = new MockPart("data", "data", ("""
|
||||
{"boxCode":"RK-001#5-1","length":60.1,"width":40.2,"height":30.3,
|
||||
"weight":12.5,"machineNo":"SIM-01","measuredAt":"2026-08-07T10:30:00"}
|
||||
""").getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||
data.getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
||||
MockMultipartFile image = new MockMultipartFile(
|
||||
"image", "carton.jpg", MediaType.IMAGE_JPEG_VALUE, new byte[] {1, 2, 3});
|
||||
|
||||
mvc.perform(multipart("/api/platform/v1/first-leg/inbounds/{inboundId}/measurement/carton-scans", 70001L)
|
||||
.file(image).part(data).param("tenantId", "10001"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value("200"))
|
||||
.andExpect(jsonPath("$.data.boxCode").value("RK-001#5-1"));
|
||||
|
||||
ArgumentCaptor<CartonMeasurementCommand> command = ArgumentCaptor.forClass(CartonMeasurementCommand.class);
|
||||
verify(cartonMeasurements).upsertForInbound(
|
||||
eq(10001L), eq(90001L), eq(70001L), command.capture());
|
||||
assertThat(command.getValue().machineNo()).isEqualTo("SIM-01");
|
||||
assertThat(command.getValue().image().getOriginalFilename()).isEqualTo("carton.jpg");
|
||||
}
|
||||
|
||||
private HandlerMethodArgumentResolver authenticationPrincipal(AuthenticatedUser user) {
|
||||
return new HandlerMethodArgumentResolver() {
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(AuthenticationPrincipal.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
org.springframework.web.bind.support.WebDataBinderFactory binderFactory) {
|
||||
return user;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+4
@@ -5,6 +5,7 @@ import com.baiyue.firstleg.dto.InboundMeasurementRequests;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@@ -25,6 +26,9 @@ class PlatformInboundMeasurementControllerContractTest {
|
||||
assertAuthority("updateConfirmedPackageCount", "hasAuthority('platform:firstleg:inbound:measure:edit')",
|
||||
AuthenticatedUser.class, long.class, long.class, long.class,
|
||||
InboundMeasurementRequests.ConfirmedPackageCountRequest.class);
|
||||
assertAuthority("simulateCartonScan", "hasAuthority('platform:firstleg:inbound:measure:edit')",
|
||||
AuthenticatedUser.class, long.class, long.class,
|
||||
InboundMeasurementRequests.CartonScanRequest.class, MultipartFile.class);
|
||||
assertAuthority("createWeightResult", "hasAuthority('platform:firstleg:inbound:weight:edit')",
|
||||
AuthenticatedUser.class, long.class, long.class,
|
||||
InboundMeasurementRequests.CreateWeightResultRequest.class);
|
||||
|
||||
@@ -111,6 +111,15 @@ class CartonMeasurementServiceTest {
|
||||
verify(images, never()).upload(any(), any(Long.TYPE), any(Long.TYPE), any(Long.TYPE), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformSimulationRejectsBoxCodeFromAnotherInboundBeforeUploadingImage() {
|
||||
assertThatThrownBy(() -> service.upsertForInbound(10001L, 90001L, 70002L, command()))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("箱号不属于当前入库单");
|
||||
|
||||
verify(images, never()).upload(any(), any(Long.TYPE), any(Long.TYPE), any(Long.TYPE), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void confirmedPackageCountUsesMeasurementOptimisticVersion() {
|
||||
InboundOrderEntity inbound = inbound("WAIT_MEASURE");
|
||||
|
||||
@@ -47,6 +47,7 @@ class InboundCartonLabelServiceTest {
|
||||
var first = cargo(80001L, 1, 3, "60", "40", "30");
|
||||
when(orderCargoClient.listForCartonLabels("internal-token", 10001L, 60001L))
|
||||
.thenReturn(ApiResponse.success(List.of(second, first)));
|
||||
when(repository.initializeOriginalPackageCount(10001L, 70001L, 5)).thenReturn(true);
|
||||
List<InboundCartonLabelEntity> inserted = new ArrayList<>();
|
||||
org.mockito.Mockito.doAnswer(invocation -> {
|
||||
inserted.add(invocation.getArgument(0));
|
||||
@@ -63,12 +64,15 @@ class InboundCartonLabelServiceTest {
|
||||
.containsExactly(80001L, 80001L, 80001L, 80002L, 80002L);
|
||||
assertThat(batch.labels().get(0).boxSpecification()).isEqualTo("60x40x30cm");
|
||||
assertThat(batch.labels().get(0).cartonText()).isEqualTo("CTNS:5-1");
|
||||
assertThat(inbound.getTotalPackageCount()).isEqualTo(5);
|
||||
assertThat(inserted).hasSize(5);
|
||||
verify(repository).initializeOriginalPackageCount(10001L, 70001L, 5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void repeatedAndPartialPrintsReadFrozenMappingWithoutReallocation() {
|
||||
InboundOrderEntity inbound = inbound();
|
||||
inbound.setTotalPackageCount(5);
|
||||
List<InboundCartonLabelEntity> existing = labels(inbound, 5);
|
||||
when(repository.lockInbound(10001L, 70001L)).thenReturn(inbound);
|
||||
when(repository.findByInbound(10001L, 70001L)).thenReturn(existing);
|
||||
@@ -82,6 +86,22 @@ class InboundCartonLabelServiceTest {
|
||||
verify(repository, never()).insert(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void refusesFrozenMappingWhenOriginalPackageCountDoesNotMatch() {
|
||||
InboundOrderEntity inbound = inbound();
|
||||
inbound.setTotalPackageCount(4);
|
||||
when(repository.lockInbound(10001L, 70001L)).thenReturn(inbound);
|
||||
when(repository.findByInbound(10001L, 70001L)).thenReturn(labels(inbound, 5));
|
||||
|
||||
assertThatThrownBy(() -> service.getOrCreate(10001L, 70001L))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("订单总箱数与已冻结箱号数量不一致");
|
||||
|
||||
verify(repository, never()).initializeOriginalPackageCount(
|
||||
org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(),
|
||||
org.mockito.ArgumentMatchers.anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolvesBoxCodeInsideCustomerBoundary() {
|
||||
InboundCartonLabelEntity label = labels(inbound(), 1).get(0);
|
||||
|
||||
@@ -81,6 +81,24 @@ class InboundMeasurementServiceTest {
|
||||
verify(repository, never()).updateInbound(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void creatingWeightResultNormalizesProviderConfidenceAtBusinessBoundary() {
|
||||
InboundOrderEntity inbound = inbound("WAIT_MEASURE");
|
||||
when(repository.findInbound(1L, 10L)).thenReturn(inbound);
|
||||
|
||||
InboundMeasurementResponses.WeightResult result = service.createWeightResult(
|
||||
1L, 10L, 9L,
|
||||
new InboundMeasurementRequests.CreateWeightResultRequest(
|
||||
120L, "FILE_WEIGHT_OCR", "285.5", new BigDecimal("285.500"), "KG",
|
||||
new BigDecimal("0.5097191334")));
|
||||
|
||||
ArgumentCaptor<InboundWeightResultEntity> saved =
|
||||
ArgumentCaptor.forClass(InboundWeightResultEntity.class);
|
||||
verify(repository).insertWeight(saved.capture());
|
||||
assertThat(saved.getValue().getConfidence()).isEqualByComparingTo("0.50972");
|
||||
assertThat(result.confidence()).isEqualByComparingTo("0.50972");
|
||||
}
|
||||
|
||||
@Test
|
||||
void confirmingLbWeightConvertsToKgAndRecalculatesOnlyConfirmedImages() {
|
||||
InboundOrderEntity inbound = inbound("WAIT_MEASURE");
|
||||
|
||||
Reference in New Issue
Block a user