# 网关配置全流程：按「本次改动的接口」从网关平台筛选 → 导出 xls → 上传到交付

适用页签：**内网网关 `innerGateway` / 外网网关 `outGateway` / 中台网关 `midGateway` / 文件流网关 `outFileGateway`**。
只针对本次改动涉及的网关配置（**路由 / 熔断 / 限流 / 参数**），从对应网关管理平台导出，再上传到交付对应页签。
`{env}` = 上线时间解析出的 `environmentType`（见 [environment-lookup.md](environment-lookup.md) 第 1 节）。

## 0. 平台 / 内外网 / 上传 bizType 映射（已实测）

| 交付网关页签 | 字段 | 平台 (context) | 主机 | **verifyMode** | 上传 bizType |
|---|---|---|---|---|---|
| 外网网关 | `outGateway` | routerView | `http://www.{env}.yljr.com` | **1（外网）** | `cicd-outGateway` |
| 内网网关 | `innerGateway` | routerView | `http://www.{env}.yljr.com` | **2（内网）** | `cicd-innerGateway` |
| 文件流网关 | `outFileGateway` | routerFileView | `http://www.{env}.yljr.com` | 1 | `cicd-outFileGateway` |
| 中台网关 | `midGateway` | routerMpsView | `http://mps.{env}.yljr.com`（`mps.` 主机） | 1 | `cicd-midGateway` |

> 内网/外网**同一个 routerView 平台**，靠 `verifyMode` 区分：`1=外网`、`2=内网`（导出文件名会带「路由-外网 / 路由-内网」）。账号一律 `admin`/`123456`。

## 1. 四种网关「类型」→ 平台页面 & 交付 `type` code

| 网关类型 | 平台页面 `show`/`export` | 检索键 | 交付 JSON `type`(gatewayType) |
|---|---|---|---|
| 路由 | `/{gw}/gatewayRouter` | `uri`(服务名) + `routerCode`(网关唯一标识=接口方法/路径) + `path` | **2**（路由） |
| 参数 | `/{gw}/gatewayAddParam` | `routerCode` | **3**（参数） |
| 限流 | `/{gw}/gatewayRateLimiter` | `routerCode` | **4**（限流） |
| 熔断 | `/{gw}/gatewayMeltdown` | `routerCode` | **5**（熔断） |

> gatewayType 全集：1服务 2路由 3参数 4限流 5熔断 6过滤 7白名单 8黑名单 9动态绕行 10日志展示。
> 熔断/限流/参数**挂在具体路由上**，检索键是 `routerCode`（= 该接口的网关唯一标识），不是服务名。

## 2. 从代码 diff 确定「本次改动的接口集合」

> ⛔ **前提：网关默认不配。** 只有用户**明确要求**配网关时才走本流程；用户没提，即使 diff 里有接口改动也一律跳过（见 SKILL.md §1.1）。
> diff 从哪来：按 [change-detection.md](change-detection.md) 拿到（本地仓库 `origin/master...FEATURE_BRANCH`，或用户粘贴）。**没有 diff 就不要开始配网关**——凭需求描述猜接口会导致导出文件多传/漏传。

抽取本次新增/改动的 Controller 接口：
- 接口路径：`@RequestMapping/@GetMapping/@PostMapping(...)`（拼上类级前缀 = 完整路径）；
- 方法名 / 路径最后一段（如 `verifyMobileRepeat`）——作为**检索关键字**（网关里 `routerCode` 常就是接口路径/方法名）；
- 本服务名 = `SERVER_NAME`（= 网关里的 `uri` / `nacos服务名`）。

得到：关键字集合 `K = {接口1关键字, 接口2关键字, ...}` + 服务名 `uri`。
> **只有这些接口**要出现在导出里；其它接口一律不许出现（硬性要求）。

## 3. 登录网关平台（按需，只登要用的）

```bash
curl -s -c gw.txt -X POST "{HOST}/{gw}/login" --data "username=admin&password=123456"   # 得 JSESSIONID
```

### ⛔ 3.1 门禁：先看测试环境有没有配，没有就不要配

登录后、导出前，**先用服务名 `uri` 在该平台检索本服务的路由**：

```bash
curl -s -b gw.txt -X POST "{HOST}/{gw}/gatewayRouter/show" --data "verifyMode={1|2}&uri={SERVER_NAME}"
```

