# 外部 API（`xlyApi`）

`xlyApi` 是外部集成方使用的 API 接口面。它作为独立的 Spring Boot WAR 运行（入口类 `xlyApi/src/main/java/com/xly/ApiApplicationBoot.java`），context-path 为 `/xlyApi`，并拥有自己的 `application-*.yml` 文件。

它最关键的设计选择是：**大多数外部端点不是硬编码在 Java 里的，而是 `sysapi` 中的数据行**。新的外部 API 和 xly 其他部分一样，是“注册成数据”的。运行时负责查找 `sApiCode`、验证调用方，并执行元数据指定的内容。

## 数据驱动分发器：`/api/invoke/{sApiCode}`

最重要的单个端点：

```http
POST /xlyApi/api/invoke/{sApiCode}
Authorization: Bearer <token>      (or query param ?authorizationt=<token>)
Content-Type: application/json
{ ...request body, passed through as `sBody` to the handler... }
```

处理器：`xlyApi/src/main/java/com/xly/api/web/ApiController.java:223` 中的 `ApiController.invoke()`。该 mapping 是 `@RequestMapping`，框架层不限定 HTTP 方法；每个 API 行的 `sysapi.sMethod` 列声明调用方应使用的动词，并由 `ApiCheckUtil` 在分发时校验。

流程：

1. 读取 `Authorization` header（回退：`authorizationt` 查询参数）。
2. 查找以 `sApiCode` 为键的 `sysapi` 行（通过 `ApiServiceImpl.invoke` → `SELECT … FROM sysapi WHERE sApiCode = #{sApiCode}`）。
3. 如果该行的 `bHasToken` 标志已设置，就 AES 解密 bearer token 取回 `corpid`，再通过 `BrandServiceImpl.selectByCorpid` 到 `sysapibrand` 校验该 `corpid`。如果品牌行的 `iLossTime` 非 0，还会检查 token 内嵌时间戳是否过期。（`sysapithirdtoken` 用于**出站** token，也就是 xly 调第三方 API；这里不用于校验入站 bearer token。）
4. 使用请求 body 合并出的参数 map，执行 `sysapi.sDataSql` 中存放的 SQL 模板。
5. 将调用写入 `sysapilog`。
6. 用 `AjaxResult` 包装结果并返回。

因此，新增一个外部 API 是管理 / PM 任务：向 `sysapi` 插入一行，提供 SQL 模板，设置标志，把 `sApiCode` 和 token 给集成方。不需要改 Java 代码。

### 需要关注的 `sysapi` 列

| 列 | 含义 |
|---|---|
| `sApiCode` | 消费方发送的路径变量。每个租户内必须唯一。 |
| `sApiName` | 人类可读标签。 |
| `sApiUrl` / `sApiUrlRef` | 计算后的 URL，即 `sApiUrlRef + sApiCode`，用于出站分发。 |
| `sMethod` | 此 API 期望的 HTTP 方法（`GET`、`POST` 等）。 |
| `sHeader`、`sBody`、`sBodyNode`、`sBodyType` | 描述入站请求形状的模板。 |
| `sDataSql` | 运行时执行的 SQL 模板。`#{xxx}` 引用会由请求参数填充。 |
| `sDataTest`、`sDataTestNode` | BACK API 测试器（`POST /api/apiTest`）使用的请求 / 响应样例。 |
| `bHasToken` | `1` 表示要求 `Authorization` 中携带 token；`0` 表示公开。 |

## API 管理端点

`/api/list/...`、`/api/data/...`、`/api/add`、`/api/edit`、`/api/remove` 是管理 `sysapi` 行本身的后台接口面（BACK 的“自定义接口”页面会调用这些）。`POST /api/getSqlTemple` 为给定 API 名称生成 SQL 骨架；`POST /api/apiTest` 使用 `sDataTest` 中的测试夹具跑一次端到端 dry-run。

## Token 端点：`/token/*`

| 端点 | 方法 | 用途 |
|---|---|---|
| `/token/getToken?corpid=&corpsecret=` | GET / POST | 为集成方的 `(corpid, corpsecret)` 组合签发 bearer token。（mapping 不限定方法。） |

返回的 token 就是 `/api/invoke/{sApiCode}` 期望在 `Authorization` 中收到的值。完整实现位于 `xlyApi/src/main/java/com/xly/api/web/TokenController.java` 及其 service。

对于 xly 自己获取的**第三方** token（为了代表客户调用集群外 API），见 `/thirdtoken/*`，它由 `sysapithirdtoken` 支撑（表注释：第三方token获取接口）。

