# Feign 服务间调用规范

> 新增服务间调用、编写 Feign 客户端时主动加载本规范。

## 基础设施

### 依赖与开关
- 依赖 `zqyl-feign`，底层走 `zqyl-rpc-okhttp`（OkHttp）。
- 启动类需 `@EnableFeignClients`（限定 `basePackages` 到本服务 feign 包）。

### 分层约定
```
application/feign/        — Feign 客户端接口（@FeignClient）
application/feign/dto/    — 调用外部服务的请求 DTO
application/feign/vo/     — 外部服务响应 VO
application/gateway/      — 防腐层：封装 Feign，解包返回壳、异常兜底/降级
```

## Feign 客户端定义

- 接口加 **S** 标识（服务间调用）。`@FeignClient` 的 `name` = 目标服务 Nacos 注册名，`path` = 目标服务上下文路径。
- ⚠ **返回壳类型按本服务实测填写，禁止预设**。中企云链本服务 Controller 统一返回 `ResultData`；而 Feign 反序列化**外部服务**的返回壳取决于被调服务——可能是 `ResultData<T>`、`ResponseInfo<T>`、或裸类型（`String`/`feign.Response`）。step-03 必须逐个 FeignClient 读方法签名确定真实壳（见 extraction-recipes.md#feign）。

```java
@FeignClient(name = "{目标服务注册名}", path = "/{目标服务上下文}")
public interface XxxFeignClient {

    // 返回壳 = 本服务实测类型（见下方「已有 Feign 客户端清单」的「返回壳」列）
    @PostMapping("/biz/queryXxx")
    {实测返回壳}<XxxVO> queryXxx(@RequestBody XxxQueryDTO req);
}
```

## 防腐层 Gateway 模式（强约定）

**为什么**：隔离外部服务契约变化、统一解包返回壳、集中异常处理与降级。业务代码只依赖 Gateway，不直接依赖 Feign 客户端。

```java
@Service
public class XxxGateway {

    @Resource
    private XxxFeignClient xxxFeignClient;

    public XxxVO queryXxx(XxxQueryDTO req) {
        {实测返回壳}<XxxVO> result = xxxFeignClient.queryXxx(req);
        // 按本服务实测的判成功方式解包（见「返回壳解包」）
        if (result == null || !{判成功}) {
            // 记日志并按本服务约定处理（返回 null / 降级 / 抛 BusinessException）
        }
        return result.getData();
    }
}
```

## 返回壳解包

按本服务**实测**的返回壳类型选择判成功方式（step-03 据扫码结果保留对应一种，删其余）：
- `ResultData<T>`：本服务常用 `"M0200".equals(result.getStatus())` 判成功，`getData()` 取数据、`getMsg()` 取错误信息（具体读一个 Gateway 防腐类确认）。
- `ResponseInfo<T>`：`"M0200".equals(resp.getStatus())`，`getData()` 取数据。
- 裸类型（`String`/`feign.Response`）：按业务自行解析。
- 统一：取数据前判 `result == null` 防 NPE。

## 已有 Feign 客户端清单

<!-- SCAN:feign-clients — step-03 按 extraction-recipes.md#feign 扫描 @FeignClient 填充下表；「返回壳」列必须逐个读方法签名实测填写，禁止预设/照搬本骨架示例；严禁照抄 pj-incentive。 -->
| Feign 接口 | 目标服务(name) | path | 返回壳(实测) | 对应 Gateway | 用途 |
|-----------|---------------|------|-------------|-------------|------|
| （扫码填充真实 Feign 客户端） | | | | | |

## 已有 Gateway 防腐层

<!-- SCAN:feign-gateways — step-03 扫描 application/gateway/*Gateway.java 填充下表。 -->
| Gateway | 封装的 Feign | 职责 |
|---------|-------------|------|
| （扫码填充真实 Gateway） | | |

## 新增 Feign 调用 Checklist

- [ ] 在 `application/feign/` 定义 `XxxFeignClient`，`@FeignClient(name, path)` 指向目标服务
- [ ] 请求 DTO / 响应 VO 放 `application/feign/dto`、`application/feign/vo`
- [ ] **返回壳类型与被调服务实际返回一致（实测，不臆测）**
- [ ] 在 `application/gateway/` 建 `XxxGateway` 封装，按实测壳解包 + 异常兜底
- [ ] 业务层只注入 `XxxGateway`，不直接注入 Feign 客户端
- [ ] 启动类 `@EnableFeignClients` 覆盖该 feign 包

## 常见错误模式

- ❌ 臆测/照搬返回壳类型 → ✅ 逐个读方法签名实测（`ResultData`/`ResponseInfo`/裸类型）
- ❌ 业务 Service 直接注入 Feign 客户端 → ✅ 经 `XxxGateway` 防腐层
- ❌ 解包方式与实际返回壳不匹配（如对 `ResultData` 用不存在的方法）→ ✅ 按实测壳选判成功方式
- ❌ 不判空直接 `getData()` → ✅ 先判 `result == null`
