# 工作区

> 分类代号：C.7  本章共 **5 个接口**

> 如果还没读过，建议先看 [入门 · 工作区](../workspace.md)，了解 vjmap 的命名空间模型。

工作区用来把地图 / KV / 全文搜索按"项目"隔开。默认所有地图都在"匿名工作区"；创建命名工作区后，URL 前缀由 `/api/v1/map/...` 变成 `/api/v1/workspace/{name}/...`。

---

## 快速索引

| 序号 | SDK 方法 | URL |
|----:|---------|-----|
| 48 | `get_workspaces` / `getWorkspaces` | `GET /api/v1/service/getWorkspace` |
| 49 | `workspace_create` / `workspaceCreate` | `POST /api/v1/service/workspaceCreate` |
| 50 | `workspace_modify` / `workspaceModify` | `POST /api/v1/service/workspaceModify` |
| 51 | `workspace_delete` / `workspaceDelete` | `DELETE /api/v1/service/workspaceDelete/{name}` |
| 52 | `workspace_status` / `workspaceStatus` | `GET /api/v1/service/workspaceStatus` |

> 49–52 在默认 casbin 策略下**仅 root** 可调（admin/guest 均被 `/api/v1/service/workspace*` 规则拒绝，见 [authentication.md §1.4](../authentication.md#14-角色权限casbin-默认策略)）。

---

## 48 · 列出工作区 `getWorkspace`

**URL**：`GET /api/v1/service/getWorkspace`

- root（`AuthorityId:root`）账户看全部工作区的完整字段
- 非 root 账户：`isPublic=true` 的工作区照常返回完整字段；`isPublic=false` 的工作区**不被过滤，而是被脱敏成只有 `{"isPublic":false}` 的占位项**（name/alias 等全空）——注意按 `w["name"]` 判空跳过

### 响应（实采：创建 1 个后）

```json
[
  {
    "name": "nb_ws_1776831982",
    "alias": "nb 临时测试",
    "isPublic": false,
    "workDir": "nb_ws_1776831982",
    "idleCloseTime": 0,
    "disableFullSearch": false,
    "createTime": "2026-04-22 12:26:22"
  }
]
```

一个工作区都没有时返回 `null`（而不是空数组 `[]`）。

### Python

```python
ws_list = svc.get_workspaces() or []
for w in ws_list:
    print(w["name"], w["alias"])
```

### Node.js

```javascript
const ws = (await svc.getWorkspaces()) || [];
for (const w of ws) console.log(w.name, w.alias);
```

---

## 49 · 创建工作区 `workspaceCreate`

**URL**：`POST /api/v1/service/workspaceCreate`

### 请求 body

| 字段 | 类型 | 说明 |
|------|------|------|
| `name` | string | 唯一工作区名（**会出现在 URL 路径**里）。后端校验：长度 ≥2，且不能含 `| - / \ : ? " < >`；**违例返回 HTTP 403**（非 `{status:"error"}` 包络——单字符名与含 `|` 名均被 403 拒绝） |
| `alias` | string | 显示名，任意字符 |
| `workDir` | string | 工作区数据目录名；空串表示与 `name` 一致 |
| `isPublic` | bool | 是否对所有登录用户可见 |
| `disableFullSearch` | bool | 是否禁用该工作区的全文搜索索引 |

### 响应（实采）

```json
{
  "data": "nb_ws_1776831982",
  "status": "ok"
}
```

### Python

```python
svc.workspace_create(
    name="myproj",
    alias="我的项目",
    is_public=False,
)
```

### Node.js

```javascript
await svc.workspaceCreate({ name: 'myproj', alias: '我的项目' });
```

---

## 50 · 修改工作区 `workspaceModify`

**URL**：`POST /api/v1/service/workspaceModify`

后端按 `name` 定位，**只更新 `alias` / `isPublic` / `disableFullSearch` 三项**（`workDir` / `idleCloseTime` / `createTime` 传了也不改）；body 里**没给的这三项会被重置为零值**（`""` / `false`）。所以常见用法是先拿 `getWorkspaces()` 的完整 obj，改完再整体 POST。

### 响应（实采）

```json
{
  "name": "nb_ws_1776831982",
  "status": "success"
}
```

### Python

```python
ws_list = svc.get_workspaces() or []
me = next(w for w in ws_list if w["name"] == "myproj")
me["alias"] = "新别名"
me["disableFullSearch"] = True
svc.workspace_modify(me)
```

### Node.js

```javascript
const ws = (await svc.getWorkspaces()) || [];
const me = ws.find(w => w.name === 'myproj');
me.alias = '新别名';
await svc.workspaceModify(me);
```

---

## 51 · 删除工作区 `workspaceDelete`

**URL**：`DELETE /api/v1/service/workspaceDelete/{name}`

### 响应（实采）

```json
{
  "name": "nb_ws_1776831982",
  "status": "success"
}
```

> ⚠️ **不可逆**：删除会停掉工作区进程、删其全文索引，并递归删除**整个工作目录**（相对 `workDir` 即 `./data/workspace/<workDir>`，若创建时给的是**绝对路径**则删的就是那个目录）——该工作区的全部图、瓦片、缓存一并消失。删前确认不需要保留数据。

### Python

```python
svc.workspace_delete("myproj")
```

### Node.js

```javascript
await svc.workspaceDelete('myproj');
```

---

## 52 · 工作区状态 `workspaceStatus`

**URL**：`GET /api/v1/service/workspaceStatus`

返回**全部**命名工作区的状态（遍历所有工作区）——没起进程的工作区也在列，`ProcIsNull:true`、`ProcIsLive:false`。只有**一个命名工作区都没有**时才返回 `null`（本地 root token 会返回 `null`）。

> ⚠️ **权限**：默认 casbin 策略只放行 root（admin/guest → HTTP 403 `权限不足`，公共云演示 token 即如此）——该接口面向管理员/后台，业务侧一般用 `getWorkspaces` 拿工作区列表即可。

### 响应（实采：无命名工作区）

```json
null
```

有工作区时返回 `WsDataStatus` 数组（Go 结构体无 json tag，键是原始字段名，**首字母大写**；另含 `AliasName`/`IsPublic`/`WorkDir`/`IdleCloseTime`/`DisableFullSearch`/`CreateTime`/`ProcIsMainProc` 等）：

```json
[
  {
    "Name": "myproj",
    "Port": 27661,
    "ProcProcessId": 12345,
    "ProcIsLive": true,
    "ProcIsNull": false,
    "RunTimeSec": 3600,
    "StartTimeSec": 12345
  }
]
```

---

## 在 SDK 中切换工作区

SDK 构造后，可以用 `svc.switch_workspace("myproj")` 把后续所有 `map/...` / `cmd/...` 调用都前缀到 `/workspace/myproj/`：

```python
svc = vjmap.Vjmap(API, TOKEN)
svc.switch_workspace("myproj")
svc.list_maps()            # -> /api/v1/workspace/myproj/cmd/listmaps/...
svc.switch_workspace("")   # 切回匿名（传空串，不是 None）
```

Node.js 同名方法 `svc.switchWorkspace("myproj")`。

---
