package com.baiyue.lastleg.discard; import com.baiyue.lastleg.discard.mapper.CargoDiscardInventoryMapper; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import org.junit.jupiter.api.Test; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; import java.util.List; import java.util.UUID; import static org.junit.jupiter.api.Assertions.*; /** 使用真实 Mapper SQL 验证已出库但箱唛状态残留时的废弃门禁。 */ class CargoDiscardAvailableStockMapperTest { /** 全部出库不可选;部分托盘仍有库存时只返回该托盘,提交旧箱号同样失败。 */ @Test void filtersShippedStockFromCandidatesCountsAndSelection() { var source = new DriverManagerDataSource("jdbc:h2:mem:" + UUID.randomUUID() + ";MODE=MySQL;DB_CLOSE_DELAY=-1", "sa", ""); var db = new JdbcTemplate(source); db.execute("CREATE TABLE ll_order(id BIGINT,customer_id VARCHAR(20),inbound_code VARCHAR(50),inbound_order_no VARCHAR(50),overseas_warehouse_id VARCHAR(20),overseas_warehouse_name VARCHAR(50),created_at TIMESTAMP,order_type VARCHAR(30))"); db.execute("CREATE TABLE ll_dropship_order_profile(id BIGINT,customer_id VARCHAR(20),order_id BIGINT,inventory_position_id BIGINT,warehouse_pallet_id BIGINT,status VARCHAR(20))"); db.execute("CREATE TABLE ll_inventory_position(id BIGINT,customer_id VARCHAR(20),balance_id BIGINT,pallet_id BIGINT,status VARCHAR(20),available_packages BIGINT)"); db.execute("CREATE TABLE ll_inventory_balance(id BIGINT,customer_id VARCHAR(20),available_packages BIGINT)"); db.execute("CREATE TABLE ll_dropship_carton(id BIGINT,customer_id VARCHAR(20),order_id BIGINT,profile_id BIGINT,status VARCHAR(20),carton_sequence INT)"); db.execute("CREATE TABLE ll_dropship_carton_sku(customer_id VARCHAR(20),carton_id BIGINT,sku_code VARCHAR(20))"); db.execute("CREATE TABLE ll_dropship_sku_inventory(id BIGINT,customer_id VARCHAR(20),order_id BIGINT,profile_id BIGINT,sku_code VARCHAR(20),product_name VARCHAR(50))"); db.update("INSERT INTO ll_order VALUES(1,'C','EXXHWK2809191130','IP-WH-20260917-979650','WH','测试仓',CURRENT_TIMESTAMP,'ONE_CLICK_DROPSHIP')"); db.update("INSERT INTO ll_dropship_order_profile VALUES(10,'C',1,100,1000,'ACTIVE'),(20,'C',1,200,2000,'ACTIVE')"); db.update("INSERT INTO ll_inventory_position VALUES(100,'C',10000,1000,'OCCUPIED',0),(200,'C',20000,2000,'OCCUPIED',0)"); db.update("INSERT INTO ll_inventory_balance VALUES(10000,'C',0),(20000,'C',0)"); // 复现库存已扣完,箱唛仍为 AVAILABLE 的状态,不能因此暴露可废弃库存。 db.update("INSERT INTO ll_dropship_carton VALUES(11,'C',1,10,'AVAILABLE',1),(21,'C',1,20,'AVAILABLE',1)"); db.update("INSERT INTO ll_dropship_carton_sku VALUES('C',11,'SKU-A'),('C',21,'SKU-A'),('C',21,'SKU-B')"); db.update("INSERT INTO ll_dropship_sku_inventory VALUES(1,'C',1,10,'SKU-A','货物A'),(2,'C',1,20,'SKU-A','货物A'),(3,'C',1,20,'SKU-B','货物B')"); var configuration = new Configuration(new Environment("test", new JdbcTransactionFactory(), source)); configuration.setMapUnderscoreToCamelCase(true); configuration.addMapper(CargoDiscardInventoryMapper.class); try (var session = new SqlSessionFactoryBuilder().build(configuration).openSession(true)) { var mapper = session.getMapper(CargoDiscardInventoryMapper.class); assertTrue(mapper.candidates("C", null).isEmpty()); assertEquals(0, mapper.countAvailableOrder("C", 1L)); assertTrue(mapper.availableOrderCartons("C", 1L).isEmpty()); assertTrue(mapper.lockSelected("C", 1L, "SKU-A", List.of(11L)).isEmpty()); assertTrue(mapper.lockAvailable("C", 1L, "SKU-A", 20).isEmpty()); db.update("UPDATE ll_inventory_position SET available_packages=1 WHERE id=200"); db.update("UPDATE ll_inventory_balance SET available_packages=1 WHERE id=20000"); session.clearCache(); assertEquals(2, mapper.candidates("C", "EXXHWK").size()); assertEquals(1, mapper.countAvailableOrder("C", 1L)); assertEquals(1, mapper.countAvailable("C", 1L, "SKU-A")); assertEquals(0, mapper.countAvailableProfile("C", 1L, 10L, "SKU-A")); assertEquals(1, mapper.countAvailableProfile("C", 1L, 20L, "SKU-B")); assertEquals(List.of(21L), mapper.availableOrderCartons("C", 1L).stream().map(c -> c.getId()).toList()); assertEquals(List.of(21L), mapper.lockSelected("C", 1L, "SKU-A", List.of(11L, 21L)).stream().map(c -> c.getId()).toList()); assertEquals(List.of(21L), mapper.lockAvailable("C", 1L, "SKU-A", 20).stream().map(c -> c.getId()).toList()); assertTrue(mapper.candidates("OTHER", null).isEmpty()); assertEquals(0, mapper.countAvailableOrder("OTHER", 1L)); // 仓位与余额必须属于相同客户,不能借用其他客户的正库存。 db.update("UPDATE ll_inventory_balance SET customer_id='OTHER' WHERE id=20000"); session.clearCache(); assertTrue(mapper.candidates("C", null).isEmpty()); } } }