# RAP 接口同步 — 细节参考

## review.md 格式

写在 `<需求文档所在目录>/interface-docs/<yyyy-mm-dd-hh-mm-ss>/review.md`。
每个接口一节，标题必须是 `## <序号>. <接口名称>`，字段表格的行标签固定，便于人工核对与后续复用。

````markdown
# 接口文档草案（请确认）

> **确认环节不可跳过**：请核对下列 **HTTP 方法**、**路径** 与 **入参/出参**，回复确认或修改意见后再执行同步。

## 1. 查询客户额度

| 字段 | 内容 |
|------|------|
| HTTP | **POST** |
| 路径 | **`/api/cust/quota/query`** |
| 仓库/项目 | 供应链金融 |
| 模块 | 客户中心 |
| 变更类型 | 新增 |
| 平台接口 ID | 34935 |
| 来源文档 | `需求文档.md` |
| 代码来源 | `src/main/java/.../CustController.java:42`（工作区未提交） |

**说明**

按客户号查询可用额度，额度为空时返回 0。

**请求体（JSON）**

```json
{
  "custNo": "C0001"
}
```

**响应（JSON）**

```json
{
  "msg": "success",
  "status": 200,
  "data": {
    "quota": 100000
  }
}
```

**原文引用（节选）**

```text
4.2 客户额度查询：入参客户号，出参可用额度……
```
````

约定：

- 「平台接口 ID」只在变更类型为「更新」时填，值来自 `rap.mjs repo <仓库id>` 或 `rap.mjs itf <接口id>`。
- 「代码来源」**新增和更新都必填**（两者都以代码为准，见 [code-extraction.md](code-extraction.md)），写文件路径:行号 + 取材基准（`（工作区未提交）` 或 `@ <commit hash>`）。只有代码尚未实现、退回文档取材时才留空，并在说明里注明「代码未实现」。
- 未从文档中解析到的内容写 `（方法待补）`、`（路径待补）`、`（请补充）`，`sync` 会拦截这些占位符而不是把它们写进 RAP。
- 响应统一归一为 `{ msg, status, data }`，`data` 为对象或数组。
- 「原文引用」可选，但填了便于用户核对提取是否走样。

## plan.json 格式

```jsonc
{
  "repositoryId": 123,          // 必填，用户手选
  "moduleId": 456,              // 必填，用户手选
  "userConfirmed": true,        // 必填，用户已确认仓库/模块/review 内容
  "interfaces": [
    {
      "changeType": "新增",      // 新增 | 更新；留空则按 method+路径 自动判定
      "interfaceId": 34935,      // 更新时可选；给了就按 id 精确定位
      "name": "查询客户额度",
      "method": "POST",
      "url": "/api/cust/quota/query",
      "description": "按客户号查询可用额度",
      "status": 200,             // 可选，默认 200
      "bodyOption": "JSON",      // 可选，默认 JSON；表单接口用 FORM_DATA / FORM_URLENCODED
      "properties": [ /* 见下 */ ]
    }
  ]
}
```

匹配规则：

| 情况 | 行为 |
|------|------|
| 给了 `interfaceId` | 必须在所选模块下，否则整条报错 |
| `changeType: 新增` | 直接创建，不做匹配 |
| `changeType: 更新` | 按 `interfaceId` 或 `method + 路径` 匹配；匹配不到整条报错 |
| `changeType` 留空 | 匹配到就更新，匹配不到就创建 |

路径匹配会去掉 query 串与结尾斜杠，method 大小写不敏感。

## properties 字段

RAP 的入参出参是**扁平列表 + parentId 树**，不是嵌套 JSON。

| 字段 | 说明 |
|------|------|
| `scope` | `request` 或 `response`，必填 |
| `name` | 字段名，必填 |
| `type` | `String` / `Number` / `Boolean` / `Object` / `Array`；脚本会把 `int`、`long`、`list` 等归一 |
| `parentId` | 顶层为 `-1`；嵌套字段填父级的 `id`（父级是新增时填其 `memory-N`） |
| `id` | 已存在字段填平台数字 id；新增字段填 `memory-N` 或省略（脚本自动分配） |
| `priority` | 已存在字段**必须**带回平台原值；新增字段由脚本从 1 递增 |
| `required` | 可选，`true` / `false` / `null` |
| `value` | 可选，示例值 |
| `description` | 可选，字段说明 |
| `pos` | 可选，request 默认 3、response 默认 2 |

