package com.baiyue.lastleg.calendar; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder; import com.baiyue.lastleg.mapper.LastlegOrderMapper; 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 java.util.List; import static org.junit.jupiter.api.Assertions.*; class WarehouseCalendarMapperTest { /** 同柜跨客户不重复,人工改期与已建计划日期分别保留。 */ @Test void deduplicatesCustomersPreservesManualAndFrozenDatesAndFindsCrossMonthRescheduling() throws Exception { var ds = new JdbcDataSource(); ds.setURL("jdbc:h2:mem:warehouse-calendar;MODE=MySQL;DB_CLOSE_DELAY=-1"); try (var connection=ds.getConnection(); var sql=connection.createStatement()) { sql.execute("CREATE TABLE ll_order(id BIGINT,customer_id VARCHAR,source_container_booking_id VARCHAR,container_no VARCHAR," + "overseas_warehouse_id VARCHAR,overseas_warehouse_name VARCHAR,overseas_warehouse_code VARCHAR," + "estimated_warehouse_at TIMESTAMP,arrival_time_version INT)"); sql.execute("CREATE TABLE ll_inbound_plan(source_order_id BIGINT,customer_id VARCHAR)"); sql.execute("CREATE TABLE ll_receiving_container_session(source_container_booking_id VARCHAR,receiving_at TIMESTAMP)"); sql.execute("INSERT INTO ll_order VALUES(1,'A','1','CONT1','W','Warehouse','W','2026-10-01 09:00:00',0)," + "(2,'B','1','CONT1','W','Warehouse','W','2026-10-01 09:00:00',0)," + "(3,'A','2','CONT2','W','Warehouse','W','2026-11-10 09:00:00',2)," + "(4,'A','3','CONT3','W','Warehouse','W','2026-11-12 09:00:00',0)"); sql.execute("INSERT INTO ll_inbound_plan VALUES(4,'A')"); } var config=new MybatisConfiguration(); config.setEnvironment(new Environment("test",new JdbcTransactionFactory(),ds)); config.addMapper(WarehouseCalendarMapper.class); config.addMapper(LastlegOrderMapper.class); try(var session=new MybatisSqlSessionFactoryBuilder().build(config).openSession()) { var rows=session.getMapper(WarehouseCalendarMapper.class).calendar(LocalDateTime.parse("2026-11-01T00:00:00"),LocalDateTime.parse("2026-12-01T00:00:00"),List.of("1")); assertEquals(3,rows.size()); assertEquals("1",rows.get(0).sourceContainerBookingId()); assertNull(rows.get(0).plannedAt()); assertEquals(2,rows.get(1).version()); assertEquals("2026-11-10T09:00",rows.get(1).plannedAt().toString()); assertEquals(1,rows.get(2).planCount()); assertNotNull(rows.get(2).plannedAt()); // 清关提柜与日历必须读取相同的人工/冻结事实,默认日期由前端统一推算。 var customs=session.getMapper(LastlegOrderMapper.class).findContainerArrivalTimes(List.of("1","2","3")); assertEquals(3,customs.size()); for(var calendar:rows) { var arrival=customs.stream().filter(row -> row.sourceContainerBookingId().equals(calendar.sourceContainerBookingId())).findFirst().orElseThrow(); assertEquals(calendar.plannedAt(),arrival.estimatedWarehouseArrivalAt()); assertEquals(calendar.actualAt(),arrival.actualWarehouseArrivalAt()); assertEquals(calendar.version(),arrival.version()); assertEquals(calendar.planCount(),arrival.planCount()); } } } }