# 防火墙（字段 `firewall`，数组）

一行 = 一条放行规则：**源服务 → 目标服务 + 目标端口**。
下面的字段与规律来自实测（扫了 3000 张交付单，410 张配过防火墙，抽样 60 张共 207 行分析）。

## 什么时候要配（触发判据）

本次改动引入了**新的跨服务/跨资源访问**，就要配：

| 命中情形 | 放行方向 |
|---|---|
| 新连的库（新 MySQL/Oracle/Mongo 实例、换库地址） | 本服务 → 库 |
| 新用的中间件实例（新 Redis、新 RocketMQ 集群、新 ES/Nacos） | 本服务 → 中间件 |
| 新调用的兄弟服务 / 外部系统（新域名、新 IP、新 HTTP 地址） | 本服务 → 对方 |
| 本次新增接口**要被别的服务调用** | 对方服务 → 本服务 |

> 只改逻辑、复用已有连接 → **不配**，保持 `[]`。
> 实测每张单**中位数只有 1 行**（最多 14 行），不要一次堆一堆。

## 字段结构（实测，9 个字段）

```json
"firewall": [
  {
    "srcServerId":   "3848178208801293328",   // 源服务 id（发起访问的一方）
    "srcServerName": "zqyl-bank-shnsh1002",   // 源服务名（英文名 serverEnName）
    "serverId":      "3716647497167275248",   // 目标服务 id（被访问的一方）
    "serverName":    "nacos",                 // 目标服务名
    "portIds":       ["3716647497167324400"], // 目标服务的端口 id 数组（多选）
    "ports":         "8848",                  // 端口值，后端回显，保存时可不传
    "port":     null, "portId": null, "srcPort": null   // 恒为 null，历史遗留字段
  }
]
```

- **只有 `srcServerId` / `srcServerName` / `serverId` / `serverName` / `portIds` 需要填**；`ports` 是后端按 portIds 回显的端口值，`port` / `portId` / `srcPort` 实测 207 行**全部为 null**，原样传 null 即可。
- **不要自己编 id**：四个 id 全部来自下面的字典接口。

## 取 id：服务/端口字典接口

```
GET /zqyl-devops-compose-api/environment/server/queryServerName?serverName={关键字}&useType=firewall
```

`serverName` 传空取全部（实测 933 条；不带 `useType` 是 1284 条，**必须带 `useType=firewall`**），传关键字做模糊过滤。返回：

```json
{ "id": "3716646840037278960",          // → 填进 serverId / srcServerId
  "serverEnName": "mysql_172.16.1.7",   // → 填进 serverName / srcServerName
  "serverName": "中文名",
  "serverType": "3712732064320064112",
  "aotuSelection": true,                 // 见下：true = 端口全选
  "portList": [ { "id": "3716646840037328112",   // → 填进 portIds
                  "portName": "服务端口", "portValue": 3306,
                  "portType": "...", "serverId": "..." } ] }
```

**`serverType` 分类**（决定你在找什么）：

| serverType | 含义 | 数量 | 例 |
|---|---|---|---|
| `3712724450416790128` | 应用服务（JAVA/PYTHON） | 447 | `zqyl-clearing`、`ysd-provider` |
| `3716733556500726256` | 外部地址（HTTP/HTTPS） | 326 | `ICBC-API-URL`、`http://172.16.2.141` |
| `3712732064320064112` | 中间件/资源 | 85 | `nacos`、`redis-public`、`mysql_172.16.1.7`、`rocketmq-public` |
| `3712724479407819376` | 应用服务（含 GATEWAY） | 71 | `zqyl-user-login`、`zqyl-job-executor` |
| `3712724462227950192` | 应用服务 | 4 | `zqyl-data-search` |

## 端口怎么选（实测规律）

| 目标服务的 `aotuSelection` | 怎么填 `portIds` | 实测 |
|---|---|---|
| `true` | **该服务 `portList` 的全部端口 id**（前端就是自动全选的） | 55/55 行全部全选，无例外 |
| `false` | **只选本次真正用到的端口**，不要全选 | 119 行部分选、33 行恰好全选（多是只有一个端口的服务） |

被选中的端口名分布：`服务端口` 197 次为主；中间件按需带 `哨兵端口`(33)、`从服务端口`(12)、`broker端口`/`nameSrv端口`/`brokerVIP端口`(各 9)、`数据端口(访问地址)`(12)、`443端口(访问地址)`(9)。

**判不准该选哪个端口** → 把该服务的 `portList`（端口名+端口值）列给用户挑，不自己拍板。

## 源服务不一定是本服务

实测 **125/207 (60%)** 的行 `srcServerName` = 本交付单的服务（出方向：本服务去访问别人），
**另外 40%** 是别的服务 → 本服务（入方向，如 `zqyl-bank-manager` → `zqyl-bank-shnsh1002`）。

所以：**不要无脑把 `srcServerId` 填成本服务的 id**。按放行方向判断——
本服务去连库/连中间件/调别人 = 本服务做源；本服务新接口要被别人调 = **对方做源、本服务做目标**。
本服务自身的 id 可从交付详情的 `serverId` 直接拿（实测与字典里该服务的 id 一致）。

## 合并语义

自然键 = **`(srcServerId, serverId)`**（同一对源→目标只留一条，端口以本次为准），与交付单现值合并，不整体替换。

## 组装示例

本服务 `zqyl-clearing` 本次新连了 `mysql_172.16.1.7`：

1. 查字典：`queryServerName?serverName=zqyl-clearing&useType=firewall` → id `3713185209482914841`
2. 查目标：`queryServerName?serverName=mysql&useType=firewall` → `mysql_172.16.1.7` id `3716646840037278960`，`aotuSelection=true`，portList 只有一个 `服务端口:3306`(id `3716646840037328112`)
3. `aotuSelection=true` → portIds 取全部：

```json
"firewall": [ { "srcServerId": "3713185209482914841", "srcServerName": "zqyl-clearing",
                "serverId": "3716646840037278960", "serverName": "mysql_172.16.1.7",
                "portIds": ["3716646840037328112"],
                "port": null, "portId": null, "srcPort": null } ]
```

> 本次不涉及 → 保持 `[]`。
