Merge branch 'dev' of http://8.163.30.128:10082/java-project/baiyue-firstleg into dev
This commit is contained in:
+1
@@ -26,6 +26,7 @@ class InboundPlanSchemaContractTest {
|
||||
assertTrue(schema.contains("create table if not exists ll_inbound_pallet_merge"));
|
||||
assertTrue(schema.contains("create table if not exists ll_inbound_fba_appointment"));
|
||||
assertTrue(schema.contains("unique key uk_ll_fba_appointment_no (customer_id, appointment_no)"));
|
||||
assertTrue(schema.contains("expected_loading_at"));
|
||||
assertTrue(schema.contains("create table if not exists ll_inbound_fba_appointment_item"));
|
||||
assertTrue(schema.contains("fba_warehouse_code"));
|
||||
assertFalse(schema.contains("cabinet_no"));
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package com.baiyue.lastleg.inboundplan.controller;
|
||||
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.lastleg.inboundplan.dto.InboundPlanRequests;
|
||||
import com.baiyue.lastleg.inboundplan.dto.UpdateFbaAppointmentRequest;
|
||||
import com.baiyue.lastleg.inboundplan.service.InboundFbaAppointmentService;
|
||||
import com.baiyue.lastleg.inboundplan.service.InboundPlanService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class InboundPlanControllerTest {
|
||||
@Mock InboundPlanService service;
|
||||
@Mock InboundFbaAppointmentService appointmentService;
|
||||
private InboundPlanController controller;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
controller = new InboundPlanController();
|
||||
ReflectionTestUtils.setField(controller, "service", service);
|
||||
ReflectionTestUtils.setField(controller, "appointmentService", appointmentService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformPageRequiresAndPassesExplicitCustomerId() {
|
||||
InboundPlanRequests.Query query = new InboundPlanRequests.Query(1, 20, null, null, null,
|
||||
null, null, null, null, null, null, null, null);
|
||||
when(service.page("100", query)).thenReturn(new PageResult<>(List.of(), 1, 20, 0, 0));
|
||||
|
||||
controller.page(platform(), "100", query);
|
||||
|
||||
verify(service).page("100", query);
|
||||
assertThatThrownBy(() -> controller.page(platform(), "", query))
|
||||
.isInstanceOf(ResponseStatusException.class)
|
||||
.satisfies(error -> org.assertj.core.api.Assertions.assertThat(
|
||||
((ResponseStatusException) error).getStatusCode().value()).isEqualTo(400));
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformAppointmentUpdateUsesExplicitCustomerScope() {
|
||||
UpdateFbaAppointmentRequest request = new UpdateFbaAppointmentRequest("100",
|
||||
LocalDateTime.of(2026, 8, 20, 10, 30), LocalDateTime.of(2026, 8, 21, 8, 0),
|
||||
"PENDING", "dock");
|
||||
|
||||
controller.updateFbaAppointment(platform(), "APT-1", request);
|
||||
|
||||
verify(appointmentService).update("100", "APT-1", request);
|
||||
}
|
||||
|
||||
private static AuthenticatedUser platform() {
|
||||
return new AuthenticatedUser(7L, 0L, 0L, 8L, "operator", "PLATFORM", "WEB",
|
||||
Set.of("platform:overseas-warehouse:inbound-order:list"));
|
||||
}
|
||||
}
|
||||
+49
-1
@@ -1,12 +1,14 @@
|
||||
package com.baiyue.lastleg.inboundplan.service;
|
||||
|
||||
import com.baiyue.lastleg.inboundplan.dto.BatchFbaAppointmentRequest;
|
||||
import com.baiyue.lastleg.inboundplan.dto.UpdateFbaAppointmentRequest;
|
||||
import com.baiyue.lastleg.inboundplan.entity.InboundFbaAppointment;
|
||||
import com.baiyue.lastleg.inboundplan.entity.InboundFbaAppointmentItem;
|
||||
import com.baiyue.lastleg.inboundplan.mapper.InboundFbaAppointmentItemMapper;
|
||||
import com.baiyue.lastleg.inboundplan.mapper.InboundFbaAppointmentMapper;
|
||||
import com.baiyue.lastleg.inboundplan.mapper.InboundFbaAppointmentQueryMapper;
|
||||
import com.baiyue.lastleg.inboundplan.model.FbaAppointmentLineSnapshot;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -35,6 +37,7 @@ class InboundFbaAppointmentServiceTest {
|
||||
|
||||
private InboundFbaAppointmentService service;
|
||||
private final LocalDateTime appointmentAt = LocalDateTime.of(2026, 8, 20, 10, 30);
|
||||
private final LocalDateTime expectedLoadingAt = LocalDateTime.of(2026, 8, 21, 8, 0);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
@@ -57,6 +60,7 @@ class InboundFbaAppointmentServiceTest {
|
||||
|
||||
assertEquals("APT-1", result.appointmentNo());
|
||||
assertEquals("ONT8", result.fbaWarehouseCode());
|
||||
assertEquals(expectedLoadingAt, result.expectedLoadingAt());
|
||||
assertEquals(2, result.selectedCount());
|
||||
verify(appointmentMapper).insert(any(InboundFbaAppointment.class));
|
||||
verify(itemMapper, org.mockito.Mockito.times(2)).insert(any(InboundFbaAppointmentItem.class));
|
||||
@@ -114,8 +118,51 @@ class InboundFbaAppointmentServiceTest {
|
||||
() -> service.batchCreate("100", "7", request(List.of(12L), "APT-1")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sameAppointmentNumberWithDifferentExpectedLoadingTimeConflicts() {
|
||||
when(appointmentMapper.selectOne(any())).thenReturn(appointment(99L, "APT-1", "ONT8"));
|
||||
InboundFbaAppointmentItem item = new InboundFbaAppointmentItem();
|
||||
item.setLineId(11L);
|
||||
when(itemMapper.selectList(any())).thenReturn(List.of(item));
|
||||
BatchFbaAppointmentRequest changed = new BatchFbaAppointmentRequest("100", List.of(11L), "APT-1",
|
||||
appointmentAt, expectedLoadingAt.plusHours(1), "SUCCESS", "dock");
|
||||
|
||||
ResponseStatusException exception = assertThrows(ResponseStatusException.class,
|
||||
() -> service.batchCreate("100", "7", changed));
|
||||
assertEquals(409, exception.getStatusCode().value());
|
||||
}
|
||||
|
||||
@Test
|
||||
void stringSnowflakeLineIdsDeserializeWithoutPrecisionLoss() throws Exception {
|
||||
String json = "{\"customerId\":\"100\",\"lineIds\":[\"9007199254740993\"],"
|
||||
+ "\"appointmentNo\":\"APT-1\",\"appointmentAt\":\"2026-08-20T10:30:00\","
|
||||
+ "\"expectedLoadingAt\":\"2026-08-21T08:00:00\",\"appointmentStatus\":\"PENDING\"}";
|
||||
|
||||
BatchFbaAppointmentRequest request = new ObjectMapper().findAndRegisterModules()
|
||||
.readValue(json, BatchFbaAppointmentRequest.class);
|
||||
|
||||
assertEquals(List.of(9007199254740993L), request.lineIds());
|
||||
assertEquals(expectedLoadingAt, request.expectedLoadingAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updatesAppointmentDetailsWithoutReplacingSelectedLines() {
|
||||
InboundFbaAppointment existing = appointment(99L, "APT-1", "ONT8");
|
||||
when(appointmentMapper.selectOne(any())).thenReturn(existing);
|
||||
LocalDateTime loadingAt = expectedLoadingAt.plusHours(2);
|
||||
|
||||
var result = service.update("100", "APT-1", new UpdateFbaAppointmentRequest(
|
||||
"100", appointmentAt.plusDays(1), loadingAt, "PENDING", "updated"));
|
||||
|
||||
assertEquals(loadingAt, result.expectedLoadingAt());
|
||||
assertEquals("PENDING", result.appointmentStatus());
|
||||
verify(appointmentMapper).updateById(existing);
|
||||
verify(itemMapper, never()).insert(any(InboundFbaAppointmentItem.class));
|
||||
}
|
||||
|
||||
private BatchFbaAppointmentRequest request(List<Long> lineIds, String number) {
|
||||
return new BatchFbaAppointmentRequest("100", lineIds, number, appointmentAt, "SUCCESS", "dock");
|
||||
return new BatchFbaAppointmentRequest("100", lineIds, number, appointmentAt, expectedLoadingAt,
|
||||
"SUCCESS", "dock");
|
||||
}
|
||||
|
||||
private FbaAppointmentLineSnapshot line(Long id, String inboundCode, String fba) {
|
||||
@@ -128,6 +175,7 @@ class InboundFbaAppointmentServiceTest {
|
||||
value.setCustomerId("100");
|
||||
value.setAppointmentNo(number);
|
||||
value.setAppointmentAt(appointmentAt);
|
||||
value.setExpectedLoadingAt(expectedLoadingAt);
|
||||
value.setAppointmentStatus("SUCCESS");
|
||||
value.setAppointmentRemark("dock");
|
||||
value.setFbaWarehouseCode(fba);
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
package com.baiyue.lastleg.inboundplan.service;
|
||||
|
||||
import com.baiyue.lastleg.inboundplan.dto.InboundPlanListResponse;
|
||||
import com.baiyue.lastleg.inboundplan.dto.InboundPlanRequests;
|
||||
import com.baiyue.lastleg.inboundplan.mapper.InboundPlanQueryMapper;
|
||||
import jakarta.validation.Validation;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class InboundPlanPaginationTest {
|
||||
@Mock InboundPlanQueryMapper queryMapper;
|
||||
private InboundPlanService service;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
service = new InboundPlanService();
|
||||
ReflectionTestUtils.setField(service, "queryMapper", queryMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryDefaultsPageAndSizeAndRejectsOversizedPages() {
|
||||
InboundPlanRequests.Query defaults = query(0, 0);
|
||||
assertThat(defaults.page()).isEqualTo(1);
|
||||
assertThat(defaults.size()).isEqualTo(20);
|
||||
|
||||
try (var validatorFactory = Validation.buildDefaultValidatorFactory()) {
|
||||
var violations = validatorFactory.getValidator().validate(query(1, 201));
|
||||
assertThat(violations).extracting(value -> value.getPropertyPath().toString()).contains("size");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void pagesAtLineGranularityAndPassesTenantAndAllFiltersToDatabase() {
|
||||
InboundPlanRequests.Query query = new InboundPlanRequests.Query(2, 10, "LIP", "IN-1",
|
||||
"PENDING_ASSIGNMENT", null, null, "ONT", "ONT8", "UNAPPOINTED", "UNSCHEDULED",
|
||||
LocalDateTime.of(2026, 8, 1, 0, 0), LocalDateTime.of(2026, 8, 31, 23, 59));
|
||||
InboundPlanListResponse row = new InboundPlanListResponse(9007199254740993L, 2L, "LIP2", "100",
|
||||
3L, "IN-1", "FIRST_LEG", "W1", "ONT", "安大略仓", "ONT8", "客户A", 10,
|
||||
BigDecimal.ONE, 1, "PENDING_ASSIGNMENT", null, null, null, null, "UNSCHEDULED",
|
||||
LocalDateTime.of(2026, 8, 20, 10, 0));
|
||||
when(queryMapper.count("100", query)).thenReturn(11L);
|
||||
when(queryMapper.rows("100", query, 10, 10)).thenReturn(List.of(row));
|
||||
|
||||
var result = service.page("100", query);
|
||||
|
||||
assertThat(result.records()).containsExactly(row);
|
||||
assertThat(result.records().get(0).lineId()).isEqualTo(9007199254740993L);
|
||||
assertThat(result.records().get(0).appointmentStatus()).isNull();
|
||||
assertThat(result.pages()).isEqualTo(2);
|
||||
verify(queryMapper).count("100", query);
|
||||
verify(queryMapper).rows("100", query, 10, 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void mapperSqlProjectsLatestAppointmentAndTenantScopedDispatchState() throws Exception {
|
||||
String sql = String.join("", InboundPlanQueryMapper.class
|
||||
.getMethod("rows", String.class, InboundPlanRequests.Query.class, long.class, long.class)
|
||||
.getAnnotation(org.apache.ibatis.annotations.Select.class).value()) + InboundPlanQueryMapper.FROM_AND_FILTER;
|
||||
assertThat(sql).contains("a2.customer_id=#{customerId}", "ORDER BY a2.created_at DESC,ai2.id DESC LIMIT 1");
|
||||
assertThat(sql).contains("di.customer_id=#{customerId} AND di.order_id=l.order_id");
|
||||
assertThat(sql).contains("q.planNo", "q.inboundCode", "q.status", "q.overseasWarehouseId",
|
||||
"q.destinationWarehouseCode", "q.warehouse",
|
||||
"q.fbaWarehouseCode", "q.appointmentStatus", "q.dispatchStatus", "q.createdFrom", "q.createdTo");
|
||||
assertThat(sql).contains("LIMIT #{limit} OFFSET #{offset}");
|
||||
}
|
||||
|
||||
private static InboundPlanRequests.Query query(long page, long size) {
|
||||
return new InboundPlanRequests.Query(page, size, null, null, null, null, null,
|
||||
null, null, null, null, null, null);
|
||||
}
|
||||
}
|
||||
+1
-22
@@ -2,7 +2,6 @@ package com.baiyue.lastleg.inboundplan.service;
|
||||
|
||||
import com.baiyue.lastleg.inboundplan.entity.InboundPlan;
|
||||
import com.baiyue.lastleg.inboundplan.dto.InboundPlanRequests;
|
||||
import com.baiyue.lastleg.inboundplan.dto.InboundPlanSummary;
|
||||
import com.baiyue.lastleg.inboundplan.entity.InboundPlanLine;
|
||||
import com.baiyue.lastleg.inboundplan.entity.InboundPlanPallet;
|
||||
import com.baiyue.lastleg.inboundplan.entity.InboundPlanPalletAllocation;
|
||||
@@ -118,33 +117,13 @@ class InboundPlanServiceTest {
|
||||
verify(orderMapper, never()).markPlanned(any(), any(), any(), org.mockito.ArgumentMatchers.anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void pagesInboundPlansWithinCustomerScope() {
|
||||
InboundPlanRequests.Query query = new InboundPlanRequests.Query(
|
||||
2, 20, "LIP", "RK", "PENDING_ASSIGNMENT", "WH-1", "ONT8", "ONT8",
|
||||
"NOT_APPOINTED", null, null);
|
||||
InboundPlanSummary row = new InboundPlanSummary(1L, "LIP1", 2L, "100", "Customer A",
|
||||
"FBA", "RK-1", "CONT-1", "WH-1", "LA Warehouse", "ONT8", "ONT8", "ONT8",
|
||||
"ONT8", 10, BigDecimal.ONE, 1, "PENDING_ASSIGNMENT", "NOT_APPOINTED", null, null);
|
||||
when(planMapper.countRows("100", query)).thenReturn(21L);
|
||||
when(planMapper.pageRows("100", query, 20L, 20L)).thenReturn(List.of(row));
|
||||
|
||||
var result = service.page("100", query);
|
||||
|
||||
assertEquals(21L, result.total());
|
||||
assertEquals(2L, result.pages());
|
||||
assertEquals("RK-1", result.records().get(0).inboundCode());
|
||||
verify(planMapper).pageRows("100", query, 20L, 20L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsReversedInboundPlanDateRange() {
|
||||
InboundPlanRequests.Query query = new InboundPlanRequests.Query(1, 20, null, null, null, null,
|
||||
null, null, null, LocalDateTime.of(2026, 8, 20, 0, 0),
|
||||
null, null, null, null, null, LocalDateTime.of(2026, 8, 20, 0, 0),
|
||||
LocalDateTime.of(2026, 8, 19, 0, 0));
|
||||
|
||||
assertThrows(ResponseStatusException.class, () -> service.page("100", query));
|
||||
verify(planMapper, never()).countRows(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ class InventoryQueryControllerTest {
|
||||
private final InventoryQueryService service = mock(InventoryQueryService.class);
|
||||
private final InventoryQueryController controller = controller();
|
||||
private final InventoryQueryRequests.BalanceQuery query = new InventoryQueryRequests.BalanceQuery(
|
||||
1, 20, null, null, null, null, null, null, null, null, null, null);
|
||||
1, 20, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
|
||||
@Test
|
||||
void merchantCannotOverrideCustomerScope() {
|
||||
|
||||
+2
-2
@@ -105,7 +105,7 @@ class InventoryQueryServiceTest {
|
||||
@Test
|
||||
void reversedLedgerDateRangeIsRejected() {
|
||||
InventoryQueryRequests.LedgerQuery query = new InventoryQueryRequests.LedgerQuery(
|
||||
1, 20, null, null, null, null, null, null, null, null, null,
|
||||
1, 20, null, null, null, null, null, null, null, null, null, null, null,
|
||||
java.time.LocalDateTime.of(2026, 8, 18, 0, 0),
|
||||
java.time.LocalDateTime.of(2026, 8, 17, 0, 0));
|
||||
|
||||
@@ -125,6 +125,6 @@ class InventoryQueryServiceTest {
|
||||
}
|
||||
|
||||
private static InventoryQueryRequests.BalanceQuery balanceQuery(String status) {
|
||||
return new InventoryQueryRequests.BalanceQuery(1, 20, null, null, null, null, null, null, null, null, null, status);
|
||||
return new InventoryQueryRequests.BalanceQuery(1, 20, null, null, null, null, null, null, null, null, null, null, null, status);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user