From c7945ad8fd53f928d40304272e86ff00c6d38dd7 Mon Sep 17 00:00:00 2001
From: ouyangxq <775153912@qq.com>
Date: Wed, 30 Sep 2026 11:18:40 +0800
Subject: [PATCH] fix(api): preserve null data in response envelopes
---
common-core/pom.xml | 1 +
.../baiyue/common/core/api/ApiResponse.java | 4 +-
.../web/ApiResponseSerializationTest.java | 43 +++++++++++++++++++
3 files changed, 47 insertions(+), 1 deletion(-)
create mode 100644 common-web/src/test/java/com/baiyue/common/web/ApiResponseSerializationTest.java
diff --git a/common-core/pom.xml b/common-core/pom.xml
index 956d0ca79..2ef4508d3 100644
--- a/common-core/pom.xml
+++ b/common-core/pom.xml
@@ -14,6 +14,7 @@
baiyue-common-core
+ com.fasterxml.jackson.corejackson-annotations
org.springframeworkspring-web
com.baomidou
diff --git a/common-core/src/main/java/com/baiyue/common/core/api/ApiResponse.java b/common-core/src/main/java/com/baiyue/common/core/api/ApiResponse.java
index d88fd5b64..488845219 100644
--- a/common-core/src/main/java/com/baiyue/common/core/api/ApiResponse.java
+++ b/common-core/src/main/java/com/baiyue/common/core/api/ApiResponse.java
@@ -1,5 +1,7 @@
package com.baiyue.common.core.api;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
import java.time.Instant;
/**
@@ -8,7 +10,7 @@ import java.time.Instant;
public record ApiResponse(
String code,
String message,
- T data,
+ @JsonInclude(JsonInclude.Include.ALWAYS) T data,
String requestId,
String traceId,
Instant timestamp) {
diff --git a/common-web/src/test/java/com/baiyue/common/web/ApiResponseSerializationTest.java b/common-web/src/test/java/com/baiyue/common/web/ApiResponseSerializationTest.java
new file mode 100644
index 000000000..d1aa71cd1
--- /dev/null
+++ b/common-web/src/test/java/com/baiyue/common/web/ApiResponseSerializationTest.java
@@ -0,0 +1,43 @@
+package com.baiyue.common.web;
+
+import com.baiyue.common.core.api.ApiResponse;
+import com.baiyue.common.core.api.ResultCode;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ApiResponseSerializationTest {
+ @Test
+ void preservesEnvelopeDataUnderNonNullConfiguration() {
+ new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class))
+ .withPropertyValues("spring.jackson.default-property-inclusion=non_null")
+ .run(context -> {
+ ObjectMapper mapper = context.getBean(ObjectMapper.class);
+ JsonNode success = mapper.readTree(mapper.writeValueAsString(ApiResponse.success(null)));
+ assertThat(success.path("code").asText()).isEqualTo("200");
+ assertThat(success.has("data")).isTrue();
+ assertThat(success.get("data").isNull()).isTrue();
+ assertThat(success.has("requestId")).isFalse();
+
+ JsonNode failure = mapper.readTree(mapper.writeValueAsString(
+ ApiResponse.failure(ResultCode.CONFLICT, "conflict", "request-1", "trace-1")));
+ assertThat(failure.path("code").asText()).isEqualTo("A0409");
+ assertThat(failure.has("data")).isTrue();
+ assertThat(failure.get("data").isNull()).isTrue();
+ assertThat(failure.path("requestId").asText()).isEqualTo("request-1");
+ assertThat(failure.path("traceId").asText()).isEqualTo("trace-1");
+
+ JsonNode populated = mapper.readTree(mapper.writeValueAsString(
+ ApiResponse.success(Map.of("status", "SUBMITTED"))));
+ assertThat(populated.path("data").path("status").asText()).isEqualTo("SUBMITTED");
+ });
+ }
+}