diff --git a/event-bridge-service/src/main/java/com/baiyue/eventbridge/service/HttpLastlegEventClient.java b/event-bridge-service/src/main/java/com/baiyue/eventbridge/service/HttpLastlegEventClient.java index 3f2a904ab..e3b3dd5b8 100644 --- a/event-bridge-service/src/main/java/com/baiyue/eventbridge/service/HttpLastlegEventClient.java +++ b/event-bridge-service/src/main/java/com/baiyue/eventbridge/service/HttpLastlegEventClient.java @@ -45,11 +45,31 @@ public class HttpLastlegEventClient implements LastlegEventClient { .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - JsonNode responseBody = objectMapper.readTree(response.body()); + JsonNode responseBody; + try { + responseBody = objectMapper.readTree(response.body()); + } catch (Exception ignored) { + responseBody = null; + } if (response.statusCode() / 100 != 2 || responseBody == null || !"200".equals(responseBody.path("code").asText())) { - throw new IllegalStateException("尾程消费失败: HTTP " + response.statusCode()); + throw new IllegalStateException(failureMessage(response.statusCode(), responseBody)); } return String.valueOf(response.statusCode()); } + + static String failureMessage(int statusCode, JsonNode body) { + String code = body == null ? "missing" : body.path("code").asText("missing"); + String message = body == null ? "non-json response" : body.path("message").asText("missing"); + String requestId = body == null ? "missing" : body.path("requestId").asText("missing"); + String traceId = body == null ? "missing" : body.path("traceId").asText("missing"); + return "尾程消费失败: HTTP " + statusCode + ", code=" + bounded(code) + + ", message=" + bounded(message) + ", requestId=" + bounded(requestId) + + ", traceId=" + bounded(traceId); + } + + private static String bounded(String value) { + String safe = value == null || value.isBlank() ? "missing" : value.replaceAll("[\\r\\n]+", " "); + return safe.length() <= 240 ? safe : safe.substring(0, 240); + } } diff --git a/event-bridge-service/src/test/java/com/baiyue/eventbridge/service/HttpLastlegEventClientTest.java b/event-bridge-service/src/test/java/com/baiyue/eventbridge/service/HttpLastlegEventClientTest.java new file mode 100644 index 000000000..b2de28029 --- /dev/null +++ b/event-bridge-service/src/test/java/com/baiyue/eventbridge/service/HttpLastlegEventClientTest.java @@ -0,0 +1,26 @@ +package com.baiyue.eventbridge.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class HttpLastlegEventClientTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void failureIncludesBusinessCodeMessageAndTraceEvidence() throws Exception { + var body = objectMapper.readTree(""" + {"code":"A0400","message":"入仓计划数据无效","requestId":"request-1","traceId":"trace-1"} + """); + + assertThat(HttpLastlegEventClient.failureMessage(200, body)) + .isEqualTo("尾程消费失败: HTTP 200, code=A0400, message=入仓计划数据无效, requestId=request-1, traceId=trace-1"); + } + + @Test + void nonJsonFailureIsStillDiagnosableWithoutLeakingRawBody() { + assertThat(HttpLastlegEventClient.failureMessage(502, null)) + .isEqualTo("尾程消费失败: HTTP 502, code=missing, message=non-json response, requestId=missing, traceId=missing"); + } +}