> **本文回答**：`vjmap.Service` 的实例化、Token 管理、工作区切换、Key 管理。

## 一、实例化

```js
const svc = new vjmap.Service(
  "https://vjmap.com/server/api/v1",     // 服务基地址
  "eyJhbGciOi..."                         // Access Token
);
```

官方公测地址：`https://vjmap.com/server/api/v1`。私有化部署改为你的域名。

Token 在 [vjmap 控制台](https://vjmap.com/app/cloud) 申请。生产环境**不要硬编码在前端**，推荐后端签发 short-lived token。

## 二、基本属性 / 方法

```js
svc.baseUrl();            // 返回 baseUrl 字符串
svc.accessToken;          // 当前 token
svc.styleVersion();       // 可用于 style.version
svc.glyphsUrl();          // 可用于 style.glyphs
svc.currentMapParam();    // 最近一次 openMap 的参数（含 mapid、version、bounds、drawBounds、darkMode）
svc.setCurrentMapParam({ darkMode: true, bounds: "[-1000,-1000,1000,1000]" });
                          // 适用于未 openMap 就需要 svc 参数的场景（空白图）
```

> ⚠️ 顶层 `vjmap.accessToken` 是**只读 getter**（赋值会抛 `only a getter`，d.ts 顶层亦未声明）——token 请在 `new Service(url, token)` 或 map `accessToken` option 里传，不要写顶层。

## 三、工作区（数据隔离）

不同工作区的数据互不可见，权限独立。

```js
svc.switchWorkspace("工作区名");   // 空字符串为默认工作区
```

```js
// 创建 / 修改工作区：云端管理平台 → 工作区管理；或 REST 接口
```

## 四、密钥管理

```js
// 把明文密码转 secretKey（不要把 secretKey 暴露给外部）
const key = svc.pwdToSecretKey("mypassword");

// 添加 secretKey：对拥有 key 的图可跳过密码输入
svc.addSecretKey(key);

// 添加 accessKey：访问权限较小，允许读，不能删
svc.addAccessKey("akxxxxxxxxxxxxxxxx");
```

权限等级：`superKey` > `secretKey` > `accessKey`。详见 [map-power](map-power.md)。

## 五、REST 底层 HTTP 方法

用于调用未封装的接口：

```js
svc.httpGet(url);                 // GET  → resolve 包装对象 {status, response, data, xhr}，业务 JSON 在 .data
svc.httpPost(url, data);          // POST body
svc.httpDel(url);                 // DELETE
svc.serviceUrl("xxx");            // 拼 {serverUrl}service/xxx（即 …/api/v1/service/xxx）；baseUrl() 则是去掉 api/v1 的站点根
```

例如：

```js
const url = svc.serviceUrl("recognizer/detail?filenames=" + prefix);
const { data } = await svc.httpGet(url);   // httpGet/httpPost/httpDel 不直接返回业务 JSON，要取 .data
```

## 六、常量枚举获取

一些后台生成的映射（图层、实体类型）在前端需用时可用：

```js
const { entTypeIdMap } = await svc.getConstData();
// entTypeIdMap: { "1": "AcDbLine", "7": "AcDbCircle", ... }
// 逆向：Object.keys(m).find(k => m[k] === "AcDbLine");
```

其他有用的字段：

- `svc.getMapLayers()`：当前打开图的所有图层列表 `[{ name, index, ... }]`。
- `svc.metadata(mapid, version)`：某张图的元数据（`bounds`/`drawBounds`、`layers`、`blockReferences`、`defaultlayer`、`layouts` 等）。

## 七、错误处理模板

HTTP 非 2xx 时 SDK 的 Promise 直接 **reject**（reject 值是 `{status, response, data, xhr}` 普通对象，不是 `Error`），这类失败**不会**出现在 `res.error` 里：

```js
async function safeOpen() {
  let res;
  try {
    res = await svc.openMap({ mapid, mapopenway: vjmap.MapOpenWay.GeomRender });
  } catch (e) {
    // e.status：403 = token 缺失/无效/过期（网关 body {code:7, msg:"Token is invalid"}）或无权限；
    //           404 = 图不存在（{error:"map file is not exist"}）；400/500 = 打开失败；0 = 网络/CORS
    let body = {};
    try { body = JSON.parse(e.response); } catch (_) { /* 非 JSON */ }
    if (e.status === 403 && body.code === 7) {
      // token 失效，跳转登录
    }
    throw new Error(body.error ?? body.msg ?? `HTTP ${e.status}`);
  }
  // 带内错误（HTTP 200）：如密码图未给/给错密码 {status:403, error:"no permission to access this map…"}
  if (res.error) throw new Error(res.error);
  return res;
}
```

## 八、常见坑

1. **不同工作区的图 mapid 冲突**：工作区是隔离的，同一 mapid 在不同工作区是两张图。调用前先 `switchWorkspace`。
2. **跨域**：vjmap 服务默认已返回 `Access-Control-Allow-Origin: *`（无 CORS 配置项）；跨域报错多是前置 Nginx/网关改写或丢了头，见 [trouble/token-cors](../trouble/token-cors.md)。
3. **Token 失效（HTTP 403，body `{code:7}`；SDK 层是 reject 不是 `res.error`）**：实际部署里通常用 2h/24h 的短 token，请注意自动续期。
4. **并发 openMap**：同一 `svc` 对象不要并发 `openMap`（会覆盖 currentMapParam）。
5. **`waitMapOpenFinish` 可能长时间挂起**：`tryTime/maxTryTimes` 参数不是快速轮询语义，自动化调用请自加 `Promise.race` 超时护栏。`getStyleLayerName(mapid,version)` 返图层名串；`getSliceCacheZoom({mapid,version})` 返 `{sliceCacheZoom}`；`cmdSwitchLayers` 返 `{layerid}`（键全小写）；**请求失败 reject 的值是普通对象不是 `Error`**（`String(e)`→`[object Object]`；形如 `{status, response, data, xhr}`，没有 `e.message`，消息在 `JSON.parse(e.response)` 的 `error`/`msg` 字段）。
