# External API (`xlyApi`) `xlyApi` is the API surface external integrators consume. It runs as its own Spring Boot WAR (entry class `xlyApi/src/main/java/com/xly/ApiApplicationBoot.java`) at context-path `/xlyApi`, with its own `application-*.yml` files. The defining design choice: **most external endpoints are not hard-coded in Java — they are rows in `sysapi`**. New external APIs are *registered as data*, like the rest of xly. The runtime's job is to look up the `sApiCode`, validate the caller, and execute whatever the metadata says. ## The data-driven dispatcher — `/api/invoke/{sApiCode}` The single most important endpoint: ``` POST /xlyApi/api/invoke/{sApiCode} Authorization: Bearer (or query param ?authorizationt=) Content-Type: application/json { ...request body, passed through as `sBody` to the handler... } ``` Handler: `ApiController.invoke()` at `xlyApi/src/main/java/com/xly/api/web/ApiController.java`. The flow: 1. Read the `Authorization` header (fallback: `authorizationt` query parameter). 2. Look up the `sysapi` row keyed by `sApiCode` (via `ApiServiceImpl.invoke` → `SELECT … FROM sysapi WHERE sApiCode = #{sApiCode}`). 3. If the row's `bHasToken` flag is set, AES-decrypt the bearer token to recover the `corpid`, then validate that `corpid` against `sysapibrand` via `BrandServiceImpl.selectByCorpid`. If the brand row's `iLossTime` is non-zero, also check the token's embedded timestamp hasn't expired. (`sysapithirdtoken` is for *outbound* tokens — xly calling third-party APIs — not for validating inbound bearer tokens here.) 4. Run the SQL template stored in `sysapi.sDataSql` with the request body merged into the parameter map. 5. Log the call to `sysapilog`. 6. Return the result wrapped in `AjaxResult`. So adding a new external API is an admin/PM task: insert a row into `sysapi`, supply the SQL template, set the flags, give the integrator the `sApiCode` and a token. No Java code change. ### `sysapi` columns of interest | Column | Meaning | |---|---| | `sApiCode` | The path variable consumers send. Must be unique per tenant. | | `sApiName` | Human label. | | `sApiUrl` / `sApiUrlRef` | Computed URL — `sApiUrlRef + sApiCode` — for outbound dispatches. | | `sMethod` | The HTTP method this API expects (`GET`, `POST`, …). | | `sHeader`, `sBody`, `sBodyNode`, `sBodyType` | Templates describing the inbound request shape. | | `sDataSql` | The SQL template the runtime executes. References to `#{xxx}` are filled from request params. | | `sDataTest`, `sDataTestNode` | Sample request/response used by the BACK API tester (`POST /api/apiTest`). | | `bHasToken` | `1` → require a token in `Authorization`; `0` → public. | ## API administration endpoints `/api/list/...`, `/api/data/...`, `/api/add`, `/api/edit`, `/api/remove` are the admin surface for managing `sysapi` rows themselves (the BACK "自定义接口" screen calls these). `POST /api/getSqlTemple` produces a SQL skeleton for a given API name; `POST /api/apiTest` runs an end-to-end dry-run against the test fixture in `sDataTest`. ## Token endpoints — `/token/*` | Endpoint | Method | Purpose | |---|---|---| | `/token/getToken?corpid=&corpsecret=` | GET / POST | Issue a bearer token for an integrator's `(corpid, corpsecret)` pair. (Mapping is method-agnostic.) | The token returned is what `/api/invoke/{sApiCode}` expects in `Authorization`. The full implementation is in `xlyApi/src/main/java/com/xly/api/web/TokenController.java` and its service. For *third-party* tokens that xly itself fetches (so it can call out-of-cluster APIs on behalf of a customer), see `/thirdtoken/*`, backed by `sysapithirdtoken` (table comment: 第三方token获取接口). ## Other curated surfaces These are smaller specialised APIs hosted in the same WAR: | Endpoint root | Controller | Purpose | |---|---|---| | `/online/api/{sApiCode}` | `OnlineController` | Renders the BACK in-browser API debug/console page for the given `sysapi` row (returns a Thymeleaf view, not API execution). | | `/online/onlineword/{sApiCode}` | `OnlineController` | Renders the "word"-style API documentation page. | | `/online/onlinelist` | `OnlineController` | Renders the online-API listing page. | | `/online/getToken` | `OnlineController` | Renders the in-browser token-acquisition helper page. | | `/pro/get/{sProName}` | `ProContentController` | Renders the BACK page that displays a stored procedure's source. | | `/pro/getData/{sProName}` | `ProContentController` | Returns the stored procedure's source text (not its result-set). | | `/pro/alert/{redisId}` | `ProContentController` | Renders the alert/notification display page for the given Redis key. | | `/pro/getAlertValue/{redisId}` | `ProContentController` | Returns the value cached in Redis under `redisId`. | | `/pro/executeSql` | `ProContentController` | Executes a `sSql` payload directly (admin-tier dev tool). | | `/thirdparty/*` | `ThirdPartyController` | CRUD over third-party-API definitions and the `checkPartyApi` validator. Backed by `sysapithirdparty`. | | `/thirdtoken/*` | `ThirdTokenController` | CRUD over outbound-token configs. Backed by `sysapithirdtoken`. | | `/brand/*` | `BrandController` | CRUD over the partner-supplier list (`sysapibrand`). | | `/json/*`, `/json/jsonEdit/{sDomId}` | `JsonController` | JSON-tree edit helpers used by the BACK API editor. | | `/interfaceDefine/callthirdparty/{interfaceInvoke}` | `InterfaceController` | Dispatch an outbound call defined in `sysapithirdparty`. | | `/gpt/chatGpt` | `GptController` | Pass-through for the experimental GPT/chat surface — out-of-scope of the framework wiki. | ## Backing tables (DB-level surface) These tables hold the API metadata. All carry `sBrandsId` / `sSubsidiaryId` for tenant scoping. | Table | Role | |---|---| | `sysapi` | API definitions used by `/api/invoke`. | | `sysapilog` | Per-call audit log. | | `sysapibrand` | Partner / supplier directory. | | `sysapithirdparty` | Outbound third-party endpoint definitions. | | `sysapithirdtoken` | Outbound third-party token configs. | | `sysapidbtodb` | DB-to-DB sync API definitions. **Owned by `xlyFlow`'s `DbToDbController`, not xlyApi** — listed here because the table lives in xlyApi's `sysapi.sql`. | | `sysapidbtodblog` | DB-to-DB sync run log. Same — written by xlyFlow. | ## How an integrator uses this A typical first integration: 1. **Have an admin register your API.** Insert a row into `sysapi` (via the BACK self-service screen or directly): pick `sApiCode`, write `sDataSql`, set `bHasToken = 1` if you want token auth, save. 2. **Get a token.** `POST /xlyApi/token/getToken?corpid=&corpsecret=`. 3. **Call the API.** `POST /xlyApi/api/invoke/` with the token in `Authorization` and your payload as the JSON body. 4. **Inspect the log.** `sysapilog` records every call, the response `iStatus`, and the body. The BACK admin screen surfaces the same. ## What this API is *not* - **Not OpenAPI-described** — the contract for any given `sApiCode` is whatever its `sysapi.sDataSql` and `sBody` rows say. Get the spec from the team that registered the API, not from a `/swagger` endpoint here. - **Not a thin wrapper over `xlyEntry`'s internal API** — these are intentionally separate surfaces. Calling `/business/*` on `xlyEntry` from outside is not supported. - **Not the inbound webhook receiver** — push events from third parties go through [`xlyInterface`](webhooks.md).