fix: allocate pallet locations on weight confirmation

This commit is contained in:
2026-09-04 13:21:37 +08:00
parent d6956d396c
commit 944ecb2bf3
21 changed files with 581 additions and 77 deletions
@@ -98,6 +98,22 @@ class InternalPalletLocationControllerTest {
.isEqualTo(ResultCode.FORBIDDEN);
}
@Test
void expandsWholeBlockOnlyForInternalCaller() {
LocationBlockDtos.ExpandRequest request = new LocationBlockDtos.ExpandRequest(
700L, 88L, 4, "expand-block-1", 9001L);
LocationBlockDtos.BlockResult expected = new LocationBlockDtos.BlockResult(700L, "OCCUPIED", List.of(
new LocationBlockDtos.Resource(700L, 704L, 43L, "A04", 4, false, "OCCUPIED")));
when(blockService.expand(request)).thenReturn(expected);
assertThat(controller.expandBlock(INTERNAL_TOKEN, request).data()).isEqualTo(expected);
verify(blockService).expand(request);
assertThatThrownBy(() -> controller.expandBlock("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)));
@@ -0,0 +1,24 @@
package com.baiyue.warehouse.controller;
import org.junit.jupiter.api.Test;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
class LocationBlockOpenApiContractTest {
@Test
void publishesAtomicExpansionWithoutShrinkOrReplacementFields() throws Exception {
String yaml = Files.readString(Path.of("src/main/resources/openapi/location-block-v1.yaml"));
assertThat(yaml)
.contains("/internal/warehouse/v1/pallet-locations/blocks/expand:")
.contains("required: [blockId, businessId, requiredSlotCount, idempotencyKey, operatorId]")
.contains("requiredSlotCount: { type: integer, minimum: 1, maximum: 200 }")
.contains("保留原资源标识")
.contains("缺口共享A0")
.doesNotContain("replacementBlockId")
.doesNotContain("releaseSlotNos");
}
}
@@ -17,6 +17,7 @@ import java.time.LocalDateTime;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -122,6 +123,59 @@ class LocationBlockServiceTest {
verify(locations, never()).lockOwned(0L, 20L, 99L);
}
@Test
void expandsOccupiedBlockWithAdjacentLocationThenA0WithoutChangingExistingSlots() {
LocationBlockEntity block = block("OCCUPIED", 88L, null);
LocationBlockSlotEntity first = slot(701L, 1, 1L, "A01", false, "OCCUPIED");
LocationBlockSlotEntity second = slot(702L, 2, 2L, "A02", false, "OCCUPIED");
LocationEntity overflow = location(99L, "OVERFLOW", "A0");
overflow.setSharedOverflow(true);
when(blocks.lockById(700L)).thenReturn(block);
when(slots.lockByBlockId(700L)).thenReturn(List.of(first, second));
when(locations.lockOwned(0L, 20L, 2L)).thenReturn(location(2L, "A", "A02", "OCCUPIED"));
when(locations.lockAvailable(0L, 20L, 1000)).thenReturn(List.of(
location(3L, "A", "A03"), location(5L, "A", "A05"), location(20L, "B", "B01")));
when(locations.lockOrCreateSharedOverflow(0L, 20L, 9001L)).thenReturn(overflow);
when(blocks.expand(700L, 88L, 2, 5, "expand-1", 0)).thenReturn(1);
LocationBlockDtos.BlockResult result = service.expand(
new LocationBlockDtos.ExpandRequest(700L, 88L, 5, "expand-1", 9001L));
assertThat(result.blockId()).isEqualTo(700L);
assertThat(result.blockStatus()).isEqualTo("OCCUPIED");
assertThat(result.resources()).extracting(LocationBlockDtos.Resource::reservationId)
.startsWith(701L, 702L);
assertThat(result.resources()).extracting(LocationBlockDtos.Resource::locationCode)
.containsExactly("A01", "A02", "A03", "A0", "A0");
assertThat(result.resources()).extracting(LocationBlockDtos.Resource::slotNo)
.containsExactly(1, 2, 3, 4, 5);
assertThat(result.resources()).extracting(LocationBlockDtos.Resource::resourceStatus)
.containsOnly("OCCUPIED");
verify(locations).changeStatus(0L, 20L, 3L, "AVAILABLE", "OCCUPIED", 0, 9001L);
verify(locations, never()).changeStatus(eq(0L), eq(20L), eq(5L), any(), any(), anyInt(), anyLong());
verify(blocks).expand(700L, 88L, 2, 5, "expand-1", 0);
}
@Test
void replaysSameExpansionAndRejectsShrink() {
LocationBlockEntity replay = block("RESERVED", 88L, "expand-1");
replay.setSlotCount(3);
List<LocationBlockSlotEntity> expanded = List.of(
slot(701L, 1, 1L, "A01", false, "RESERVED"),
slot(702L, 2, 2L, "A02", false, "RESERVED"),
slot(703L, 3, 3L, "A03", false, "RESERVED"));
when(blocks.lockById(700L)).thenReturn(replay);
when(slots.lockByBlockId(700L)).thenReturn(expanded);
assertThat(service.expand(new LocationBlockDtos.ExpandRequest(700L, 88L, 3, "expand-1", 9001L))
.resources()).hasSize(3);
assertThatThrownBy(() -> service.expand(
new LocationBlockDtos.ExpandRequest(700L, 88L, 2, "expand-2", 9001L)))
.isInstanceOf(com.baiyue.common.core.exception.BizException.class)
.hasMessageContaining("不允许缩容");
verify(blocks, never()).expand(anyLong(), anyLong(), anyInt(), anyInt(), any(), anyInt());
}
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));