From 3e9fc95659bc82e71b7bd09597bf66679de819a1 Mon Sep 17 00:00:00 2001
From: litianxiang
Date: Wed, 20 May 2026 17:19:15 +0800
Subject: [PATCH] =?UTF-8?q?fegin=E6=8A=A5=E9=94=99=E4=BC=A0=E9=80=92?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../controller/BuyerAccountController.java | 2 +-
.../module/feign/config/FeignConfig.java | 9 +-
.../decoder/BusinessErrorFeignDecoder.java | 92 +++++++++++++++++++
.../service/impl/BuyerOrderServiceImpl.java | 5 -
src/main/resources/bootstrap.yml | 2 +-
5 files changed, 102 insertions(+), 8 deletions(-)
create mode 100644 src/main/java/com/aida/buyer/module/feign/decoder/BusinessErrorFeignDecoder.java
diff --git a/src/main/java/com/aida/buyer/module/account/controller/BuyerAccountController.java b/src/main/java/com/aida/buyer/module/account/controller/BuyerAccountController.java
index 7ecf54e..176c07e 100644
--- a/src/main/java/com/aida/buyer/module/account/controller/BuyerAccountController.java
+++ b/src/main/java/com/aida/buyer/module/account/controller/BuyerAccountController.java
@@ -26,7 +26,7 @@ public class BuyerAccountController {
}
@PostMapping("/register")
- @Operation(summary = "注册")
+ @Operation(summary = "获取验证码后注册")
public Boolean register(@Valid @RequestBody RegisterDTO dto) {
return buyerAccountService.register(dto);
}
diff --git a/src/main/java/com/aida/buyer/module/feign/config/FeignConfig.java b/src/main/java/com/aida/buyer/module/feign/config/FeignConfig.java
index 0f79869..2ea01ad 100644
--- a/src/main/java/com/aida/buyer/module/feign/config/FeignConfig.java
+++ b/src/main/java/com/aida/buyer/module/feign/config/FeignConfig.java
@@ -1,12 +1,14 @@
package com.aida.buyer.module.feign.config;
+import com.aida.buyer.module.feign.decoder.BusinessErrorFeignDecoder;
import com.aida.buyer.module.feign.interceptor.InternalCallInterceptor;
+import com.fasterxml.jackson.databind.ObjectMapper;
import feign.RequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
- * Feign 全局配置:注册所有 Feign 拦截器。
+ * Feign 全局配置:注册所有 Feign 拦截器和 Decoder。
*/
@Configuration
public class FeignConfig {
@@ -15,4 +17,9 @@ public class FeignConfig {
public RequestInterceptor internalCallInterceptor() {
return new InternalCallInterceptor();
}
+
+ @Bean
+ public BusinessErrorFeignDecoder businessErrorFeignDecoder(ObjectMapper objectMapper) {
+ return new BusinessErrorFeignDecoder(objectMapper);
+ }
}
diff --git a/src/main/java/com/aida/buyer/module/feign/decoder/BusinessErrorFeignDecoder.java b/src/main/java/com/aida/buyer/module/feign/decoder/BusinessErrorFeignDecoder.java
new file mode 100644
index 0000000..38ab7f5
--- /dev/null
+++ b/src/main/java/com/aida/buyer/module/feign/decoder/BusinessErrorFeignDecoder.java
@@ -0,0 +1,92 @@
+package com.aida.buyer.module.feign.decoder;
+
+import com.aida.buyer.common.exception.BusinessException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import feign.FeignException;
+import feign.Request;
+import feign.Response;
+import feign.codec.DecodeException;
+import feign.codec.Decoder;
+import lombok.RequiredArgsConstructor;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Feign 全局 Decoder。
+ *
+ * 背景:seller 端使用 {@code @RestControllerAdvice} 将 {@code BusinessException}
+ * 转为 HTTP 200 + {@code Response.fail(code, msg)} 返回。
+ * 默认 Decoder 只看 HTTP 状态码,2xx 一律视为成功,导致 buyer 端无法感知远程业务错误。
+ *
+ * 本 Decoder 在解析响应体时:
+ *
+ * - 若 HTTP 状态码为 2xx,先将 body 读取为字符串并解析为标准 {@code Response};
+ * - 若 {@code errCode != 0},抛出 {@link BusinessException},错误信息为远程返回的 errMsg;
+ * - 否则使用 {@link ObjectMapper} 将 body 反序列化为目标类型 {@code T}。
+ *
+ *
+ *
+ * HTTP 非 2xx 状态码(如 500、服务不可用)由 Feign 默认 ErrorDecoder 抛出
+ * {@link FeignException},不在本 Decoder 处理范围内。
+ */
+@RequiredArgsConstructor
+public class BusinessErrorFeignDecoder implements Decoder {
+
+ private final ObjectMapper objectMapper;
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
+ if (!is2xx(response.status())) {
+ throw FeignException.errorStatus(requestKey(response), response);
+ }
+
+ String body = readBody(response);
+ if (body == null || body.isBlank()) {
+ return null;
+ }
+
+ // 先按标准 Response 格式解析,校验 errCode
+ com.aida.buyer.common.result.Response> genericResp = objectMapper.readValue(body, com.aida.buyer.common.result.Response.class);
+ if (genericResp.getErrCode() != 0) {
+ throw new BusinessException(
+ genericResp.getErrCode(),
+ genericResp.getErrMsg() != null ? genericResp.getErrMsg() : "远程服务业务错误"
+ );
+ }
+
+ // errCode == 0,ObjectMapper 反序列化为目标类型
+ return objectMapper.readValue(body, objectMapper.getTypeFactory().constructType(type));
+ }
+
+ private boolean is2xx(int status) {
+ return status >= 200 && status < 300;
+ }
+
+ private String readBody(Response response) throws IOException {
+ if (response.body() == null) {
+ return null;
+ }
+ try (BufferedReader reader = new BufferedReader(
+ new InputStreamReader(response.body().asInputStream(), StandardCharsets.UTF_8))) {
+ StringBuilder sb = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ sb.append(line);
+ }
+ return sb.toString();
+ }
+ }
+
+ private String requestKey(Response response) {
+ Request request = response.request();
+ return request.method() + " " + request.url();
+ }
+}
diff --git a/src/main/java/com/aida/buyer/module/order/service/impl/BuyerOrderServiceImpl.java b/src/main/java/com/aida/buyer/module/order/service/impl/BuyerOrderServiceImpl.java
index ad8d946..9462ddf 100644
--- a/src/main/java/com/aida/buyer/module/order/service/impl/BuyerOrderServiceImpl.java
+++ b/src/main/java/com/aida/buyer/module/order/service/impl/BuyerOrderServiceImpl.java
@@ -1,7 +1,6 @@
package com.aida.buyer.module.order.service.impl;
import com.aida.buyer.common.context.UserContext;
-import com.aida.buyer.common.exception.BusinessException;
import com.aida.buyer.common.result.PageResponse;
import com.aida.buyer.common.result.Response;
import com.aida.buyer.module.account.entity.BuyerAccount;
@@ -46,10 +45,6 @@ public class BuyerOrderServiceImpl implements IBuyerOrderService {
dto.setBuyerUsername(account.getUsername());
Response orderResp = orderFeignClient.createOrder(dto);
- if (orderResp == null || orderResp.getErrCode() != 0) {
- throw new BusinessException("创建订单失败");
- }
-
CreateOrderResultVO result = orderResp.getData();
// TODO:调用支付模块,传入buyerId,result.getOrderIds(),result.getTotalAmount()
diff --git a/src/main/resources/bootstrap.yml b/src/main/resources/bootstrap.yml
index cc6b401..214a200 100644
--- a/src/main/resources/bootstrap.yml
+++ b/src/main/resources/bootstrap.yml
@@ -4,7 +4,7 @@
# ============================================================
nacos:
- namespace: ltx
+ namespace: dev
host: 18.167.251.121:28848
username: nacos
password: Aidlab123123!