Merge remote-tracking branch 'origin/test' into test
This commit is contained in:
@@ -1422,7 +1422,6 @@ CREATE TABLE fl_consumed_event (
|
||||
|
||||
CREATE TABLE fl_container_booking (
|
||||
id bigint unsigned NOT NULL COMMENT '货柜预订ID(雪花ID)',
|
||||
booking_no varchar(40) NOT NULL COMMENT '平台唯一货柜预订单号',
|
||||
container_no varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '真实集装箱号权威快照;同一订舱内唯一,不同提单允许复用;确认前允许修订',
|
||||
cabinet_no varchar(64) NOT NULL COMMENT '平台唯一内部柜号',
|
||||
source_type varchar(24) NOT NULL DEFAULT 'SIMULATION' COMMENT '记录来源:SIMULATION内部模拟柜、OCEAN_BOOKING订舱真实柜',
|
||||
@@ -1477,7 +1476,6 @@ CREATE TABLE fl_container_booking (
|
||||
updated_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '货柜资料最后更新时间',
|
||||
version int unsigned NOT NULL DEFAULT '0' COMMENT '乐观锁版本号',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_container_booking_no (booking_no),
|
||||
UNIQUE KEY uk_container_booking_cabinet (cabinet_no),
|
||||
UNIQUE KEY uk_container_booking_ocean_container (ocean_booking_id,container_no),
|
||||
KEY idx_container_booking_container_no (container_no,updated_at,id),
|
||||
@@ -5337,7 +5335,7 @@ CREATE TRIGGER trg_container_booking_before_update BEFORE UPDATE ON fl_container
|
||||
SELECT MAX(plan_status) INTO current_plan_status
|
||||
FROM baiyue_firstleg.fl_loading_plan WHERE container_booking_id = OLD.id;
|
||||
IF current_plan_status IN ('LOADING', 'COMPLETED') AND (
|
||||
NOT (OLD.id <=> NEW.id) OR NOT (OLD.booking_no <=> NEW.booking_no)
|
||||
NOT (OLD.id <=> NEW.id)
|
||||
OR NOT (OLD.cabinet_no <=> NEW.cabinet_no)
|
||||
OR NOT (OLD.channel_group_code <=> NEW.channel_group_code)
|
||||
OR NOT (OLD.channel_code <=> NEW.channel_code) OR NOT (OLD.channel_name <=> NEW.channel_name)
|
||||
|
||||
@@ -13,7 +13,7 @@ public final class ContainerOverviewDtos {
|
||||
public record Overview(ContainerBookingEntityView container, Long loadingPlanId, PdaSummary pda,
|
||||
List<Document> documents, ExceptionState exception, List<ExceptionHistory> exceptionHistory,
|
||||
List<TimelineEntry> timeline) {}
|
||||
public record ContainerBookingEntityView(Long id, String bookingNo, String cabinetNo, String containerNo,
|
||||
public record ContainerBookingEntityView(Long id, String cabinetNo, String containerNo,
|
||||
String status, Long warehouseId, String warehouseName, String voyageNo, String vesselName,
|
||||
String billOfLadingNo, Long shippingCompanyId, String shippingCompanyName,
|
||||
String departurePortCode, String departurePortName, String destinationPortCode,
|
||||
|
||||
@@ -48,7 +48,7 @@ public final class SmartLoadingDtos { private SmartLoadingDtos(){}
|
||||
if (size == 0) size = 20;
|
||||
}
|
||||
}
|
||||
public record WorkbenchContainer(Long bookingId, String bookingNo, String cabinetNo, String containerNo,
|
||||
public record WorkbenchContainer(Long bookingId, String cabinetNo, String containerNo,
|
||||
Long warehouseId, String warehouseName, String channelGroupCode, String channelCode, String channelName, String containerType,
|
||||
BigDecimal capacityCbm, BigDecimal maxWeightKg,
|
||||
Long planId, String planNo, String planStatus, Integer version) {}
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.time.LocalDateTime;
|
||||
@TableName("fl_container_booking")
|
||||
public class ContainerBookingEntity {
|
||||
@TableId(type = IdType.ASSIGN_ID) private Long id;
|
||||
private String bookingNo;
|
||||
private String containerNo;
|
||||
private String cabinetNo;
|
||||
private String sourceType;
|
||||
|
||||
@@ -335,7 +335,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ba
|
||||
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT b.id bookingId,b.booking_no bookingNo,b.cabinet_no cabinetNo,b.container_no containerNo,
|
||||
SELECT b.id bookingId,b.cabinet_no cabinetNo,b.container_no containerNo,
|
||||
b.warehouse_id warehouseId,b.warehouse_name warehouseName,b.channel_group_code channelGroupCode,b.channel_code channelCode,
|
||||
b.channel_name channelName,b.container_type containerType,b.capacity_cbm capacityCbm,
|
||||
b.max_weight_kg maxWeightKg,p.id planId,p.plan_no planNo,
|
||||
|
||||
+1
-1
@@ -2,6 +2,6 @@ package com.baiyue.firstleg.portschedule.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public record ArrivalCalendarEntry(Long containerBookingId, String bookingNo, String containerNo,
|
||||
public record ArrivalCalendarEntry(Long containerBookingId, String cabinetNo, String containerNo,
|
||||
String vesselName, String voyageNo, String destinationPortName, String destinationWarehouseCode,
|
||||
LocalDateTime arrivalAt, String arrivalType) {}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import org.apache.ibatis.annotations.Update;
|
||||
@Mapper
|
||||
public interface PortScheduleMapper extends BaseMapper<PortScheduleEntity> {
|
||||
@Select("""
|
||||
SELECT c.id containerBookingId,c.booking_no bookingNo,c.container_no containerNo,
|
||||
SELECT c.id containerBookingId,c.cabinet_no cabinetNo,c.container_no containerNo,
|
||||
c.vessel_name vesselName,c.voyage_no voyageNo,c.destination_port_name destinationPortName,
|
||||
c.destination_warehouse_code destinationWarehouseCode,
|
||||
COALESCE(n.actual_at,c.actual_port_arrival_at,c.estimated_port_arrival_at) arrivalAt,
|
||||
|
||||
@@ -42,7 +42,7 @@ public class ContainerOverviewService {
|
||||
.toList();
|
||||
PdaSummary pda = task == null ? null : pda(task);
|
||||
List<TimelineEntry> timeline = timeline(id, task == null ? null : task.id(), exceptionHistory);
|
||||
return new Overview(new ContainerBookingEntityView(booking.getId(), booking.getBookingNo(),
|
||||
return new Overview(new ContainerBookingEntityView(booking.getId(),
|
||||
booking.getCabinetNo(), booking.getContainerNo(), booking.getPlanStatus(), booking.getWarehouseId(),
|
||||
booking.getWarehouseName(), booking.getVoyageNo(), booking.getVesselName(), booking.getBillOfLadingNo(),
|
||||
booking.getShippingCompanyId(), booking.getShippingCompanyName(), booking.getDeparturePortCode(),
|
||||
|
||||
@@ -63,7 +63,7 @@ public class ContainerService {
|
||||
.map(ChannelRow::getChannelCode).anyMatch(channelGroup.channelCodes()::contains);
|
||||
if (!activeGroup) throw conflict("所选物流渠道分组在当前仓库已失效,请重新选择");
|
||||
ContainerBookingEntity value = new ContainerBookingEntity(); value.setId(IdWorker.getId());
|
||||
value.setBookingNo(documentNumbers.next("CB")); value.setCabinetNo(documentNumbers.nextCabinetNo());
|
||||
value.setCabinetNo(documentNumbers.nextCabinetNo());
|
||||
value.setSourceType("SIMULATION"); value.setRecordStatus("ACTIVE");
|
||||
value.setChannelGroupCode(channelGroup.code()); value.setChannelCode(null); value.setChannelName(null);
|
||||
value.setWarehouseId(warehouse.id()); value.setWarehouseName(warehouse.warehouseName());
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ import java.util.Set;
|
||||
public class FirstlegDocumentNumberGenerator {
|
||||
private static final ZoneId BUSINESS_ZONE = ZoneId.of("Asia/Shanghai");
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyMMdd", Locale.ROOT);
|
||||
private static final Set<String> SUPPORTED_PREFIXES = Set.of("OB", "TA", "CB", "LP", "DP");
|
||||
private static final Set<String> SUPPORTED_PREFIXES = Set.of("OB", "TA", "LP", "DP");
|
||||
private static final long MAX_DAILY_SEQUENCE = 9_999L;
|
||||
|
||||
private final JdbcTemplate jdbc;
|
||||
|
||||
+1
-1
@@ -3,6 +3,6 @@ package com.baiyue.firstleg.terminaldrayage.dto;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/** Original pickup appointment wall time; the source does not establish a port time zone. */
|
||||
public record PickupCalendarEntry(Long containerBookingId, String bookingNo, String containerNo,
|
||||
public record PickupCalendarEntry(Long containerBookingId, String cabinetNo, String containerNo,
|
||||
String destinationPortName, String destinationWarehouseCode, String fleetSupplierName,
|
||||
LocalDateTime pickupAppointmentAt, LocalDateTime actualPickupAt) { }
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import java.util.List;
|
||||
@Mapper
|
||||
public interface TerminalDrayageMapper extends BaseMapper<TerminalDrayageEntity> {
|
||||
@Select("""
|
||||
SELECT c.id containerBookingId,c.booking_no bookingNo,c.container_no containerNo,
|
||||
SELECT c.id containerBookingId,c.cabinet_no cabinetNo,c.container_no containerNo,
|
||||
c.destination_port_name destinationPortName,c.destination_warehouse_code destinationWarehouseCode,
|
||||
t.fleet_supplier_name fleetSupplierName,t.pickup_appointment_at pickupAppointmentAt,NULL actualPickupAt
|
||||
FROM fl_terminal_drayage t JOIN fl_container_booking c ON c.id=t.container_booking_id
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ public class TerminalDrayageCalendarService {
|
||||
for(var fact:shipping.containerActualFacts(ids.subList(start,Math.min(start+200,ids.size()))))
|
||||
if(fact.pickedUpAt()!=null) actual.put(fact.containerBookingId(),fact.pickedUpAt());
|
||||
}
|
||||
var enriched=records.stream().map(row->new PickupCalendarEntry(row.containerBookingId(),row.bookingNo(),row.containerNo(),
|
||||
var enriched=records.stream().map(row->new PickupCalendarEntry(row.containerBookingId(),row.cabinetNo(),row.containerNo(),
|
||||
row.destinationPortName(),row.destinationWarehouseCode(),row.fleetSupplierName(),row.pickupAppointmentAt(),actual.get(row.containerBookingId()))).toList();
|
||||
return result(enriched,rows.size()>1000,unknown);
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ BEGIN
|
||||
SELECT MAX(plan_status) INTO current_plan_status
|
||||
FROM fl_loading_plan WHERE container_booking_id = OLD.id;
|
||||
IF current_plan_status IN ('LOADING', 'COMPLETED') AND (
|
||||
NOT (OLD.id <=> NEW.id) OR NOT (OLD.booking_no <=> NEW.booking_no)
|
||||
NOT (OLD.id <=> NEW.id)
|
||||
OR NOT (OLD.cabinet_no <=> NEW.cabinet_no)
|
||||
OR NOT (OLD.channel_group_code <=> NEW.channel_group_code)
|
||||
OR NOT (OLD.channel_code <=> NEW.channel_code) OR NOT (OLD.channel_name <=> NEW.channel_name)
|
||||
|
||||
@@ -35,7 +35,6 @@ CREATE TABLE IF NOT EXISTS fl_document_sequence (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_container_booking (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '货柜预订ID(雪花ID)',
|
||||
booking_no VARCHAR(40) NOT NULL COMMENT '平台唯一货柜预订单号',
|
||||
container_no VARCHAR(64) NULL COMMENT '国内柜次真实集装箱号权威快照;ISO 6346标准格式;同一订舱内唯一,不同提单允许复用;订舱关联可选写入、提柜后可原子补录、预装纠错命令可修改',
|
||||
cabinet_no VARCHAR(64) NOT NULL COMMENT '平台唯一内部柜号',
|
||||
source_type VARCHAR(24) NOT NULL DEFAULT 'SIMULATION' COMMENT '记录来源:SIMULATION内部模拟柜、OCEAN_BOOKING订舱真实柜',
|
||||
@@ -90,7 +89,6 @@ CREATE TABLE IF NOT EXISTS fl_container_booking (
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '货柜资料最后更新时间',
|
||||
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_container_booking_no (booking_no),
|
||||
UNIQUE KEY uk_container_booking_cabinet (cabinet_no),
|
||||
UNIQUE KEY uk_container_booking_ocean_container (ocean_booking_id,container_no),
|
||||
KEY idx_container_booking_container_no (container_no,updated_at,id),
|
||||
@@ -895,7 +893,7 @@ BEGIN
|
||||
SELECT MAX(plan_status) INTO current_plan_status
|
||||
FROM fl_loading_plan WHERE container_booking_id = OLD.id;
|
||||
IF current_plan_status IN ('LOADING', 'COMPLETED') AND (
|
||||
NOT (OLD.id <=> NEW.id) OR NOT (OLD.booking_no <=> NEW.booking_no)
|
||||
NOT (OLD.id <=> NEW.id)
|
||||
OR NOT (OLD.cabinet_no <=> NEW.cabinet_no)
|
||||
OR NOT (OLD.channel_code <=> NEW.channel_code)
|
||||
OR NOT (OLD.channel_name <=> NEW.channel_name)
|
||||
@@ -1542,9 +1540,9 @@ BEGIN
|
||||
END IF;
|
||||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_container_booking'
|
||||
AND column_name IN ('booking_no', 'cabinet_no', 'warehouse_id', 'bill_of_lading_no',
|
||||
AND column_name IN ('cabinet_no', 'warehouse_id', 'bill_of_lading_no',
|
||||
'vessel_name', 'voyage_no', 'container_type', 'capacity_cbm',
|
||||
'max_weight_kg', 'terminal_trucking_company_id', 'terminal_trucking_company')) <> 11 THEN
|
||||
'max_weight_kg', 'terminal_trucking_company_id', 'terminal_trucking_company')) <> 10 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:货柜原型字段缺失';
|
||||
END IF;
|
||||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||||
|
||||
@@ -28,7 +28,7 @@ paths:
|
||||
type: object
|
||||
properties:
|
||||
containerBookingId: {type: string}
|
||||
bookingNo: {type: string}
|
||||
cabinetNo: {type: string}
|
||||
containerNo: {type: string, nullable: true}
|
||||
vesselName: {type: string, nullable: true}
|
||||
voyageNo: {type: string, nullable: true}
|
||||
|
||||
@@ -2323,13 +2323,12 @@ components:
|
||||
allowedActions: {type: array, items: {type: string, enum: [SELECT_FLEET, SCHEDULE_PICKUP, RECORD_EMPTY_RETURN, SEND_PICKUP_EMAIL]}}
|
||||
ContainerBooking:
|
||||
type: object
|
||||
required: [id, bookingNo, cabinetNo, sourceType, recordStatus, warehouseId, warehouseName, channelGroupCode, containerType, capacityCbm, maxWeightKg, planStatus, exceptionActive, destinationClearanceDocumentStatus, customsDeclarationDocumentStatus, deliveryOrderDocumentStatus, version]
|
||||
required: [id, cabinetNo, sourceType, recordStatus, warehouseId, warehouseName, channelGroupCode, containerType, capacityCbm, maxWeightKg, planStatus, exceptionActive, destinationClearanceDocumentStatus, customsDeclarationDocumentStatus, deliveryOrderDocumentStatus, version]
|
||||
properties:
|
||||
id: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
currentPortScheduleId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true, readOnly: true, description: 当前运输读模型由 latestForDispatch 选中的船期记录 ID;无匹配时为空,仅用于定位关联船期,不作为业务编号展示}
|
||||
planId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true, description: 关联智能装柜计划ID;未创建计划时为空}
|
||||
planNo: {type: string, nullable: true, pattern: '^LP-[0-9]{6}-[0-9]{4}$', example: LP-260818-0001, description: 预装与确认装柜清单共用的稳定编号;未创建计划时为空}
|
||||
bookingNo: {type: string, pattern: '^CB-[0-9]{6}-[0-9]{4}$', example: CB-260818-0001, description: 平台内部货柜登记号}
|
||||
containerNo: {type: string, maxLength: 64, nullable: true}
|
||||
cabinetNo: {type: string, pattern: '^D[0-9]{4,}$', example: D0001, description: 平台全局连续且不按日期重置的稳定柜次号}
|
||||
sourceType: {type: string, enum: [SIMULATION, OCEAN_BOOKING], description: SIMULATION 为内部模拟柜;OCEAN_BOOKING 为订舱登记直接生成的真实柜}
|
||||
@@ -2606,11 +2605,10 @@ components:
|
||||
pages: {type: string, pattern: '^[0-9]+$'}
|
||||
SmartLoadingWorkbenchContainer:
|
||||
type: object
|
||||
required: [bookingId, bookingNo, cabinetNo, warehouseId, warehouseName, channelGroupCode,
|
||||
required: [bookingId, cabinetNo, warehouseId, warehouseName, channelGroupCode,
|
||||
containerType, capacityCbm, maxWeightKg, planStatus, version]
|
||||
properties:
|
||||
bookingId: {type: string, pattern: '^[1-9][0-9]*$', description: containerBookingId 技术关联}
|
||||
bookingNo: {type: string, description: 货柜登记业务号}
|
||||
cabinetNo: {type: string, description: 稳定柜次业务号}
|
||||
containerNo: {type: string, nullable: true, description: 真实集装箱号,允许在装柜前后补}
|
||||
warehouseId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
|
||||
@@ -74,7 +74,7 @@ components:
|
||||
exception: {$ref: '#/components/schemas/ExceptionState'}
|
||||
exceptionHistory: {type: array, items: {$ref: '#/components/schemas/ExceptionHistory'}}
|
||||
timeline: {type: array, items: {$ref: '#/components/schemas/TimelineEntry'}}
|
||||
Container: {type: object, properties: {id: {type: integer, format: int64}, bookingNo: {type: string, pattern: '^CB-[0-9]{6}-[0-9]{4}$', example: CB-260818-0001}, cabinetNo: {type: string, pattern: '^D[0-9]{4,}$', example: D0001}, containerNo: {type: string}, status: {type: string}, warehouseId: {type: integer, format: int64}, warehouseName: {type: string}, voyageNo: {type: string}, vesselName: {type: string}, pickupCode: {type: string, pattern: '^[0-9]{1,64}$', nullable: true}, pickupCodeUpdatedBy: {type: integer, format: int64, nullable: true}, pickupCodeUpdatedByName: {type: string, nullable: true}, pickupCodeUpdatedAt: {type: string, format: date-time, nullable: true}, version: {type: integer, minimum: 0}}}
|
||||
Container: {type: object, properties: {id: {type: integer, format: int64}, cabinetNo: {type: string, pattern: '^D[0-9]{4,}$', example: D0001}, containerNo: {type: string}, status: {type: string}, warehouseId: {type: integer, format: int64}, warehouseName: {type: string}, voyageNo: {type: string}, vesselName: {type: string}, pickupCode: {type: string, pattern: '^[0-9]{1,64}$', nullable: true}, pickupCodeUpdatedBy: {type: integer, format: int64, nullable: true}, pickupCodeUpdatedByName: {type: string, nullable: true}, pickupCodeUpdatedAt: {type: string, format: date-time, nullable: true}, version: {type: integer, minimum: 0}}}
|
||||
PdaSummary: {type: object, properties: {taskId: {type: integer, format: int64}, loadingPlanId: {type: integer, format: int64}, containerBookingId: {type: integer, format: int64}, taskStatus: {type: string, description: '持久化任务态:LOADING/COMPLETED'}, workflowStatus: {type: string, enum: [IN_PROGRESS, AWAITING_REVIEW, COMPLETED], description: 'PDA作业展示态'}, taskVersion: {type: integer}, itemCount: {type: integer}, completedItemCount: {type: integer}, unresolvedItemCount: {type: integer}, photoCounts: {type: array, items: {$ref: '#/components/schemas/PhotoCount'}}, photos: {type: array, items: {$ref: '#/components/schemas/Photo'}}, exceptionReasons: {type: array, items: {type: string}}}}
|
||||
PhotoCount: {type: object, properties: {evidenceType: {type: string}, count: {type: integer}}}
|
||||
Photo: {type: object, properties: {evidenceId: {type: integer, format: int64}, tenantId: {type: integer, format: int64}, inboundId: {type: integer, format: int64}, evidenceType: {type: string}, fileId: {type: integer, format: int64}, fileName: {type: string}, status: {type: string, enum: [ACTIVE, VOID]}, downloadPath: {type: string, nullable: true, description: '仅 ACTIVE 照片提供鉴权下载路径;VOID 历史照片为空'}, uploadedAt: {type: string, format: date-time}}}
|
||||
|
||||
@@ -33,7 +33,7 @@ paths:
|
||||
type: object
|
||||
properties:
|
||||
containerBookingId: {type: string, description: 仅用于来源详情定位}
|
||||
bookingNo: {type: string}
|
||||
cabinetNo: {type: string}
|
||||
containerNo: {type: string, nullable: true}
|
||||
destinationPortName: {type: string, nullable: true}
|
||||
destinationWarehouseCode: {type: string, nullable: true}
|
||||
|
||||
+1
-1
@@ -128,7 +128,7 @@ class ContainerControllerContractTest {
|
||||
.contains("required: [oceanBookingId, requirementId, expectedVersion]")
|
||||
.contains("containerNo: {type: string, nullable: true")
|
||||
.doesNotContain("required: [oceanBookingId, requirementId, containerNo, expectedVersion]")
|
||||
.contains("required: [id, bookingNo, cabinetNo, sourceType, recordStatus")
|
||||
.contains("required: [id, cabinetNo, sourceType, recordStatus")
|
||||
.contains("sourceType: {type: string, enum: [SIMULATION, OCEAN_BOOKING]")
|
||||
.contains("recordStatus: {type: string, enum: [ACTIVE, MERGED]")
|
||||
.contains("mergedIntoContainerBookingId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true");
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.baiyue.firstleg.portschedule.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;
|
||||
import org.apache.ibatis.mapping.Environment;
|
||||
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
|
||||
import org.h2.jdbcx.JdbcDataSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.time.LocalDateTime;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class ArrivalCalendarMapperTest {
|
||||
@Test
|
||||
void readsTheExistingCabinetNumberWithoutASecondContainerNumberColumn() throws Exception {
|
||||
var ds = new JdbcDataSource();
|
||||
ds.setURL("jdbc:h2:mem:arrival-cabinet-number;MODE=MySQL;DB_CLOSE_DELAY=-1");
|
||||
try (var connection = ds.getConnection(); var sql = connection.createStatement()) {
|
||||
sql.execute("CREATE TABLE fl_container_booking(id BIGINT,cabinet_no VARCHAR,container_no VARCHAR," +
|
||||
"vessel_name VARCHAR,voyage_no VARCHAR,destination_port_name VARCHAR,destination_warehouse_code VARCHAR," +
|
||||
"actual_port_arrival_at TIMESTAMP,estimated_port_arrival_at TIMESTAMP,record_status VARCHAR)");
|
||||
sql.execute("CREATE TABLE fl_carrier_schedule_node_fact(id BIGINT,container_booking_id BIGINT," +
|
||||
"standard_node VARCHAR,actual_at TIMESTAMP,observed_at TIMESTAMP)");
|
||||
sql.execute("INSERT INTO fl_container_booking VALUES(1,'D0007','TEST-CONT','Vessel','Voyage','Port'," +
|
||||
"'US-W',NULL,'2026-11-25 08:00:00','ACTIVE')");
|
||||
}
|
||||
var config = new MybatisConfiguration();
|
||||
config.setEnvironment(new Environment("test", new JdbcTransactionFactory(), ds));
|
||||
config.addMapper(PortScheduleMapper.class);
|
||||
try (var session = new MybatisSqlSessionFactoryBuilder().build(config).openSession()) {
|
||||
var rows = session.getMapper(PortScheduleMapper.class).arrivalCalendar(
|
||||
LocalDateTime.parse("2026-11-01T00:00:00"), LocalDateTime.parse("2026-12-01T00:00:00"));
|
||||
assertEquals(1, rows.size());
|
||||
assertEquals("D0007", rows.get(0).cabinetNo());
|
||||
assertEquals(1L, rows.get(0).containerBookingId());
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-8
@@ -13,6 +13,7 @@ import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -25,6 +26,7 @@ import static org.mockito.Mockito.when;
|
||||
class SailingCalendarServiceTest {
|
||||
@Mock private SailingCalendarRepository repository;
|
||||
@InjectMocks private SailingCalendarService service;
|
||||
private final LocalDateTime copiedDeparture = LocalDateTime.now(ZoneId.of("Asia/Shanghai")).plusDays(14);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
@@ -33,8 +35,8 @@ class SailingCalendarServiceTest {
|
||||
|
||||
@Test
|
||||
void replaysAnExistingCopyInsteadOfReturningAnInternalError() {
|
||||
SailingEntity source = sailing(1L, "SL-SOURCE", "MANUAL", "QA-20261005", LocalDateTime.of(2026, 10, 5, 10, 0));
|
||||
SailingEntity existing = sailing(2L, "SL-COPY", "COPY", "QA-20261005", LocalDateTime.of(2026, 10, 12, 10, 0));
|
||||
SailingEntity source = sailing(1L, "SL-SOURCE", "MANUAL", "QA-20261005", copiedDeparture.minusDays(7));
|
||||
SailingEntity existing = sailing(2L, "SL-COPY", "COPY", "QA-20261005", copiedDeparture);
|
||||
SailingHistoryEntity history = new SailingHistoryEntity();
|
||||
history.setAction("COPY"); history.setReason("复制自SL-SOURCE"); history.setSailingId(2L);
|
||||
when(repository.sailing(1L)).thenReturn(source);
|
||||
@@ -45,7 +47,7 @@ class SailingCalendarServiceTest {
|
||||
when(repository.bookingSummary(any(SailingEntity.class))).thenReturn(null);
|
||||
|
||||
var result = service.copy(1L, 7L, "admin",
|
||||
new CopyRequest(existing.getEtdAt(), LocalDateTime.of(2026, 10, 9, 18, 0)));
|
||||
new CopyRequest(existing.getEtdAt(), copiedDeparture.minusDays(3)));
|
||||
|
||||
assertThat(result.sailing().id()).isEqualTo(2L);
|
||||
verify(repository, never()).insert(any(SailingEntity.class));
|
||||
@@ -53,13 +55,13 @@ class SailingCalendarServiceTest {
|
||||
|
||||
@Test
|
||||
void reportsBusinessConflictForAnUnrelatedExistingSailing() {
|
||||
SailingEntity source = sailing(1L, "SL-SOURCE", "MANUAL", "QA-20261005", LocalDateTime.of(2026, 10, 5, 10, 0));
|
||||
SailingEntity existing = sailing(2L, "SL-OTHER", "TEMPLATE", "QA-20261005", LocalDateTime.of(2026, 10, 12, 10, 0));
|
||||
SailingEntity source = sailing(1L, "SL-SOURCE", "MANUAL", "QA-20261005", copiedDeparture.minusDays(7));
|
||||
SailingEntity existing = sailing(2L, "SL-OTHER", "TEMPLATE", "QA-20261005", copiedDeparture);
|
||||
when(repository.sailing(1L)).thenReturn(source);
|
||||
when(repository.sailingByBusinessKey(8L, "MATSON", "CNNGB", "USLGB", existing.getEtdAt(), "QA-20261005")).thenReturn(existing);
|
||||
|
||||
assertThatThrownBy(() -> service.copy(1L, 7L, "admin",
|
||||
new CopyRequest(existing.getEtdAt(), LocalDateTime.of(2026, 10, 9, 18, 0))))
|
||||
new CopyRequest(existing.getEtdAt(), copiedDeparture.minusDays(3))))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("已存在航次 QA-20261005");
|
||||
verify(repository, never()).insert(any(SailingEntity.class));
|
||||
@@ -67,11 +69,11 @@ class SailingCalendarServiceTest {
|
||||
|
||||
@Test
|
||||
void rejectsCutoffAtOrAfterCopiedDeparture() {
|
||||
SailingEntity source = sailing(1L, "SL-SOURCE", "MANUAL", "QA-20261005", LocalDateTime.of(2026, 10, 5, 10, 0));
|
||||
SailingEntity source = sailing(1L, "SL-SOURCE", "MANUAL", "QA-20261005", copiedDeparture.minusDays(7));
|
||||
when(repository.sailing(1L)).thenReturn(source);
|
||||
|
||||
assertThatThrownBy(() -> service.copy(1L, 7L, "admin",
|
||||
new CopyRequest(LocalDateTime.of(2026, 10, 12, 10, 0), LocalDateTime.of(2026, 10, 12, 10, 0))))
|
||||
new CopyRequest(copiedDeparture, copiedDeparture)))
|
||||
.isInstanceOf(BizException.class)
|
||||
.hasMessageContaining("收货截止必须早于开船");
|
||||
verify(repository, never()).insert(any(SailingEntity.class));
|
||||
|
||||
@@ -52,7 +52,6 @@ class ContainerServiceTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUpWarehouse() {
|
||||
when(documentNumbers.next("CB")).thenReturn("CB-260818-0001");
|
||||
when(documentNumbers.nextCabinetNo()).thenReturn("D0001");
|
||||
when(smartLoadingService.createForContainer(anyLong(), anyLong(), anyString())).thenAnswer(invocation ->
|
||||
new com.baiyue.firstleg.dto.SmartLoadingDtos.Plan(501L, "LP-260818-0001", "EDITING",
|
||||
@@ -76,8 +75,9 @@ class ContainerServiceTest {
|
||||
verify(repository).insert(captor.capture());
|
||||
ContainerBookingEntity saved = captor.getValue();
|
||||
assertThat(created).isSameAs(saved);
|
||||
assertThat(saved.getBookingNo()).isEqualTo("CB-260818-0001");
|
||||
assertThat(saved.getCabinetNo()).isEqualTo("D0001");
|
||||
verify(documentNumbers).nextCabinetNo();
|
||||
verify(documentNumbers, never()).next("CB");
|
||||
assertThat(saved.getContainerNo()).isNull();
|
||||
assertThat(saved.getOceanBookingId()).isNull();
|
||||
assertThat(saved.getChannelGroupCode()).isEqualTo("MATSON_CLX");
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ class FirstlegDocumentNumberGeneratorTest {
|
||||
|
||||
assertThat(generator.next("OB")).isEqualTo("OB-260818-0001");
|
||||
assertThat(generator.next("TA")).isEqualTo("TA-260818-0001");
|
||||
assertThat(generator.next("CB")).isEqualTo("CB-260818-0001");
|
||||
assertThatThrownBy(() -> generator.next("CB")).isInstanceOf(IllegalArgumentException.class);
|
||||
assertThat(generator.next("LP")).isEqualTo("LP-260818-0001");
|
||||
assertThat(generator.nextCabinetNo()).isEqualTo("D0001");
|
||||
|
||||
|
||||
@@ -627,7 +627,7 @@ class SmartLoadingServiceTest {
|
||||
|
||||
@Test
|
||||
void pagesWorkbenchContainersWithTheSameGlobalFilters() {
|
||||
WorkbenchContainer container = new WorkbenchContainer(99L, "CB-1", "CAB-1", null,
|
||||
WorkbenchContainer container = new WorkbenchContainer(99L, "D0001", null,
|
||||
100L, "义乌仓", "MATSON_CLX", null, null, "40HQ",
|
||||
new BigDecimal("68"), new BigDecimal("19500"),
|
||||
1L, "LP-1", "EDITING", 2);
|
||||
|
||||
+3
-3
@@ -71,10 +71,10 @@ class TaillegRealProducerAcceptanceTest {
|
||||
}
|
||||
|
||||
private void seedCompletedLoadingPlan() {
|
||||
jdbc.update("INSERT IGNORE INTO fl_container_booking(id,booking_no,container_no,cabinet_no,warehouse_id," +
|
||||
jdbc.update("INSERT IGNORE INTO fl_container_booking(id,container_no,cabinet_no,warehouse_id," +
|
||||
"warehouse_name,customer_id,customer_name,estimated_port_arrival_at,container_type,created_by,updated_by) " +
|
||||
"VALUES(?,?,?,?,?,?,?,?,?,?,1,1)",
|
||||
BOOKING, MARKER + "-BOOK", MARKER + "-CNTR", MARKER + "-CAB", 92L, "真实验收海外仓",
|
||||
"VALUES(?,?,?,?,?,?,?,?,?,1,1)",
|
||||
BOOKING, MARKER + "-CNTR", MARKER + "-CAB", 92L, "真实验收海外仓",
|
||||
CUSTOMER, "真实验收客户", java.time.LocalDateTime.of(2026, 9, 30, 12, 0), "40HQ");
|
||||
jdbc.update("INSERT IGNORE INTO fl_loading_plan(id,plan_no,container_booking_id,plan_status,created_by," +
|
||||
"updated_by,version) VALUES(?,?,?,'LOADING',1,1,0)", PLAN, MARKER + "-PLAN", BOOKING);
|
||||
|
||||
+3
-2
@@ -14,11 +14,11 @@ class TerminalDrayageCalendarMapperTest {
|
||||
@Test void respectsExclusiveRangeActiveDepartureWithoutDuplicatingContainersOrOwningShippingFacts() throws Exception {
|
||||
var ds=new JdbcDataSource();ds.setURL("jdbc:h2:mem:pickup-calendar;MODE=MySQL;DB_CLOSE_DELAY=-1");
|
||||
try(var c=ds.getConnection();var s=c.createStatement()) {
|
||||
s.execute("CREATE TABLE fl_container_booking(id BIGINT,booking_no VARCHAR,container_no VARCHAR,destination_port_name VARCHAR,destination_warehouse_code VARCHAR,record_status VARCHAR)");
|
||||
s.execute("CREATE TABLE fl_container_booking(id BIGINT,cabinet_no VARCHAR,container_no VARCHAR,destination_port_name VARCHAR,destination_warehouse_code VARCHAR,record_status VARCHAR)");
|
||||
s.execute("CREATE TABLE fl_terminal_drayage(container_booking_id BIGINT,fleet_supplier_name VARCHAR,pickup_appointment_at TIMESTAMP)");
|
||||
s.execute("CREATE TABLE fl_outbound_dispatch(container_booking_id BIGINT,confirmed_at TIMESTAMP,reversed_at TIMESTAMP,status VARCHAR)");
|
||||
for(int id=1;id<=5;id++) {
|
||||
s.execute("INSERT INTO fl_container_booking VALUES("+id+",'CB','CONT','Port','US-W','"+(id==4?"CANCELLED":"ACTIVE")+"')");
|
||||
s.execute("INSERT INTO fl_container_booking VALUES("+id+",'D000"+id+"','CONT','Port','US-W','"+(id==4?"CANCELLED":"ACTIVE")+"')");
|
||||
s.execute("INSERT INTO fl_terminal_drayage VALUES("+id+",'Fleet','"+(id==3?"2026-11-01 00:00:00":"2026-10-01 09:00:00")+"')");
|
||||
s.execute("INSERT INTO fl_outbound_dispatch VALUES("+id+",'2026-09-01',"+(id==5?"'2026-09-02'":"NULL")+",'WAITING_FOR_LOADING')");
|
||||
}
|
||||
@@ -28,6 +28,7 @@ class TerminalDrayageCalendarMapperTest {
|
||||
try(var session=new MybatisSqlSessionFactoryBuilder().build(config).openSession()) {
|
||||
var rows=session.getMapper(TerminalDrayageMapper.class).calendar(LocalDateTime.parse("2026-10-01T00:00:00"),LocalDateTime.parse("2026-11-01T00:00:00"),1001);
|
||||
assertEquals(List.of(1L,2L),rows.stream().map(r->r.containerBookingId()).toList());assertNull(rows.get(0).actualPickupAt());
|
||||
assertEquals("D0001", rows.get(0).cabinetNo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ class TerminalDrayageCalendarServiceTest {
|
||||
final com.baiyue.firstleg.portschedule.service.CarrierScheduleQueryService shipping=mock(com.baiyue.firstleg.portschedule.service.CarrierScheduleQueryService.class);
|
||||
final TerminalDrayageCalendarService service=new TerminalDrayageCalendarService(mapper,warehouses,shipping,"test");
|
||||
final LocalDate from=LocalDate.of(2026,10,1),to=from.plusDays(31);
|
||||
PickupCalendarEntry row(long id,String code) {return new PickupCalendarEntry(id,"CB-"+id,"TEST-CONT","Port",code,"Fleet",from.atTime(9,0),null);}
|
||||
PickupCalendarEntry row(long id,String code) {return new PickupCalendarEntry(id,"D000"+id,"TEST-CONT","Port",code,"Fleet",from.atTime(9,0),null);}
|
||||
@Test void batchesTheCatalogAndKeepsUnknownScopeVisibleWithoutGuessingPortTimeZone() {
|
||||
when(mapper.calendar(any(),any(),eq(1001))).thenReturn(List.of(row(1,"US-W"),row(2,"CA-W"),row(3,"UNKNOWN")));
|
||||
when(warehouses.catalog("test")).thenReturn(ApiResponse.success(List.of(
|
||||
|
||||
Reference in New Issue
Block a user