# 通用约定（Conventions）

所有接口的共性规则集中在这里，具体接口文档不再重复。

## 1. Base URL 与前缀

- 默认：`http://127.0.0.1:27660/api/v1`
- 文档里写成 `{base}`；SDK 自动补 `/` 结尾
- 三种子前缀：

| 前缀 | 用途 |
|------|------|
| `{base}/map/...` | 默认工作区的所有图相关接口 |
| `{base}/workspace/{name}/...` | 命名工作区下的图相关接口（与 `/map/` 等价但隔离存储） |
| `{base}/service/...` | 与图无关的工作区/KV/符号库/全文搜索/AI 等接口 |

系统级根路径：

| 路径 | 说明 |
|------|------|
| `/heart` | 健康检查 |
| `/version` | 版本 |

## 2. HTTP 方法

- **cmd 接口**（路径形如 `/api/v1/map/cmd/<cmd>/<mapid>/<version>`）**既接受 GET 也接受 POST**；网关 `Router.Any` 统一挂载。SDK 默认走 **POST**，原因：
  1. 请求体大时 GET 超长
  2. 语义上是"动作"而非"只读资源"
- 其它接口遵循标准 REST：GET 查、POST 创建、PUT 修改、DELETE 删除

## 3. 鉴权

> 详情见 [`03-authentication.md`](authentication.md)。

- **必带**：`token=<JWT>`——请求头 `token`（优先）或 query `?token=`；REST 不认 `Authorization: Bearer`
- **加密图**：请求头 `secretKey: <key1>_;_<key2>...` 放各图 secretKey（→ admin），`accessKey: <ak1>_;_<ak2>...` 放各图的 18 位 `ak` 开头 accessKey（→ 仅浏览）或实例 `map_super_key`（→ 所有图 admin）；也可放 query/body 同名驼峰键，但**同名 header 会覆盖 body**
- accessKey 是**每图一把**（首次设密时由服务端生成），不是跨图通用的全局 key；跨图通用的只有 super key，且只在 `accessKey` 位置生效

## 4. 路径占位符

| 占位 | 常用取值 | 含义 |
|------|---------|------|
| `{mapid}` | `sys_zp`、`_`、`_null` | 图 ID；`_` / `_null` 用在不需要 mapid 的 cmd 上 |
| `{version}` | `v1`、`_` | 版本号；mapid 为真实图名时 `_` 表示**最新版本**（后端自动取最大版本），mapid 为 `_`/`_null` 时与版本无关 |
| `{layer}` | `"0,1,2"`、`default`、空 | 图层名/ID；多个用逗号 |
| `{z}/{x}/{y}` | `10/854/402` | 标准 XYZ 瓦片坐标 |
| `{key}` | 用户自定义 | KV 存储的 key（建议 URL-safe） |

**重要**：SDK 会对占位符做 `encodeURIComponent`，别再手动编码。

## 5. 请求体约定

| 场景 | Content-Type | 备注 |
|------|--------------|------|
| 绝大多数 cmd | `application/json` | SDK 默认 |
| `/map/uploads` 文件上传 | `multipart/form-data` | 后端取第一个文件字段，字段名不限 |
| `/service/uploadImg` 图片上传 | `multipart/form-data` | 字段名必须是 **`upload`**（不是 `file`） |
| KV 存值 `POST /service/data/{key}` | `application/json` | `{"value":"...","prop":"..."}`，两者必须是字符串（`ShouldBindJSON`）；二进制先 base64 |

## 6. 响应结构

两类并存，SDK 都能识别：

### 6.1 web服务 网关标准响应

```json
{
  "code": 0,
  "msg": "",
  "data": { /* 实际内容 */ }
}
```

- `code == 0` → SDK unwrap 成 `data`
- `code != 0` → SDK 抛 `VjmapError(status=200, body={...})`

### 6.2 图形服务

```json
{
  "bounds": [...],
  "layers": [...],
  ...
}
```

直接返回，不包 `{code,msg,data}`。

### 6.2b service 子接口的 `{status,data}` 形态

`/api/v1/service/...` 下的部分接口（符号库、WebCAD、工作区等 Go 侧 handler）不走网关包装，直接返回：

```json
{ "status": "ok", "data": { ... } }
```

SDK 的 unwrap 只在 `code/data/msg` 三键同时存在时触发，因此这类响应**原样透传**——业务侧取 `.data` 字段（符号库示例见 [api/10-symbols](api/10-symbols.md)）。

### 6.3 错误响应

- HTTP 4xx / 5xx：SDK 抛 `VjmapError(status=4xx|5xx, url, body)`
- cmd 执行失败但网关路由成功：多数 cmd 用 `{error: "..."}` 或 `{result: {succeed: false}}` 表达；SDK 不会自动判断业务成功/失败，请业务侧自行校验

## 7. 分页 / 列表

大部分列出接口没有分页，一次返回全部：

- `listmaps`：按 mapid 聚合，返回 `{mapid: [ {version, db, filename, ...} ]}`；带 `curPage`/`pageCount` 时改回分页信封（见 [api/01 §9](api/01-map-management.md#9-列出地图-list_maps)）
- `symbol/list/{cat}`：`{data:{list,total,page,pageSize}}` 分页对象（非数组）
- `getWorkspace`：数组

少数有 `limit` / `offset` / `pageSize` / `pageNum`（例如 `fullsearch/search`），每个接口文档单独说明。

## 8. 幂等性与重试

| 操作 | 幂等 | 可自动重试？ |
|------|------|-------------|
| `openmap` (GeomRender) | 否 | 否，会占用 图形服务 子进程 |
| `metadata` / `listmaps` / `runstatus` | 是 | 是 |
| `updatemap` / `updateMetadata` | 否 | 否 |
| `deletemap` / `deletestyle` | 是 | 是，但业务需防误触 |
| 瓦片 GET | 是 | 是 |

SDK 不内置重试；

## 9. 超时

- SDK 默认 60 秒；超长 cmd（`composeNewMap`、`saveOfflineTileDb`、`extractTable` on 大图）可能要 3-5 分钟，调用时显式放大超时。**注意单位不同**：Python `_request(timeout=300)` 是**秒**，Node `_request(..., { timeout: 300000 })` 是**毫秒**（构造参数同单位：Python `timeout=60.0` s / Node `timeout=60000` ms）。
- 图形服务单请求默认 `600000 ms = 10 min`（`runstatus.process.reqTimeout`），超过会被强杀。

## 10. 文件下载 / 流式接口

导出 PDF、DXF、离线瓦片包、缩略图等接口返回 **二进制流**。SDK 里对应方法：

- Python：`svc._request("POST", url, stream=True)` 返回原始 `requests.Response`，自己 `.iter_content()` 写文件
- Node：`svc._request('POST', url, { stream: true })` 返回原始 `Response`，自己 `await res.arrayBuffer()` 或管道到文件

## 11. 命名风格（跨语言）

| 语言 | 风格 | 例 |
|------|------|----|
| Python SDK | `snake_case` | `open_map`, `close_map`, `raster_tile_url` |
| Node SDK | `camelCase` | `openMap`, `closeMap`, `rasterTileUrl` |
| 文档正文 | 二者并列列出 | 每个接口都给两种示例 |

## 12. 日志与可观测

- `runstatus?detail=true`：看引擎 pid、活跃请求、子进程等
- `viewlogs?record=100`：看最近 100 条日志；含 cmd 耗时、错误
