优化
This commit is contained in:
+33
@@ -134,9 +134,42 @@ class CabinetLoadingEvidenceServiceTest {
|
||||
verify(files, times(1)).upload(anyString(), anyLong(), anyLong(), anyString(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipartReplayRejectsChangedContentBeforeUploadingAnotherFile() {
|
||||
var first = new MockMultipartFile("file", "photo.png", "image/png", new byte[] {1, 2, 3});
|
||||
var changed = new MockMultipartFile("file", "photo.png", "image/png", new byte[] {1, 2, 4});
|
||||
CabinetLoadingEvidenceEntity existing = evidence("PHOTO_STEP_1");
|
||||
existing.setSourceTaskId(20L);
|
||||
existing.setOperationKey("same-key");
|
||||
existing.setRequestHash(uploadHashForTest(8L, 0, "PHOTO_STEP_1", null, "remark", first));
|
||||
when(mapper.byOperation(20L, "same-key")).thenReturn(existing);
|
||||
|
||||
assertThatThrownBy(() -> service.upload(20L, 8L, "same-key", 0, "PHOTO_STEP_1",
|
||||
null, "remark", changed, 9L, "operator"))
|
||||
.isInstanceOf(BizException.class).hasMessageContaining("请求内容不一致");
|
||||
|
||||
verify(files, never()).upload(anyString(), anyLong(), anyLong(), anyString(), any());
|
||||
}
|
||||
|
||||
private static CabinetLoadingEvidenceEntity evidence(String type) {
|
||||
CabinetLoadingEvidenceEntity value = new CabinetLoadingEvidenceEntity();
|
||||
value.setEvidenceType(type); value.setStatus("ACTIVE"); value.setContainerBookingId(99L);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String uploadHashForTest(long warehouseId, int expectedVersion, String evidenceType,
|
||||
String sealNo, String remark, MockMultipartFile multipart) {
|
||||
try {
|
||||
var digest = java.security.MessageDigest.getInstance("SHA-256");
|
||||
String metadata = warehouseId + "|" + expectedVersion + "|" + evidenceType + "|"
|
||||
+ java.util.Objects.toString(sealNo, "") + "|" + java.util.Objects.toString(remark, "") + "|"
|
||||
+ java.util.Objects.toString(multipart.getOriginalFilename(), "") + "|"
|
||||
+ java.util.Objects.toString(multipart.getContentType(), "") + "|" + multipart.getSize() + "|";
|
||||
digest.update(metadata.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||
digest.update(multipart.getBytes());
|
||||
return java.util.HexFormat.of().formatHex(digest.digest());
|
||||
} catch (java.io.IOException | java.security.NoSuchAlgorithmException exception) {
|
||||
throw new IllegalStateException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -75,6 +75,7 @@ class SmartLoadingCandidateMapperContractTest {
|
||||
.contains("status.name() == 'WAIT_MEASURE'")
|
||||
.contains("status.name() == 'WAIT_SPLIT'")
|
||||
.contains("status.name() == 'LOADING'")
|
||||
.contains("NOT EXISTS (", "active_item.inbound_id=o.id", "active_item.deleted=0")
|
||||
.contains("current_group.id groupId")
|
||||
.contains("group_member_count.group_id=current_group.id")
|
||||
.doesNotContain("fl_container_booking")
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.baiyue.firstleg.dto.LoadingPickDtos.SkipRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.BatchRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.BatchItemRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.ClaimRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.AssignRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.CompleteLoadingRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.Task;
|
||||
import com.baiyue.firstleg.client.WarehousePalletLocationClient;
|
||||
@@ -513,6 +514,99 @@ class LoadingPickServiceTest {
|
||||
verify(repository, times(1)).createAssignment(20L, 9L, "operator", 9L, "operator", "CLAIMED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void claimRechecksSameOperationAfterWaitingForTaskLock() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickOperationEntity completed = taskOperation("claim-race", "CLAIM");
|
||||
when(repository.operation("claim-race")).thenReturn(null, completed);
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.task(20L)).thenReturn(task);
|
||||
when(repository.items(20L)).thenReturn(List.of());
|
||||
|
||||
Task replay = service.claim(20L, new ClaimRequest(0, "claim-race"), 9L, "operator");
|
||||
|
||||
assertThat(replay.id()).isEqualTo(20L);
|
||||
verify(repository, never()).createAssignment(anyLong(), anyLong(), anyString(), anyLong(),
|
||||
anyString(), anyString());
|
||||
verify(smartLoadingRepository, never()).startPicking(anyLong(), anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void assignRechecksSameOperationAfterWaitingForTaskLock() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickOperationEntity completed = taskOperation("assign-race", "ASSIGN");
|
||||
when(repository.operation("assign-race")).thenReturn(null, completed);
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.task(20L)).thenReturn(task);
|
||||
when(repository.items(20L)).thenReturn(List.of());
|
||||
|
||||
Task replay = service.assign(20L, new AssignRequest(11L, "picker", 0, "assign-race"),
|
||||
9L, "operator");
|
||||
|
||||
assertThat(replay.id()).isEqualTo(20L);
|
||||
verify(repository, never()).createAssignment(anyLong(), anyLong(), anyString(), anyLong(),
|
||||
anyString(), anyString());
|
||||
verify(repository, never()).reassign(anyLong(), anyLong(), anyString(), anyLong(),
|
||||
anyString(), anyString(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void pickRechecksSameHistoryAfterWaitingForTaskLock() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickHistoryEntity completed = itemHistory("pick-race", "PICK",
|
||||
"20|30|101|2|MANUAL|[]");
|
||||
when(repository.history("pick-race")).thenReturn(null, completed);
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.assignment(20L)).thenReturn(assignment());
|
||||
when(repository.items(20L, 101L)).thenReturn(List.of(item()));
|
||||
|
||||
Task replay = service.pick(20L, 30L,
|
||||
new PickRequest(101L, 2, List.of(), 0, "pick-race"), 9L, "operator");
|
||||
|
||||
assertThat(replay.id()).isEqualTo(20L);
|
||||
verify(repository, never()).pick(anyLong(), anyInt(), anyString(), anyString(), anyInt(), anyLong());
|
||||
verify(repository, never()).insert(any(LoadingPickHistoryEntity.class));
|
||||
verify(repository, never()).touchTask(anyLong(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void partialRechecksSameHistoryAfterWaitingForTaskLock() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickHistoryEntity completed = itemHistory("partial-race", "PARTIAL",
|
||||
"PARTIAL|20|30|101|1|MANUAL|[]");
|
||||
when(repository.history("partial-race")).thenReturn(null, completed);
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.assignment(20L)).thenReturn(assignment());
|
||||
when(repository.items(20L, 101L)).thenReturn(List.of(item()));
|
||||
|
||||
Task replay = service.partial(20L, 30L,
|
||||
new PartialPickRequest(101L, 1, List.of(), 0, "partial-race"), 9L, "operator");
|
||||
|
||||
assertThat(replay.id()).isEqualTo(20L);
|
||||
verify(repository, never()).partial(anyLong(), anyInt(), anyString(), anyString(), anyInt(), anyLong());
|
||||
verify(repository, never()).insert(any(LoadingPickHistoryEntity.class));
|
||||
verify(repository, never()).touchTask(anyLong(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void skipRechecksSameHistoryAfterWaitingForTaskLock() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
LoadingPickHistoryEntity completed = itemHistory("skip-race", "SKIP",
|
||||
"SKIP|20|30|101|damaged");
|
||||
when(repository.history("skip-race")).thenReturn(null, completed);
|
||||
when(repository.taskForUpdate(20L)).thenReturn(task);
|
||||
when(repository.assignment(20L)).thenReturn(assignment());
|
||||
when(repository.items(20L, 101L)).thenReturn(List.of(item()));
|
||||
|
||||
Task replay = service.skip(20L, 30L,
|
||||
new SkipRequest(101L, 0, "skip-race", "damaged"), 9L, "operator");
|
||||
|
||||
assertThat(replay.id()).isEqualTo(20L);
|
||||
verify(repository, never()).skip(anyLong(), anyInt(), anyLong());
|
||||
verify(repository, never()).insert(any(LoadingPickHistoryEntity.class));
|
||||
verify(repository, never()).touchTask(anyLong(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsPartialPickLockedUntilManualCompletion() {
|
||||
LoadingPickTaskEntity task = task();
|
||||
@@ -1289,6 +1383,41 @@ class LoadingPickServiceTest {
|
||||
return assignment;
|
||||
}
|
||||
|
||||
private static LoadingPickOperationEntity taskOperation(String operationId, String action) {
|
||||
LoadingPickOperationEntity operation = new LoadingPickOperationEntity();
|
||||
operation.setPickTaskId(20L);
|
||||
operation.setOperationId(operationId);
|
||||
operation.setRequestHash(hashForTest(action + "|20|0"));
|
||||
if ("ASSIGN".equals(action)) {
|
||||
operation.setRequestHash(hashForTest("ASSIGN|20|11|picker|0"));
|
||||
}
|
||||
operation.setActionType(action);
|
||||
operation.setOperatorId(9L);
|
||||
operation.setOperatorName("operator");
|
||||
return operation;
|
||||
}
|
||||
|
||||
private static LoadingPickHistoryEntity itemHistory(String operationId, String action, String canonical) {
|
||||
LoadingPickHistoryEntity history = new LoadingPickHistoryEntity();
|
||||
history.setPickTaskId(20L);
|
||||
history.setPickItemId(30L);
|
||||
history.setOperationId(operationId);
|
||||
history.setRequestHash(hashForTest(canonical));
|
||||
history.setActionType(action);
|
||||
history.setOperatorId(9L);
|
||||
history.setOperatorName("operator");
|
||||
return history;
|
||||
}
|
||||
|
||||
private static String hashForTest(String value) {
|
||||
try {
|
||||
return java.util.HexFormat.of().formatHex(java.security.MessageDigest.getInstance("SHA-256")
|
||||
.digest(value.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
|
||||
} catch (java.security.NoSuchAlgorithmException exception) {
|
||||
throw new IllegalStateException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static GroupSnapshot coloadGroup(String status, Long lockedPlanId, Member... members) {
|
||||
GroupSnapshot group = mock(GroupSnapshot.class);
|
||||
when(group.groupId()).thenReturn(700L);
|
||||
|
||||
@@ -81,7 +81,6 @@ import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
|
||||
@SpringJUnitConfig(SmartLoadingServiceTest.Config.class)
|
||||
class SmartLoadingServiceTest {
|
||||
@@ -108,6 +107,7 @@ class SmartLoadingServiceTest {
|
||||
when(documentNumbers.next("LP")).thenReturn("LP-260818-0001");
|
||||
when(loadingPickService.mutationSnapshot(anyLong())).thenReturn(
|
||||
new LoadingPickService.MutationSnapshot(null, null, null, Map.of()));
|
||||
when(repository.occupiedOrderIds(any(), anySet())).thenReturn(List.of());
|
||||
when(cargoClient.detail(anyString(), anyLong(), anyLong(), anyLong())).thenReturn(
|
||||
ApiResponse.success(new OrderCargoClient.CargoBinding(20L, 30L, 1, "货物", 5,
|
||||
new BigDecimal("10"), new BigDecimal("2"))));
|
||||
@@ -224,9 +224,9 @@ class SmartLoadingServiceTest {
|
||||
100L, "EXX_TRUCK", "EXX海卡", 3, LocalDateTime.parse("2026-09-01T09:00:00"),
|
||||
"PENDING_REVIEW");
|
||||
when(pendingReviewOrders.page(anyString(), anyList(), isNull(), isNull(), isNull(),
|
||||
isNull(), isNull(), anyLong(), anyLong())).thenAnswer(invocation -> {
|
||||
long page = invocation.getArgument(7);
|
||||
long size = invocation.getArgument(8);
|
||||
isNull(), isNull(), anyList(), anyLong(), anyLong())).thenAnswer(invocation -> {
|
||||
long page = invocation.getArgument(8);
|
||||
long size = invocation.getArgument(9);
|
||||
return ApiResponse.success(new PageResult<>(page == 1 ? List.of(pending) : List.of(),
|
||||
page, size, 1, 1));
|
||||
});
|
||||
@@ -248,11 +248,15 @@ class SmartLoadingServiceTest {
|
||||
.containsExactly(true, true);
|
||||
ArgumentCaptor<InboundCandidateQuery> firstlegQueries =
|
||||
ArgumentCaptor.forClass(InboundCandidateQuery.class);
|
||||
verify(repository, atLeastOnce()).globalCandidateUnits(firstlegQueries.capture(), anySet());
|
||||
verify(repository).globalCandidateUnits(firstlegQueries.capture(), anySet());
|
||||
assertThat(firstlegQueries.getValue().page()).isEqualTo(1);
|
||||
assertThat(firstlegQueries.getValue().size()).isEqualTo(19);
|
||||
assertThat(firstlegQueries.getAllValues()).allSatisfy(requested ->
|
||||
assertThat(requested.orderStatuses()).containsExactly(
|
||||
CandidateOrderStatus.WAIT_MEASURE, CandidateOrderStatus.WAIT_SPLIT,
|
||||
CandidateOrderStatus.LOADING));
|
||||
verify(pendingReviewOrders).page(anyString(), anyList(), isNull(), isNull(), isNull(),
|
||||
isNull(), isNull(), anyList(), eq(1L), eq(20L));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -311,9 +315,10 @@ class SmartLoadingServiceTest {
|
||||
"EXXHK2609020361", 2092797167699816450L, "客户", 193000000000200002L,
|
||||
"EXX_TRUCK", "EXX海卡", 100, LocalDateTime.parse("2026-09-02T15:31:36"),
|
||||
"PENDING_REVIEW");
|
||||
when(repository.occupiedOrderIds(eq(query), anySet())).thenReturn(List.of(998L));
|
||||
when(pendingReviewOrders.page(anyString(), anyList(), eq(193000000000200002L),
|
||||
isNull(), eq("EXXHK2609020361"), eq("2026-09-02"),
|
||||
eq("2026-09-02"), eq(1L), eq(20L)))
|
||||
eq("2026-09-02"), eq(List.of(998L)), eq(1L), eq(20L)))
|
||||
.thenReturn(ApiResponse.success(new PageResult<>(List.of(row), 1, 20, 1, 1)));
|
||||
|
||||
var result = service.inboundCandidates(query);
|
||||
@@ -329,6 +334,7 @@ class SmartLoadingServiceTest {
|
||||
});
|
||||
verify(repository, never()).globalCandidateUnits(any(), anySet());
|
||||
verify(repository, never()).globalCandidatesByUnits(anyList());
|
||||
verify(repository).occupiedOrderIds(eq(query), anySet());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -348,7 +354,7 @@ class SmartLoadingServiceTest {
|
||||
var cargo = new OrderCargoClient.CargoBinding(20L, orderId, 1, "货物", 5,
|
||||
new BigDecimal("50"), new BigDecimal("2.5"));
|
||||
when(pendingReviewOrders.page(anyString(), isNull(), isNull(), eq(customerId),
|
||||
eq(inboundCode), isNull(), isNull(), eq(1L), eq(2L)))
|
||||
eq(inboundCode), isNull(), isNull(), isNull(), eq(1L), eq(2L)))
|
||||
.thenReturn(ApiResponse.success(new PageResult<>(List.of(pending), 1, 2, 1, 1)));
|
||||
when(cargoClient.inboundSnapshot(anyString(), eq(customerId), eq(orderId)))
|
||||
.thenReturn(ApiResponse.success(snapshot));
|
||||
@@ -430,7 +436,7 @@ class SmartLoadingServiceTest {
|
||||
List.of("EXX"), null, null, null, null, null,
|
||||
List.of(CandidateOrderStatus.PENDING_REVIEW), 1, 20);
|
||||
when(pendingReviewOrders.page(anyString(), anyList(), isNull(), isNull(), isNull(),
|
||||
isNull(), isNull(), eq(1L), eq(20L))).thenThrow(new RuntimeException("offline"));
|
||||
isNull(), isNull(), anyList(), eq(1L), eq(20L))).thenThrow(new RuntimeException("offline"));
|
||||
|
||||
assertThatThrownBy(() -> service.inboundCandidates(query))
|
||||
.isInstanceOf(BizException.class)
|
||||
|
||||
Reference in New Issue
Block a user