fix(firstleg): refresh recommendation capacity

This commit is contained in:
2026-08-30 16:06:20 +08:00
parent 4581c8a294
commit c9ee319e9d
2 changed files with 94 additions and 2 deletions
@@ -262,6 +262,87 @@ class SmartLoadingServiceTest {
verify(repository, never()).touch(anyLong(), anyInt(), anyLong(), anyString());
}
@Test
void recommendRechecksCurrentCapacitySkipsStaleGroupAndContinuesWithLaterCandidate() {
Candidate ordinary = candidate(9L, 10L, 20L, 30L, 4);
Candidate groupFirst = candidate(9L, 11L, 21L, 31L, 4);
Candidate groupSecond = candidate(9L, 12L, 22L, 32L, 4);
Candidate later = candidate(9L, 13L, 23L, 33L, 1);
GroupSnapshot group = group("ACTIVE", null,
new Member(9L, 31L, 11L, 4), new Member(9L, 32L, 12L, 4));
ContainerBookingEntity small = booking();
small.setCapacityCbm(new BigDecimal("5"));
AtomicInteger version = new AtomicInteger();
List<LoadingPlanItemEntity> saved = new ArrayList<>();
when(repository.plan(1L)).thenAnswer(invocation -> plan("EDITING", version.get()));
when(repository.planByBooking(99L)).thenAnswer(invocation -> plan("EDITING", version.get()));
when(repository.booking(99L)).thenReturn(small);
when(repository.items(1L)).thenAnswer(invocation -> List.copyOf(saved));
when(repository.candidates(100L)).thenReturn(List.of(ordinary, groupFirst, groupSecond, later));
when(repository.candidate(1L, 100L, 10L, 20L)).thenReturn(ordinary);
when(repository.candidate(1L, 100L, 13L, 23L)).thenReturn(later);
when(repository.activeItemsByInbound(anyLong(), anyLong(), anyLong())).thenReturn(List.of());
when(repository.activeItem(anyLong(), anyLong(), anyLong())).thenReturn(null);
when(repository.touch(eq(1L), anyInt(), eq(7L), eq("u"))).thenAnswer(invocation -> {
int expected = invocation.getArgument(1);
if (expected != version.get()) return 0;
version.incrementAndGet();
return 1;
});
doAnswer(invocation -> {
LoadingPlanItemEntity item = invocation.getArgument(0);
item.setId(800L + saved.size());
saved.add(item);
return null;
}).when(repository).insert(any(LoadingPlanItemEntity.class));
when(cargoClient.detail(anyString(), anyLong(), anyLong(), anyLong())).thenAnswer(invocation -> {
long orderId = invocation.getArgument(2);
long detailId = invocation.getArgument(3);
return ApiResponse.success(new OrderCargoClient.CargoBinding(detailId, orderId, 1,
"货物", detailId == 23L ? 1 : 4, BigDecimal.TEN, BigDecimal.ONE));
});
when(coloadGroups.findByInbound(9L, 11L)).thenReturn(group);
when(coloadGroups.findByInbound(9L, 12L)).thenReturn(group);
Plan result = service.recommend(1L,
new RecommendRequest(0, "recommend-refresh-group-capacity"), 7L, "u");
assertThat(result.version()).isEqualTo(2);
assertThat(saved).extracting(LoadingPlanItemEntity::getInboundId,
LoadingPlanItemEntity::getPackageCount).containsExactly(
org.assertj.core.groups.Tuple.tuple(10L, 4),
org.assertj.core.groups.Tuple.tuple(13L, 1));
verify(coloadGroups, never()).lockWholeGroupForAssignment(anyLong(), anyLong(), anyLong(),
anyString(), anyLong(), anyString());
}
@Test
void manualGroupAssignmentOverCurrentCapacityStillReturnsA0422() {
Candidate first = candidate(9L, 11L, 21L, 31L, 4);
Candidate second = candidate(9L, 12L, 22L, 32L, 4);
GroupSnapshot group = group("ACTIVE", null,
new Member(9L, 31L, 11L, 4), new Member(9L, 32L, 12L, 4));
ContainerBookingEntity small = booking();
small.setCapacityCbm(new BigDecimal("3"));
when(repository.plan(1L)).thenReturn(plan("EDITING", 0));
when(repository.planByBooking(99L)).thenReturn(plan("EDITING", 0));
when(repository.booking(99L)).thenReturn(small);
when(repository.items(1L)).thenReturn(List.of());
when(repository.candidates(100L)).thenReturn(List.of(first, second));
when(repository.activeItemsByInbound(anyLong(), anyLong())).thenReturn(List.of());
when(coloadGroups.findByInbound(9L, 11L)).thenReturn(group);
when(coloadGroups.findByInbound(9L, 12L)).thenReturn(group);
BizException error = catchThrowableOfType(() -> service.assignGroup(1L,
new com.baiyue.firstleg.dto.SmartLoadingDtos.AssignGroupRequest(
11L, 0, "manual-group-too-large"), 7L, "u"), BizException.class);
assertThat(error.getResultCode()).isEqualTo(ResultCode.VALIDATION_FAILED);
assertThat(error).hasMessageContaining("110%上限");
verify(coloadGroups, never()).lockWholeGroupForAssignment(anyLong(), anyLong(), anyLong(),
anyString(), anyLong(), anyString());
}
@Test
void adjustAndIndividualDeleteRejectColoadMembersBeforeAnyWrite() {
LoadingPlanEntity plan = plan("EDITING", 0);