| 检索结果 | 结论 | 动作 |
|---|---|---|
| 该服务**一条都没有** | 本服务不走这个网关 | **该页签保持 `[]`**，不导出、不上传。摘要写「测试环境 {env} 的 {平台} 无本服务配置，已跳过」 |
| 有该服务，但**本次改动接口查不到** | 这个接口不走网关 | **直接跳过该接口**（不问用户、不卡流程），摘要列一行「`/xxx` 在 {平台} 未查到，已跳过」。**不许自己编一条塞进导出**。其余命中的接口照常导出 |
| 有该服务且命中本次改动接口 | 正常 | 继续第 4 步导出 |

四个网关页签（内网 `verifyMode=2` / 外网 `verifyMode=1` / 文件流 routerFileView / 中台 routerMpsView）**各自独立判断**，不要因为一个平台有配置就顺带把别的也配上。
熔断/限流/参数同理：某类型在测试环境查不到，就是本次不涉及该类型，跳过即可（`super_melt.xls` 为空是正常结果，不是失败）。

**底线：导出内容必须是测试环境上真实存在的配置。测试环境没有的，一律不配、不造。**

## 4. 对每个 (交付网关页签 = 平台+verifyMode) × 每个 (类型 路由/熔断/限流/参数)：得到**一份**恰好的 xls

### ⛔ 4.0 两级筛选，禁止全量导出
**任何检索/导出都必须带过滤条件**，不许无条件拉全量（全量 1–2MB，且必然混入其它服务的配置）：

1. **第一级 · 按服务名收窄** —— 路由带 `uri={SERVER_NAME}` 导出该服务全部路由（实测 ~13KB），得到该服务真实存在的 `routerCode` 全集 **G**；
2. **第二级 · 按接口收窄** —— 用本次改动接口在 G 里匹配得到 **R**，再按 R 的公共前缀（同模块）或逐接口 `routerCode=`（跨模块）过滤导出，最后精确删行/合并留 R。

熔断/限流/参数的表单**没有 `uri` 字段**，第一级用「G 里该服务的 routerCode 公共前缀」当过滤词代替服务名，**同样不许全量拉**。
若某类型第一级筛完就是 0 条 → 本次不涉及该类型，跳过。

> **交付一个网关类型只能传一份文件**（不能一接口一份）。两条路，按「改动接口是否同一模块」选：
> - **同一模块（有公共前缀）→ A**：过滤出一个尽量小的超集 → **精确删行**留 R；
> - **跨多个模块 → B**：逐接口各导一小份 → **合并**成一份并精确留 R。
> 检索/导出字段是 LIKE 子串、一次导出只认单个 `id`（实测 `id=3,4`→0、`id=3&id=4`→只取一个），**不靠过滤精确**，靠删行/合并 + 回验精确。

### 4.1 从代码 diff 得到「本次改动接口」的精确标识集合 R
- 抽取本次**新增/修改**的 Controller 接口，拼出**完整路径** = 类级 `@RequestMapping`(含可能的 `${...}` 占位、常量) + 方法级 `@GetMapping/@PostMapping/...`。
- 网关每条的 **网关唯一标识(routerCode)** 就是接口完整路径（如 `/userCenter/register/registerUser`）。

**归一化匹配规则（尽量确定、对不上就问）：**
1. 先用「服务名 `uri`」导出该服务全部路由（4.2），拿到该服务**真实存在**的 routerCode 全集 G。
2. 把代码拼出的路径 P 与 G 做匹配，依次尝试：**① 完全相等**；② 去掉两侧多余 `/`、统一大小写后相等；③ P 是否等于某 routerCode 去掉「固定网关前缀」后的值（前缀从 G 里观察，如都带 `/服务上下文`）。
3. **一个 P 命中 0 条** → 该接口没有此类型网关配置（正常，别编造）；**命中 ≥2 条** → 有歧义，**停下问用户**。
4. 通配/动态路径（`/**`、`/{id}`）用**精确相等**匹配那条通配路由本身，不要拿它去套具体接口。
- `R = 命中的 routerCode 精确字符串集合`。后续删行**只按精确相等**（`.../verifyMobileRepeat` 不能连带 `.../verifyMobileRepeat-1`）。

