# 从本地代码提取接口定义

**新增和更新都以代码为事实来源**——需求文档写的是「应该做成什么样」，代码是「实际做成了什么样」。RAP 要反映后者。新增接口的 Controller / DTO 通常就躺在工作区里（多为未追踪的新文件），没有理由绕过它去抄文档。

> **先定「改了哪些接口」，再看本文的「怎么提字段」。**
> 分支确认、取材基准（工作区未提交改动 / 用户指定 commit）、改动 → 接口映射、改动清单表 → [change-detection.md](change-detection.md)。
> 本文只管：拿到一批 Java 文件之后，怎么把它们变成 RAP 的 method / path / properties。

## 第 1 步：从 Controller 提取 method 与 path

path = 类级 `@RequestMapping` 的值 + 方法级注解的值，拼接后规范化（去掉重复斜杠，保留前导 `/`）。

```java
@RestController
@RequestMapping("/api/cust")          // ← 前缀
public class CustController {

    @PostMapping("/quota/query")      // ← 后缀，method = POST
    public R<QuotaVO> queryQuota(@RequestBody QuotaQueryDTO dto) { ... }
}
```

→ `POST /api/cust/quota/query`

| 注解 | HTTP 方法 |
|------|-----------|
| `@GetMapping` | GET |
| `@PostMapping` | POST |
| `@PutMapping` | PUT |
| `@DeleteMapping` | DELETE |
| `@PatchMapping` | PATCH |
| `@RequestMapping(method = RequestMethod.X)` | X |
| `@RequestMapping` 未指定 method | 按项目里同类接口的惯例判断，判不准就问用户，**不要默认 POST** |

path 里有 `{id}` 这类占位符时，原样保留写进 RAP。

## 第 2 步：提取入参

| 参数写法 | 落到 RAP |
|----------|----------|
| `@RequestBody XxxDTO dto` | 展开 DTO 全部字段为 `scope: request` |
| `@RequestParam("x") String x` | 一条 `scope: request`，名字用注解里的值 |
| `@PathVariable("id") Long id` | 一条 `scope: request` |
| `@RequestHeader("token") String t` | 一条 `scope: request`，description 里注明是 header |
| 无注解的简单类型 | 按 query 参数处理 |
| `HttpServletRequest` / `HttpServletResponse` | **跳过**，不是业务参数 |

## 第 3 步：提取出参

返回类型常见三种包装，都要**拆到最内层业务对象**：

```java
public R<QuotaVO> queryQuota(...)                      // R<T> / Result<T> / ResponseEntity<T>
public TransferResponseDTO<CustCreateDTO> create(...)  // 项目自定义包装
public void handle(...)                                // 无返回体，只写统一响应壳
```

RAP 侧统一归一为：

```
response: msg (String) / status (Number) / data (Object 或 Array)
```

`data` 的子字段来自泛型实参那个类。返回 `R<List<XxxVO>>` 时 `data` 是 `Array`，子字段来自 `XxxVO`。

如果项目的统一响应壳字段名不是 `msg`/`status`/`data`（比如 `code`/`message`/`result`），**以代码里的实际字段名为准**，别硬套。

## 第 4 步：DTO 字段 → RAP properties

### 类型映射

| Java 类型 | RAP `type` |
|-----------|-----------|
| `String` `char` `Character` `enum` | `String` |
| `int` `Integer` `long` `Long` `short` `BigDecimal` `BigInteger` `double` `float` | `Number` |
| `boolean` `Boolean` | `Boolean` |
| `Date` `LocalDate` `LocalDateTime` `Timestamp` | `String`（description 里注明格式，如 `yyyy-MM-dd HH:mm:ss`） |
| `List<T>` `Set<T>` `T[]` | `Array`，子字段展开 `T` |
| `Map<String, ?>` | `Object`，无法展开，description 注明「动态键」 |
| 自定义类 | `Object`，递归展开其字段 |
| `Object` | `Object`，description 注明类型不确定 |

### 嵌套与继承

- 嵌套对象：父字段 `type: Object`，子字段 `parentId` 指向父字段的 id。
- **继承**：`class CustCreateDTO extends ParentDTO` 要把父类字段一起展开，父类字段排在前面。这是最容易漏的一项。
- 泛型：`TransferResponseDTO<CustCreateDTO>` 展开时用实参替换类型变量。
- 循环引用：同一个类嵌套自己时展开一层就停，description 注明。

### 字段名

用序列化后的名字，不是 Java 字段名：

- 有 `@JsonProperty("cust_no")` → 用 `cust_no`
- 有 `@JsonNaming` 或全局蛇形配置 → 按转换后的名字
- 都没有 → 用字段名原样

### required

`@NotNull` `@NotBlank` `@NotEmpty` `@Valid` 配套的必填校验 → `required: true`；没有校验注解就填 `false` 或留 `null`，别一律写 `true`。

### description

按这个顺序取，取到就停：

1. `@ApiModelProperty("客户号")` / `@Schema(description = "客户号")`
2. 字段上的 `/** 客户号 */` 或 `// 客户号`
3. 需求文档里对应字段的说明
4. 都没有就留空——**不要用字段名硬编一句中文**

### 跳过的字段

`serialVersionUID`、`static` 字段、`transient` 字段、`@JsonIgnore` 标注的字段。

## 第 5 步：与 RAP 现状比对

先判定这条是新增还是更新：拿代码提取的 **method + 路径**去 `node "$RAP" repo <仓库id>` 返回的模块接口列表里匹配。匹配不到就是新增，到这一步为止；匹配到就是更新，继续：

```bash
node "$RAP" itf <接口id>
```

拿代码提取的字段和平台现有字段逐条比：

| 情况 | 处理 |
|------|------|
| 平台有、代码也有 | 带回平台的 `id` 与 `priority`，用代码的 type / required / description |
| 平台有、代码没有 | **不要静默删除**——先问用户是「代码里已删除，RAP 也该删」还是「漏提取了」 |
| 平台没有、代码有 | 新增字段，`id` 用 `memory-N` 或省略 |

字段增删是不可逆的，比对结果要在确认环节明确列给用户看。

## 写进 review.md

每条接口都要写代码位置和取材基准，便于事后追溯：

```markdown
| 变更类型 | 更新 |
| 平台接口 ID | 34901 |
| 代码来源 | `src/main/java/com/zqyl/report/controller/DataUpdateController.java:42`（工作区未提交） |
```

或

```markdown
| 代码来源 | `src/main/java/.../DataUpdateController.java:42` @ `a1b2c3d`（feature/quota 分支） |
```

## 常见坑

| 坑 | 后果 |
|----|------|
| 只读 Controller 没读 DTO | 入参出参全空 |
| 漏了父类字段 | RAP 少一批公共字段（`ParentDTO` 里的 traceId、reqTime 之类） |
| 把统一响应壳当成业务字段 | `data` 下面该有的东西跑到顶层去了 |
| 用 Java 字段名而不是 `@JsonProperty` 名 | 前端按 RAP 对接直接调不通 |
| 拿工作区内容当成用户指定 commit 的内容 | 同步进去的是还没提交的半成品 |
| 代码和需求文档冲突时选了文档 | RAP 与实际实现不一致，等于没同步 |

代码和需求文档对不上时：**以代码为准**，但要在确认环节把差异点列给用户，让用户判断是代码写错了还是文档过期了。
