liuchengsen 4 тижнів тому
батько
коміт
0cff351fba

+ 16 - 0
backend-java/src/main/java/com/pharmacopoeia/config/UnifiedResponse.java

@@ -0,0 +1,16 @@
+package com.pharmacopoeia.config;
+
+import java.lang.annotation.*;
+
+/**
+ * 标记 Controller 或方法,表示返回结果由 {@link UnifiedResponseBodyAdvice}
+ * 统一包装为 {@link com.pharmacopoeia.dto.ApiResponse} 格式。
+ *
+ * <p>流式端点(SSE)或返回 {@code com.pharmacopoeia.dto.ApiResponse} 的方法
+ * 不会被二次包装。
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface UnifiedResponse {
+}

+ 45 - 0
backend-java/src/main/java/com/pharmacopoeia/config/UnifiedResponseBodyAdvice.java

@@ -0,0 +1,45 @@
+package com.pharmacopoeia.config;
+
+import com.pharmacopoeia.dto.ApiResponse;
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+/**
+ * 对标记了 {@link UnifiedResponse} 的 Controller / 方法,
+ * 自动将返回值包装为统一的 {@link ApiResponse} 格式:
+ * {@code {code: 200, message: "ok", data: <原返回值> }}。
+ *
+ * <ul>
+ *   <li>已经是 ApiResponse → 不包装</li>
+ *   <li>流式(Flux / SSE)→ 不会进入此 Advice</li>
+ * </ul>
+ */
+@RestControllerAdvice
+public class UnifiedResponseBodyAdvice implements ResponseBodyAdvice<Object> {
+
+    @Override
+    public boolean supports(MethodParameter returnType,
+                           Class<? extends HttpMessageConverter<?>> converterType) {
+        // 类或方法上有 @UnifiedResponse 才包装
+        if (returnType.getMethodAnnotation(UnifiedResponse.class) != null) {
+            return true;
+        }
+        return returnType.getContainingClass().getAnnotation(UnifiedResponse.class) != null;
+    }
+
+    @Override
+    public Object beforeBodyWrite(Object body, MethodParameter returnType,
+                                  MediaType selectedContentType,
+                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
+                                  ServerHttpRequest request, ServerHttpResponse response) {
+        if (body instanceof ApiResponse) {
+            return body;
+        }
+        return ApiResponse.success(body);
+    }
+}