> ⚠️ **本次「删除」的接口不在本流程内**：本流程只处理新增/修改接口的网关配置导出。若本次删了接口、需要**下线对应网关路由**，那是网关侧的删除操作，**不通过交付导出文件表达**——单独提示用户去网关平台处理，本 skill 不代劳。

### 4.2 导出「超集」——按「是否同一模块」选 A/B，尽量小（避免下载 1–2MB 全量）
导出 = `goExportBack`：POST `/{gw}/{type}/export`（表单字段同 `/show`），返回真正的 `.xls`。第 1 列 = **网关唯一标识(routerCode)**（无主键 id 列），用来删行/合并。

- **过滤要点（实测）**：`uri` 只对**路由**有效；熔断/限流/参数**只能按 `routerCode` 子串 LIKE**，且**过滤值要去掉前导 `/`**（带 `/` 会 0 命中）；`deletedFlag=1` 去噪；`/show` 有 100 行上限、`/export` 无 → **核对只在导出/合并后的文件上做**。
- **大小实测（限流类）**：全量 `1.35MB`｜`routerCode=userCenter` `157KB`｜`routerCode=userCenter/register`（控制器级）`10.7KB`｜单接口 `5KB`。

**路由（gatewayRouter，有 uri）**：一律 `uri={SERVER_NAME}` 导出（实测 ~13KB）→ 走 4.3-A 删行。
```bash
curl -s -b gw.txt -X POST "{HOST}/{gw}/gatewayRouter/export" \
  --data "verifyMode={1或2}&uri={SERVER_NAME}&deletedFlag=1" -o super_route.xls
```

**熔断/限流/参数（无 uri）**：先看 R 的 routerCode 是否**同一模块**（有公共路径前缀）：
- **同一模块 → 方案 A（前缀过滤 + 删行）**：`routerCode={公共前缀，无前导斜杠}`（如 `userCenter/register`）→ ~10KB 超集 → 4.3-A 删行。A 回验不过（前缀漏接口）→ 退 B。
  ```bash
  curl -s -b gw.txt -X POST "{HOST}/{gw}/gatewayRateLimiter/export" \
    --data "verifyMode={1或2}&routerCode=userCenter/register&deletedFlag=1" -o super_ratelimit.xls
  ```
- **跨多个模块 → 方案 B（逐接口导出 + 合并）**：对 R 每个接口 `routerCode={接口尾段，无斜杠}` 各导一份（每份 ~5KB）到一个目录 → 4.3-B 合并。
  ```bash
  curl -s -b gw.txt -X POST "{HOST}/{gw}/gatewayRateLimiter/export" \
    --data "verifyMode={1或2}&routerCode=registerUser&deletedFlag=1" -o parts/p1.xls
  ```

### 4.3 得到一份恰好的 xls（都保留表头、精确留 R、回验不多不少；**能打开 xls 就有 Excel/WPS，无需 Python**）
先把 R 写进 `keep.txt`（每行一个 routerCode，用**导出文件里的带斜杠精确值**）：
```
/userCenter/register/registerUser
/userCenter/register/verifyMobileRepeat
```

**A · 超集删行** —— [../scripts/trim_gateway_xls.ps1](../scripts/trim_gateway_xls.ps1)（PowerShell+Excel，无 Python）：
```powershell
powershell -ExecutionPolicy Bypass -File scripts\trim_gateway_xls.ps1 -Src super_route.xls -Out 路由.xls -KeepFile keep.txt
```
> 已实测：31 行超集 + 3 接口 → 输出恰好 4 行、真 BIFF `.xls`、不误伤 `-1/-3`。

**B · 多份合并留 R** —— [../scripts/merge_gateway_xls.ps1](../scripts/merge_gateway_xls.ps1)（PowerShell+Excel，无 Python）：
```powershell
powershell -ExecutionPolicy Bypass -File scripts\merge_gateway_xls.ps1 -SrcDir parts -Out 限流.xls -KeepFile keep.txt
```
> 已实测：两份"逐接口"导出（各含兄弟 `v2/registerUser`、`-1`、`-3`）→ 合并后恰好 2 行、兄弟全删、真 BIFF `.xls`。

