fix(dispatch): persist and reuse the resolved draft group
This commit is contained in:
+12
-3
@@ -207,7 +207,12 @@ public class DispatchService {
|
||||
DispatchDraft draft = ensureDraft(operatorId, request.draftToken());
|
||||
DispatchDraftContainer boundContainer = request.draftContainerId() == null ? null : draftContainers.selectById(request.draftContainerId());
|
||||
if (boundContainer == null && request.draftContainerId() == null) {
|
||||
boundContainer = new DispatchDraftContainer(); boundContainer.setId(IdWorker.getId()); boundContainer.setDraftId(draft.getId()); boundContainer.setDraftToken(request.draftToken()); boundContainer.setSequenceNo(1); boundContainer.setCapacity(500); boundContainer.setCreatedAt(LocalDateTime.now()); boundContainer.setUpdatedAt(LocalDateTime.now()); draftContainers.insert(boundContainer);
|
||||
var existing = draftContainers.findByToken(request.draftToken());
|
||||
if (existing.size() > 1) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "存在多个装车分组,请明确选择目标分组");
|
||||
if (existing.size() == 1) boundContainer = existing.get(0);
|
||||
else {
|
||||
boundContainer = new DispatchDraftContainer(); boundContainer.setId(IdWorker.getId()); boundContainer.setDraftId(draft.getId()); boundContainer.setDraftToken(request.draftToken()); boundContainer.setSequenceNo(1); boundContainer.setCapacity(500); boundContainer.setCreatedAt(LocalDateTime.now()); boundContainer.setUpdatedAt(LocalDateTime.now()); draftContainers.insert(boundContainer);
|
||||
}
|
||||
}
|
||||
if (boundContainer == null || !request.draftToken().equals(boundContainer.getDraftToken()) || !Objects.equals(boundContainer.getDraftId(), draft.getId())) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "装车托盘不存在");
|
||||
DispatchPalletCandidate candidate = requireDraftSource(customerId, request);
|
||||
@@ -219,7 +224,11 @@ public class DispatchService {
|
||||
}
|
||||
if (active != null) {
|
||||
if (request.draftToken().equals(active.getDraftToken())
|
||||
&& Objects.equals(trimToNull(operatorId), active.getOperatorId())) return;
|
||||
&& Objects.equals(trimToNull(operatorId), active.getOperatorId())) {
|
||||
if (!Objects.equals(boundContainer.getId(), active.getDraftContainerId()))
|
||||
throw new ResponseStatusException(HttpStatus.CONFLICT, "托盘已锁定到其他装车分组,请先移除后重新选择");
|
||||
return;
|
||||
}
|
||||
throw new ResponseStatusException(HttpStatus.CONFLICT,
|
||||
"Pallet is already locked by another dispatch draft: " + candidate.palletNo());
|
||||
}
|
||||
@@ -235,7 +244,7 @@ public class DispatchService {
|
||||
lock.setReturnReceiptId(candidate.returnReceiptId());
|
||||
lock.setWarehousePalletId(candidate.warehousePalletId());
|
||||
lock.setDraftToken(request.draftToken());
|
||||
lock.setDraftContainerId(request.draftContainerId());
|
||||
lock.setDraftContainerId(boundContainer.getId());
|
||||
lock.setOperatorId(trimToNull(operatorId));
|
||||
lock.setStatus("ACTIVE");
|
||||
lock.setLockedAt(now);
|
||||
|
||||
+57
@@ -160,6 +160,10 @@ class DispatchServiceTest {
|
||||
verify(locks).insert(lock.capture());
|
||||
assertEquals("draft-1", lock.getValue().getDraftToken());
|
||||
assertEquals("7", lock.getValue().getOperatorId());
|
||||
ArgumentCaptor<DispatchDraftContainer> group = ArgumentCaptor.forClass(DispatchDraftContainer.class);
|
||||
verify(draftContainers).insert(group.capture());
|
||||
assertNotNull(group.getValue().getId());
|
||||
assertEquals(group.getValue().getId(), lock.getValue().getDraftContainerId());
|
||||
assertNotNull(lock.getValue().getExpiresAt());
|
||||
assertEquals(null, lock.getValue().getDispatchOrderId());
|
||||
verify(locks).clearInactiveActiveKey("100", "PLAN:10");
|
||||
@@ -175,6 +179,59 @@ class DispatchServiceTest {
|
||||
verify(locks).insert(any(DispatchPalletLock.class));
|
||||
}
|
||||
|
||||
private DispatchDraftContainer existingDraftGroup() {
|
||||
DispatchDraft draft = new DispatchDraft();
|
||||
draft.setId(20L); draft.setDraftToken("draft-1"); draft.setOperatorId("7");
|
||||
when(drafts.lockActive("draft-1", "7")).thenReturn(draft);
|
||||
DispatchDraftContainer group = new DispatchDraftContainer();
|
||||
group.setId(21L); group.setDraftId(20L); group.setDraftToken("draft-1");
|
||||
return group;
|
||||
}
|
||||
|
||||
@Test void omittedGroupReusesTheOnlyGroupAndPersistsItsIdentity() {
|
||||
var group = existingDraftGroup();
|
||||
when(draftContainers.findByToken("draft-1")).thenReturn(List.of(group));
|
||||
when(candidates.lockSelected("100", List.of(10L))).thenReturn(List.of(candidate(10L, null, "ONT8")));
|
||||
service.lockDraftPallet("100", "7", new DispatchRequests.DraftPallet(10L, "draft-1"));
|
||||
var lock = ArgumentCaptor.forClass(DispatchPalletLock.class);
|
||||
verify(locks).insert(lock.capture());
|
||||
assertEquals(21L, lock.getValue().getDraftContainerId());
|
||||
verify(draftContainers, never()).insert(any(DispatchDraftContainer.class));
|
||||
}
|
||||
|
||||
@Test void omittedGroupReplayDoesNotCreateAnotherGroupOrLock() {
|
||||
var group = existingDraftGroup();
|
||||
when(draftContainers.findByToken("draft-1")).thenReturn(List.of(group));
|
||||
when(candidates.lockSelected("100", List.of(10L))).thenReturn(List.of(candidate(10L, null, "ONT8")));
|
||||
var lock = new DispatchPalletLock(); lock.setDraftToken("draft-1"); lock.setOperatorId("7"); lock.setDraftContainerId(21L);
|
||||
when(locks.lockActive("100", "PLAN:10")).thenReturn(lock);
|
||||
service.lockDraftPallet("100", "7", new DispatchRequests.DraftPallet(10L, "draft-1"));
|
||||
verify(draftContainers, never()).insert(any(DispatchDraftContainer.class));
|
||||
verify(locks, never()).insert(any(DispatchPalletLock.class));
|
||||
}
|
||||
|
||||
@Test void multipleGroupsRequireExplicitSelectionWithoutCreatingAnotherGroup() {
|
||||
var group = existingDraftGroup();
|
||||
var second = new DispatchDraftContainer(); second.setId(22L);
|
||||
when(draftContainers.findByToken("draft-1")).thenReturn(List.of(group, second));
|
||||
assertEquals(400, assertThrows(ResponseStatusException.class, () ->
|
||||
service.lockDraftPallet("100", "7", new DispatchRequests.DraftPallet(10L, "draft-1"))).getStatusCode().value());
|
||||
verify(draftContainers, never()).insert(any(DispatchDraftContainer.class));
|
||||
verify(locks, never()).insert(any(DispatchPalletLock.class));
|
||||
}
|
||||
|
||||
@Test void replayCannotSilentlyMoveAPalletToAnotherGroup() {
|
||||
var group = existingDraftGroup();
|
||||
when(draftContainers.selectById(21L)).thenReturn(group);
|
||||
when(candidates.lockSelected("100", List.of(10L))).thenReturn(List.of(candidate(10L, null, "ONT8")));
|
||||
var lock = new DispatchPalletLock(); lock.setDraftToken("draft-1"); lock.setOperatorId("7"); lock.setDraftContainerId(22L);
|
||||
when(locks.lockActive("100", "PLAN:10")).thenReturn(lock);
|
||||
assertEquals(409, assertThrows(ResponseStatusException.class, () ->
|
||||
service.lockDraftPallet("100", "7", new DispatchRequests.DraftPallet(10L, "draft-1", 21L))).getStatusCode().value());
|
||||
assertEquals(22L, lock.getDraftContainerId());
|
||||
verify(locks, never()).insert(any(DispatchPalletLock.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void preArrangeAcceptsVolumeOnlyPlanPallet() {
|
||||
when(candidates.lockSelected("100", List.of(10L)))
|
||||
|
||||
Reference in New Issue
Block a user