优化
This commit is contained in:
+19
-7
@@ -4,6 +4,7 @@ import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import java.lang.reflect.Method;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -12,16 +13,27 @@ class PlatformSmartLoadingControllerContractTest {
|
||||
authority("draftByBooking","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class);
|
||||
authority("detail","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class);
|
||||
authority("candidates","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class,long.class);
|
||||
authority("inboundCandidates","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class,long.class);
|
||||
authority("inboundCandidates","platform:firstleg:smart-loading:list",AuthenticatedUser.class,InboundCandidateQuery.class);
|
||||
authority("containers","platform:firstleg:smart-loading:list",AuthenticatedUser.class,WorkbenchContainerQuery.class);
|
||||
authority("channels","platform:firstleg:smart-loading:list",AuthenticatedUser.class,long.class);
|
||||
}
|
||||
@Test void openApiRequiresBookingAndWarehouseForCandidates() throws Exception {
|
||||
@Test void openApiFreezesFilteredWorkbenchContainers() throws Exception {
|
||||
String yaml=java.nio.file.Files.readString(java.nio.file.Path.of("src/main/resources/openapi/firstleg-v1.yaml"));
|
||||
assertThat(yaml).contains("/api/platform/v1/first-leg/smart-loading/candidates:")
|
||||
.contains("/api/platform/v1/first-leg/smart-loading/channels:")
|
||||
.contains("零候选或零可用柜次的有效渠道仍返回")
|
||||
.contains("name: bookingId, in: query, required: true")
|
||||
.contains("name: warehouseId, in: query, required: true");
|
||||
assertThat(yaml).contains("/api/platform/v1/first-leg/smart-loading/containers:")
|
||||
.contains("SmartLoadingWorkbenchContainerPageEnvelope:")
|
||||
.contains("只返回 WAIT_LOADING、EDITING 或 LOADING");
|
||||
}
|
||||
@Test void openApiFreezesGlobalPagedInboundCandidates() throws Exception {
|
||||
Method method=PlatformSmartLoadingController.class.getDeclaredMethod(
|
||||
"inboundCandidates",AuthenticatedUser.class,InboundCandidateQuery.class);
|
||||
assertThat(method.getAnnotation(GetMapping.class).value()).containsExactly("/inbound-candidates");
|
||||
String yaml=java.nio.file.Files.readString(java.nio.file.Path.of("src/main/resources/openapi/firstleg-v1.yaml"));
|
||||
String contract=yaml.substring(yaml.indexOf("/api/platform/v1/first-leg/smart-loading/inbound-candidates:"),
|
||||
yaml.indexOf("/api/platform/v1/first-leg/loading-associations/summary:"));
|
||||
assertThat(contract).contains("name: channelGroupCodes")
|
||||
.contains("name: warehouseId")
|
||||
.contains("SmartLoadingInboundCandidatePageEnvelope")
|
||||
.doesNotContain("name: bookingId");
|
||||
}
|
||||
@Test void everyMutationDeclaresDedicatedPermission() throws Exception {
|
||||
authority("assign","platform:firstleg:smart-loading:assign",AuthenticatedUser.class,long.class,AssignRequest.class);
|
||||
|
||||
+32
@@ -6,6 +6,9 @@ import org.apache.ibatis.annotations.Select;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.WorkbenchContainerQuery;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -49,6 +52,35 @@ class SmartLoadingCandidateMapperContractTest {
|
||||
.isLessThan(sql.indexOf("o.order_status_snapshot orderStatusSnapshot"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void globalCandidateQueryHasOptionalWarehouseAndNoContainerJoin() throws Exception {
|
||||
Method list = SmartLoadingCandidateMapper.class.getDeclaredMethod("globalCandidates", Long.class);
|
||||
String sql = String.join(" ", list.getAnnotation(Select.class).value());
|
||||
|
||||
assertThat(sql)
|
||||
.contains("#{warehouseId} IS NULL OR o.warehouse_id=#{warehouseId}")
|
||||
.contains("o.process_status='WAIT_SPLIT'")
|
||||
.doesNotContain("fl_container_booking")
|
||||
.doesNotContain("bookingId");
|
||||
}
|
||||
|
||||
@Test
|
||||
void workbenchContainerQueryExcludesCompletedAndMatchesRelatedInboundFacts() throws Exception {
|
||||
Method list = SmartLoadingCandidateMapper.class.getDeclaredMethod("workbenchContainers",
|
||||
Page.class, WorkbenchContainerQuery.class, Set.class, String.class);
|
||||
String sql = String.join(" ", list.getAnnotation(Select.class).value());
|
||||
|
||||
assertThat(sql)
|
||||
.contains("p.plan_status IN ('EDITING','LOADING')")
|
||||
.contains("b.channel_code IN")
|
||||
.contains("fl_loading_plan_item filter_item")
|
||||
.contains("fl_inbound_order filter_inbound")
|
||||
.contains("filter_item.inbound_code LIKE")
|
||||
.contains("filter_inbound.expected_arrival_at")
|
||||
.contains("filter_inbound.process_status='LOADING'")
|
||||
.doesNotContain("p.plan_status IN ('EDITING','LOADING','COMPLETED')");
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailUsesSameBatchRule() throws Exception {
|
||||
Method detail = SmartLoadingCandidateMapper.class.getDeclaredMethod(
|
||||
|
||||
@@ -7,7 +7,12 @@ import com.baiyue.firstleg.client.OrderCargoClient;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.AssignRequest;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.AdjustRequest;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.Candidate;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.CandidateOrderStatus;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.DeleteRequest;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.GroupMemberCandidate;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.InboundCandidateQuery;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.WorkbenchContainer;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.WorkbenchContainerQuery;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.Plan;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.RecommendRequest;
|
||||
import com.baiyue.firstleg.dto.SmartLoadingDtos.WarehouseRemarkRequest;
|
||||
@@ -39,6 +44,7 @@ import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.sql.SQLException;
|
||||
@@ -144,6 +150,89 @@ class SmartLoadingServiceTest {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void pagesGlobalCandidatesWithoutBindingAContainer() {
|
||||
Candidate clx = withChannel(candidate(9L, 10L, 20L, 30L, 4),
|
||||
"MATSON_CLX_TRUCK", "美森正班海卡");
|
||||
Candidate max = withChannel(candidate(19L, 11L, 21L, 31L, 6),
|
||||
"MATSON_MAX_TRUCK", "美森加班海卡");
|
||||
when(repository.globalCandidates(null)).thenReturn(List.of(clx, max));
|
||||
|
||||
var result = service.inboundCandidates(new InboundCandidateQuery(
|
||||
List.of("MATSON_CLX"), null, null, null, null, null, null, 1, 20));
|
||||
|
||||
assertThat(result.total()).isEqualTo(1);
|
||||
assertThat(result.pages()).isEqualTo(1);
|
||||
assertThat(result.records()).singleElement().satisfies(candidate -> {
|
||||
assertThat(candidate.inboundId()).isEqualTo(10L);
|
||||
assertThat(candidate.channelCode()).isEqualTo("MATSON_CLX_TRUCK");
|
||||
});
|
||||
verify(repository).globalCandidates(null);
|
||||
verify(repository, never()).booking(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void pagesWorkbenchContainersWithTheSameGlobalFilters() {
|
||||
WorkbenchContainer container = new WorkbenchContainer(99L, "CB-1", "CAB-1", null,
|
||||
100L, "义乌仓", "MATSON_CLX_TRUCK", "美森正班海卡", "40HC",
|
||||
1L, "LP-1", "EDITING", 2);
|
||||
WorkbenchContainerQuery query = new WorkbenchContainerQuery(List.of("MATSON_CLX"), 100L,
|
||||
9L, "IN-10", LocalDate.parse("2026-09-01"), LocalDate.parse("2026-09-30"),
|
||||
CandidateOrderStatus.WAIT_SPLIT, 1, 20);
|
||||
when(repository.workbenchContainers(eq(query), any())).thenReturn(
|
||||
new com.baiyue.common.core.api.PageResult<>(List.of(container), 1, 20, 1, 1));
|
||||
|
||||
var result = service.containers(query);
|
||||
|
||||
assertThat(result.records()).containsExactly(container);
|
||||
@SuppressWarnings("unchecked") ArgumentCaptor<java.util.Set<String>> channels =
|
||||
ArgumentCaptor.forClass(java.util.Set.class);
|
||||
verify(repository).workbenchContainers(eq(query), channels.capture());
|
||||
assertThat(channels.getValue()).containsExactlyInAnyOrder(
|
||||
"MATSON_CLX_TRUCK", "MATSON_CLX_PARCEL");
|
||||
}
|
||||
|
||||
@Test
|
||||
void treatsWholeColoadGroupAsOneStablePagingUnit() {
|
||||
Candidate first = withArrival(withChannel(candidate(9L, 10L, 20L, 30L, 4),
|
||||
"MATSON_CLX_TRUCK", "美森正班海卡"), "2026-09-01T09:00:00");
|
||||
Candidate second = withArrival(withChannel(candidate(19L, 11L, 21L, 31L, 6),
|
||||
"MATSON_CLX_TRUCK", "美森正班海卡"), "2026-09-10T09:00:00");
|
||||
GroupSnapshot group = group("ACTIVE", null,
|
||||
new Member(9L, 30L, 10L, "I10", 4), new Member(19L, 31L, 11L, "I11", 6));
|
||||
when(repository.globalCandidates(100L)).thenReturn(List.of(first, second));
|
||||
when(coloadGroups.findByInbound(9L, 10L)).thenReturn(group);
|
||||
when(coloadGroups.findByInbound(19L, 11L)).thenReturn(group);
|
||||
|
||||
var result = service.inboundCandidates(new InboundCandidateQuery(
|
||||
List.of("MATSON_CLX"), 100L, null, null, LocalDate.parse("2026-09-10"),
|
||||
LocalDate.parse("2026-09-10"), null, 1, 1));
|
||||
|
||||
assertThat(result.total()).isEqualTo(1);
|
||||
assertThat(result.records()).singleElement().satisfies(candidate -> {
|
||||
assertThat(candidate.coLoadGroupId()).isEqualTo(700L);
|
||||
assertThat(candidate.availablePackageCount()).isEqualTo(10);
|
||||
assertThat(candidate.groupMembers()).extracting(GroupMemberCandidate::inboundId)
|
||||
.containsExactly(10L, 11L);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void excludesIncompleteColoadGroupInsteadOfPublishingPartialMember() {
|
||||
Candidate first = withChannel(candidate(9L, 10L, 20L, 30L, 4),
|
||||
"MATSON_CLX_TRUCK", "美森正班海卡");
|
||||
GroupSnapshot group = group("ACTIVE", null,
|
||||
new Member(9L, 30L, 10L, "I10", 4), new Member(19L, 31L, 11L, "I11", 6));
|
||||
when(repository.globalCandidates(100L)).thenReturn(List.of(first));
|
||||
when(coloadGroups.findByInbound(9L, 10L)).thenReturn(group);
|
||||
|
||||
var result = service.inboundCandidates(new InboundCandidateQuery(
|
||||
List.of("MATSON_CLX"), 100L, null, null, null, null, null, 1, 20));
|
||||
|
||||
assertThat(result.records()).isEmpty();
|
||||
assertThat(result.total()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void channelDirectoryKeepsActiveZeroStatChannelAndAggregatesCandidateAndContainerFacts() {
|
||||
ChannelRow maxTruck = channel("MATSON_MAX_TRUCK", "美森加班海卡");
|
||||
@@ -1124,6 +1213,16 @@ class SmartLoadingServiceTest {
|
||||
value.orderStatusSnapshot(), value.expectedArrivalAt());
|
||||
}
|
||||
|
||||
private static Candidate withArrival(Candidate value, String expectedArrivalAt) {
|
||||
return new Candidate(value.accountId(), value.inboundId(), value.inboundDetailId(), value.orderId(),
|
||||
value.orderNo(), value.inboundCode(), value.warehouseId(), value.channelCode(), value.channelName(),
|
||||
value.destinationWarehouseId(), value.destinationWarehouseCode(), value.destinationWarehouseName(),
|
||||
value.customerName(), value.goodsName(), value.availablePackageCount(), value.unitGrossWeightKg(),
|
||||
value.unitNetWeightKg(), value.unitVolumeCbm(), value.coLoadGroupId(), value.groupNo(),
|
||||
value.memberCount(), value.groupTotalBoxes(), value.locked(), value.blockReason(),
|
||||
value.orderStatusSnapshot(), LocalDateTime.parse(expectedArrivalAt));
|
||||
}
|
||||
|
||||
private static ChannelRow channel(String channelCode, String channelName) {
|
||||
ChannelRow value = new ChannelRow();
|
||||
value.setChannelCode(channelCode); value.setChannelName(channelName);
|
||||
|
||||
Reference in New Issue
Block a user