This commit is contained in:
2026-10-20 14:22:31 +08:00
parent 19745e46c1
commit a870b920fe
95 changed files with 2616 additions and 275 deletions
@@ -0,0 +1,63 @@
package com.baiyue.lastleg.transfer;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baiyue.lastleg.entity.LastlegOrder;
import com.baiyue.lastleg.inventory.query.mapper.InventoryPieceMapper;
import com.baiyue.lastleg.transfer.mapper.TransferQuantityMapper;
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 static org.junit.jupiter.api.Assertions.*;
class TransferQuantityMapperTest {
/** 使用真实MyBatis注册解析动态XML,避免仅javac放过未转义比较符。 */
@Test void registersBothDynamicQuantityMappers() {
var configuration = new MybatisConfiguration();
assertDoesNotThrow(() -> configuration.addMapper(TransferQuantityMapper.class));
assertDoesNotThrow(() -> configuration.addMapper(InventoryPieceMapper.class));
assertTrue(configuration.hasStatement(TransferQuantityMapper.class.getName() + ".rows"));
assertTrue(configuration.hasStatement(InventoryPieceMapper.class.getName() + ".rows"));
}
/** 真正执行SQL核对未知件数、出库后历史实收、同托重叠及客户隔离。 */
@Test void executesQuantityFactsWithoutLosingUnknownsOrShippedHistory() throws Exception {
var ds = new JdbcDataSource();
ds.setURL("jdbc:h2:mem:transfer-quantity-" + System.nanoTime() + ";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)");
sql.execute("CREATE TABLE ll_warehouse_transfer_order(id BIGINT,customer_id VARCHAR,lastleg_order_id BIGINT,package_count BIGINT)");
sql.execute("CREATE TABLE ll_warehouse_transfer_item(customer_id VARCHAR,transfer_order_id BIGINT,declared_quantity BIGINT)");
sql.execute("CREATE TABLE ll_warehouse_transfer_carton(customer_id VARCHAR,transfer_order_id BIGINT,transfer_pallet_id BIGINT,counted_at TIMESTAMP,status VARCHAR,counted_quantity BIGINT)");
sql.execute("CREATE TABLE ll_warehouse_transfer_pallet(id BIGINT,customer_id VARCHAR,warehouse_pallet_id BIGINT)");
sql.execute("INSERT INTO ll_order VALUES(1,'A'),(2,'A'),(3,'B')");
sql.execute("INSERT INTO ll_warehouse_transfer_order VALUES(11,'A',1,3),(12,'A',2,1),(13,'B',3,1)");
sql.execute("INSERT INTO ll_warehouse_transfer_item VALUES('A',11,10),('A',11,NULL),('A',12,4),('B',13,999)");
sql.execute("INSERT INTO ll_warehouse_transfer_pallet VALUES(21,'A',100),(22,'B',100)");
sql.execute("INSERT INTO ll_warehouse_transfer_carton VALUES('A',11,21,CURRENT_TIMESTAMP,'COUNTED',5),('A',11,21,CURRENT_TIMESTAMP,'SHIPPED',7),('A',11,NULL,NULL,'NOT_RECEIVED',NULL),('A',12,NULL,CURRENT_TIMESTAMP,'DISCARDED',NULL),('B',13,22,CURRENT_TIMESTAMP,'COUNTED',999)");
}
var configuration = new MybatisConfiguration();
configuration.setEnvironment(new Environment("test", new JdbcTransactionFactory(), ds));
configuration.addMapper(TransferQuantityMapper.class);
try (var session = new MybatisSqlSessionFactoryBuilder().build(configuration).openSession()) {
var mapper = session.getMapper(TransferQuantityMapper.class);
var filter = new QueryWrapper<LastlegOrder>().eq("customer_id", "A").orderByAsc("id");
var rows = mapper.rows(filter);
assertEquals(2, rows.size());
var first = rows.get(0);
assertNull(first.declaredPieceCount());
assertEquals(12L, first.receivedPieceCount());
assertEquals(2L, first.receivedCartonCount());
assertEquals(1L, first.receivedPalletCount());
assertEquals(1L, first.remainingPalletCount());
assertEquals(1L, first.shippedPalletCount());
assertNull(rows.get(1).receivedPieceCount());
var pallets = mapper.pallets(filter);
assertEquals(2, pallets.size());
assertTrue(pallets.stream().allMatch(pallet -> pallet.customerId().equals("A")));
assertTrue(mapper.rows(new QueryWrapper<LastlegOrder>().eq("customer_id", "NONE")).isEmpty());
}
}
}
@@ -18,7 +18,7 @@ class MerchantWarehouseTransferControllerTest {
private final com.baiyue.lastleg.transfer.service.WarehouseTransferForecastService forecastService = mock(com.baiyue.lastleg.transfer.service.WarehouseTransferForecastService.class);
private final MerchantWarehouseTransferController controller = controller();
private final WarehouseTransferRequests.Query query = new WarehouseTransferRequests.Query(
1, 20, null, null, null, null, null, null, null, null);
1, 20, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
@Test
void merchantPageUsesOnlyAuthenticatedTenant() {
@@ -26,6 +26,13 @@ class MerchantWarehouseTransferControllerTest {
verify(service).page("100", query);
}
/** 汇总只使用已认证客户,平台身份不可借客户入口读取数量。 */
@Test void quantityTotalsUsesOnlyAuthenticatedCustomer() {
controller.quantities(user(100L,"MERCHANT"),query);
verify(service).quantityTotals("100",query);
assertThrows(ResponseStatusException.class,() -> controller.quantities(user(0L,"PLATFORM"),query));
}
@Test
void merchantDetailUsesOnlyAuthenticatedTenant() {
controller.detail(user(100L, "MERCHANT"), "TR001");
@@ -0,0 +1,57 @@
package com.baiyue.lastleg.transfer.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baiyue.lastleg.entity.LastlegOrder;
import com.baiyue.lastleg.transfer.dto.TransferQuantityView;
import com.baiyue.lastleg.transfer.mapper.TransferQuantityMapper.PalletFact;
import com.baiyue.lastleg.transfer.repository.TransferQuantityRepository;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class TransferQuantityServiceTest {
/** 库存转运签收使用目的仓实收凭据,同时保留原仓已出库托盘。 */
@Test void destinationReceiptUsesExactOriginalPiecesAndPhysicalTargetPallets() {
var repository=mock(TransferQuantityRepository.class);
var service=new TransferQuantityService(); ReflectionTestUtils.setField(service,"repository",repository);
var filter=new QueryWrapper<LastlegOrder>().eq("id",3);
when(repository.rows(filter)).thenReturn(List.of(new TransferQuantityView(3L,14L,null,2,0,0,0,0,0,0)));
when(repository.movements(filter)).thenReturn(List.of(
new com.baiyue.lastleg.transfer.mapper.TransferQuantityMapper.MovementFact(3L,"A",1,100L,"RECEIVED",1L,200L,10L),
new com.baiyue.lastleg.transfer.mapper.TransferQuantityMapper.MovementFact(3L,"A",1,101L,"RECEIVED",2L,201L,4L)));
var result=service.totals(filter);
assertEquals(14L,result.receivedPieceCount()); assertEquals(2,result.receivedCartonCount());
assertEquals(2,result.receivedPalletCount()); assertEquals(2,result.shippedPalletCount());
assertEquals(2,result.shippedCartonCount());
}
/** 完整筛选总量保留未知,托盘按客户去重且剩余与出库集合允许重叠。 */
@Test void keepsUnknownsAndDeduplicatesOverlappingPalletsAcrossCustomers() {
var repository = mock(TransferQuantityRepository.class);
var service = new TransferQuantityService(); ReflectionTestUtils.setField(service,"repository",repository);
var filter = new QueryWrapper<LastlegOrder>().eq("order_type","WAREHOUSE_TRANSFER");
when(repository.rows(filter)).thenReturn(List.of(
new TransferQuantityView(1L,null,12L,3,2,1,1,1,1,1),
new TransferQuantityView(2L,4L,null,1,1,1,1,0,1,0)));
when(repository.pallets(filter)).thenReturn(List.of(new PalletFact("A",100L,"COUNTED"),
new PalletFact("A",100L,"SHIPPED"),new PalletFact("A",100L,"COUNTED"),new PalletFact("B",100L,"COUNTED")));
var totals = service.totals(filter);
assertEquals(2,totals.orderCount()); assertNull(totals.declaredPieceCount()); assertEquals(4,totals.knownDeclaredPieceCount());
assertEquals(1,totals.unknownDeclaredOrderCount()); assertNull(totals.receivedPieceCount());
assertEquals(12,totals.knownReceivedPieceCount()); assertEquals(1,totals.unknownReceivedOrderCount());
assertEquals(2,totals.receivedPalletCount()); assertEquals(2,totals.remainingPalletCount()); assertEquals(1,totals.shippedPalletCount());
verify(repository).rows(filter); verify(repository).pallets(filter);
}
/** 空筛选返回准确零值,非转运订单无需额外数量读取。 */
@Test void emptyFilterIsZeroAndNonTransferPageSkipsRepository() {
var repository = mock(TransferQuantityRepository.class);
var service = new TransferQuantityService(); ReflectionTestUtils.setField(service,"repository",repository);
var order = new LastlegOrder(); order.setOrderType("RETURN_RELABEL");
assertTrue(service.forOrders(List.of(order)).isEmpty()); verifyNoInteractions(repository);
var totals = service.totals(new QueryWrapper<LastlegOrder>().apply("1=0"));
assertEquals(0L,totals.declaredPieceCount()); assertEquals(0L,totals.receivedPieceCount()); assertEquals(0,totals.receivedPalletCount());
}
}
@@ -27,7 +27,6 @@ class WarehouseTransferCartonCountTest {
service = spy(new WarehouseTransferService());
ReflectionTestUtils.setField(service, "transfers", orders);
var pickupRepository = org.mockito.Mockito.mock(com.baiyue.lastleg.fleet.pickup.FleetPickupRepository.class);
org.mockito.Mockito.lenient().when(pickupRepository.pdaSource(anyString(), anyString())).thenAnswer(call -> orders.lockForPda(call.getArgument(1), call.getArgument(0)));
ReflectionTestUtils.setField(service, "pickupService", new com.baiyue.lastleg.fleet.pickup.FleetPickupService(pickupRepository,null,null,null,null,null,new ObjectMapper()));
ReflectionTestUtils.setField(service, "transferCartons", cartons);
ReflectionTestUtils.setField(service, "transferItems", items);
@@ -143,7 +143,7 @@ class WarehouseTransferServiceTest {
when(relabelApplications.selectList(any())).thenReturn(List.of(relabel));
var result = service.page("TENANT-A", new WarehouseTransferRequests.Query(
1, 20, null, null, null, null, null, null, null, null));
1, 20, null, null, null, null, null, null, null, null, null, null, null, null, null, null));
assertEquals("RL-20260923-000001", result.records().get(0).relabelNo());
assertEquals("COMPLETED", result.records().get(0).relabelStatus());
@@ -162,7 +162,7 @@ class WarehouseTransferServiceTest {
when(transfers.selectPage(any(), any())).thenReturn(resultPage);
var result = service.page("TENANT-A", new WarehouseTransferRequests.Query(
1, 20, null, null, null, null, null, null, null, null));
1, 20, null, null, null, null, null, null, null, null, null, null, null, null, null, null));
assertEquals(1, result.records().size());
assertNull(result.records().get(0).relabelStatus());
@@ -233,7 +233,6 @@ class WarehouseTransferServiceTest {
});
ReflectionTestUtils.setField(service, "transfers", transfers);
var pickupRepository = org.mockito.Mockito.mock(com.baiyue.lastleg.fleet.pickup.FleetPickupRepository.class);
org.mockito.Mockito.lenient().when(pickupRepository.pdaSource(anyString(), anyString())).thenAnswer(call -> transfers.lockForPda(call.getArgument(1), call.getArgument(0)));
ReflectionTestUtils.setField(service, "pickupService", new com.baiyue.lastleg.fleet.pickup.FleetPickupService(pickupRepository,null,null,null,null,null,new ObjectMapper()));
ReflectionTestUtils.setField(service, "transferItems", transferItems);
ReflectionTestUtils.setField(service, "transferCartons", transferCartons);
@@ -521,8 +520,6 @@ class WarehouseTransferServiceTest {
transfer.setInboundCode("IN-1");
transfer.setNeedRelabel(true);
transfer.setPackageCount(1);
var selected = new WarehouseTransferCarton();
selected.setId(51L); selected.setCustomerCartonMark("CURRENT-BOX");
when(inboundSnapshots.sourceCartons("TENANT-A", "WH-1", "IN-1"))
.thenReturn(List.of(new InboundSnapshotMapper.SourceCarton("CURRENT-BOX", "SKU-1", null, null, null, null, null)));
when(transfers.lockNo("TENANT-A", "WTR-1")).thenReturn(transfer);