关键约束：

- **一次请求提交 request + response 全量**。脚本固定发 `summary.posFilter = 1`（与网页端合并提交一致）。只提交单侧时 RAP 会按侧覆盖，另一侧被清空——脚本会在结果里给 `warnings`，但不会替你补齐。
- **更新已有接口前先 `rap.mjs itf <id>`**，把要保留的字段连同原 `id` 与 `priority` 一起放进 `properties`；没带回来的字段等于删除。
- 新增字段的 `memory-N` 只是提交时的临时标识，落库后由 RAP 分配真实数字 id。

`props-from-json` 可以把示例 JSON 转成这个结构：

```bash
node "$RAP" props-from-json /tmp/example.json
node "$RAP" props-from-json -          # 从 stdin 读，省去临时文件
```

输入 `{"request": {...}, "response": {...}}`（可只给一侧），输出全部带 `memory-N` id 的新增属性（对象递归展开，数组取第一个元素作为子结构模板）。生成后需要自己补 `description`、调整 `required`。

## 脚本命令

`$RAP` = `<skill目录>/scripts/rap.mjs` 的绝对路径。不带参数运行会打印完整用法。

| 命令 | 说明 |
|------|------|
| `doctor` | 自检：版本、Node、平台、skill 目录、baseUrl 可达性、会话状态、相关环境变量 |
| `login [--stdin]` | 登录并保存会话；`--stdin` 从管道读 `{baseUrl,username,password}`，适合密码含特殊字符。登录后用 `/repository/owned` 验证会话真实可用，验不过就删掉会话文件 |
| `status` | 会话是否存在、账号、已存活分钟数、是否 stale |
| `logout` | 删除会话文件 |
| `repos` | `/repository/owned` + `/repository/joined` 合并列表 |
| `repo <id>` | `/repository/get`，返回模块及各模块下接口（含 locker） |
| `itf <id>` | `/interface/get`，返回接口元信息与全量 properties |
| `props-from-json <file\|->` | 示例 JSON → 扁平属性列表 |
| `sync <plan.json>` | 批量创建/更新 |
| `raw <GET\|POST> <path> [bodyFile]` | 直接打 RAP 接口，兜底用 |

退出码：正常 `0`；命令抛错或 `sync` 有失败条目时 `1`，错误 JSON 打在 stderr。

## 环境变量

| 变量 | 用途 |
|------|------|
| `RAP_BASE_URL` | API 根地址，默认 `http://rap.corp.yljr.com:8080`；该域名漏写端口时自动补 `:8080` |
| `RAP_USERNAME` / `RAP_PASSWORD` | 仅 `login` 时需要，不落盘；密码含 `$` `!` `"` `'` 等字符时改用 `login --stdin` |
| `RAP_WEB_BASE_URL` | 编辑器页地址，默认取 `RAP_BASE_URL` 并去掉 `:8080` |
| `RAP_LOGIN_PATH` | 登录路径，默认 `/account/login` |
| `RAP_SESSION_FILE` | 会话文件路径，默认 `<临时目录>/xiaoma-generate-rap-interface/session.json`，权限 0600 |
| `RAP_LOG_FILE` | 设了就把 HTTP 往返写成 JSONL（密码字段脱敏），排查登录问题用 |

## RAP 平台行为备忘

- 登录尝试四种形态：JSON `{email,password}`、表单 `email`、表单 `account`、表单 `username`，拿到 cookie 即止。
- `/interface/create` 的 `id` 固定传 `0`，成功后接口 id 在 `data.itf.id`。
- **创建后接口处于锁定态**，需 `POST /interface/unlock` 再 `POST /interface/update` 写元信息，否则名称/描述可能不生效。
- `/interface/unlock` 只有**当前锁定人**能成功；被他人锁定时脚本记 `unlocked: false` 并继续尝试后续更新，多半会失败，需要联系锁定人。
- 编辑器链接是 `/repository/editor?id=<仓库>&mod=<模块>&itf=<接口>`，走 80 端口；`/interface/detail/{id}` 不是给用户点的。
