This commit is contained in:
2026-09-29 18:06:18 +08:00
parent 54ad26c607
commit 2c60a29e0f
30 changed files with 384 additions and 139 deletions
@@ -0,0 +1,55 @@
package com.baiyue.common.web;
import com.baiyue.common.core.api.ApiResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import feign.FeignException;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Set;
/** Explicit failure classification and bounded, credential-free delivery diagnostics. */
public class EventDeliveryFailure extends IllegalStateException {
private static final Set<String> PERMANENT_CODES = Set.of("A0401", "A0403", "EVT_PROTOCOL");
private final boolean permanent;
public EventDeliveryFailure(boolean permanent, String message) {
super(message);
this.permanent = permanent;
}
public static void requireSuccessful(ApiResponse<?> response) {
requireSuccessful(response, Set.of());
}
public static void requireSuccessful(ApiResponse<?> response, Set<String> additionalPermanentCodes) {
if (response == null) throw new EventDeliveryFailure(false, "Consumer returned an empty response");
if (!response.successful()) {
throw new EventDeliveryFailure(response.code() != null && (PERMANENT_CODES.contains(response.code()) || additionalPermanentCodes.contains(response.code())),
"Consumer rejected event: code=" + response.code() + ", message=" + response.message()
+ ", requestId=" + response.requestId() + ", traceId=" + response.traceId());
}
if (response.data() == null) throw new EventDeliveryFailure(false, "Consumer ACK is missing");
}
public static boolean isPermanent(Throwable failure) {
Set<Throwable> seen = Collections.newSetFromMap(new IdentityHashMap<>());
for (Throwable current = failure; current != null && seen.add(current); current = current.getCause()) {
if (current instanceof EventDeliveryFailure delivery && delivery.permanent) return true;
if (current instanceof FeignException http && (http.status() == 401 || http.status() == 403)) return true;
}
return false;
}
public static String diagnostic(Throwable failure, String internalToken) {
Throwable current = failure;
Set<Throwable> seen = Collections.newSetFromMap(new IdentityHashMap<>());
while (current.getCause() != null && seen.add(current)
&& ("NoFallbackAvailableException".equals(current.getClass().getSimpleName())
|| "No fallback available.".equals(current.getMessage()))) current = current.getCause();
String message;
if (current instanceof FeignException http) message = "HTTP " + http.status();
else if (current instanceof JsonProcessingException) message = "Invalid event JSON";
else message = current.getMessage() == null ? "unknown" : current.getMessage();
String text = current.getClass().getSimpleName() + ": " + message;
if (internalToken != null && !internalToken.isBlank()) text = text.replace(internalToken, "***");
text = text.replaceAll("(?i)Bearer\\s+[^\\s,;\\\"']+", "Bearer ***")
.replaceAll("(?i)((?:X-Internal-Service-Token|Authorization|access[_-]?token|refresh[_-]?token)\\s*[=:]\\s*)[^\\s,;]+", "$1***")
.replaceAll("[\\r\\n\\t]+", " ");
return text.length() > 500 ? text.substring(0, 500) : text;
}
}