fix(firstleg): stop retrying deterministic completion preflight conflicts
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package com.baiyue.firstleg.service;
|
package com.baiyue.firstleg.service;
|
||||||
|
|
||||||
|
import com.baiyue.common.core.api.ResultCode;
|
||||||
import com.baiyue.common.core.exception.BizException;
|
import com.baiyue.common.core.exception.BizException;
|
||||||
import com.baiyue.firstleg.entity.LoadingCompletionCommandEntity;
|
import com.baiyue.firstleg.entity.LoadingCompletionCommandEntity;
|
||||||
import com.baiyue.firstleg.repository.LoadingCompletionCommandRepository;
|
import com.baiyue.firstleg.repository.LoadingCompletionCommandRepository;
|
||||||
@@ -63,7 +64,13 @@ public class LoadingCompletionWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isDeterministicLocalBusinessFailure(String stage, Throwable cause) {
|
private static boolean isDeterministicLocalBusinessFailure(String stage, Throwable cause) {
|
||||||
return cause instanceof BizException && java.util.Set.of(
|
if (!(cause instanceof BizException business)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ("COMPLETED_EVENT_READY".equals(stage)) {
|
||||||
|
return business.getResultCode() == ResultCode.CONFLICT;
|
||||||
|
}
|
||||||
|
return java.util.Set.of(
|
||||||
"OWNER_DISCOVERY", "OWNER_READY", "PLAN_GATE", "TASK_REVIEW",
|
"OWNER_DISCOVERY", "OWNER_READY", "PLAN_GATE", "TASK_REVIEW",
|
||||||
"PLAN_COMPLETE", "COMPLETED_EVENT").contains(stage);
|
"PLAN_COMPLETE", "COMPLETED_EVENT").contains(stage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,57 @@ class LoadingCompletionWorkerTest {
|
|||||||
verify(picks).failCompletion(20L);
|
verify(picks).failCompletion(20L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void completedEventPreflightBusinessFailureIsTerminalOnFirstAttempt() {
|
||||||
|
LoadingCompletionCommandEntity command = command(0);
|
||||||
|
when(commands.due(10)).thenReturn(List.of(command));
|
||||||
|
when(commands.claim(eq(900L), any(String.class), any(LocalDateTime.class))).thenReturn(true);
|
||||||
|
doThrow(new LoadingPickService.CompletionProcessingException("COMPLETED_EVENT_READY",
|
||||||
|
new BizException(ResultCode.CONFLICT, "FBA warehouse internal code is missing")))
|
||||||
|
.when(loadingPicks).processCompletion(eq(command), any(String.class));
|
||||||
|
when(commands.fail(eq(900L), any(String.class), eq("FAILED"), any(LocalDateTime.class),
|
||||||
|
eq("COMPLETED_EVENT_READY"), eq(ResultCode.CONFLICT.code()), eq(null), eq(null))).thenReturn(true);
|
||||||
|
|
||||||
|
worker.dispatch();
|
||||||
|
|
||||||
|
verify(commands).fail(eq(900L), any(String.class), eq("FAILED"), any(LocalDateTime.class),
|
||||||
|
eq("COMPLETED_EVENT_READY"), eq(ResultCode.CONFLICT.code()), eq(null), eq(null));
|
||||||
|
verify(picks).failCompletion(20L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void completedEventPreflightUnexpectedFailureRemainsRetryable() {
|
||||||
|
LoadingCompletionCommandEntity command = command(0);
|
||||||
|
when(commands.due(10)).thenReturn(List.of(command));
|
||||||
|
when(commands.claim(eq(900L), any(String.class), any(LocalDateTime.class))).thenReturn(true);
|
||||||
|
doThrow(new LoadingPickService.CompletionProcessingException("COMPLETED_EVENT_READY",
|
||||||
|
new IllegalStateException("temporary database failure")))
|
||||||
|
.when(loadingPicks).processCompletion(eq(command), any(String.class));
|
||||||
|
|
||||||
|
worker.dispatch();
|
||||||
|
|
||||||
|
verify(commands).fail(eq(900L), any(String.class), eq("RETRYABLE"), any(LocalDateTime.class),
|
||||||
|
eq("COMPLETED_EVENT_READY"), eq("S0500"), eq(null), eq(null));
|
||||||
|
verify(picks, org.mockito.Mockito.never()).failCompletion(org.mockito.ArgumentMatchers.anyLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void completedEventPreflightWarehouseUnavailableBusinessFailureRemainsRetryable() {
|
||||||
|
LoadingCompletionCommandEntity command = command(0);
|
||||||
|
when(commands.due(10)).thenReturn(List.of(command));
|
||||||
|
when(commands.claim(eq(900L), any(String.class), any(LocalDateTime.class))).thenReturn(true);
|
||||||
|
// LoadingPalletLabelService wraps warehouse Feign/circuit-breaker failures in this BizException.
|
||||||
|
doThrow(new LoadingPickService.CompletionProcessingException("COMPLETED_EVENT_READY",
|
||||||
|
new BizException(ResultCode.SERVICE_UNAVAILABLE, "warehouse master data temporarily unavailable")))
|
||||||
|
.when(loadingPicks).processCompletion(eq(command), any(String.class));
|
||||||
|
|
||||||
|
worker.dispatch();
|
||||||
|
|
||||||
|
verify(commands).fail(eq(900L), any(String.class), eq("RETRYABLE"), any(LocalDateTime.class),
|
||||||
|
eq("COMPLETED_EVENT_READY"), eq(ResultCode.SERVICE_UNAVAILABLE.code()), eq(null), eq(null));
|
||||||
|
verify(picks, org.mockito.Mockito.never()).failCompletion(org.mockito.ArgumentMatchers.anyLong());
|
||||||
|
}
|
||||||
|
|
||||||
private static LoadingCompletionCommandEntity command(int attemptCount) {
|
private static LoadingCompletionCommandEntity command(int attemptCount) {
|
||||||
LoadingCompletionCommandEntity command = new LoadingCompletionCommandEntity();
|
LoadingCompletionCommandEntity command = new LoadingCompletionCommandEntity();
|
||||||
command.setId(900L);
|
command.setId(900L);
|
||||||
|
|||||||
Reference in New Issue
Block a user