优化
This commit is contained in:
+20
@@ -4,7 +4,9 @@ import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.warehouse.dto.PalletAllocationDtos;
|
||||
import com.baiyue.warehouse.dto.LocationDtos;
|
||||
import com.baiyue.warehouse.dto.LocationBlockDtos;
|
||||
import com.baiyue.warehouse.service.PalletLocationAllocationService;
|
||||
import com.baiyue.warehouse.service.LocationBlockService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
@@ -22,11 +24,13 @@ class InternalPalletLocationControllerTest {
|
||||
private static final String INTERNAL_TOKEN = "test-internal-service-token";
|
||||
|
||||
private final PalletLocationAllocationService service = mock(PalletLocationAllocationService.class);
|
||||
private final LocationBlockService blockService = mock(LocationBlockService.class);
|
||||
private final InternalPalletLocationController controller = new InternalPalletLocationController();
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ReflectionTestUtils.setField(controller, "service", service);
|
||||
ReflectionTestUtils.setField(controller, "blockService", blockService);
|
||||
ReflectionTestUtils.setField(controller, "internalToken", INTERNAL_TOKEN);
|
||||
controller.initializeToken();
|
||||
}
|
||||
@@ -78,6 +82,22 @@ class InternalPalletLocationControllerTest {
|
||||
verify(service).occupiedResources(0L, "firstleg-service", 88L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void reservesWholeBlockOnlyForInternalCaller() {
|
||||
LocationBlockDtos.ReserveRequest request = new LocationBlockDtos.ReserveRequest(20L, "firstleg-service",
|
||||
88L, "RK-001", 3, "reserve-block-1", 9001L, java.time.LocalDateTime.now().plusHours(1));
|
||||
LocationBlockDtos.BlockResult expected = new LocationBlockDtos.BlockResult(700L, "RESERVED", List.of(
|
||||
new LocationBlockDtos.Resource(700L, 701L, 40L, "A01", 1, false, "RESERVED")));
|
||||
when(blockService.reserve(request)).thenReturn(expected);
|
||||
|
||||
assertThat(controller.reserveBlock(INTERNAL_TOKEN, request).data()).isEqualTo(expected);
|
||||
verify(blockService).reserve(request);
|
||||
assertThatThrownBy(() -> controller.reserveBlock("invalid", request))
|
||||
.isInstanceOf(BizException.class)
|
||||
.extracting(exception -> ((BizException) exception).getResultCode())
|
||||
.isEqualTo(ResultCode.FORBIDDEN);
|
||||
}
|
||||
|
||||
private static PalletAllocationDtos.TransferRequest request() {
|
||||
return new PalletAllocationDtos.TransferRequest(10001L, "FIRSTLEG", 88L, 89L,
|
||||
"RK-001-S1", "transfer-88-89", 9001L, List.of(new PalletAllocationDtos.TransferItem(40L, 5)));
|
||||
|
||||
+7
@@ -47,6 +47,13 @@ class WarehouseSchemaContractTest {
|
||||
@Test
|
||||
void shouldHandleCase04() throws IOException {
|
||||
String sql = read("phase2-pallet-location-schema.sql");
|
||||
assertThat(sql).contains("CREATE TABLE IF NOT EXISTS wh_location_block (")
|
||||
.contains("CREATE TABLE IF NOT EXISTS wh_location_block_slot (")
|
||||
.contains("UNIQUE KEY uk_location_block_active_normal")
|
||||
.contains("UNIQUE KEY uk_location_block_active_business")
|
||||
.contains("block_status IN ('RESERVED','OCCUPIED')")
|
||||
.contains("resource_status IN ('RESERVED','OCCUPIED')")
|
||||
.contains("COMMENT='整票库位块槽位;空槽也由当前入仓码独占'");
|
||||
assertColumns(sql, "wh_pallet", "tenant_id", "warehouse_id", "pallet_code", "pallet_type",
|
||||
"capacity_cbm", "weight_kg", "color", "remark", "deleted", "version");
|
||||
assertThat(tableDefinition(sql, "wh_pallet"))
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
package com.baiyue.warehouse.service;
|
||||
|
||||
import com.baiyue.warehouse.dto.LocationBlockDtos;
|
||||
import com.baiyue.warehouse.entity.LocationBlockEntity;
|
||||
import com.baiyue.warehouse.entity.LocationBlockSlotEntity;
|
||||
import com.baiyue.warehouse.entity.LocationEntity;
|
||||
import com.baiyue.warehouse.entity.WarehouseEntity;
|
||||
import com.baiyue.warehouse.mapper.LocationBlockMapper;
|
||||
import com.baiyue.warehouse.mapper.LocationBlockSlotMapper;
|
||||
import com.baiyue.warehouse.repository.LocationRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class LocationBlockServiceTest {
|
||||
private final LocationRepository locations = mock(LocationRepository.class);
|
||||
private final LocationBlockMapper blocks = mock(LocationBlockMapper.class);
|
||||
private final LocationBlockSlotMapper slots = mock(LocationBlockSlotMapper.class);
|
||||
private final LocationBlockService service = new LocationBlockService();
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ReflectionTestUtils.setField(service, "locations", locations);
|
||||
ReflectionTestUtils.setField(service, "blocks", blocks);
|
||||
ReflectionTestUtils.setField(service, "slots", slots);
|
||||
WarehouseEntity warehouse = new WarehouseEntity();
|
||||
warehouse.setId(20L);
|
||||
warehouse.setTenantId(0L);
|
||||
warehouse.setStatus("ACTIVE");
|
||||
when(locations.findWarehouse(0L, 20L)).thenReturn(warehouse);
|
||||
when(blocks.selectOne(any())).thenReturn(null);
|
||||
when(blocks.insert(any(LocationBlockEntity.class))).thenReturn(1);
|
||||
when(slots.insert(any(LocationBlockSlotEntity.class))).thenReturn(1);
|
||||
when(locations.changeStatus(anyLong(), anyLong(), anyLong(), any(), any(), anyInt(), anyLong())).thenReturn(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void reservesSameZoneAndPrefixContinuousRunAheadOfEarlierGap() {
|
||||
when(locations.lockAvailable(0L, 20L, 1000)).thenReturn(List.of(
|
||||
location(1L, "A", "A01"), location(2L, "A", "A03"),
|
||||
location(3L, "B", "B01"), location(4L, "B", "B02"), location(5L, "B", "B03")));
|
||||
|
||||
LocationBlockDtos.BlockResult result = service.reserve(request(3));
|
||||
|
||||
assertThat(result.blockStatus()).isEqualTo("RESERVED");
|
||||
assertThat(result.resources()).extracting(LocationBlockDtos.Resource::locationCode)
|
||||
.containsExactly("B01", "B02", "B03");
|
||||
assertThat(result.resources()).extracting(LocationBlockDtos.Resource::slotNo).containsExactly(1, 2, 3);
|
||||
assertThat(result.resources()).allSatisfy(item -> {
|
||||
assertThat(item.blockId()).isEqualTo(result.blockId());
|
||||
assertThat(item.sharedOverflow()).isFalse();
|
||||
assertThat(item.resourceStatus()).isEqualTo("RESERVED");
|
||||
});
|
||||
verify(locations, never()).lockOrCreateSharedOverflow(anyLong(), anyLong(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsAllOrdinaryLocationsAndUsesSharedA0ForOnlyTheMissingSlots() {
|
||||
LocationEntity ordinary = location(1L, "A", "A01");
|
||||
LocationEntity overflow = location(99L, "OVERFLOW", "A0");
|
||||
overflow.setSharedOverflow(true);
|
||||
when(locations.lockAvailable(0L, 20L, 1000)).thenReturn(List.of(ordinary));
|
||||
when(locations.lockOrCreateSharedOverflow(0L, 20L, 9001L)).thenReturn(overflow);
|
||||
|
||||
LocationBlockDtos.BlockResult result = service.reserve(request(3));
|
||||
|
||||
assertThat(result.resources()).extracting(LocationBlockDtos.Resource::locationCode)
|
||||
.containsExactly("A01", "A0", "A0");
|
||||
assertThat(result.resources()).extracting(LocationBlockDtos.Resource::sharedOverflow)
|
||||
.containsExactly(false, true, true);
|
||||
verify(locations).changeStatus(0L, 20L, 1L, "AVAILABLE", "RESERVED", 0, 9001L);
|
||||
verify(locations, never()).changeStatus(eq(0L), eq(20L), eq(99L), any(), any(), anyInt(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void transfersOwnershipWithoutReleasingAnySlot() {
|
||||
LocationBlockEntity block = block("OCCUPIED", 88L, null);
|
||||
when(blocks.lockById(700L)).thenReturn(block);
|
||||
when(blocks.transfer(700L, 88L, 89L, "RK-S1", "transfer-1", 0)).thenReturn(1);
|
||||
when(slots.lockByBlockId(700L)).thenReturn(List.of(slot(701L, 1, 1L, "A01", false, "OCCUPIED")));
|
||||
|
||||
LocationBlockDtos.BlockResult result = service.transfer(
|
||||
new LocationBlockDtos.TransferRequest(700L, 88L, 89L, "RK-S1", "transfer-1", 9001L));
|
||||
|
||||
assertThat(result.blockStatus()).isEqualTo("OCCUPIED");
|
||||
verify(blocks).transfer(700L, 88L, 89L, "RK-S1", "transfer-1", 0);
|
||||
verify(slots, never()).changeBlockStatus(anyLong(), any(), any());
|
||||
verify(locations, never()).changeStatus(anyLong(), anyLong(), anyLong(), any(), any(), anyInt(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void releasesEveryNormalSlotTogetherAndLeavesSharedA0Available() {
|
||||
LocationBlockEntity block = block("OCCUPIED", 88L, null);
|
||||
LocationBlockSlotEntity first = slot(701L, 1, 1L, "A01", false, "OCCUPIED");
|
||||
LocationBlockSlotEntity overflow = slot(702L, 2, 99L, "A0", true, "OCCUPIED");
|
||||
when(blocks.lockById(700L)).thenReturn(block);
|
||||
when(slots.lockByBlockId(700L)).thenReturn(List.of(first, overflow));
|
||||
when(locations.lockOwned(0L, 20L, 1L)).thenReturn(location(1L, "A", "A01", "OCCUPIED"));
|
||||
when(slots.changeBlockStatus(700L, "OCCUPIED", "RELEASED")).thenReturn(2);
|
||||
when(blocks.changeStatus(700L, 88L, "OCCUPIED", "RELEASED", "release-1", 0)).thenReturn(1);
|
||||
|
||||
LocationBlockDtos.BlockResult result = service.release(new LocationBlockDtos.ReleaseRequest(
|
||||
700L, 88L, "release-1", 9001L, "ALL_PACKAGES_PICKED"));
|
||||
|
||||
assertThat(result.resources()).extracting(LocationBlockDtos.Resource::resourceStatus)
|
||||
.containsOnly("RELEASED");
|
||||
verify(locations).changeStatus(0L, 20L, 1L, "OCCUPIED", "AVAILABLE", 0, 9001L);
|
||||
verify(locations, never()).lockOwned(0L, 20L, 99L);
|
||||
}
|
||||
|
||||
private static LocationBlockDtos.ReserveRequest request(int slots) {
|
||||
return new LocationBlockDtos.ReserveRequest(20L, "firstleg-service", 88L, "RK-001", slots,
|
||||
"reserve-1", 9001L, LocalDateTime.now().plusHours(1));
|
||||
}
|
||||
|
||||
private static LocationEntity location(long id, String zone, String code) {
|
||||
return location(id, zone, code, "AVAILABLE");
|
||||
}
|
||||
|
||||
private static LocationEntity location(long id, String zone, String code, String status) {
|
||||
LocationEntity entity = new LocationEntity();
|
||||
entity.setId(id);
|
||||
entity.setTenantId(0L);
|
||||
entity.setWarehouseId(20L);
|
||||
entity.setZoneCode(zone);
|
||||
entity.setLocationCode(code);
|
||||
entity.setSharedOverflow(false);
|
||||
entity.setStatus(status);
|
||||
entity.setVersion(0);
|
||||
return entity;
|
||||
}
|
||||
|
||||
private static LocationBlockEntity block(String status, long businessId, String operationKey) {
|
||||
LocationBlockEntity entity = new LocationBlockEntity();
|
||||
entity.setId(700L);
|
||||
entity.setTenantId(0L);
|
||||
entity.setWarehouseId(20L);
|
||||
entity.setSourceService("firstleg-service");
|
||||
entity.setBusinessId(businessId);
|
||||
entity.setInboundCode("RK-001");
|
||||
entity.setSlotCount(2);
|
||||
entity.setBlockStatus(status);
|
||||
entity.setLastOperationKey(operationKey);
|
||||
entity.setVersion(0);
|
||||
return entity;
|
||||
}
|
||||
|
||||
private static LocationBlockSlotEntity slot(long id, int slotNo, long locationId, String code,
|
||||
boolean overflow, String status) {
|
||||
LocationBlockSlotEntity entity = new LocationBlockSlotEntity();
|
||||
entity.setId(id);
|
||||
entity.setTenantId(0L);
|
||||
entity.setBlockId(700L);
|
||||
entity.setSlotNo(slotNo);
|
||||
entity.setLocationId(locationId);
|
||||
entity.setLocationCode(code);
|
||||
entity.setSharedOverflow(overflow);
|
||||
entity.setResourceStatus(status);
|
||||
entity.setVersion(0);
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user