fix: harden smart loading push idempotency
This commit is contained in:
@@ -252,9 +252,13 @@ public class SmartLoadingService {
|
||||
try {
|
||||
if (repository.push(planId, key, request.expectedVersion(), operatorId, operatorName) != 1) throw conflict("装柜计划已被并发修改");
|
||||
} catch (DuplicateKeyException e) { throw conflict("幂等键已用于其他推送"); }
|
||||
history(planId, null, "PUSH_PRELOAD", "EDITING", "EDITING", key,
|
||||
json(values("version", request.expectedVersion())),
|
||||
json(values("requestHash", requestHash)), operatorId, operatorName);
|
||||
try {
|
||||
history(planId, null, "PUSH_PRELOAD", "EDITING", "EDITING", key,
|
||||
json(values("version", request.expectedVersion())),
|
||||
json(values("requestHash", requestHash)), operatorId, operatorName);
|
||||
} catch (DuplicateKeyException exception) {
|
||||
throw conflict("Idempotency-Key已用于不同推送请求");
|
||||
}
|
||||
LoadingPlanEntity pushed = repository.plan(planId);
|
||||
loadingModelBridge.bindAllocatedBeforeLoading(planId, operatorId, operatorName, key);
|
||||
loadingPickService.beginIfReady(pushed.getContainerBookingId(), operatorId, operatorName);
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -477,7 +478,7 @@ class SmartLoadingServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void rollsBackPushOrchestrationBeforePdaWhenBindingFails() {
|
||||
void bindingFailureStopsBeforePdaAndPropagatesForTransactionRollback() {
|
||||
LoadingPlanEntity editing = plan("EDITING", 3);
|
||||
LoadingPlanEntity pushed = plan("EDITING", 4);
|
||||
pushed.setPushIdempotencyKey("key");
|
||||
@@ -493,6 +494,26 @@ class SmartLoadingServiceTest {
|
||||
|
||||
assertThat(error.getMessage()).contains("预装计划不存在");
|
||||
verify(repository).push(1, "key", 3, 7, "u");
|
||||
verify(repository).insert(any(LoadingPlanHistoryEntity.class));
|
||||
verify(loadingPickService, never()).beginIfReady(anyLong(), anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void concurrentPushKeyClaimReturnsConflictBeforeBridgeAndPda() {
|
||||
LoadingPlanEntity editing = plan("EDITING", 3);
|
||||
when(repository.plan(1)).thenReturn(editing);
|
||||
when(repository.items(1)).thenReturn(List.of(item(8, 9, 20, 1)));
|
||||
when(repository.booking(99)).thenReturn(booking());
|
||||
when(repository.push(1, "race-key", 3, 7, "u")).thenReturn(1);
|
||||
doThrow(new DuplicateKeyException("duplicate operation_id"))
|
||||
.when(repository).insert(any(LoadingPlanHistoryEntity.class));
|
||||
|
||||
BizException error = catchThrowableOfType(
|
||||
() -> service.pushPreload(1, "race-key", new PushRequest(3), 7, "u"), BizException.class);
|
||||
|
||||
assertThat(error.getResultCode()).isEqualTo(ResultCode.CONFLICT);
|
||||
assertThat(error.getMessage()).contains("不同推送请求");
|
||||
verify(loadingModelBridge, never()).bindAllocatedBeforeLoading(anyLong(), anyLong(), anyString(), anyString());
|
||||
verify(loadingPickService, never()).beginIfReady(anyLong(), anyLong(), anyString());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user