优化
This commit is contained in:
+1
-1
@@ -106,7 +106,7 @@ class ContainerControllerContractTest {
|
||||
.contains("未满配 requirementId")
|
||||
.contains("containerNo 可空")
|
||||
.contains("提柜后由拖车链调用柜次服务原子补录真实柜号")
|
||||
.contains("校验 ISO 6346 结构、设备类别、校验位和全局唯一性")
|
||||
.contains("校验 ISO 6346 结构、设备类别和校验位,并保证同一订舱内唯一")
|
||||
.doesNotContain("/api/platform/v1/containers/{id}/transport:")
|
||||
.doesNotContain("ContainerOceanBookingUnlinkRequest")
|
||||
.doesNotContain("ContainerTransportUpdateRequest")
|
||||
|
||||
+1
@@ -52,6 +52,7 @@ class OceanBookingMapperContractTest {
|
||||
com.baomidou.mybatisplus.extension.plugins.pagination.Page.class, LinkCandidateQuery.class)
|
||||
.getAnnotation(Select.class).value());
|
||||
assertThat(sql)
|
||||
.contains("booking.bill_of_lading_no")
|
||||
.contains("booking.status='RELEASED'")
|
||||
.contains("booking.warehouse_id=#{query.warehouseId}")
|
||||
.contains("booking.channel_code=#{query.channelGroupCode}")
|
||||
|
||||
+12
@@ -4,6 +4,8 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -23,6 +25,8 @@ class OceanBookingSqlContractTest {
|
||||
.contains("customer_name VARCHAR(128) NULL COMMENT '可选客户名称快照'")
|
||||
.contains("MODIFY COLUMN customer_id BIGINT UNSIGNED NULL COMMENT '可选客户ID快照'")
|
||||
.contains("MODIFY COLUMN customer_name VARCHAR(128) NULL COMMENT '可选客户名称快照'")
|
||||
.contains("UNIQUE KEY uk_ocean_booking_bill_of_lading (bill_of_lading_no)")
|
||||
.contains("ADD UNIQUE KEY uk_ocean_booking_bill_of_lading (bill_of_lading_no)")
|
||||
.doesNotContain("service_term")
|
||||
.doesNotContain("freight_term")
|
||||
.contains("carrier_code VARCHAR(16) NULL COMMENT '船司标准编码")
|
||||
@@ -33,6 +37,14 @@ class OceanBookingSqlContractTest {
|
||||
.doesNotContain("ALTER TABLE fl_container_booking");
|
||||
}
|
||||
|
||||
@Test
|
||||
void billOfLadingNumberIsUniqueInModuleAndUnifiedInitialization() throws Exception {
|
||||
String unified = Files.readString(Path.of("../deploy/sql/头程数据库统一增量-MySQL8.sql"));
|
||||
for (String source : new String[] {sql(), unified}) {
|
||||
assertThat(source).contains("UNIQUE KEY uk_ocean_booking_bill_of_lading (bill_of_lading_no)");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void enforcesStateIdempotencyAndAuditContracts() throws Exception {
|
||||
assertThat(sql())
|
||||
|
||||
+13
-1
@@ -326,7 +326,7 @@ class OceanBookingServiceTest {
|
||||
|
||||
@Test
|
||||
void linkCandidatesDefaultsPaginationAndDelegatesOneNormalizedSetQuery() {
|
||||
LinkCandidate candidate = new LinkCandidate(101L, "OB-260818-0001", 201L, "40HQ",
|
||||
LinkCandidate candidate = new LinkCandidate(101L, "OB-260818-0001", "BL-001", 201L, "40HQ",
|
||||
new BigDecimal("68"), new BigDecimal("19500"), 3, 1, 2,
|
||||
"客户A", "深圳中转仓", "美森正班");
|
||||
when(repository.linkCandidates(any())).thenReturn(new PageResult<>(List.of(candidate), 1, 20, 1, 1));
|
||||
@@ -347,6 +347,18 @@ class OceanBookingServiceTest {
|
||||
verify(repository, never()).containers(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsDuplicateBillOfLadingNumberBeforeCreatingDraft() {
|
||||
stubMasterData();
|
||||
when(repository.billOfLadingNoExists("BL-001", null)).thenReturn(true);
|
||||
|
||||
assertThatThrownBy(() -> service.create(9L, "admin", "duplicate-bill",
|
||||
new CreateRequest(0, completeDraft())))
|
||||
.isInstanceOf(BizException.class).hasMessageContaining("提单号已存在");
|
||||
|
||||
verify(repository, never()).insert(any(OceanBookingEntity.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void linkCandidatesReturnsNormalEmptyPageWithOneSetQueryAndNoDiagnosticFields() {
|
||||
when(repository.linkCandidates(any())).thenReturn(new PageResult<>(List.of(), 1, 20, 0, 0));
|
||||
|
||||
+5
-3
@@ -106,16 +106,18 @@ class ContainerBookingMapperContractTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("初始化与统一增量锁定真实柜号权威、唯一和ISO结构约束")
|
||||
@DisplayName("初始化与统一增量锁定真实柜号权威、订舱内唯一和ISO结构约束")
|
||||
void databaseContractsFreezeAuthoritativeContainerNumber() throws Exception {
|
||||
String module = Files.readString(Path.of("src/main/resources/db/phase3-smart-loading-schema.sql"));
|
||||
String unified = Files.readString(Path.of("../deploy/sql/头程数据库统一增量-MySQL8.sql"));
|
||||
assertThat(module.toLowerCase())
|
||||
.contains("国内唯一权威真实集装箱号")
|
||||
.contains("国内柜次真实集装箱号权威快照")
|
||||
.contains("同一订舱内唯一,不同提单允许复用")
|
||||
.contains("提柜后可原子补录");
|
||||
for (String source : new String[] {module, unified}) {
|
||||
assertThat(source.toLowerCase())
|
||||
.contains("unique key uk_container_booking_container_no (container_no)")
|
||||
.contains("unique key uk_container_booking_ocean_container (ocean_booking_id,container_no)")
|
||||
.contains("key idx_container_booking_container_no (container_no,updated_at,id)")
|
||||
.contains("constraint chk_container_booking_iso6346")
|
||||
.contains("^[a-z]{3}[ujz][0-9]{7}$");
|
||||
}
|
||||
|
||||
+2
-1
@@ -68,7 +68,8 @@ class SmartLoadingSqlContractTest {
|
||||
.contains("source_type varchar(24) NOT NULL DEFAULT 'SIMULATION'")
|
||||
.contains("record_status varchar(16) NOT NULL DEFAULT 'ACTIVE'")
|
||||
.contains("merged_into_container_booking_id bigint unsigned DEFAULT NULL")
|
||||
.contains("UNIQUE KEY uk_container_booking_container_no (container_no)")
|
||||
.contains("UNIQUE KEY uk_container_booking_ocean_container (ocean_booking_id,container_no)")
|
||||
.contains("KEY idx_container_booking_container_no (container_no,updated_at,id)")
|
||||
.contains("KEY idx_container_booking_merge (record_status,merged_into_container_booking_id)")
|
||||
.contains("CREATE TRIGGER trg_loading_plan_before_update")
|
||||
.contains("装柜状态只允许单向推进")
|
||||
|
||||
@@ -246,7 +246,7 @@ class ContainerServiceTest {
|
||||
|
||||
assertThat(result.getContainerNo()).isNull();
|
||||
assertThat(result.getCabinetNo()).isEqualTo("柜-001");
|
||||
verify(repository, never()).containerNoExists(anyString(), anyLong());
|
||||
verify(repository, never()).containerNoExists(anyLong(), anyString(), anyLong());
|
||||
verify(repository).linkReleasedBooking(eq(11L), eq(21L), eq(31L), isNull(),
|
||||
any(OceanBookingEntity.class), eq(2), eq(9L), eq("admin"));
|
||||
verify(oceanBookings).insert(any(OceanBookingContainerAllocationEntity.class));
|
||||
@@ -303,12 +303,12 @@ class ContainerServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("提柜补录真实柜号仍拒绝全局重复号")
|
||||
@DisplayName("提柜补录真实柜号拒绝同一订舱内重复号")
|
||||
void rejectsDuplicateRealContainerNumberAfterPickup() {
|
||||
ContainerBookingEntity linked = booking("EDITING", 3);
|
||||
linked.setContainerNo(null); linked.setOceanBookingId(21L); linked.setOceanBookingRequirementId(31L);
|
||||
when(repository.find(11L)).thenReturn(linked);
|
||||
when(repository.containerNoExists("MSCU6639870", 11L)).thenReturn(true);
|
||||
when(repository.containerNoExists(21L, "MSCU6639870", 11L)).thenReturn(true);
|
||||
|
||||
assertThatThrownBy(() -> service.assignRealContainerNo(
|
||||
11L, "MSCU6639870", 3, 9L, "admin"))
|
||||
@@ -331,10 +331,10 @@ class ContainerServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("首次关联规范化后按全局唯一真实柜号拒绝冲突")
|
||||
@DisplayName("首次关联规范化后按同一订舱唯一真实柜号拒绝冲突")
|
||||
void rejectsNormalizedContainerNumberConflict() {
|
||||
stubLinkContext();
|
||||
when(repository.containerNoExists("MSCU6639870", 11L)).thenReturn(true);
|
||||
when(repository.containerNoExists(21L, "MSCU6639870", 11L)).thenReturn(true);
|
||||
|
||||
assertThatThrownBy(() -> service.linkOceanBooking(11L, 9L, "admin",
|
||||
new OceanBookingLinkRequest(21L, 31L, "ms cu6639870", 2)))
|
||||
|
||||
@@ -276,19 +276,19 @@ class LoadingListServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsGlobalUniqueConflictIncludingConcurrentConstraintRace() {
|
||||
void rejectsSameBookingContainerConflictIncludingConcurrentConstraintRace() {
|
||||
AggregateRow before = row("EDITING", 3);
|
||||
before.setContainerNo("MSCU6639870");
|
||||
when(repository.aggregate(10L)).thenReturn(before);
|
||||
when(containerRepository.containerNoExists("CSQU3054383", 30L)).thenReturn(true);
|
||||
when(containerRepository.containerNoExists(90L, "CSQU3054383", 30L)).thenReturn(true);
|
||||
|
||||
assertThatThrownBy(() -> service.updateTransport(10L, 100L, 9L, "operator", "unique-key",
|
||||
new TransportUpdateRequest("CSQU3054383", null, 3, "唯一冲突")))
|
||||
.isInstanceOf(BizException.class).hasMessageContaining("已存在");
|
||||
|
||||
when(containerRepository.containerNoExists("CSQU3054383", 30L)).thenReturn(false);
|
||||
when(containerRepository.containerNoExists(90L, "CSQU3054383", 30L)).thenReturn(false);
|
||||
when(repository.updateTransport(10L, 3, "CSQU3054383", null, 9L, "operator"))
|
||||
.thenThrow(new org.springframework.dao.DuplicateKeyException("uk_container_booking_container_no"));
|
||||
.thenThrow(new org.springframework.dao.DuplicateKeyException("uk_container_booking_ocean_container"));
|
||||
assertThatThrownBy(() -> service.updateTransport(10L, 100L, 9L, "operator", "race-key",
|
||||
new TransportUpdateRequest("CSQU3054383", null, 3, "并发唯一冲突")))
|
||||
.isInstanceOf(BizException.class).hasMessageContaining("已存在");
|
||||
@@ -961,6 +961,7 @@ class LoadingListServiceTest {
|
||||
row.setPlanStatus(status);
|
||||
row.setWarehouseId(100L);
|
||||
row.setChannelCode("OA_LA_TRUCK");
|
||||
row.setOceanBookingId(90L);
|
||||
row.setOutboundConfirmedAt(java.time.LocalDateTime.now());
|
||||
row.setVersion(version);
|
||||
return row;
|
||||
|
||||
Reference in New Issue
Block a user