## 其他收敛后的接口面

这些是同一个 WAR 中托管的较小专用 API：

| 端点前缀 | Controller | 用途 |
|---|---|---|
| `/online/api/{sApiCode}` | `OnlineController` | 为给定 `sysapi` 行渲染 BACK 浏览器内 API 调试 / 控制台页面（返回 Thymeleaf view，不执行 API）。 |
| `/online/onlineword/{sApiCode}` | `OnlineController` | 渲染“word”风格 API 文档页面。 |
| `/online/onlinelist` | `OnlineController` | 渲染在线 API 列表页面。 |
| `/online/getToken` | `OnlineController` | 渲染浏览器内 token 获取辅助页面。 |
| `/pro/get/{sProName}` | `ProContentController` | 渲染 BACK 页面，用于展示某个存储过程源码。 |
| `/pro/getData/{sProName}` | `ProContentController` | 返回存储过程源码文本（不是结果集）。 |
| `/pro/alert/{redisId}` | `ProContentController` | 为给定 Redis key 渲染预警 / 通知展示页面。 |
| `/pro/getAlertValue/{redisId}` | `ProContentController` | 返回 Redis 中 `redisId` 对应的缓存值。 |
| `/pro/executeSql` | `ProContentController` | 直接执行 `sSql` 载荷（管理级开发工具）。 |
| `/thirdparty/*` | `ThirdPartyController` | 第三方 API 定义 CRUD，以及 `checkPartyApi` 校验器。由 `sysapithirdparty` 支撑。 |
| `/thirdtoken/*` | `ThirdTokenController` | 出站 token 配置 CRUD。由 `sysapithirdtoken` 支撑。 |
| `/brand/*` | `BrandController` | 伙伴 / 供应商列表（`sysapibrand`）CRUD。 |
| `/json/*`, `/json/jsonEdit/{sDomId}` | `JsonController` | BACK API 编辑器使用的 JSON 树编辑辅助端点。 |
| `/interfaceDefine/callthirdparty/{interfaceInvoke}` | `InterfaceController` | 分发由 `sysapithirdparty` 定义的出站调用。 |
| `/gpt/chatGpt` | `GptController` | 实验性 GPT/chat 接口的透传；不属于框架 Wiki 范围。 |

## 支撑表（数据库层接口面）

这些表保存 API 元数据。所有表都带有 `sBrandsId` / `sSubsidiaryId`，用于租户作用域。

| 表 | 角色 |
|---|---|
| `sysapi` | `/api/invoke` 使用的 API 定义。 |
| `sysapilog` | 每次调用的审计日志。 |
| `sysapibrand` | 伙伴 / 供应商目录。 |
| `sysapithirdparty` | 出站第三方端点定义。 |
| `sysapithirdtoken` | 出站第三方 token 配置。 |
| `sysapidbtodb` | DB-to-DB 同步 API 定义。**由 `xlyFlow` 的 `DbToDbController` 拥有，不由 xlyApi 拥有**；列在这里是因为该表位于 xlyApi 的 `sysapi.sql` 中。 |
| `sysapidbtodblog` | DB-to-DB 同步运行日志。同上，由 xlyFlow 写入。 |

## 集成方如何使用

典型的首次集成：

1. **让管理员注册你的 API。** 向 `sysapi` 插入一行（通过 BACK 自助页面或直接写库）：选择 `sApiCode`，编写 `sDataSql`，如果需要 token 认证则设置 `bHasToken = 1`，保存。
2. **获取 token。** `POST /xlyApi/token/getToken?corpid=<your_id>&corpsecret=<your_secret>`。
3. **调用 API。** 使用 `Authorization` 中的 token 和 JSON body 调用 `POST /xlyApi/api/invoke/<your_sApiCode>`。
4. **检查日志。** `sysapilog` 会记录每次调用、响应 `iStatus` 和 body。BACK 管理页面也会展示同样的信息。

## 这个 API 不是什么

- **不是 OpenAPI 描述的接口。** 任意 `sApiCode` 的契约由其 `sysapi.sDataSql` 和 `sBody` 行决定。规格要向注册该 API 的团队获取，而不是从这里的 `/swagger` 端点获取。
- **不是 `xlyEntry` 内部 API 的薄封装。** 这些是有意分开的接口面。不支持从外部调用 `xlyEntry` 上的 `/business/*`。
- **不是入站 webhook 接收器。** 第三方推送事件走 [`xlyInterface`](webhooks.md)。
