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 状态码(如 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!