两脚本都：留一个表头、按 col1 `routerCode` **精确等于** R 的行、去重、**回验输出==R∩来源**（多/少即报错删文件）、列出「R 里但来源没有」的接口（无网关配置，属正常，**不编造**）。

> ✅ **格式已实测**：A(删行)/B(合并) 生成的 `.xls`（Excel COM 重写、样式丢但值完整）通过网关页面导入接口 `POST /{gw}/gatewayRouter/uploadImport`（multipart `uploadFile`+`verifyMode`）**均导入成功**（`成功条数=行数、失败0`），且按 `routerCode` **upsert 幂等、不产生重复行**。说明重写后的 xls 被网关导入解析器接受——交付上传用它没有格式风险。
- 跨平台（有 Python）：`trim_gateway_xls.py`（`pip install xlrd xlwt`）等价于 A；B 暂只有 PowerShell 版。
- 纯手工兜底：Excel/WPS 打开超集，只留表头 + keep.txt 那几行、删其余、另存 `.xls`（我会把保留/删除清单给你）。
- ⚠️ 从 PowerShell 里跑（别从 Git-Bash 传 `/开头` 参数，会被转成路径）；用 `-KeepFile` 最稳。

### 4.4 跳过 & 结果
- `R` 与该类型**无交集**（来源里一个都没有）→ 该(页签,类型)本次不涉及 → **不生成、不上传**。
- 有交集 → 得到**一份**恰好的 `路由.xls` / `限流.xls` …，连同其网关类型 `type` code 记下，进入上传。

## 5. 上传到交付并组装报文（纯 HTTP）

每个类型**一份** xls：
```bash
curl -s -b <交付平台cookie> -X POST "http://devops.test.yljr.com/zqyl-devops-file-api/file/uploadFile" \
  -F "file=@路由.xls" -F "bizType={对应页签bizType}"      # 拿 data.fileNo / data.showName
```
组装成对应页签数组，**每个网关类型一行**（`type` 用该文件的网关类型 code）。
每行字段（**已按真实交付单核对**）：`type` / `fileName` / `fileNo` / `gatewayContent:""` / `downloadUrl` / `viewUrl`（后两者都填上传返回的 `downLoadUrl`）：
```json
"outGateway": [
  { "type": 2, "fileName": "路由.xls", "fileNo": "<fileNo>", "gatewayContent": "",
    "downloadUrl": "<downLoadUrl>", "viewUrl": "<downLoadUrl>" },
  { "type": 4, "fileName": "限流.xls", "fileNo": "<fileNo>", "gatewayContent": "",
    "downloadUrl": "<downLoadUrl>", "viewUrl": "<downLoadUrl>" }
]
```
- `bizType`（上传参数）按**页签**定（cicd-outGateway / cicd-innerGateway / cicd-outFileGateway / cicd-midGateway）；
- `type`（JSON 字段）按**文件的网关类型**定：**路由=2、参数=3、限流=4、熔断=5**（已用真实交付单核对：路由文件 `type:2`、参数文件 `type:3`）；
- 外网放 `outGateway`(vm1)、内网放 `innerGateway`(vm2)、文件流放 `outFileGateway`、中台放 `midGateway`；
- **合并语义**：同一页签**按 `type` 一类一份**——同 type 的旧文件换成本次新文件，其它 type 的行保留（不要整包覆盖）；
- **本次不涉及的网关页签保持 `[]`**，随主流程 `saveDeliver` 一起提交（见 [payload-skeleton.md](payload-skeleton.md)）。

## 6. 硬性要求 & 停下问用户

- **未改动接口绝不出现在最终 xls 里**；**本次改动且有网关配置的接口一定要在**——靠 4.3 的删行 + 回验保证（`trim_gateway_xls.py` 回验不过就报错，不产出可疑文件）。
- **删行只删行、保留表头、不改任何单元格内容**（内容仍是平台导出的原值）。
- **某网关类型本次不涉及（R 与超集无交集）→ 不生成、不上传**（对应页签保持 `[]`）。
- 分不清某接口属于内网还是外网（vm1/vm2）、或该不该进某平台、或代码路径与 routerCode 对不上 → **停下问用户**，不猜。
- 全程只读网关平台（登录/检索/导出都不改网关配置）；写入只发生在交付 `saveDeliver`（见顶层 ⛔ 约束）。
