This commit is contained in:
2026-10-21 07:53:31 +08:00
parent af5cf1e3bd
commit f00d0e16cf
147 changed files with 1696 additions and 872 deletions
@@ -164,6 +164,46 @@ class HttpWarehouseResourceGatewayTest {
server.verify();
}
/** 实际SHIP后报废的原托扫码仍拒绝;保留提供者业务码/原因,不伪装为服务故障或按ID重试。 */
@Test
void inspectPreservesScrappedPalletBusinessConflictWithoutFallback() {
server.expect(requestTo("http://warehouse/internal/v1/warehouse-pallets/resolve/D1747-1-001"))
.andRespond(withSuccess("""
{"code":"A0409","message":"托盘已报废","data":null}
""", MediaType.APPLICATION_JSON));
assertThatThrownBy(() -> gateway.inspectPallet("D1747-1-001"))
.isInstanceOfSatisfying(ResponseStatusException.class, error -> {
assertThat(error.getStatusCode()).isEqualTo(HttpStatus.CONFLICT);
assertThat(error.getReason()).isEqualTo("托盘已报废");
});
server.verify();
}
/** 按ID读取使用同一响应边界,错误码即使带data也不能被当作合法实托。 */
@Test
void idLookupRejectsBusinessConflictEvenWithPayload() {
server.expect(requestTo("http://warehouse/internal/v1/warehouse-pallets/resolve/id/31"))
.andRespond(withSuccess("""
{"code":"A0409","message":"托盘状态冲突","data":{"palletId":31,"palletCode":"PAL-1",
"warehouseId":88,"locationId":40,"status":"IDLE","version":1}}
""", MediaType.APPLICATION_JSON));
assertThatThrownBy(() -> gateway.resolvePallet(31L))
.isInstanceOfSatisfying(ResponseStatusException.class, error ->
assertThat(error.getStatusCode()).isEqualTo(HttpStatus.CONFLICT));
server.verify();
}
/** 同一个状态冲突无论通过HTTP状态还是业务信封传输,都不能降为400或503。 */
@Test
void inspectPreservesHttpConflict() {
server.expect(requestTo("http://warehouse/internal/v1/warehouse-pallets/resolve/PAL-CONFLICT"))
.andRespond(withStatus(HttpStatus.CONFLICT));
assertThatThrownBy(() -> gateway.inspectPallet("PAL-CONFLICT"))
.isInstanceOfSatisfying(ResponseStatusException.class, error ->
assertThat(error.getStatusCode()).isEqualTo(HttpStatus.CONFLICT));
server.verify();
}
@Test
void resolveReservedFallsBackToConfirmedPhysicalBindingForEarlierPlanDisplayCode() {
server.expect(requestTo("http://warehouse/internal/v1/warehouse-pallets/resolve/IP-ACY2-20260909-166786-P001"))
@@ -359,7 +359,7 @@ class ContainerInboundPlanConversionCoordinatorTest {
new ContainerInboundPlanPreviewResponse.Group("ONE_CLICK_DROPSHIP", "31", "US-LAX-01", "Los Angeles",
1, 1, 1, 1, 0, 0, 1, 0, List.of(overseas))), null);
when(plans.previewContainer(BOOKING_ID)).thenReturn(preview);
when(numbers.next(eq("INBOUND_PLAN"), any(String.class), any(Long.class)))
when(numbers.next(eq("INBOUND_PLAN")))
.thenReturn("IP-FBA", "IP-OVERSEAS");
when(plans.sourcePalletNos("100", 11L, "ONE_CLICK_DROPSHIP")).thenReturn(List.of("MSCU6639870-ABE3-001"));
when(plans.sourcePalletNos("200", 12L, "ONE_CLICK_DROPSHIP")).thenReturn(List.of("MSCU6639870-US-LAX-01-001"));
@@ -392,7 +392,7 @@ class ContainerInboundPlanConversionCoordinatorTest {
List.of(new ContainerInboundPlanPreviewResponse.Group("FIRST_LEG", "31", "US-LAX-01", "Los Angeles",
1, 1, 2, 0, 2, 0, 2, 0, List.of(overseas))), null);
when(plans.previewContainer(BOOKING_ID)).thenReturn(preview);
when(numbers.next(eq("INBOUND_PLAN"), any(String.class), any(Long.class))).thenReturn("IP-OVERSEAS");
when(numbers.next(eq("INBOUND_PLAN"))).thenReturn("IP-OVERSEAS");
when(plans.sourcePalletNos("200", 12L, "FIRST_LEG")).thenReturn(List.of());
ContainerPlanConversionCommand command = ReflectionTestUtils.invokeMethod(coordinator, "prepareCommand",
@@ -424,7 +424,7 @@ class ContainerInboundPlanConversionCoordinatorTest {
List.of(new ContainerInboundPlanPreviewResponse.Group("FBA", null, "ACY2", "Amazon ACY2",
1, 1, 3, 2, 1, 0, 3, 0, List.of(order))), null);
when(plans.previewContainer(BOOKING_ID)).thenReturn(preview);
when(numbers.next(eq("INBOUND_PLAN"), any(String.class), any(Long.class))).thenReturn("IP-ACY2");
when(numbers.next(eq("INBOUND_PLAN"))).thenReturn("IP-ACY2");
when(plans.sourcePalletNos("100", 11L, "FIRST_LEG")).thenReturn(List.of(
"D0004-101-001", "D0004-101-002"));
@@ -475,7 +475,7 @@ class ContainerInboundPlanConversionCoordinatorTest {
assertEquals(21L, preparation.items().get(0).planId());
assertEquals("IP-MANUAL", preparation.items().get(0).planNo());
verify(numbers, never()).next(any(String.class), any(String.class), any(Long.class));
verify(numbers, never()).next(any(String.class));
}
private ContainerInboundPlanPreviewResponse.Order previewOrder(String customerId, String orderId,
@@ -262,6 +262,7 @@ class FbaAppointmentPersistenceTest {
@Bean com.baiyue.lastleg.mapper.LastlegOrderMapper orders(){var mock=mock(com.baiyue.lastleg.mapper.LastlegOrderMapper.class);var row=new com.baiyue.lastleg.entity.LastlegOrder();row.setId(100L);row.setCustomerId("C");row.setInboundCode("WT-REAL");when(mock.selectById(100L)).thenReturn(row);return mock;}
@Bean AppointmentExternalSourceGateway bookings(){var mock=mock(AppointmentExternalSourceGateway.class);when(mock.resolve(anyList())).thenReturn(List.of(new AppointmentExternalSourceGateway.Source("C","B-1","TR-1","RECEIVED","100","FBA:ONT8",100L,10,2L)));return mock;}
@Bean AppointmentSourceLinker linker(com.baiyue.lastleg.mapper.LastlegOrderMapper orders,InboundFbaAppointmentItemMapper items,AppointmentExternalSourceGateway bookings){return new InboundAppointmentSourceLinker(orders,items,bookings);}
@Bean com.baiyue.lastleg.service.LastlegDocumentNumberGenerator documentNumbers() { return com.baiyue.lastleg.service.TestDocumentNumbers.documents(); }
@Bean InboundFbaAppointmentService service(){return new InboundFbaAppointmentService();}
@Bean com.baiyue.lastleg.appointmentwork.AppointmentWorkMapper workMapper(SqlSessionTemplate session){return session.getMapper(com.baiyue.lastleg.appointmentwork.AppointmentWorkMapper.class);}
/** 使用生产持久层,继续验证需求失效与预约写入的同事务行为。 */
@@ -33,6 +33,7 @@ class InboundFbaAppointmentServiceTest {
headers=mock(InboundFbaAppointmentMapper.class);items=mock(InboundFbaAppointmentItemMapper.class);query=mock(InboundFbaAppointmentQueryMapper.class);
commands=mock(FbaAppointmentCommandMapper.class);sources=mock(AppointmentSourceService.class);flags=mock(AppointmentBatchPreviewMapper.class);
service=new InboundFbaAppointmentService();
org.springframework.test.util.ReflectionTestUtils.setField(service, "documentNumbers", com.baiyue.lastleg.service.TestDocumentNumbers.documents());
ReflectionTestUtils.setField(service,"receiving",mock(com.baiyue.lastleg.inboundplan.receiving.FbaReceivingStore.class));
ReflectionTestUtils.setField(service,"receivingWork",mock(com.baiyue.lastleg.inboundplan.receiving.FbaAppointmentWorkPort.class));
ReflectionTestUtils.setField(service,"workChanges",mock(com.baiyue.lastleg.inboundplan.work.AppointmentWorkAppointmentChanges.class));
@@ -95,7 +96,7 @@ class InboundFbaAppointmentServiceTest {
}
@Test void savesCrossCustomerArrangementBeforeAnyInboundPlanOrExternalNumberExists() {
var result=create("create");assertTrue(result.appointmentNo().startsWith("AP-"));assertNull(result.confirmationNo());assertNull(result.appointmentAt());
var result=create("create");assertTrue(result.appointmentNo().matches("YY[0-9]{12}"));assertNull(result.confirmationNo());assertNull(result.appointmentAt());
assertEquals(LocalDate.of(2026,10,15),result.plannedDate());assertEquals(2,result.selectedCount());assertEquals("PENDING",result.appointmentStatus());
assertEquals(List.of("A","B"),result.items().stream().map(FbaAppointmentResponse.Item::customerId).toList());verify(query,never()).selectedLines(anyList());
}
@@ -75,7 +75,7 @@ class InboundPlanServiceTest {
ReflectionTestUtils.setField(service, "commandMapper", commandMapper);
ReflectionTestUtils.setField(service, "sourcePalletMapper", sourcePalletMapper);
ReflectionTestUtils.setField(service, "objectMapper", JsonMapper.builder().findAndAddModules().build());
ReflectionTestUtils.setField(service, "orderNoGenerator", new com.baiyue.lastleg.service.LastlegOrderNoGenerator());
ReflectionTestUtils.setField(service, "orderNoGenerator", com.baiyue.lastleg.service.TestDocumentNumbers.orders());
org.mockito.Mockito.lenient().when(lineMapper.selectList(any())).thenReturn(List.of());
org.mockito.Mockito.lenient().when(palletMapper.selectList(any())).thenReturn(List.of());
org.mockito.Mockito.lenient().when(allocationMapper.selectList(any())).thenReturn(List.of());