> **本文回答**：后端 REST 接口的基本组织方式，以及如何在不经过前端 SDK 时直接调用。

## 一、服务地址

```
BaseURL = https://vjmap.com/server/api/v1
```

私有化部署改为 `https://your-host/api/v1`。

## 二、鉴权

所有接口以 `?token=xxx` 或 HTTP Header `token: xxx` 传 Access Token。

对于有密码保护的图，还需要：

- `?secretKey=...`（全权限）
- `?accessKey=...`（访问权限）

## 三、常用接口

> ⚠️ 路径形态：`/api/v1/map/` 下的固定 action 只有 `openmap`、`updatemap`、`tile`、`uploads`、`mapfile`、`cmd`、`activate` 等；其余能力（查询/列表/元数据/导出/WMS）统一走 **`/map/cmd/{cmd}/{mapid}/{version}`** 命令通道，不是独立路由。`/api/v1/service/` 是 Go 服务组（全搜索、字体、数据存取、识别器等）。

| 接口 | 方法 | 作用 |
|---|---|---|
| `/map/uploads` | POST (multipart) | 上传 DWG，返回 `{fileid, mapid}` |
| `/map/openmap/{mapid}` | GET / POST | 创建或打开图（含 `fileid`, `geom`, `bkcolor`, `layeron`, `layeroff`, `clipbounds`, `stylename`, ...） |
| `/map/cmd/deletemap/{mapid}/{version}` | GET / POST | 删除图（`version="*"` 删全部版本，`version="_"` 只删最新版本；`retainVersionMaxCount` 保留最近 N 个） |
| `/map/tile/{mapid}/{version}/{layer}/{z}/{x}/{y}` | GET | 栅格瓦片（PNG；`{layer}` 是样式**图层名**，即 openMap 返回的 `res.layer` / `svc.getStyleLayerName()`，不是 `style.name`） |
| `/map/tile/{mapid}/{version}/{layer}/{z}/{x}/{y}.mvt` | GET | 矢量瓦片 |
| `/map/cmd/wms/{mapid}/{version}?service=WMS&request=GetMap&...` | GET | WMS GetMap 图块（支持任意坐标系转换） |
| `/service/map/tile/web/{z}/{x}/{y}` | GET | 互联网地图瓦片代理（`svc.webMapUrl()` 产物） |
| `/service/map/tile/webmap` | GET | 互联网地图转 WMS 代理 |
| `/map/cmd/queryFeatures/{mapid}/{version}` | GET/POST | 要素查询，`querytype` = `point`/`rect`/`condition`/`expresion`（后者为历史拼写）分别对应点/矩形/条件/表达式查询 |
| `/map/cmd/metadata/{mapid}/{version}` | GET/POST | 元数据 |
| `/map/cmd/{command}/{mapid}/{version}` | GET/POST | 执行命令（`exportPdf`, `exportDxf`, `getDataBounds`, `coordtransform`, `objectDetection`, ...）；`objectDetection`/`extractTable`/`extractCenterLines`/`composeNewMap` 走 `/map/cmd/{cmd}/_null/v1`，mapid 放参数里 |
| `/map/cmd/listmaps/{mapid}/{version}` | GET/POST | 图形列表（`curPage`/`pageCount`/`mapIds`） |
| `/server/ai/mcp` | GET (SSE) | MCP Server-Sent Events 入口（AI 用；挂在 `/server` 下，非 `/api/v1`） |
| `/server/mcp` | POST/GET (Streamable HTTP) | 后台服务 MCP（`cad_*` 图纸工具；`?tools=`/`?exclude=` 筛选、`?workspace=` 绑定工作区；私有化需 `mcp.enable=true`），见 [ai-features](ai-features.md) §三 |
| `/service/fullsearch/search` | GET/POST | 全文搜索（另有 `count`/`suggestions`/`searchByIDPrefix`） |

完整接口清单见：[https://vjmap.com/guide/restinterface.html](https://vjmap.com/guide/restinterface.html)

## 四、直接调用示例（Node.js）

### 上传图并创建

```js
const axios = require("axios");
const FormData = require("form-data");
const fs = require("fs");

const baseUrl = "https://vjmap.com/server/api/v1";
const token = "eyJhbGci...";

// 1) 上传
const form = new FormData();
form.append("fileToUpload", fs.createReadStream("./drawing.dwg"));
const u = await axios.post(`${baseUrl}/map/uploads?token=${token}`, form, {
  headers: form.getHeaders()
});
// u.data = { fileid, mapid, uploadname }

// 2) 打开
const mapid = "my_first_map";
const url = `${baseUrl}/map/openmap/${mapid}?geom=true&fileid=${u.data.fileid}&token=${token}`;
const r = await axios.get(url);
```

### 直接查询

```js
const resp = await axios.post(
  `${baseUrl}/map/cmd/queryFeatures/${mapid}/v1?token=${token}`,
  {
    querytype: "condition",
    condition: `name='7'`,          // 条件表达式
    fields: "objectid,center,radius",
    limit: 10000
  }
);
```

## 五、与前端 SDK 的对应关系

| 前端 | REST |
|---|---|
| `svc.openMap(...)` | `/map/openmap/{mapid}` |
| `svc.conditionQueryFeature(...)` | `POST /map/cmd/queryFeatures/{mapid}/{version}`，`querytype:"condition"` |
| `svc.execCommand(cmd, params, mapid, version)` | `/map/cmd/{cmd}/{mapid}/{version}` |
| `svc.wmsTileUrl(...)` | 构造 `/map/cmd/wms/{mapid}/{version}?service=WMS&request=GetMap&...` URL |
| `svc.rasterTileUrl()` | `/map/tile/{mapid}/{version}/{layer}/{z}/{x}/{y}` 模板 |

## 六、常见坑

1. **URL 没带 token** → 403，body `{code:7, msg:"Token is invalid"}`（401 只用于服务未激活/超最大用户数）。可以 `?token=xxx` 或 `header: { token }`。
2. **POST JSON** 但 Content-Type 没写 → 后端按 form 解析失败。
3. **删图通配符写错** → `version="*"` 才是删除该 mapid 所有版本（`retainVersionMaxCount` 可保留最近 N 个）；`"_"` 会被归一化为"最新版本"，只删最新一份，不是删全部。
4. **直接用 tile URL 但没 token** → 403。瓦片 URL 必须签 token（通常 SDK 会拼好）。
