FtpTool/docs/prod-api-v1.md
dark 114bcf33d8 feat: 同步链路支持版本分支目录映射、动态快照分支和 ackFail 定向重拉
- Git -> PROD 改为按 branch 作为 configVersion
- 按 airportId/appName/fileName 目录结构解析 pushConfig 参数
- PROD -> Git 改为写入 snapshot-branch/<configVersion> 动态分支
- pullConfig 支持 configVersion/fileName 可选过滤
- 抽出 ConfigCryptoService,统一收口加解密扩展点
- ackFail 落库增加重试上下文,支持按 airportId/appName/configVersion/fileName 定向重拉
- 同步更新测试、接口文档和 current.md
2026-04-28 14:49:33 +08:00

347 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 生产端配置同步接口文档 V1
## 1. 文档目的
本文档基于 `testapi.txt` 的正式协议,说明 Git 直连同步工具如何对接生产侧接口。
当前文档同时覆盖两层内容:
1. 接口协议本身
2. 当前代码如何把 Git 仓库结构映射成接口参数
## 2. 当前 Git 仓库约定
### 2.1 Git -> PROD 输入约定
当前实现约定:
- `git.repo.scan-branch` 指向当前待同步的版本分支
- **分支名本身就是 `configVersion`**
- 分支内目录结构必须为:
```text
<airportId>/<appName>/<模块内文件相对路径>
```
示例:
```text
R_XXX_V3.0.3_XXX
├─ PEK
│ └─ monitor
│ ├─ application.yml
│ └─ jobs/sync-job.json
└─ SHA
└─ gate
└─ gate-rule.json
```
映射规则:
- `airportId` = 路径第 1 段
- `appName` = 路径第 2 段
- `fileName` = 从第 3 段开始的模块内相对路径
- `configVersion` = 当前 Git 分支名
例如文件:
```text
PEK/monitor/jobs/sync-job.json
```
会被映射为:
```json
{
"airportId": "PEK",
"appName": "monitor",
"configVersion": "R_XXX_V3.0.3_XXX",
"fileName": "jobs/sync-job.json"
}
```
### 2.2 PROD -> Git 输出约定
当前实现会把 `pullConfig` 返回项按版本分组后落到本地 staging 目录,目录结构同样为:
```text
<airportId>/<appName>/<fileName>
```
之后按动态分支提交到 `git.repo.snapshot-branch/<configVersion>`
## 3. 统一接口约定
### 3.1 编码与格式
- 编码:`UTF-8`
- 返回格式:`JSON`
- 鉴权:请求头中携带 `token`
### 3.2 成功判定
接口成功时返回:
- `code = "0"`
- `msg = "ok"`
### 3.3 失败响应格式
```json
{
"code": "XXX-00-00-XXX",
"data": null,
"msg": "errmsg!",
"timestamp": "1776735560594"
}
```
## 4. 接口一:推送 Git 配置到生产
### 4.1 基本信息
- 地址:`http://ip:port/pic_bus_manage_monitor/configSync/pushConfig`
- 方法:`POST`
- 内容格式:`JSON`
- 鉴权Header 中携带 `token`
### 4.2 请求参数
无 URL 参数。
### 4.3 请求体
请求体为 JSON 数组,每个数组元素代表一个配置文件:
```json
[
{
"airportId": "PEK",
"appName": "monitor",
"configVersion": "R_XXX_V3.0.3_XXX",
"configContent": "配置内容",
"fileName": "jobs/sync-job.json"
}
]
```
字段说明:
| 字段 | 必填 | 说明 |
| --- | --- | --- |
| `airportId` | 是 | 机场编码,来自 Git 路径第 1 段 |
| `appName` | 是 | 模块名,来自 Git 路径第 2 段 |
| `configVersion` | 是 | 配置版本号,当前实现取 Git 分支名 |
| `configContent` | 是 | 文件内容,当前实现按 UTF-8 文本直接读取 |
| `fileName` | 是 | 模块内文件相对路径,不包含 `airportId/appName` 前缀 |
### 4.4 响应体
```json
{
"code": "0",
"data": {
"ackFail": [
{
"airportId": "PEK",
"appName": "monitor",
"configVersion": "R_XXX_V3.0.3_XXX",
"fileName": "jobs/sync-job.json"
}
]
},
"msg": "ok"
}
```
字段说明:
- `ackFail`:本次推送失败的配置项列表
- 如果 `ackFail` 为空或不存在,可视为本次推送成功
- 如果 `ackFail` 非空,当前代码直接抛错,视为本次同步失败
### 4.5 当前实现说明
- 第一次推送走全量
- 后续优先走最小增量
- 如果检测到文件删除,会自动回退到全量推送
- 基线目录按版本分支隔离保存
- `configContent` 当前已统一经过 `ConfigCryptoService`
- 默认实现仍为透传,正式加密算法待补
## 5. 接口二:从生产拉取配置到 Git
### 5.1 基本信息
- 地址:`http://ip:port/pic_bus_manage_monitor/configSync/pullConfig`
- 方法:`GET`
- 内容格式:`JSON`
- 鉴权Header 中携带 `token`
### 5.2 请求参数
接口协议中的字段如下:
```json
{
"airportId": "PEK",
"appName": "monitor",
"configVersion": "R_XXX_V3.0.3_XXX",
"fileName": "jobs/sync-job.json",
"ackSuc": "1,2",
"ackFail": "9"
}
```
说明:
- 由于接口是 `GET`,当前实现按 query string 传参
- 当前实现已经支持:
- `airportId` 可选过滤
- `appName` 可选过滤
- `ackSuc` 自动回传
- `ackFail` 自动回传
- 当前已经支持:
- `configVersion` 可选过滤
- `fileName` 可选过滤
### 5.3 当前请求行为
当前代码只有在配置了过滤条件时才会发送:
- `prod.api.airport-id`
- `prod.api.app-name`
- `prod.api.pull-config-version`
- `prod.api.pull-file-name`
如果这两个配置为空,则不会在请求中携带它们,此时依赖生产端返回所有已审核且未同步的配置。
### 5.4 响应体
```json
{
"code": "0",
"data": [
{
"id": "1",
"airportId": "PEK",
"appName": "monitor",
"configVersion": "R_XXX_V3.0.3_XXX",
"configContent": "配置内容(加密)",
"fileName": "jobs/sync-job.json"
}
],
"msg": "ok"
}
```
字段说明:
| 字段 | 说明 |
| --- | --- |
| `id` | 配置记录标识 |
| `airportId` | 机场编码 |
| `appName` | 模块名 |
| `configVersion` | 配置版本号 |
| `configContent` | 配置内容 |
| `fileName` | 模块内文件相对路径 |
### 5.5 当前实现说明
- `pullConfig` 返回项会先按 `configVersion` 分组,再分别落盘为 `airportId/appName/fileName`
- 如果某组内所有项的 `configVersion` 相同,则该值作为该组的 `sourceVersion`
- 如果某组缺少统一版本号,则该组退回为内容哈希作为 `sourceVersion`
- `configContent` 当前已统一经过 `ConfigCryptoService`,默认实现仍为透传
- 当本地处理失败时,会把失败项写入 ACK 重试表,并在下次按 `airportId/appName/configVersion/fileName` 定向重拉
## 6. 接口三:获取 token
### 6.1 基本信息
- 地址:`http://ip:port/pic_bus_manage_monitor/pam-monitor/login`
- 方法:`POST`
- 内容格式:`JSON`
### 6.2 请求体
```json
{
"name": "XXXxx",
"password": ""
}
```
### 6.3 响应体
```json
{
"code": "0",
"data": {
"token": "tetttwe",
"expireTime": "2026-07-11 11:11:11"
},
"msg": "ok"
}
```
### 6.4 当前实现说明
- 如果 `prod.api.token` 已显式配置,则优先使用静态 token
- 如果未配置静态 token则调用 `login` 接口获取 token
- token 会按 `expireTime` 做本地缓存
## 7. 当前代码实现对齐情况
当前接口适配实现:
- [ProdConfigApiService.java](e:/AIcoding/FtpTool/src/main/java/com/ftptool/sync/service/ProdConfigApiService.java)
当前已经对齐到以下协议:
- `pushConfig` 使用 `POST + JSON 数组`
- `pullConfig` 使用 `GET + JSON 响应`
- `login` 使用 `POST + JSON`
- `ackSuc/ackFail` 已接入请求回传和本地状态更新
- `pushConfig` 参数已按 `airportId/appName/fileName` 目录结构解析
- `pullConfig` 响应已按 `airportId/appName/fileName` 结构恢复到本地目录
当前仍保留的 `TODO`
-`ConfigCryptoService` 的透传实现替换为正式加解密算法
## 8. 当前配置项说明
建议配置:
```properties
prod.api.base-url=https://prod.example.com
prod.api.push-path=/pic_bus_manage_monitor/configSync/pushConfig
prod.api.pull-path=/pic_bus_manage_monitor/configSync/pullConfig
prod.api.login-path=/pic_bus_manage_monitor/pam-monitor/login
prod.api.token=replace-me
prod.api.token-header-name=token
prod.api.airport-id=
prod.api.app-name=
prod.api.pull-config-version=
prod.api.pull-file-name=
prod.api.login-name=
prod.api.login-password=
```
说明:
- `prod.api.airport-id``prod.api.app-name` **不再用于 pushConfig 参数组装**
- 它们当前只作为 `pullConfig` 的可选过滤条件
- `prod.api.pull-config-version``prod.api.pull-file-name` 可用于精细过滤拉取范围
- 如果希望拉全量已审核数据,建议留空
## 9. 联调建议
联调时建议优先确认:
1. `token` 请求头名称是否就是 `token`
2. `pullConfig` 的 GET 参数是否按 query string 传递
3. `configVersion/fileName` 过滤规则是否需要尽快接入
4. `configContent` 加密/解密算法何时补齐
5. `fileName` 是否始终是模块内相对路径,而不是包含 `airportId/appName`