# PlatformAPIs_1.4.25

## 文档说明
- 本文档基于 PlatformAPIs_v1.3 升级。
- v1.4 变更：新增第 12 章「资源中心（Resource Center）」接口，包含 catalog、detail、search、publish、submit、import、mylist、delete、admin/review、admin/list 共 10 个接口。
- v1.4.1 变更：
  - §11.1 publish 新增 `docVersion` 递增校验，新版本号必须高于当前版本（支持语义版本比较，如 4.1 → 4.2、4.1 → 4.1.1）。
  - §11.3 get、§11.7 getById 新增可选参数 `currentVersion`，版本未变化时跳过正文下载，返回 `upToDate: true`。
  - §11.2 list 及 §12 资源中心列表接口的 `pageSize` 默认值改为 50，上限调整为 1000。
- v1.4.3 变更：
  - §11 新增 §11.10 `updateMeta` 接口，支持仅更新文档元数据（name、docVersion、description）而不创建新版本。支持通过 `key` 或 `id` 定位文档，两者都传时以 `id` 为准。
- v1.4.4 变更：
  - §11.5 `getVersion` 入参从 `version`（number）改为 `docVersion`（string），按语义版本号获取指定版本。
  - §11.6 `resolveRef` 的 `@v` 语法改为接受 docVersion 字符串（如 `PlatformAPIs@v1.4.3`），不再是内部版本序号。
  - §11.1 `publish` 新增 `docVersion` 唯一性校验：同一 key 下不允许发布重复的 docVersion。
- v1.4.5 变更：
  - §10 新增 §10.11 `deleteModule` 真删除接口：同时清理 S3 上该模块所有版本/文件类型的全部对象，并物理删除 `factory_modules` 主记录；不可逆。
  - §10.9 `updateEmbedding` 拼接源补齐：新增 `category`、`family`、`group`、`moduleName`，让向量召回能感知模块归类与族/组导航；并明确 `updateModuleMeta` 已会自动尽力刷新该向量。
- v1.4.6 变更：
  - §6 改为「项目 Deploy（Project Deploy）」章节，补充项目发布接口：发布前先调用 `getPublishTicket` 获取票据，再调用 `/work/publish/{workId}` 提交加密后的案例数据。
  - §6 明确“发布”和“上架”是两个独立步骤；如果希望发布后正式生效，需要手动再调用 `launchVersion`。
  - §2.10 / §6.2 补充 `launchVersion` 的下架语义：`version` 传字符串 `off-shelf` 时，项目下架。
- v1.4.7 变更：
  - §6 补充版本口径：发布接口响应中的 `version` 才是发布版本号；调用方可在本地把它记作 `publishedVersion`。`/ih5/editor/work/get` 返回的 `version` 是线上文件版本，不作为发布版本号口径使用。
  - §6 新增本地调用核心流程：详细说明“先发布，再拿发布响应中的 `version` 调用 `launchVersion` 上架”的标准顺序，并补充 publish-only 路径。
- v1.4.9 变更：
  - §0 / §5 / §6 统一 VLCode 本地工具术语：`compile` / `parse` / `编译` / `解析` 指使用 parser 把 VL 项目部署到线上预览服务；`deploy` 指发布并上架。
  - §5.1 `parsevl` 新增 `resetData` 参数说明，并保留 `resetDbData` 作为兼容别名；默认不允许清空预览数据。
  - §6.4 新增预览/发布数据同步接口：`syncToPublish` 与 `syncToPreview`，用于在预览数据表和发布数据表之间按方向覆盖同步数据。
  - §10 合并组件工厂导航与过滤 patch：补齐 `family` / `group` / `navOrder` 字段、结构化过滤、`updateModuleMeta`、导航字段存储与索引约定。
- v1.4.10 变更：
  - §10 调整组件工厂模块元数据口径：`metadataJson` 只保存公开组件合同；预览 frame 尺寸由模块记录顶层 `previewFrameJson` 维护；`metadataJson` 标准字段不再包含 `preview` 与 `layout`。
  - §10.1 / §10.2 / §10.10 接入模块级 `previewFrameJson` 字段，覆盖上传、详情读取与元数据更新链路。
  - §10.1.1 重写组件元数据 schema：明确标准字段集合为 `family` / `group` / `summary` / `keywords` / `useCases` / `notFor` / `order` / `dependsOn` / `interfaceMeta`；`interfaceMeta.*` 增加 `example` / `required` / `nullable` 字段约定，并说明 parser 输出对齐迁移。
- v1.4.11 变更：
  - §7.2 `listCost` 补齐完整请求参数、返回字段与 `CostDetail` 结构说明。
- v1.4.12 变更：
  - §6 新增 §6.5 `exportTable` 表数据导出接口，补充下载型 HTTP 接口的请求参数、`header` 编码规则与调用示例。
- v1.4.13 变更：
  - §5.1 / §5.2 / §5.3 补充 parser `dbEngine` 参数：默认 `pg`，可传 `mysql` 将项目解析/部署到 MySQL 数据库；MySQL 模式下不支持 `VEC` / `VECTOR` / `BOOL` / `BOOLEAN` 数据表字段。
- v1.4.14 变更：
  - §10.1 / §10.2 清理组件工厂元数据读取口径，明确 `metadataJson` 原始字符串仅作为存储值保留，客户端以解析后的 `metadata.interfaceMeta` 与模块顶层 `previewFrameJson` 为标准读取入口。
  - §5.1 / §5.2 / §5.3 补充 parser lint mode 参数：`lintMode:"default"` 为默认放行模式，`lintMode:"strict"` 打开组件公开接口 meta / contract 等严格校验；`mode` 作为兼容别名，调用方优先使用 `lintMode`。
- v1.4.15 变更：
  - §4 明确 Project Settings 属于项目工程配置与平台部署配置，不属于 VL 语法规范；`Config/project.settings.json` 的字段权威来源归入 Platform API / Project Config 口径。
  - §4 扩展作品配置字段边界：域名、Loading、favicon、`hideJs`、`stage` 可进入 `project.settings.json`；接口密钥、支付/微信/地图等第三方接口配置和私有部署导出配置不进入普通 settings。
  - §5.1 / §5.4 明确 settings 同步口径：`parsePjt` 是完整 compile 方法，只消费部分预览设置；`syncPjtSettings` 是独立 parser action，用于按 `Config/project.settings.json` 同步作品配置。
  - §4 / §5 明确平台默认预览域名不支持自定义 `previewPath`；业务自定义路径仅适用于自定义预览域名。
- v1.4.16 变更：
  - 新增 §14「管理后台与内部运维接口（Admin / Internal Ops）」，基于 `/Users/ivx/Downloads/小工具案例.json` 中沉淀的 admin 调用提取。
  - 覆盖 admin 登录、当前管理员信息、作品上架/下架、复制作品/整组作品、UA 过滤、访问限制、欠费屏蔽、缓存清理、短链、数据库描述/解密/自定义表、Lambda 创建、数据恢复、归属校验与作品转移等接口。
  - 明确 `/api/{nid}/...` 案例私有接口、旧外部辅助接口和第三方业务配置不进入 Platform API 通用接口口径。
- v1.4.17 变更：
  - §5 parser 入口新增 `region` 参数：默认 `"en"` 部署至英文服 `https://editor.visuallogic.ai`，传 `"cn"` 部署至中文服 `https://ai.ivx.cn`。
  - §5.1 / §5.4 明确 `region` 是 parser handler 级平台区服选择，会在 action 分发前设置平台 API base URL；非法值返回 `region must be one of: en, cn`。
- v1.4.18 变更：
  - §5.1 / §5.2 / §5.3 新增 parser `inline` 参数：只有显式传 `inline:true` 时，解析器进入 inline CSS 测试模式，允许不提供 Theme，并放开组件实例上的直接 CSS 属性、CSS 白名单、节点适用范围和 `sk.*` 范围限制。
  - §5.1 明确 `files + targetGid` 是合法直传部署模式：源码可来自一个本地新项目或空 config 项目，但部署目标为现有 `targetGid` 组应用；`gid` 可作为 `targetGid` 兼容别名。
- v1.4.19 变更：
  - §0.3 / §1 补充平台 base URL 口径：英文服与中文服是两套接口路径一致的平台环境，只是 base URL 不同。
  - §5.1 修正中文服 parser 平台 API base URL 为 `https://ai.ivx.cn`。
  - §4.3 / §5.1 / §5.4 明确 `.vx` 文件名是前端应用稳定绑定键，不支持通过修改 `Apps/*.vx` 文件名来表达应用 rename；应用显示名称、域名、路径等应通过 settings / 平台作品信息修改。
  - §5.1 明确 `targetGid` 与 `Config/project.state.json.gid` 必须一致，`nid` 必须真实属于目标 `gid`，不再退回顺序匹配。
  - §5.1 明确 parser 保留目标 `gid` 下未被本次源码覆盖的其他应用，仅通过 `excessCases` 提示，不提供删除入口。
  - §5.1 明确 `parsePjt` 与 `lintPjt` 职责分离，调用方需要阻断错误时应先执行 `lintPjt`。
  - §5.1 明确目标组元数据、`serviceList`、`mainNid`、`info` 等字段按保守 PATCH 语义更新，避免覆盖同组其他应用信息。
- v1.4.20 变更：
  - §5.1 新增 parser `dbSchemaMode:"bindOnly"` 口径，用于旧项目绑定既有数据库表；请求参数 `bindOnly:true` / `vdbBindOnly:true` / `vdbSchemaMode:"bindOnly"` 与 `.vdb` 根节点 `bindOnly:true` 均可触发。
  - §5.1 明确 bindOnly 模式不会创建表、创建字段、创建索引、写表关系、创建 email storage、清表或导入 `.vdb data`，只读取既有表 alias 做 `VirtualTable` 绑定。
  - §5.1 明确 bindOnly 模式下缺表、缺 email storage、`.vdb` 字段不存在于旧库、字段类型与旧库不兼容、或同时请求 `resetData/resetDbData=true` 时，parser 必须以 error 拒绝部署。
- v1.4.21 变更：
  - 新增 §13「admin接口」，收录 edten `/edt/admin/*` 新 admin 接口，明确新 admin 使用普通平台登录态加服务端系统管理员 UID 白名单，不继承旧 `/ih5/admin/auth/login` 与 `ih5admin` Cookie 体系。
  - §13.2 新增发布域名强制设置接口，可按 `nid` 把发布版 `domain/path` 转移到指定作品，并自动清理其他作品的同域名占用与缓存。
- v1.4.22 变更：
  - §13.3 新增工作组核心信息强制修改接口，可按 `gid` 修复工作组基础字段、`info`、归属 `uid`、内含 `nids` 与 `mainNid`，并清理 work group、groupInfo、作品和作品数据相关缓存。
  - §5.1 明确一个 `gid` 可以承载多个独立 VL 项目；每个 VL 项目保持一个 backend nid，parser 部署时通过前端作品 `extra.bgNid` 记录当前前端所属后台。
- v1.4.23 变更：
  - §0.3 明确中文服 `https://ai.ivx.cn` 与英文服 `https://editor.visuallogic.ai` 具备相同 Platform API 路径能力；调用中文服接口时使用相同相对路径并仅替换 base URL 前缀。
- v1.4.24 变更：
  - 规范化 SysDoc `docVersion` 为三段语义版本号，承接上一版的 parser phase / MySQL deploy validation 文档内容。
- v1.4.25 变更：
  - 清理文档正文中的旧四段版本引用，保持文档标题、SysDoc `docVersion` 与产品绑定目标均为三段语义版本号。
- 目标：为每个接口补齐「入参、回参、注意事项、调用示例」。

## 0. AI 助手推荐工作流

### 0.1 标准开发流程

除非用户特殊说明，AI 助手操作 VL 项目时统一采用 **Workspace 模式**，流程如下：

1. **新建项目**（仅首次）：调用 `createProject/v3`（§2.2）获取项目 `gid`，将 `gid` 记入会话上下文（session），后续所有操作复用该 `gid`。
2. **写入文件**：调用 `writeFiles`（§3.1）将 `.vx` / `.sc` 源文件批量写入 workspace，参数 `gid` 使用会话中的项目 ID。
3. **compile 预览**：调用 `parsevl`（§5.1）时 **不传 `file`**，仅传 `targetGid`，引擎直接从 workspace 读取最新文件并解析并部署到线上预览服务；若源码来自本地直传文件集，也可以同时传 `files + targetGid`，表示把这批文件部署到现有组应用。
4. **查看结果**：从响应 `data.apps.frontends["<app>.vx"].previewUrl` 取预览根地址，拼接 `/route/{path}` 访问具体页面。

### 0.2 关键约定

VLCode 本地工具术语：

- `compile` / `parse` / `编译` / `解析` 某个 VL 项目：指使用 parser 执行 `parsePjt`，把项目代码部署到线上预览服务；它只生成或更新预览资源，不代表发布版本，也不代表上架。
- `deploy`：指发布并上架，标准链路是先调用 §6.1 发布接口生成发布版本，再调用 §6.2 `launchVersion` 把该版本切为正式入口。
- 当调用方只想更新预览，应调用 §5.1 `parsevl`；当调用方想让正式入口生效，应在预览确认后继续执行 §6 的发布并上架链路。

- **不使用 ZIP 上传模式**（不传 `file` 字段）。所有源文件通过 workspace 接口管理。
- **`gid` 是项目的唯一标识**：`writeFiles`、`listFile`、`readFile`、`parsevl` 等接口均以 `gid` 定位项目。
- **已有项目**：若用户提供了 `gid`（如 `gid=1440`），直接使用，无需新建；parser §5.1 中使用 `targetGid` 指定该目标组应用，`gid` 仅作为兼容别名。
- **parsevl 最简调用**：

```json
{
  "action": "parsePjt",
  "targetGid": 1440,
  "download": true,
  "resetData": false
}
```

`projectName` 可选，不影响解析结果；`file` 留空或不传；`resetData` 默认 `false`，表示 compile 预览时不允许自动清空现有数据表。

### 0.3 平台区服与 base URL

英文服与中文服是两套接口路径一致的平台环境，只是 base URL 不同。中文服 `https://ai.ivx.cn` 可以完整操作本文档中定义的中文服 Platform API，能力范围与英文服 `https://editor.visuallogic.ai` 一致；调用同一个接口时保持相同 `Path`，仅替换 base URL 前缀。本文档中未写完整域名的接口 `Path` 均为相对路径，调用方应按目标区服拼接对应 base URL。

| 区服 | region | base URL |
|------|--------|----------|
| 英文服 / VisualLogic | `"en"` | `https://editor.visuallogic.ai` |
| 中文服 / iVX | `"cn"` | `https://ai.ivx.cn` |

约束：
- 同一次登录、用户信息、workspace、workGroup、work、db、发布与 parser compile 链路必须使用同一个区服。
- 英文服 Cookie 只用于英文服 base URL；中文服 Cookie 只用于中文服 base URL。
- `gid`、`nid` 只在其所在区服内有意义；同一个数字 id 不应跨区服推断为同一个线上项目。

## 1. 认证与用户（Auth / User）

### 1.1 企业账号登录
- Name: `login`
- Method: `POST`
- Path: `/ih5/app/user/login`
- 英文服完整 URL：`https://editor.visuallogic.ai/ih5/app/user/login`
- 中文服完整 URL：`https://ai.ivx.cn/ih5/app/user/login`

请求参数：

```json
{
  "username": "",
  "password": "",
  "companyName": ""
}
```

响应参数：
- `token` string

响应示例：

```json
{
  "token": ""
}
```

注意事项：
- `token` / 会话态用于后续受保护接口调用。
- 登录接口本身不携带 `region` 参数；区服由请求使用的 base URL 决定。
- 调用方拿到 `token` 后，应按 `ih5bearer=<token>` 形式写入 Cookie，并只用于同一区服的后续请求。

### 1.2 当前用户信息
- Name: `userinfo`
- Method: `POST`
- Path: `/ih5/app/user/userinfo`
- 英文服完整 URL：`https://editor.visuallogic.ai/ih5/app/user/userinfo`
- 中文服完整 URL：`https://ai.ivx.cn/ih5/app/user/userinfo`

请求参数：

```json
{}
```

响应核心字段：
- `id`, `name`, `email`, `phone`, `companyName`, `eid`, `userType`, `worksCount`, `isPro`, `eTime`

注意事项：
- 鉴权主要依赖 Cookie/登录态。
- `userinfo` 返回的是当前 Cookie 所属区服的当前用户信息。

### 1.3 编辑用户信息
- Name: `userInfoEdit`
- Method: `POST`
- Path: `/ih5/app/user/userInfoEdit`

请求参数：

```json
{
  "nickName": "OneAng1",
  "phone": "19811111111",
  "email": "3921@qq.com",
  "picture": "//file.visuallogic.ai/v35/int/upic10130354.png"
}
```

响应参数：

```json
{}
```

注意事项：
- 原始文档返回为空对象表示成功。

### 1.4 Google 登录/注册
- Name: `googleLoginOrRegister`
- Method: `POST`
- Path: `/api/12025157/googleLoginOrRegister`

请求参数：
- `email` string
- `name` string
- `id` string（Google ID）
- `picture` string

响应核心字段：
- `code`, `reason`, `data`, `gid`

### 1.5 第三方登录
- Name: `thirdLogin`
- Method: `GET`
- Path: `/ih5/app/user/thirdLogin`

请求参数（query）：
- `code`, `idKey`, `iconKey`, `nickNameKey`, `name`

响应参数：

```json
{}
```

### 1.6 Google OAuth 用户信息
- Name: `Google OAuth userinfo`
- Method: `GET`
- Path: `https://www.googleapis.com/oauth2/v2/userinfo`

请求头:
- `Authorization: Bearer {access_token}`

响应核心字段：
- `id`, `email`, `verified_email`, `name`, `picture`, `locale`, `hd`

注意事项：
- 401 常见于 token 过期/失效。

## 2. 项目与应用管理（Project / Work / App）

### 2.1 模板列表
- Name: `listTemplate`
- Method: `POST`
- Path: `/edt/aiChat/listTemplate`

请求参数：
- `Ids` array
- `Keyword` string
- `Cursor` integer
- `Limit` integer
- `IsDel` boolean

响应核心字段：
- `status`, `detail`, `data[]`
- `data[]` 内含：`id`, `uid`, `projectName`, `gitRepoUrl`, `latestCommitHash`, `fileJson`, `createAt`, `isDel`, `mark`

### 2.2 创建项目 V3
- Name: `createProject/v3`
- Method: `POST`
- Path: `/edt/aiChat/createProject/v3`

请求参数：
- `projectName` string（可选，初始化项目名称）

请求示例：

```json
{
  "projectName": "My Project"
}
```

响应示例：

```json
{
  "code": 0,
  "data": {
    "id": 123,
    "uid": "1",
    "projectName": "My Project",
    "gitRepoUrl": "123",
    "latestCommitHash": "",
    "fileJson": "{}",
    "createAt": "2024-01-01 12:00:00",
    "isDel": 0,
    "mark": ""
  }
}
```

注意事项：
- 支持在初始化时传入 `projectName`。
- 不传 `projectName` 时，由系统按默认规则生成项目名。

### 2.3 修改项目
- Name: `modifyProject`
- Method: `POST`
- Path: `/edt/aiChat/modifyProject`

请求参数：

```json
{
  "pid": 123,
  "projectName": "Updated Name"
}
```

响应示例：

```json
{
  "code": 0,
  "data": null
}
```

### 2.4 用户组应用列表
- Name: `apps/list`
- Method: `POST`
- Path: `/ih5/app/apps/list`

请求参数：
- `asc`, `offset`, `limit`, `orderBy`, `groupTitle`, `workGroup`

响应核心字段：
- `list[]`
- `list[]` 内含：`gid`, `uid`, `nids`, `title`, `createdAt`, `mainNid`, `workDatas[]`, `info`

注意事项：
- `info` 为 JSON 字符串，需二次解析。

### 2.5 收藏列表（Projects 菜单）
- Name: `work/list`
- Method: `POST`
- Path: `/edt/work/list`

请求参数：
- `asc`, `cooperate`, `folderId`, `groupTitle`, `isBan`, `isLaunch`, `isMark`, `lastTimestamp`, `limit`, `ntype`, `orderBy`, `published`, `tags`, `title`, `workGroup`

响应核心字段：
- `status`, `detail`, `data.total`, `data.works[]`

### 2.6 工作组详情
- Name: `workGroup/get`
- Method: `POST`
- Path: `/ih5/editor/workGroup/get`

请求参数：

```json
{
  "gid": 1402
}
```

响应核心字段：
- `gid`, `uid`, `nids`, `title`, `createdAt`, `works[]`, `mainNid`

### 2.7 删除组应用
- Name: `delGroup`
- Method: `POST`
- Path: `/edt/work/delGroup`

请求参数：
- `gid` integer（必填）

响应参数：

```json
{}
```

### 2.8 删除应用
- Name: `delete`
- Method: `POST`
- Path: `/ih5/editor/work/delete`

请求参数：
- `nid` integer（必填）

响应参数：

```json
{}
```

### 2.9 收藏/取消收藏
- Name: `setNodeMark`
- Method: `POST`
- Path: `/ih5/app/apps/setNodeMark`

请求参数：
- `nid` integer
- `isMark` boolean

响应参数：

```json
{}
```

### 2.10 上架/下架版本
- Name: `launchVersion`
- Method: `POST`
- Path: `/ih5/editor/work/launchVersion`

请求参数：
- `nid` integer
- `version` string

响应参数：

```json
{}
```

注意事项：
- `version` 传已发布版本号时，上架该版本为当前正式版本。
- `version` 传字符串 `off-shelf` 时，项目下架。

### 2.11 修改应用信息（Work）
- Name: `modify`
- Method: `POST`
- Path: `/ih5/editor/work/modify`

请求参数：
- `nid` integer（必填）
- `title` string（修改标题时必填）
- `bgNid` integer（可选，前端应用绑定的后台作品 nid；服务端写入该前端作品的 `extra.bgNid`）
- 也可承载配置类参数，见第 4 章

响应参数：

```json
{}
```

## 3. Workspace 文件与文档（Workspace）

通用约定：
- 全部为 `POST /ih5/editor/workspace/*`
- `version` 从 `1` 递增
- 读取接口 `version` 可选：不传读最新，传入读历史版本

### 3.1 File 接口（gid 级别）

#### createFile
- Path: `/ih5/editor/workspace/createFile`

请求：

```json
{
  "gid": 1234,
  "path": "xx"
}
```

注意事项：
- 文件必须先创建后写入。

#### listFile
- Path: `/ih5/editor/workspace/listFile`

请求：

```json
{
  "gid": 1234
}
```

响应：

```json
{
  "files": [
    {
      "gid": 1234,
      "path": "xx",
      "version": 1,
      "status": 0
    }
  ]
}
```

注意事项：
- `status=0` 为普通状态。
- `version=0` 表示已创建未写入内容。

#### writeFile
- Path: `/ih5/editor/workspace/writeFile`

请求：

```json
{
  "gid": 1234,
  "path": "xx",
  "content": ""
}
```

响应：

```json
{
  "gid": 1234,
  "path": "xx",
  "version": 2
}
```

#### readFile
- Path: `/ih5/editor/workspace/readFile`

请求：

```json
{
  "gid": 1234,
  "path": "xx",
  "version": 2
}
```

响应：

```json
{
  "version": 2,
  "status": 0,
  "content": "xx"
}
```

#### deleteFile
- Path: `/ih5/editor/workspace/deleteFile`

请求：

```json
{
  "gid": 1234,
  "path": "xx"
}
```

#### createFiles
- Path: `/ih5/editor/workspace/createFiles`

请求：

```json
{
  "gid": 1234,
  "paths": ["a.txt", "b.txt"]
}
```

#### readFiles
- Path: `/ih5/editor/workspace/readFiles`

请求：

```json
{
  "gid": 1234,
  "paths": ["a.txt", "b.txt"]
}
```

响应：

```json
{
  "files": [
    {
      "gid": 1234,
      "path": "a.txt",
      "content": "xx"
    }
  ]
}
```

#### writeFiles
- Path: `/ih5/editor/workspace/writeFiles`

请求：

```json
{
  "gid": 1234,
  "files": [
    {
      "path": "a.txt",
      "content": "..."
    },
    {
      "path": "b.txt",
      "content": "..."
    }
  ]
}
```

#### deleteFiles
- Path: `/ih5/editor/workspace/deleteFiles`

请求：

```json
{
  "gid": 1234,
  "paths": ["a.txt", "b.txt"]
}
```

### 3.2 Doc 接口（uid 级别）

#### createDoc
- Path: `/ih5/editor/workspace/createDoc`

请求：

```json
{
  "path": "xx",
  "shared": false
}
```

#### listDoc
- Path: `/ih5/editor/workspace/listDoc`

请求：

```json
{
  "shared": false
}
```

响应：

```json
{
  "docs": [
    {
      "uid": "xx",
      "path": "xx",
      "version": 1
    }
  ]
}
```

#### writeDoc
- Path: `/ih5/editor/workspace/writeDoc`

请求：

```json
{
  "path": "xx",
  "content": "",
  "shared": false
}
```

响应：

```json
{
  "path": "xx",
  "version": 2
}
```

#### readDoc
- Path: `/ih5/editor/workspace/readDoc`

请求：

```json
{
  "path": "xx",
  "version": 2,
  "shared": false
}
```

响应：

```json
{
  "version": 2,
  "content": "xx"
}
```

#### deleteDoc
- Path: `/ih5/editor/workspace/deleteDoc`

请求：

```json
{
  "path": "xx",
  "shared": false
}
```

字段说明：
- `gid`: File 空间标识
- `uid`: Doc 空间标识（由服务返回）
- `shared`: Doc 空间开关

## 4. 作品配置（Config）

本章是项目工程配置与平台部署配置的权威口径。`Config/project.settings.json` 是项目文件中的用户可读配置与平台回传快照，不属于 VL 语法规范，不参与 `.vx` / `.vs` / `.sc` / `.cp` / `.vth` 的语言解析规则定义。

Project Settings 的推荐边界：

- 可进入 `Config/project.settings.json`：域名与路径、预览域名与路径、Loading、favicon、`hideJs`、`stage`、平台回传的 `gid` / `nid` / `previewUrl` / `publishUrl`。
- 不进入普通 settings：微信、支付、QQ、邮件、地图等第三方接口密钥或接口配置；私有部署导出配置；数据库、Redis、微服务、文件存储等 Docker / runtime 配置。
- 环境变量定义和环境变量值分开管理：parser 可从 VL 项目声明中同步环境变量定义，预览版值和发布版值属于平台环境配置，不应被 `parsePjt` 当作普通 settings 改写。
- 敏感字段不得写入 `Config/project.settings.json`；若需要本地持有，应走独立 secret 文件或平台安全配置。

### 4.1 获取作品配置
- Name: `getConfig`
- Method: `POST`
- Path: `/ih5/editor/work/getConfig`

请求参数：
- `type` string（固定 `settings`）
- `nid` number（作品 ID）

请求示例：

```json
{
  "type": "settings",
  "nid": 12028028
}
```

响应核心字段：
- `customDomain`, `domain`, `previewDomain`, `previewPath`, `path`
- `pubRoot`, `preRoot`
- `loadingInfoState`, `loading`, `favicon`, `hideJs`, `stage`

`loading` 子字段：
- `bgColor`, `loadingGif`, `width`, `height`, `x`, `y`, `percent.show`, `progressBar.type`, `progressBar.show`

`stage` 子字段：
- `width`, `height`, `viewport`, `hideCornerIcon`

注意事项：
- `previewDomain` 为空时使用系统默认预览域名。
- `domain` / `path` 描述发布地址，`previewDomain` / `previewPath` 描述预览地址。
- `pubRoot=true` 时发布地址使用域名根路径；`preRoot=true` 时预览地址使用预览域名根路径。
- 使用平台默认预览域名时，不支持自定义 `previewPath` 或根路径；预览地址由平台生成。
- 只有绑定自定义预览域名时，才应配置 `/ourtools` 这类业务自定义 `previewPath`。

### 4.2 设置作品配置
- Name: `modify`（配置用途）
- Method: `POST`
- Path: `/ih5/editor/work/modify`

公共参数：
- `nid` number（必填）

响应：

```json
{}
```

功能一：发布域名/路径
- 参数：`domain`, `customDomain`, `previewDomain`, `path`, `pubRoot`, `previewPath`, `preRoot`

示例：

```json
{
  "domain": "www.visuallogic.ai",
  "customDomain": true,
  "previewDomain": "",
  "path": "/play/HLYsGEV0",
  "pubRoot": false,
  "previewPath": "/play/HLYsGEV0",
  "preRoot": false,
  "nid": 12028028
}
```

功能二：Loading 配置
- 参数：`settings`（JSON 字符串）
- `settings` 内含：`loadingInfoState`, `loading`

示例：

```json
{
  "settings": "{\"loading\":{\"bgColor\":\"#A34141\",\"loadingGif\":\"S-xxx.svg\",\"width\":100,\"height\":100,\"percent\":{\"show\":true},\"progressBar\":{\"type\":\"bar\",\"show\":true},\"x\":\"50%\",\"y\":\"50%\"},\"loadingInfoState\":true}",
  "nid": 12028028
}
```

功能三：Favicon 配置
- 参数：`title`（可选）、`settings`（JSON 字符串，内含 `favicon`）

示例：

```json
{
  "nid": 12028028,
  "title": "TodoApp344",
  "settings": "{\"favicon\":\"E-65278ff1e5354dece7fa444dcc44bc6a_3557.svg\"}"
}
```

注意事项：
- `pubRoot=true` 时发布地址为 `https://{domain}/`；否则为 `https://{domain}{path}`。
- `preRoot=true` 时预览地址为 `https://{previewDomain}/`；否则为 `https://{previewDomain}{previewPath}`。
- `title` 属于作品基本信息，`favicon` 属于 settings JSON；封面、简介、标签等发布 Web 应用界面字段不在当前 `settings` 核心字段内。
- Loading 与 favicon 可通过 `settings` JSON 合并更新；调用方应先读取现有 settings 后做字段级合并，避免覆盖其他已有配置。
- 当使用平台默认预览域名时，调用方不得设置自定义 `previewPath`；若需要 `/ourtools` 这类业务路径，必须先绑定自定义预览域名。

### 4.3 Project Settings 文件结构

`Config/project.settings.json` 用于本地项目保存作品配置声明与平台同步快照。推荐结构：

```json
{
  "gid": 2166,
  "backend": {
    "nid": 12029196,
    "previewUrl": "https://v4pre.visuallogic.ai/play/PZXqDEBr"
  },
  "frontends": {
    "SystemDocs.vx": {
      "nid": 12029195,
      "customDomain": true,
      "previewDomain": "preview.example.com",
      "previewPath": "/ourtools",
      "previewRoot": false,
      "domain": "www.ivx.cn",
      "path": "/ourtools",
      "releaseDomain": "www.ivx.cn",
      "releasePath": "/ourtools",
      "releaseRoot": false,
      "favicon": "E-65278ff1e5354dece7fa444dcc44bc6a_3557.svg",
      "hideJs": true,
      "loadingInfoState": true,
      "loading": {
        "bgColor": "#FFFFFF",
        "loadingGif": "85195cbaf13e0d5a073309016d40bc",
        "x": "50%",
        "y": "50%",
        "width": 100,
        "height": 100,
        "percent": { "show": true },
        "progressBar": { "show": true, "type": "bar" }
      },
      "stage": {
        "width": 1920,
        "height": 1080,
        "viewport": "auto",
        "hideCornerIcon": true
      },
      "previewUrl": "https://preview.example.com/ourtools",
      "publishUrl": "https://www.ivx.cn/ourtools"
    }
  }
}
```

字段说明：

| 字段 | 类型 | 说明 |
|------|------|------|
| `gid` | number | 项目组 ID，平台回传快照字段 |
| `backend` | object | 后台作品配置与回传快照 |
| `frontends` | object | 前端应用配置，key 为 `.vx` 文件名 |
| `nid` | number | 作品 ID，平台回传快照字段 |
| `customDomain` | boolean | 是否启用自定义域名 |
| `previewDomain` | string | 自定义预览域名 |
| `previewPath` | string | 预览路径；平台默认预览域名下不支持自定义，业务自定义路径需配合自定义预览域名 |
| `previewRoot` / `preRoot` | boolean | 是否使用预览域名根路径；`preRoot` 为平台字段名 |
| `domain` / `releaseDomain` | string | 自定义发布域名；`domain` 为平台字段名 |
| `path` / `releasePath` | string | 自定义发布路径；`path` 为平台字段名 |
| `releaseRoot` / `pubRoot` | boolean | 是否使用发布域名根路径；`pubRoot` 为平台字段名 |
| `favicon` | string | 网站图标资源 ID |
| `hideJs` | boolean | 是否隐藏 JS 代码 |
| `loadingInfoState` | boolean | 是否启用自定义 Loading |
| `loading` | object | Loading 配置 |
| `stage` | object | 舞台 / 展示设置 |
| `previewUrl` | string | 平台确认后的预览地址，回传快照字段 |
| `publishUrl` | string | 平台确认后的发布地址，回传快照字段 |

规范边界：

- `project.settings.json` 不定义 VL 语法，也不影响 VL 源码语义。
- `frontends` 的 key 必须是对应 `Apps/*.vx` 文件名；该文件名是前端应用稳定绑定键，不是展示名称。
- 已绑定平台 `nid` 的前端应用不支持通过改 `.vx` 文件名来表达 rename；调用方不得为了修改应用显示名称而重命名 `Apps/*.vx` 文件或改写 `frontends` key。
- `parsePjt` 可读取其中的预览部署输入并返回最新快照，但本地文件是否回写由外层 IDE / agent 决定。
- `syncPjtSettings` 是独立的 parser action，用于只同步 settings，不重新编译 VL 源码，详见 §5.4。
- 第三方接口配置中包含 `APP Secret`、商户密钥、PemKey、Token 等敏感字段时，不得写入 `project.settings.json`。
- 私有部署导出配置用于生成 Docker / runtime `config.json`，不属于普通作品 settings。

## 5. Compile、预览与打包（Compile / Preview / Package）

### 5.1 parsevl（parsePjt）
- Name: `parsevl`
- Method: `POST`
- Path: `https://editor.visuallogic.ai/edtfn/parsevl`
- 当前生产入口：`https://editor.visuallogic.ai/edtfn/parsevl`
- 当前 latest 测试入口：`https://editor.visuallogic.ai/devfn/parsevl`

接口意义：
- `parsevl` 是 parser 的统一 HTTP 入口；当 `action=parsePjt` 时，该接口在 VLCode 本地工具语境中对应 `compile`。
- `compile` 的职责是读取 VL 项目源码，解析成平台案例，并把结果部署到线上预览资源层，返回 `previewUrl`、`nid`、`gid` 等预览所需信息。
- `parsePjt` 默认执行完整 compile；当调用方需要把“解析”和“部署预览”拆成两步时，可通过 `phase` 参数指定只解析或只部署。
- `phase:"parse"` 只读取源码并生成可部署解析产物，不创建案例、不保存案例、不创建或修改数据库、不生成预览；`phase:"deploy"` 消费前一次 `phase:"parse"` 返回的 `deployInput`，只执行部署预览副作用。
- `compile` 不生成发布版本，不切换正式入口，也不等同于 §6 的 `deploy`。
- 若要发布并上架，必须在 `compile` 成功并验收预览后，继续调用 §6.1 `publish` 与 §6.2 `launchVersion`。

请求头：
- `Content-Type: application/json`
- `Cookie`（鉴权）
- `Origin`（可选）

请求参数：
- `action` string（固定 `parsePjt`）
- `phase` string（可选，默认 `"all"`；允许值：`"all"` / `"parse"` / `"deploy"`；兼容别名 `"compile"` 等价于 `"all"`；`"all"` 保持当前完整 compile 行为）
- `files` array|object（可选，直传项目文件；优先级高于 `file` / `targetGid`）
- `targetGid` number（workspace 模式必填，引擎直接从 workspace 读取源文件）
- `gid` number（可选，`targetGid` 的兼容别名；新调用方优先使用 `targetGid`）
- `deployInput` object（`phase:"deploy"` 必填；由同一 parser 版本的 `phase:"parse"` 响应返回，供部署阶段消费）
- `projectName` string（可选，不影响解析结果）
- `file` string（ZIP base64，**仅兼容模式使用，默认不传**）
- `download` boolean（可选）
- `resetData` boolean（可选，默认 `false`；是否允许 compile 时按本地数据声明重置预览数据）
- `resetDbData` boolean（可选，兼容别名；含义同 `resetData`，新调用方优先使用 `resetData`）
- `dbEngine` string（可选，默认 `"pg"`；允许值：`"pg"` / `"mysql"`）
- `lintMode` string（可选，默认 `"default"`；允许值：`"default"` / `"strict"`）
- `mode` string（可选，兼容别名；含义同 `lintMode`，新调用方优先使用 `lintMode`）
- `inline` boolean（可选，默认 `false`；显式 `true` 时进入 inline CSS 测试模式）
- `region` string（可选，默认 `"en"`；允许值：`"en"` / `"cn"`；选择部署目标平台区服）
- `dbSchemaMode` string（可选，默认 `"managed"`；允许值：`"managed"` / `"bindOnly"`；控制 `.vdb` 数据库结构处理模式）
- `vdbSchemaMode` string（可选，兼容别名；含义同 `dbSchemaMode`，新调用方优先使用 `dbSchemaMode`）
- `bindOnly` boolean（可选，兼容快捷参数；`true` 等价于 `dbSchemaMode:"bindOnly"`）
- `vdbBindOnly` boolean（可选，兼容快捷参数；`true` 等价于 `dbSchemaMode:"bindOnly"`）

`region` 区服映射：

| 值 | 平台区服 | parser 使用的平台 API base URL |
|------|------|------|
| `"en"` | 英文服 / VisualLogic | `https://editor.visuallogic.ai` |
| `"cn"` | 中文服 / iVX | `https://ai.ivx.cn` |

请求示例（files 直传模式）：

```json
{
  "action": "parsePjt",
  "files": [
    { "path": "Apps/Main.vx", "content": "// VL_VERSION:3.8\n..." },
    { "path": "Sections/Home.sc", "content": "// VL_VERSION:3.8\n..." }
  ],
  "targetGid": 1440,
  "download": false,
  "resetData": false,
  "dbEngine": "pg",
  "lintMode": "default",
  "inline": false,
  "region": "en"
}
```

请求示例（workspace 模式，**推荐**）：

```json
{
  "action": "parsePjt",
  "targetGid": 1440,
  "download": true,
  "resetData": false,
  "dbEngine": "mysql",
  "lintMode": "default",
  "inline": false,
  "region": "cn"
}
```

请求示例（本地新项目文件集直传到现有 gid）：

```json
{
  "action": "parsePjt",
  "targetGid": 1440,
  "files": {
    "Apps/Main.vx": "// VL_VERSION:4.2.14\n<App-Main \"root\">",
    "Sections/Home.sc": "// VL_VERSION:4.2.14\n<Section-Home \"root\">"
  },
  "download": false,
  "resetData": false
}
```

请求示例（inline CSS 测试模式）：

```json
{
  "action": "parsePjt",
  "targetGid": 1440,
  "files": {
    "Apps/Main.vx": "// VL_VERSION:4.2.14\n<App-Main \"root\">"
  },
  "inline": true,
  "lintMode": "strict"
}
```

请求示例（bindOnly 绑定旧数据库）：

```json
{
  "action": "parsePjt",
  "targetGid": 1440,
  "files": {
    "Database/Legacy.vdb": "<Database-Legacy bindOnly:true>",
    "Services/Main.vs": "// VL_VERSION:4.2.14\n<ServiceDomain-Main>"
  },
  "dbSchemaMode": "bindOnly",
  "resetData": false
}
```

请求示例（只解析，不部署）：

```json
{
  "action": "parsePjt",
  "phase": "parse",
  "targetGid": 1440,
  "files": {
    "Apps/Main.vx": "// VL_VERSION:4.3.1\n<App-Main \"root\">"
  },
  "dbEngine": "pg",
  "lintMode": "default",
  "inline": false,
  "region": "en"
}
```

请求示例（只部署前一次解析产物）：

```json
{
  "action": "parsePjt",
  "phase": "deploy",
  "targetGid": 1440,
  "deployInput": {
    "...": "phase parse returned deploy input"
  },
  "download": false,
  "resetData": false,
  "dbEngine": "pg",
  "region": "en"
}
```

响应核心字段：
- `code`
- `data.phase`（仅 `phase:"parse"` / `phase:"deploy"` 返回；未传 `phase`、`phase:"all"` 或 `phase:"compile"` 为兼容保持历史响应，不返回该字段）
- `data.gid`
- `data.apps`
- `data.nids`
- `data.packageUrls`
- `data.errList`
- `data.appCaseJsonMap`
- `data.projectSettings`
- `data.deployInput`（仅 `phase:"parse"` 返回；供后续 `phase:"deploy"` 使用）

`data.apps` 结构（建议）：
- `backend`: `{ "nid": number, "previewUrl": string }`
- `frontends`: `{ "<appVxFileName>": { "nid": number, "previewUrl": string } }`

示例：

```json
{
  "code": 0,
  "message": "success",
  "data": {
    "gid": 1402,
    "apps": {
      "backend": {
        "nid": 12028028,
        "previewUrl": "https://preview.example.com/debug/backend"
      },
      "frontends": {
        "A.vx": {
          "nid": 12028029,
          "previewUrl": "https://preview.example.com/play/A"
        },
        "C.vx": {
          "nid": 12028030,
          "previewUrl": "https://preview.example.com/play/C"
        }
      }
    },
    "nids": [12028028, 12028029, 12028030],
    "packageUrls": [],
    "errList": [],
    "appCaseJsonMap": {},
    "projectSettings": {
      "gid": 1402,
      "backend": {
        "nid": 12028028,
        "previewUrl": "https://preview.example.com/debug/backend"
      },
      "frontends": {
        "A.vx": {
          "nid": 12028029,
          "previewDomain": "preview.example.com",
          "previewPath": "/play/A",
          "previewUrl": "https://preview.example.com/play/A"
        },
        "C.vx": {
          "nid": 12028030,
          "previewUrl": "https://preview.example.com/play/C"
        }
      }
    }
  }
}
```

失败响应核心字段：
- `code`
- `reason`

注意事项：
- 未传 `phase` 或传 `"all"` 时，`parsePjt` 响应结构和副作用保持现有完整 compile 行为不变。
- `phase` 只接受 `"all"`、`"parse"`、`"deploy"`，并兼容接受 `"compile"` 作为 `"all"` 别名；其他值返回参数错误：`phase must be one of: all, parse, deploy`。
- `phase:"parse"` 是解析产物生成阶段，不属于 compile 完成态；它不保存平台案例、不创建或修改数据表、不写 `bgNid`、不同步 preview settings、不返回 `apps/nids/previewUrl` 作为部署结果。
- `phase:"parse"` 返回的 `deployInput` 必须可 JSON 序列化，调用方可以临时保存并在后续 `phase:"deploy"` 请求中原样传回；不同 parser 合同版本之间不保证 `deployInput` 可互用。
- `phase:"deploy"` 必须传 `deployInput`；缺失或格式错误时返回参数错误，不得退回重新解析源码。
- `phase:"deploy"` 不读取 `files` / `file` 作为源码输入，不重新执行源码解析；它只消费 `deployInput` 并执行部署预览副作用。
- `phase:"deploy"` 的成功响应应与默认完整 compile 的核心响应一致，包含 `gid`、`apps`、`nids`、`errList`、`projectSettings`、`appPreviewUrlMap`、`excessCases` 与 `timings` 等字段。
- `phase:"deploy"` 仍必须执行部署阶段安全校验，包括 `targetGid` 与 `Config/project.state.json.gid` 一致性、声明 `nid` 归属、目标 gid 权限、bindOnly 预检和 `resetData` 破坏性边界；MySQL 字段兼容性在解析阶段以 fatal 形式保证，`phase:"deploy"` 不重复复检。
- `phase:"deploy"` 仍按 `resetData=false` 默认值处理；未得到明确授权时不得因解析/部署分离而自动清表或重置预览数据。
- `phase:"parse"` 若使用 workspace 模式读取 `targetGid` 文件，仍需要有效登录 Cookie；使用 `files` / `file` 直传时只做无副作用解析。
- `files` 有值时优先按直传文件模式处理，不读取 workspace；此时仍可同时传 `targetGid`，表示把这批直传文件部署到该现有组应用。
- **默认使用 workspace 模式**：不传 `file`，仅传 `targetGid`，引擎从 workspace 读取最新源文件。
- `files` 支持两种格式：
  - `[{ "path": "...", "content": "..." }]`
  - `{ "Apps/Main.vx": "...", "Sections/Home.sc": { "content": "..." } }`
- `file` 有值则按 ZIP 上传模式处理（仅兼容场景，一般不使用）。
- `files` 为空、`file` 为空且 `targetGid` 有值则按 workspace 模式处理。
- `file` 为空且 `targetGid` 有值则按 workspace 模式处理。
- `files`、`file` 和 `targetGid` 都为空会报参数错误。
- `targetGid` 传值类型为 number（例如 `1440`）；`gid` 是兼容别名，新调用方优先使用 `targetGid`。
- 若同时传 `targetGid` 与 `Config/project.state.json.gid`，两者必须一致；不一致时应返回 deploy validation failed，不得继续保存案例。
- `files + targetGid` 适用于“本地项目本身是新项目、`Config/project.state.json` / config 为空，但需要直接部署到一个现有平台 gid”的场景。
- `inline` 默认 `false`；只有显式传 `true` 才进入 inline CSS 测试模式。
- `inline:true` 用于测试“不使用 Theme、完全直接写 CSS”的灵活度：解析器不要求 Theme，且放开组件实例上的直接 CSS 属性、CSS 白名单、CSS 结构属性、节点适用范围和 `sk.*` 范围限制。
- `inline:true` 是测试/对比开关，不应作为正式项目规范写法；不传或传 `false` 时仍按当前 Theme / skin / skeleton 规则解析与校验。
- 除 `phase:"parse"` 且使用 `files` / `file` 直传的无副作用解析外，该接口依赖有效登录 Cookie；默认完整 compile、`phase:"deploy"`、以及 `phase:"parse"` 的 workspace 模式都必须携带有效登录态。
- `resetData=false` 或未传时，若数据表结构同步失败，接口应直接报错退出，不允许为了 compile 成功而自动清空现有表数据。
- `resetData=true` 时，接口才允许执行破坏性数据重置：清空现有预览表数据、清空列、重建字段和索引、再按本地 mock data / seed data 导入测试数据。
- `resetDbData` 为兼容别名；若调用方同时传 `resetData` 与 `resetDbData`，以 `resetData` 为准。
- 日常 compile 默认应使用 `resetData=false`；只有明确要重置预览数据时才传 `true`。
- 未经用户明确授权，不得在 `compile`、发布或上架链路中把 `resetData` 设为 `true`。
- `dbEngine` 控制本次解析/部署使用的数据库引擎：未传时默认 `pg`；传 `"mysql"` 时，数据库创建、表结构同步与数据源相关处理按 MySQL 目标执行。
- `dbEngine` 只接受 `"pg"` 或 `"mysql"`；其他值返回参数错误：`dbEngine must be one of: pg, mysql`。
- `dbEngine:"mysql"` 模式下，parser 会在解析阶段阻断 MySQL 当前不支持的数据表字段类型：`VEC` / `VECTOR` / `BOOL` / `BOOLEAN`；默认完整 compile 会因此在进入部署副作用前失败，`phase:"deploy"` 不重复执行该字段兼容性复检。调用方应先改表字段类型或继续使用默认 `pg`。
- `dbSchemaMode` 控制 `.vdb` 数据库结构处理：未传时默认 `"managed"`，由 parser 创建或同步数据表结构；传 `"bindOnly"` 时进入旧库只绑定模式。
- `dbSchemaMode` 只接受 `"managed"` 或 `"bindOnly"`；其他值返回参数错误：`dbSchemaMode must be one of: managed, bindOnly`。
- `vdbSchemaMode` 是 `dbSchemaMode` 的兼容别名；`bindOnly:true` 或 `vdbBindOnly:true` 是快捷写法，等价于 `dbSchemaMode:"bindOnly"`。
- `.vdb` 根节点声明 `bindOnly:true` 也会触发 bindOnly；请求参数或 `.vdb` 标记任一方声明 bindOnly 时，本次部署即按 bindOnly 执行。
- `dbSchemaMode:"bindOnly"` 用于旧项目绑定既有数据库表。parser 只读取目标 `gid` 下已有表 alias，将其解析为真实 `dbId` 并回填给 `VirtualTable` / 后台数据源，不创建或修改旧库结构。
- bindOnly 模式要求存在明确目标组应用；调用方应传 `targetGid`，或在 `Config/project.state.json.gid` 中声明既有 `gid`。没有既有目标 `gid` 时，parser 必须拒绝部署。
- bindOnly 模式下，`.vdb` 中的表名必须能匹配目标 `gid` 下已存在的数据表 alias；缺表时 parser 必须在环境准备和 backend case 创建前返回 error。
- bindOnly 模式下，`.vdb` 声明字段必须已经存在于旧库真实字段中，且字段类型必须兼容；字段缺失或类型不匹配时 parser 必须返回 error，不得降级为 warning 后继续部署。
- bindOnly 模式下，`.vdb` 中字段、索引、关系只作为本地绑定和类型注解，不会下发到旧库；parser 不创建表、创建字段、创建索引、写表关系、创建 email storage、清表或导入 `.vdb data`。
- bindOnly 模式禁止 `resetData=true` 或 `resetDbData=true`；若同时请求数据重置，parser 必须返回 error，因为旧库结构和数据在本次 compile 中只读。
- bindOnly 模式下若后台存在 email 组件，对应 email storage 必须已经存在；缺少 email storage 时 parser 必须返回 error，不得自动创建。
- `lintMode:"default"` 是默认模式，会放行一部分组件公开接口 meta / contract 兼容性诊断，适合日常 parse 与旧项目迁移。
- `lintMode:"strict"` 会把组件公开接口 meta / contract 等严格诊断暴露为 error，适合组件工厂、组件规范审计、上线前强校验。
- `mode` 是 `lintMode` 的兼容别名；若两者同时传，`lintMode` 优先生效。
- `parsePjt` 与 `lintPjt` 职责分离：`parsePjt` 不把普通 `summary.errors > 0` 当作部署 gate；需要阻断错误时，调用方必须先执行 `lintPjt` 并在 `summary.errors > 0` 时停止。`parsePjt` 内部只阻断 fatal parse failure、参数错误、目标 `gid/nid` 绑定冲突等会导致错误写入的部署校验失败。
- `inline:true` 与 `lintMode:"strict"` 可以同时使用；此时 strict 仍保留非 CSS 规则，但 CSS / Theme 相关限制按 inline 模式放开。
- `region` 在 parser handler 入口统一处理，发生在 action 分发前；`parsePjt` 内部创建/修改项目组、作品、数据库、settings 等平台 API 调用都会使用该区服对应的 base URL。
- `region` 未传时默认 `"en"`，保持既有英文服行为；传 `"cn"` 时部署到中文服。
- `region` 只接受 `"en"` 或 `"cn"`；其他值返回参数错误：`region must be one of: en, cn`，且不会继续进入解析或部署流程。
- **默认一律使用** `https://editor.visuallogic.ai/edtfn/parsevl` 作为正式稳定入口；只有在调用方明确要求“线上测试版 / latest”时，才使用 `https://editor.visuallogic.ai/devfn/parsevl`。
- `https://editor.visuallogic.ai/edtfn/parsevl` 当前转发到带 `prod` alias / Provisioned Concurrency 的线上 parser。
- `https://editor.visuallogic.ai/devfn/parsevl` 当前转发到 `$LATEST`，适合验证最新改动，不保证与正式版一致。
- 正式入口基于 alias 版本发布；若新正式版出现问题，可将 `prod` alias 回滚到上一个已发布版本，而无需更换正式地址。
- workspace 模式下，`data.apps` 为必返字段：`backend`/`frontends` 均返回 `nid + previewUrl`；其中 `backend` 也可预览（调试界面）。
- 每次解析按 `Apps/*.vx` 文件名同步前端应用映射；`.vx` 文件名是稳定绑定键，不支持作为常规 rename 工作流修改。若需要改变应用显示名称，应修改平台作品信息或 settings，而不是改文件名。
- `targetGid` 部署时，parser 应优先按 `Config/project.state.json` / `Config/project.settings.json` 中声明的 `nid` 匹配已有应用；声明的 `nid` 不属于目标 `gid` 时应报错，不得退回 group 顺序匹配。
- parser 永远不得删除目标 `gid` 下未出现在本次源码中的其他应用；返回值中的 `excessCases` 只用于提示调用方当前目标组里存在未被本次源码覆盖的作品，不提供 parser 侧清理入口。
- 增量加入新应用时，未声明 `nid` 的 `.vx` 文件应创建为新前端应用。
- 一个 `gid` 可以承载多个独立 VL 项目；每个 VL 项目仍保持一个 backend nid，当前 VL 项目的所有 frontend 应绑定到该 backend nid。
- parser 在部署时应对当前 VL 项目的每个 frontend 调用 `/ih5/editor/work/modify` 写入 `bgNid`，使前端作品的 `node_vx.extra.bgNid` 指向当前 VL 项目的 backend nid；已有 frontend 应更新该字段，新建 frontend 应在创建后写入该字段。
- `workGroup.mainNid` 不再作为多项目 `gid` 下前后端绑定的权威来源；新建 group 时可继续写入用于旧平台兼容，`targetGid` 增量部署时不得为了当前 VL 项目覆盖既有 `mainNid`。
- `targetGid` 指向已有组应用时，parser 不应把请求中的 `projectName` 同步为组标题；组标题属于目标组现有元数据。
- parser 更新后台 `serviceList` 时应按当前 backend `nid` 替换自身服务条目，并保留同一组应用中其他 backend / 其他来源的 `serviceList` 条目、`mainNid` 和 `info` 中的其他字段。
- 平台 work / workGroup 的 modify 类调用应按 PATCH 语义只传明确要改的字段，避免默认重置封面、简介、body 或其他元数据。
- `parsePjt` 是完整 compile 方法，会解析源码、保存案例、同步预览资源，并可读取 `Config/project.settings.json` 中的预览部署输入。
- `parsePjt` 不应被当作 settings 专用保存接口；只想同步域名、Loading、favicon、`stage` 等配置时，应调用 §5.4 `syncPjtSettings`。
- `data.projectSettings` 是 parser 返回的最新 settings 快照，外层 IDE / agent 可用于更新本地 `Config/project.settings.json`；是否落盘由外层决定。
- 日常 compile 默认只把 `previewDomain` / `previewPath` / `previewRoot` / `customDomain` 作为预览部署输入；发布上架仍以 §6 为准。
- 使用平台默认预览域名时，compile 结果的预览地址由平台生成；调用方不应通过 `project.settings.json` 设置自定义 `previewPath`。

页面路由拼接规则：
- 响应中 `previewUrl` 为应用根地址（如 `https://v4pre.visuallogic.ai/play/u75Otyj7`）。
- 访问具体页面时，需在根地址后追加 `/route/{path}`，其中 `{path}` 对应 `app.vx` 中 `<Page> path:”N”` 的值。
- 示例：若 `previewUrl` 为 `https://v4pre.visuallogic.ai/play/u75Otyj7`，页面 `path:”4”`，则完整访问地址为 `https://v4pre.visuallogic.ai/play/u75Otyj7/route/4`。
- `/route/` 段由系统自动插入，不需要在 `app.vx` 中声明。

### 5.2 lintPjt（VL 项目静态校验）
- Name: `lintPjt`
- Method: `POST`
- Path: 与 `parsevl` 同一端点（`/edtfn/parsevl`），通过 `action` 字段区分
- 说明：仅执行静态校验，**不生成案例、不保存、不产生预览**。比 `parsePjt` 更快，适合 AI 在 `parsePjt` 之前先行调用以发现并修正错误。

接口意义：
- `lintPjt` 是 compile 前的静态守门接口。
- 该接口只检查 VL 项目源码结构、语法、组件映射、样式坐标和确定性规则错误，不创建或修改平台案例。
- `lintPjt` 成功不代表预览已更新；只有后续 `parsePjt` 成功才完成 `compile`。

请求头：
- `Content-Type: application/json`
- `Cookie`（鉴权）

请求参数：
- `action` string（固定 `lintPjt`）
- `files` array|object（可选，直传项目文件；优先级高于 `targetGid`）
- `targetGid` number（从 workspace 读取源文件）
- `dbEngine` string（可选，默认 `"pg"`；允许值：`"pg"` / `"mysql"`）
- `lintMode` string（可选，默认 `"default"`；允许值：`"default"` / `"strict"`）
- `mode` string（可选，兼容别名；含义同 `lintMode`，新调用方优先使用 `lintMode`）
- `inline` boolean（可选，默认 `false`；显式 `true` 时进入 inline CSS 测试模式）

请求示例：

```json
{
  "action": "lintPjt",
  "targetGid": 1494,
  "dbEngine": "pg",
  "lintMode": "default",
  "inline": false
}
```

或：

```json
{
  "action": "lintPjt",
  "lintMode": "strict",
  "files": [
    { "path": "Apps/Main.vx", "content": "// VL_VERSION:3.8\n..." }
  ]
}
```

响应核心字段：
- `code` number（0 = 成功）
- `data.errList` array — 所有校验错误/警告
- `data.summary` object — `{ "errors": number, "warnings": number }`

响应示例：

```json
{
  "code": 0,
  "message": "success",
  "data": {
    "errList": [
      {
        "level": "error",
        "type": "unknownCompTypeError",
        "message": "Unknown component type \"FrontendApp\" in <FrontendApp-StyleTestApp>. ...",
        "lineNumber": 11,
        "lineVL": "<FrontendApp-StyleTestApp \"styleTestApp\">",
        "path": "Apps/StyleTest.vx"
      }
    ],
    "summary": { "errors": 1, "warnings": 0 }
  }
}
```

errList 错误对象结构：

| 字段 | 类型 | 说明 |
|------|------|------|
| `level` | `"error"` \| `"warning"` | 严重级别 |
| `type` | string | 错误类型标识（见下表） |
| `message` | string | 人类/AI 可读的详细错误描述 |
| `lineNumber` | number | 出错行号（源文件内） |
| `lineVL` | string | 出错行的原始 VL 代码 |
| `path` | string | 源文件相对路径（如 `Apps/Main.vx`、`Sections/Home.sc`） |
| `suggestion` | string? | 可选修复建议 |

主要错误类型速查表（`type` 字段）：

| type | level | 含义 |
|------|-------|------|
| `unknownCompTypeError` | error | 组件类型不在 ivxMap 中（如 AI 编造的 `<FrontendApp-...>`） |
| `appStructureError` | error | .vx 文件结构错误（如 Page 未直接挂在 App 下） |
| `styleCompileError` | error | 样式坐标编译校验（维度/点/必需维度等不合规） |
| `forbiddenSyntaxError` | error | 使用了 JS 关键字（let/const/var 等） |
| `levelJumpError` | error | 缩进层级跳变（一次跳多级） |
| `levelError` | error | 组件层级与章节不匹配 |
| `topEntryError` | error | 文件顶层入口不唯一 |
| `topLevelError` | error | 顶层标签出现在非法位置 |
| `sectionNestError` | error | Section 内嵌套了另一个 Section |
| `invalidChapterError` | error | 章节名不合法 |
| `formulaError` | error | 表达式/属性值解析错误（含 sk.* 静态值错误） |
| `propError` | warning | 组件属性未找到映射 |
| `compDefMissingError` | warning | 组件定义不存在 |
| `vecSourceError` | error | 向量字段缺少 vecSource |
| `pipeRefExtDepError` | error | Pipe 引用了外部依赖 |

AI 调用建议：
1. 在 `writeFiles` 之后、`parsePjt` 之前调用 `lintPjt`。
2. 若 `summary.errors > 0`，根据 `errList` 中的 `message`、`path`、`lineNumber` 定位并修正源文件，重新 `writeFiles` + `lintPjt`，直到 `errors` 为 0。
3. `warnings` 可视情况处理（不阻塞 parse）。
4. 确认无 error 后再调用 `parsePjt` 生成案例和预览。
5. 日常项目校验默认使用 `lintMode:"default"`；需要暴露组件公开接口 meta / contract 严格错误时，显式使用 `lintMode:"strict"`。
6. 若后续 `parsePjt` 准备以 `dbEngine:"mysql"` 部署，前置 `lintPjt` 也应传同样的 `dbEngine`，以便提前发现 MySQL 不支持的表字段类型。
7. `lintPjt` 支持与 `parsePjt` 相同的 `inline` 测试模式；传 `inline:true` 时，CSS / Theme 相关限制按 inline 模式放开，但接口仍然只做静态校验。

### 5.3 tryParsePjt（VL 项目完整分析预检）
- Name: `tryParsePjt`
- Method: `POST`
- Path: 与 `parsevl` 同一端点（`/edtfn/parsevl`），通过 `action` 字段区分

接口意义：
- `tryParsePjt` 用于在不落平台资源的前提下执行接近 `parsePjt` 的完整解析分析。
- 该接口适合检查 `backendCaseJson`、`appCaseJsonMap`、服务与页面映射等完整分析结果。
- `tryParsePjt` 不创建案例、不保存案例、不生成预览、不修改数据表，也不属于 `compile` 完成态。

请求参数：
- `action` string（固定 `tryParsePjt`）
- `files` array|object（可选，直传项目文件；优先级高于 `targetGid`）
- `targetGid` number（从 workspace 读取源文件）
- `dbEngine` string（可选，默认 `"pg"`；允许值：`"pg"` / `"mysql"`）
- `lintMode` string（可选，默认 `"default"`；允许值：`"default"` / `"strict"`）
- `mode` string（可选，兼容别名；含义同 `lintMode`，新调用方优先使用 `lintMode`）

请求示例：

```json
{
  "action": "tryParsePjt",
  "targetGid": 1494,
  "dbEngine": "mysql",
  "lintMode": "default",
  "inline": false
}
```

响应核心字段：
- `code` number（0 = 成功）
- `data.errList` array
- `data.backendCaseJson` object
- `data.appCaseJsonMap` object

注意事项：
- `tryParsePjt` 是分析预检，不应触发 `resetData`，也不允许清空或重置任何预览数据。
- 若只需要快速静态错误列表，优先用 `lintPjt`。
- 若需要更新线上预览资源，必须调用 `parsePjt`。
- `tryParsePjt` 支持与 `parsePjt` 相同的 `dbEngine` 与 `lintMode`；它只做分析与校验，不会创建数据库、案例或预览资源。
- `tryParsePjt` 支持与 `parsePjt` 相同的 `inline` 测试模式；传 `inline:true` 时仅影响本次分析校验，不产生平台副作用。
- 需要在不产生副作用的前提下预检 MySQL 兼容性时，使用 `tryParsePjt` 并传 `dbEngine:"mysql"`。

### 5.4 syncPjtSettings（同步项目 settings）
- Name: `syncPjtSettings`
- Method: `POST`
- Path: 与 `parsevl` 同一端点（`/edtfn/parsevl`），通过 `action` 字段区分

接口意义：
- `syncPjtSettings` 是 settings 专用同步接口，用于读取项目文件中的 `Config/project.state.json` 与 `Config/project.settings.json`，并把 settings 同步到对应平台作品。
- 该接口不重新解析 VL 业务源码，不保存 caseJson，不创建数据库，不生成发布版本，也不执行上架。
- 它适合在用户只修改域名、Loading、favicon、`hideJs`、`stage` 等作品配置时调用，避免为了保存配置触发完整 `parsePjt`。
- 它会校验 `project.state.json.frontends` 与项目 `.vx` 文件集合、`project.settings.json.frontends` 的应用绑定关系。

请求头：
- `Content-Type: application/json`
- `Cookie`（鉴权）

请求参数：
- `action` string（固定 `syncPjtSettings`）
- `files` array|object（可选，直传完整项目文件；优先级高于 `file` / `targetGid`）
- `targetGid` number（可选，从 workspace 读取项目文件）
- `file` string（ZIP base64，兼容模式）
- `region` string（可选，默认 `"en"`；允许值：`"en"` / `"cn"`；选择同步 settings 的目标平台区服）

请求示例：

```json
{
  "action": "syncPjtSettings",
  "targetGid": 2166,
  "region": "en"
}
```

或：

```json
{
  "action": "syncPjtSettings",
  "region": "cn",
  "files": {
    "Config/project.state.json": {
      "content": "{\"gid\":2166,\"backend\":\"Services\",\"frontends\":{\"SystemDocs.vx\":{}}}"
    },
    "Config/project.settings.json": {
      "content": "{\"frontends\":{\"SystemDocs.vx\":{\"previewDomain\":\"preview.example.com\",\"previewPath\":\"/ourtools\",\"favicon\":\"doc.svg\",\"hideJs\":true}}}"
    },
    "Apps/SystemDocs.vx": {
      "content": "// VL_VERSION:4.2\n<App-SystemDocs \"root\">"
    }
  }
}
```

响应核心字段：
- `code` number（0 = 成功）
- `message` string
- `data.gid` number
- `data.nids` number[]
- `data.apps` object
- `data.projectSettings` object
- `data.errList` array
- `data.summary` object

响应示例：

```json
{
  "code": 0,
  "message": "success",
  "data": {
    "gid": 2166,
    "nids": [12029196, 12029195],
    "apps": {
      "backend": {
        "nid": 12029196,
        "previewUrl": "https://v4pre.visuallogic.ai/play/PZXqDEBr"
      },
      "frontends": {
        "SystemDocs.vx": {
          "nid": 12029195,
          "previewUrl": "https://preview.example.com/ourtools"
        }
      }
    },
    "projectSettings": {
      "gid": 2166,
      "backend": {
        "nid": 12029196,
        "previewUrl": "https://v4pre.visuallogic.ai/play/PZXqDEBr"
      },
      "frontends": {
        "SystemDocs.vx": {
          "nid": 12029195,
          "previewDomain": "preview.example.com",
          "previewPath": "/ourtools",
          "favicon": "doc.svg",
          "hideJs": true,
          "previewUrl": "https://preview.example.com/ourtools"
        }
      }
    },
    "errList": [],
    "summary": { "errors": 0, "warnings": 0 }
  }
}
```

同步字段：

| project.settings 字段 | 平台字段 / 目标 | 说明 |
|------|------|------|
| `previewDomain` | `previewDomain` | 自定义预览域名 |
| `previewPath` | `previewPath` | 预览路径；平台默认预览域名下不支持自定义，业务自定义路径需配合自定义预览域名 |
| `previewRoot` / `preRoot` | `preRoot` | 是否使用预览根路径 |
| `customDomain` | `customDomain` | 是否启用自定义域名 |
| `domain` / `releaseDomain` | `domain` | 自定义发布域名 |
| `path` / `releasePath` | `path` | 自定义发布路径 |
| `releaseRoot` / `pubRoot` | `pubRoot` | 是否使用发布根路径 |
| `favicon` | work settings `favicon` | 网站图标 |
| `hideJs` | work settings `hideJs` | 是否隐藏 JS 代码 |
| `loadingInfoState` | work settings `loadingInfoState` | 是否启用自定义 Loading |
| `loading` | work settings `loading` | Loading 配置 |
| `stage` | work settings `stage` | 展示 / 舞台配置 |

注意事项：
- `syncPjtSettings` 要求项目中存在 `Config/project.settings.json`；不存在时返回 settings validation failed。
- `Config/project.state.json` 必须包含 `gid`、`backend`、`frontends`，并且 `frontends` 中记录的 `.vx` 文件必须存在。
- `project.settings.json.frontends` 中的 key 必须能在 `project.state.json.frontends` 中找到对应项。
- `project.state.json.frontends`、`project.settings.json.frontends` 与 `Apps/*.vx` 文件名必须保持一致；`.vx` 文件名是前端应用稳定绑定键，不支持通过改文件名来表达应用 rename。
- 接口会按 `gid` 查找平台 work group，再按 backend / frontend 应用映射找到对应 `nid`。
- 该接口可修改平台作品配置，因此需要登录 Cookie。
- `region` 含义同 §5.1：未传默认 `"en"`；传 `"cn"` 时 settings 同步到中文服；非法值返回 `region must be one of: en, cn`。
- 当使用平台默认预览域名时，不应同步自定义 `previewPath`；`/ourtools` 这类业务自定义路径只适用于自定义预览域名。
- 该接口不处理第三方接口密钥、私有部署导出配置、数据库 / Redis / 微服务 runtime config。
- 该接口不负责发布或上架；如需正式入口生效，仍需按 §6 执行发布与上架。

## 6. 项目 Deploy（Project Deploy）

项目 Deploy 分为“发布”和“上架”两个独立步骤。在 VLCode 本地工具语境中，`deploy` 专指“发布并上架”，不包括 §5 的 `compile` 预览部署。

- 发布：生成一个新的可访问版本，并返回该次发布的 `version`、`publishUrl`、`previewUrl` 等信息。
- 上架：把某个已发布版本切为当前正式版本；正式访问入口最终以当前上架版本为准。
- 如果希望“发布并上架”，调用方需要手动依次完成：获取发布票据 → 发布项目 → 上架目标版本。平台不会在发布成功后自动上架该版本。
- `compile` 与 `deploy` 的边界：`compile` 只更新预览资源；`deploy` 会生成发布版本并切换正式入口，应在预览验收后执行。

版本口径约定：

- 发布版本号：以发布接口响应中的 `version` 为准；调用方也可以在本地把这个值记作 `publishedVersion`。
- 线上文件版本：`/ih5/editor/work/get` 返回的 `version` 属于线上文件版本口径，用于描述当前线上案例文件状态，不作为发布版本号使用。
- 上架时传给 `launchVersion.version` 的，应当是发布接口本次返回的 `version`，而不是 `work/get.version`。

### 6.1 发布项目

发布项目分为两个连续调用：先获取发布票据，再调用正式发布接口。

#### 6.1.1 获取发布 Ticket
- Name: `getPublishTicket`
- Method: `POST`
- Path: `/ih5/editor/work/getPublishTicket`

接口意义：
- 获取一次发布操作所需的短期票据。
- 该接口不创建发布版本，不修改正式入口，只为后续 `/work/publish/{workId}` 提供 `ticket`。

请求参数：

```json
{
  "nid": 12097559
}
```

请求字段：
- `nid` number（必填，案例 ID）

响应示例：

```json
{
  "token": "00qH5D6INxA3Ja0Q",
  "rank": 1,
  "total": 1
}
```

响应字段：
- `token` string：发布票据，正式发布时通过 `ticket` query 参数传入
- `rank` number：当前发布任务在队列中的排名
- `total` number：当前发布队列总数

注意事项：
- `ticket` 具有时效性，建议获取后立即用于发布。
- 接口调用需要携带当前登录态。

#### 6.1.2 发布案例
- Name: `publish`
- Method: `POST`
- Path: `/work/publish/{workId}`
- Content-Type: `application/octet-stream`

接口意义：
- 基于当前案例数据生成一个新的已发布版本。
- 该接口会返回本次发布版本号 `version` 与发布地址，但不会自动上架该版本。
- 在 VLCode 本地工具语境中，单独调用 `publish` 只完成 deploy 的第一半；只有继续调用 `launchVersion` 后，正式入口才会切换。

完整 URL 示例：

```http
POST https://editor.visuallogic.ai/work/publish/d5s73tjc1t2c73ac4qmg-sticky?v41=1&nid=12097559&uid=10011846&eid=10000586&ticket=00qH5D6INxA3Ja0Q&publishType=web&oldkey=1
```

Path 参数：
- `workId` string（必填，待发布的作品版本 ID）

Query 参数：
- `v41` number（必填，固定传 `1`）
- `nid` number|string（必填，案例 ID）
- `uid` number|string（必填，用户 ID）
- `ticket` string（必填，`getPublishTicket` 返回的 `token`）
- `oldkey` number（必填，固定传 `1`）
- `gid` number|string（可选，组应用 ID）
- `eid` number|string（可选，企业 ID）
- `publishType` string（可选，发布类型）
- `disableEncrypt` boolean（可选，是否禁用加密）

请求体说明：
- 请求体不能直接传原始 JSON 字符串。
- 调用方需要先准备完整案例 JSON 数据，再进行压缩加密，并将加密后的二进制数据以 `application/octet-stream` 方式提交。
- 原始案例 JSON 顶层通常包含：`case`、`stage`、`server`。

请求体示例（原始结构）：

```json
{
  "case": {},
  "stage": {},
  "server": {}
}
```

响应示例：

```json
{
  "publishUrl": "https://fileae1710511797.v4dev.h5app.com/play/NVfg5RPD",
  "previewUrl": "https://pre.dev.h5app.com/play/NVfg5RPD",
  "path": "/play/NVfg5RPD",
  "link": "NVfg5RPD",
  "version": "1",
  "workId": "d5s73tjc1t2c73ac4qmg-6"
}
```

重要响应字段：
- `publishUrl` string：该次发布生成的发布地址
- `previewUrl` string：该次发布生成的预览地址
- `path` string：播放路径
- `link` string：播放链接标识
- `version` string|number：本次发布版本号
- `workId` string：发布后关联的作品版本 ID

注意事项：
- 响应中的 `version` 就是该次发布的版本号；如果调用方后续需要按平台版本路由约定拼接 `?version=xx`，其中 `xx` 使用该字段值。
- 调用方本地若想把发布版本号单独命名为 `publishedVersion`，可直接取响应中的 `version`。
- 不要用 `/ih5/editor/work/get` 返回的 `version` 代替发布版本号；两者口径不同。
- 发布成功只代表生成了一个新的已发布版本，不代表它已经成为正式上架版本。
- 如果要让本次发布结果成为正式入口，需要再调用 `launchVersion` 上架该 `version`。

### 6.2 上架/下架版本
- Name: `launchVersion`
- Method: `POST`
- Path: `/ih5/editor/work/launchVersion`

接口意义：
- 将某个已发布版本切换为正式入口当前版本。
- `version` 传已发布版本号时表示上架该版本；`version` 传 `off-shelf` 时表示下架项目。
- 在 VLCode 本地工具语境中，`publish` 成功后再调用本接口，才算完成完整 `deploy`。

请求参数：

```json
{
  "nid": 12028017,
  "version": "1"
}
```

下架示例：

```json
{
  "nid": 12028017,
  "version": "off-shelf"
}
```

响应参数：

```json
{}
```

注意事项：
- `version` 传某个已发布版本号时，将该版本上架为当前正式版本。
- `version` 传字符串 `off-shelf` 时，项目下架。
- 空对象通常表示成功。

### 6.3 本地调用核心流程

本地调用“发布并上架”的标准顺序如下：

1. 准备发布输入。
2. 调用 `getPublishTicket` 获取发布票据。
3. 调用 `/work/publish/{workId}` 发布，记录响应中的 `version`。
4. 调用 `launchVersion`，把上一步返回的 `version` 上架。
5. 如需校验页面版本地址，可拼接 `publishUrl?version=<publishResponse.version>`。

#### 6.3.1 标准发布并上架顺序

推荐变量约定：

- `nid`：案例 ID
- `workId`：本次要发布的作品版本 ID
- `publishedVersion`：发布接口响应中的 `version`

标准顺序：

```text
getPublishTicket(nid)
→ publish(workId, ticket, body)
→ 从 publish 响应读取 version
→ launchVersion(nid, version)
```

对应含义：

- `publish` 负责生成一个新的发布版本。
- `launchVersion` 负责把某个已发布版本切成正式版本。
- 只有完成 `launchVersion` 后，正式入口才切到该版本。

#### 6.3.2 本地 publish-only 路径

如果只想发布一个新版本、暂时不切正式入口，则流程停在 publish：

```text
getPublishTicket(nid)
→ publish(workId, ticket, body)
→ 记录 publish 响应中的 version / publishUrl
```

此时：

- 新版本已经存在。
- 正式入口是否切换，取决于是否后续再调 `launchVersion`。
- 建议把 `publishUrl?version=<publishResponse.version>` 作为该次发布版本的精确访问地址记录下来。

#### 6.3.3 本地“发布并上架”示例

步骤 1：获取发布票据

```bash
curl -X POST "https://editor.visuallogic.ai/ih5/editor/work/getPublishTicket" \
  -H "Content-Type: application/json" \
  -H "Cookie: ih5_bearer=YOUR_COOKIE" \
  -d '{"nid":12029195}'
```

假设响应为：

```json
{
  "token": "abc123"
}
```

步骤 2：发布案例

```bash
curl -X POST "https://editor.visuallogic.ai/work/publish/d70vtl39vhttqe6hj8j0-sticky?v41=1&nid=12029195&uid=10000607&eid=10000586&gid=2166&ticket=abc123&publishType=web&oldkey=1" \
  -H "Content-Type: application/octet-stream" \
  -H "Cookie: ih5_bearer=YOUR_COOKIE" \
  --data-binary "@publish-body.bin"
```

假设发布响应为：

```json
{
  "publishUrl": "https://file4ec59ae07148.v4.visuallogic.ai/play/pGSHrVIN",
  "previewUrl": "https://editor.visuallogic.ai/sr",
  "version": "2"
}
```

此时：

- `publishedVersion = "2"`
- 版本地址可记为：
  `https://file4ec59ae07148.v4.visuallogic.ai/play/pGSHrVIN?version=2`

步骤 3：上架刚发布的版本

```bash
curl -X POST "https://editor.visuallogic.ai/ih5/editor/work/launchVersion" \
  -H "Content-Type: application/json" \
  -H "Cookie: ih5_bearer=YOUR_COOKIE" \
  -d '{"nid":12029195,"version":"2"}'
```

这里传入的 `"2"` 必须来自上一步发布接口响应中的 `version`，而不是 `work/get.version`。

#### 6.3.4 本地调用注意事项

- `publish` 与 `launchVersion` 必须按顺序调用；不要先上架、后发布。
- 上架版本号一律取本次 `publish` 响应中的 `version`。
- `work/get.version` 是线上文件版本口径，不作为发布版本号，也不推荐直接传给 `launchVersion`。
- 如果调用方只想下架项目，可直接调用 `launchVersion`，并传：
  `{"nid":12029195,"version":"off-shelf"}`
- 如果当前线上产物已经正确、只想基于当前产物再发一个版本，可以跳过 `parse`，直接读取当前案例内容后执行 `getPublishTicket → publish`。

### 6.4 预览/发布数据同步

预览/发布数据同步用于在同一个数据表的预览版数据与发布版数据之间做方向性覆盖同步。

通用约定：
- `syncToPublish`：把预览版数据同步到发布版数据。
- `syncToPreview`：把发布版数据同步到预览版数据。
- 两个接口只处理数据同步方向，不负责发布项目版本，也不负责上架版本。
- 同步会用源环境数据覆盖目标环境数据；调用前必须确认方向，并按业务需要做好二次确认、备份或操作日志记录。
- 该同步默认要求预览版与发布版表结构兼容；字段结构冲突时应先修正 schema，再执行同步。

请求头：
- `Content-Type: application/json`
- `Cookie`（鉴权）

#### 6.4.1 预览版数据同步到发布版
- Name: `syncToPublish`
- Method: `POST`
- Path: `/ih5/editor/db/syncToPublish`

接口意义：
- 将指定数据库的预览版数据覆盖同步到发布版数据。
- 适用于预览环境数据已经验收，希望让发布环境数据与预览环境保持一致的场景。
- 该接口不执行项目 `deploy`，也不会自动调用 `publish` 或 `launchVersion`。

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `dbId` | string | 是 | 数据库完整 ID，例如 `n12097884_d7pmmgrc1t2c73dh1odg` |
| `nid` | number | 是 | 应用/案例 ID，例如 `12097884` |

请求示例：

```json
{
  "dbId": "n12097884_d7pmmgrc1t2c73dh1odg",
  "nid": 12097884
}
```

调用示例：

```bash
curl -X POST "https://editor.visuallogic.ai/ih5/editor/db/syncToPublish" \
  -H "Content-Type: application/json" \
  -H "Cookie: ih5_bearer=YOUR_COOKIE" \
  -d '{"dbId":"n12097884_d7pmmgrc1t2c73dh1odg","nid":12097884}'
```

#### 6.4.2 发布版数据同步到预览版
- Name: `syncToPreview`
- Method: `POST`
- Path: `/ih5/editor/db/syncToPreview`

接口意义：
- 将指定数据库的发布版数据覆盖同步到预览版数据。
- 适用于需要把正式环境当前数据拉回预览环境做排查、回归、修复或对齐的场景。
- 该接口会覆盖预览环境目标数据，调用前应确认不会误删预览调试数据。

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `dbId` | string | 是 | 数据库完整 ID，例如 `n12097884_d7pmmgrc1t2c73dh1odg` |
| `nid` | number | 是 | 应用/案例 ID，例如 `12097884` |

请求示例：

```json
{
  "dbId": "n12097884_d7pmmgrc1t2c73dh1odg",
  "nid": 12097884
}
```

调用示例：

```bash
curl -X POST "https://editor.visuallogic.ai/ih5/editor/db/syncToPreview" \
  -H "Content-Type: application/json" \
  -H "Cookie: ih5_bearer=YOUR_COOKIE" \
  -d '{"dbId":"n12097884_d7pmmgrc1t2c73dh1odg","nid":12097884}'
```

#### 6.4.3 dbId 规则、错误处理与安全要求

`dbId` 必须传完整数据库 ID。常见规则如下：

| 数据库范围 | 规则 | 示例 |
|------------|------|------|
| 应用级数据库 | `n{nid}_{原始dbId}` | `n12097884_d7pmmgrc1t2c73dh1odg` |
| 用户级数据库 | `u{uid}_{原始dbId}` | `u10001_customer` |
| 组级数据库 | `g{gid}_{原始dbId}` | `g20001_order` |
| 企业级数据库 | `e{eid}_{原始dbId}` | `e30001_product` |
| DynamoDB/缓存数据库 | 使用当前表 ID 原样传入 | `Nxxxx` 或实际表 ID |

前端编辑器内部调用时可自动补充 `nid`；外部系统直接调用接口时，应在 JSON 请求体中显式传入 `nid`。

错误处理：

| HTTP 状态码 | 说明 |
|-------------|------|
| `200` | 同步成功 |
| `403` | 权限不足、登录态失效或无权操作该数据表 |
| `409` | 数据结构不一致或字段冲突，无法同步 |
| `413` | 同步数据量或请求体过大 |
| `500` | 服务端同步失败 |
| 其他非 `200` | 同步失败 |

注意事项：
- 调用前必须确认同步方向：`syncToPublish` 是预览到发布，`syncToPreview` 是发布到预览。
- 目标环境原有数据会被源环境数据覆盖，不得在未确认的情况下自动重试或批量触发。
- 建议记录调用人、调用时间、`dbId`、`nid`、同步方向和 HTTP 状态码，便于审计与问题排查。
- 如返回 `409`，通常需要先调整两侧数据表字段结构，使预览版与发布版结构保持一致后再同步。
- 本接口属于数据同步接口，不等同于 `resetData=true`；它不会按本地 mock data 重建预览表数据。

### 6.5 表数据导出

表数据导出用于把指定数据表中的记录按给定表头映射导出为下载文件。

通用约定：
- `exportTable` 是浏览器下载型 HTTP 接口，成功时直接返回文件流，不是标准 JSON 响应。
- 典型调用方式是前端拼接好 URL 后使用浏览器打开，或通过 `window.open(...)` 触发下载。
- 导出目标由 `isPublish` 决定：`true` 导出发布数据，`false` 导出预览数据。

请求方式：
- `GET`

#### 6.5.1 导出数据表
- Name: `exportTable`
- Method: `GET`
- Path: `/ih5/resource/exportTable`

接口意义：
- 根据项目、数据表和表头映射导出表数据文件。
- 适用于运营导表、人工核对、离线分析或由前端触发下载的场景。
- 该接口只负责导出文件，不负责同步预览/发布数据，也不负责项目 `deploy`。

请求参数（query）：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `nid` | string | 是 | 案例 / 项目 ID |
| `isPublish` | boolean \| string | 是 | 是否导出发布数据。`true` 表示发布数据，`false` 表示预览数据 |
| `dbId` | string | 是 | 数据库 ID |
| `header` | string | 是 | URL 编码后的 JSON 字符串，用于定义导出表头映射 |
| `_locOffset` | number | 否 | 时区偏移秒数，建议传调用方本地时区对应值 |

请求示例（URL）：

```text
https://editor.visuallogic.ai/ih5/resource/exportTable?nid=12030033&isPublish=false&dbId=g97_d7sovdnb4k8st5o1u1h0&header=%7B%22_id%22%3A%22DataID%22%2C%22_user%22%3A%22Submitter%22%2C%22_create%22%3A%22CreationTime%22%7D&_locOffset=28800
```

调用示例（JavaScript）：

```js
const baseUrl = 'https://editor.visuallogic.ai'
const nid = '12030033'
const isPublish = false
const dbId = 'g97_d7sovdnb4k8st5o1u1h0'
const locOffset = 28800

const headerMap = {
  _id: 'DataID',
  _user: 'Submitter',
  _create: 'CreationTime'
}

const header = encodeURIComponent(JSON.stringify(headerMap))

const url =
  baseUrl +
  '/ih5/resource/exportTable' +
  '?nid=' +
  encodeURIComponent(nid) +
  '&isPublish=' +
  isPublish +
  '&dbId=' +
  encodeURIComponent(dbId) +
  '&header=' +
  header +
  '&_locOffset=' +
  locOffset

window.open(url, '_blank')
```

响应行为：
- 成功时返回导出文件，并由浏览器触发下载。
- 该接口不是标准 JSON 响应接口；程序化调用时需自行处理文件流、响应头和文件保存。
- 若请求参数错误、权限不足或数据表不可访问，通常表现为下载失败或返回错误页/错误响应。

#### 6.5.2 `header` 编码规则

`header` 不是普通文本，必须按以下顺序构造：

1. 先构造表头映射对象。
2. 对对象执行 `JSON.stringify(...)`。
3. 再对结果执行 `encodeURIComponent(...)`。

最小示例：

```json
{
  "_id": "DataID",
  "_user": "Submitter",
  "_create": "CreationTime"
}
```

编码示例：

```js
const headerMap = {
  _id: 'DataID',
  _user: 'Submitter',
  _create: 'CreationTime'
}

const header = encodeURIComponent(JSON.stringify(headerMap))
```

常见内置字段示例：
- `_id`：数据记录 ID
- `_user`：提交人
- `_create`：创建时间

#### 6.5.3 使用注意事项

- `header` 必须先做 `JSON.stringify`，再做 `encodeURIComponent`；若顺序错误，服务端容易解析失败。
- `isPublish` 必须与目标数据环境一致；导出正式数据时传 `true`，导出预览数据时传 `false`。
- `_locOffset` 建议传调用方本地时区对应的秒级偏移值；东八区常见值为 `28800`。
- `dbId` 应传当前目标数据表 ID；若业务侧保存的是完整表 ID，应按实际线上可访问值原样传入。
- 该接口稳定对外形态是 HTTP 下载接口，不是前端组件方法，也不适合按 JSON API 方式解析响应。

## 7. 计费与余额（Payment / Billing）

### 7.1 查询余额
- Name: `queryRemain`
- Method: `POST`
- Path: `/ih5/app/pay/queryRemain`

请求参数：

```json
{}
```

响应参数：
- `balance` number

### 7.2 查询消费记录
- Name: `listCost`
- Method: `POST`
- Path: `/ih5/app/pay/listCost`

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `limit` | uint64 | 否 | 分页条数 |
| `offset` | uint64 | 否 | 分页偏移量 |
| `orderBy` | string | 否 | 排序字段，默认 `createdAt` |
| `asc` | bool | 否 | 是否升序排序 |
| `timeStart` | string | 否 | 起始时间 |
| `timeEnd` | string | 否 | 结束时间 |
| `nid` | int64 | 否 | 案例 ID |
| `type` | model.CostDetailType | 否 | 扣费详细类型 |
| `costType` | model.CostType | 否 | 扣费类型 |

请求示例：

```json
{
  "limit": 20,
  "offset": 0,
  "orderBy": "createdAt",
  "asc": false,
  "timeStart": "2026-05-01 00:00:00",
  "timeEnd": "2026-05-04 23:59:59",
  "nid": 12097884,
  "type": 0,
  "costType": 0
}
```

响应核心字段：
- `costs[]`, `count`

响应参数：

| 字段 | 类型 | 说明 |
|------|------|------|
| `costs` | CostDetail[] | 费用项目统计 |
| `count` | int64 | 符合筛选条件的总数 |

`CostDetail` 结构：

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | int64 | 账单编号 |
| `uid` | int64 | 用户 ID |
| `nid` | int64 | 案例 ID |
| `createdAt` | string | 扣费时间 |
| `title` | string | 案例名称 |
| `amount` | base.NullInt | 扣费金额 |
| `detail` | base.NullString | 扣费详情 |
| `type` | model.CostDetailType | 扣费详细类型 |
| `costType` | model.CostType | 扣费类型 |

响应示例：

```json
{
  "costs": [
    {
      "id": 10001,
      "uid": 10000607,
      "nid": 12097884,
      "createdAt": "2026-05-04 12:30:00",
      "title": "案例名称",
      "amount": 100,
      "detail": "扣费详情",
      "type": 0,
      "costType": 0
    }
  ],
  "count": 1
}
```

注意事项：
- 不传 `orderBy` 时默认按 `createdAt` 排序。
- `amount` 与 `detail` 是 nullable 字段，调用方应按空值处理。

### 7.3 查询余额变动记录
- Name: `listBalanceRecord`
- Method: `POST`
- Path: `/ih5/app/pay/listBalanceRecord`

请求参数：
- `limit`, `offset`, `nid`

响应核心字段：
- `items[]`（`value`, `reason`, `time`, `type`）

### 7.4 创建充值订单
- Name: `createChargeOrder`
- Method: `POST`
- Path: `/ih5/app/pay/createChargeOrder`

请求参数：
- `payType`, `title`, `description`, `balance`

响应核心字段：
- `codeUrl`, `tradeNo`, `fee`, `title`, `description`

注意事项：
- `tradeNo` 可用于支付网关流程（如 Stripe Checkout）。

## 8. 通知（Notice）

### 8.1 获取通知列表
- Name: `getNoticeList`
- Method: `POST`
- Path: `/edt/editor/customapi/getNoticeList`

请求参数：
- `offset` integer（从 1 开始）
- `limit` integer
- `type` array

响应核心字段：
- `list[]`, `total`, `detail`, `timeStamp`

### 8.2 标记通知已读
- Name: `noticeRead`
- Method: `POST`
- Path: `/edt/editor/customapi/noticeRead`

请求参数：
- 无

响应核心字段：
- `code`, `data`, `reason`

## 9. 回收站（Recycle Bin）

### 9.1 查询回收站
- Name: `recycle/list`
- Method: `POST`
- Path: `/ih5/app/recycle/list`

请求参数：
- `offset`, `limit`, `title`

响应核心字段：
- `list[]`, `count`

### 9.2 恢复回收站项目
- Name: `recycle/restore`
- Method: `POST`
- Path: `/ih5/app/recycle/restore`

请求参数：
- `nid` integer

响应：

```json
{}
```

### 9.3 删除回收站项目/清空
- Name: `recycle/delete`
- Method: `POST`
- Path: `/ih5/app/recycle/delete`

请求参数：
- `nids` array
- `isAll` boolean

请求示例：

```json
{
  "nids": [],
  "isAll": true
}
```

响应：

```json
{}
```

## 10. 组件工厂（Component Factory）

通用约定：
- 路由前缀：`/edt/componentfactory/`
- 统一方法：`POST`，Content-Type: `application/json`
- 读接口需要登录态鉴权
- 写接口需要登录态鉴权 + 租户权限校验
- 系统模块（tid=null）仅系统租户（eid=10000586）可维护，所有租户可读取
- 租户私有模块仅所属租户可读写
- 组件身份层与导航层分离：`importName` 是稳定主键；`category` / `family` / `group` / `navOrder` 用于浏览导航、筛选与排序。
- `family` / `group` 不进入 `importName`，导航重组不得导致组件稳定主键变化。
- 浏览导航推荐按 `category > family > group > component` 组织；搜索结果页可直接展示命中的 `category` / `family` / `group` / `moduleName`。
- 组件元数据按职责拆分为三类，互不混用：
  - 公开组件合同：`metadataJson` / parsed `metadata`，用于线上组件仓库搜索、下载说明、IDE 展示、AI 组件理解和使用说明。
  - 预览外框：模块记录顶层 `previewFrameJson`，由平台预览壳或本地预览壳消费。
  - 内部工程约束：源码 `@contract component sizeMode`，仅供 parser / lint / factory authoring checks 使用，不进入 `metadataJson`，也不进入任何对外字段。

### 10.1 注册新模块
- Name: `publishModule`
- Method: `POST`
- Path: `/edt/componentfactory/publishmodule`

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `category` | string | 是 | 分类，如 `f_form`、`b_auth`、`m_chat` |
| `family` | string | 是 | 二级导航分组，如 `choice-form`、`common-chart` |
| `group` | string | 否 | 三级导航分组，如 `single-choice`、`core-5`；未传时为空字符串或默认分组 |
| `navOrder` | number | 否 | 同层级排序值，默认 `0`；不参与组件身份识别 |
| `moduleName` | string | 是 | PascalCase 模块名 |
| `description` | string | 是 | 自然语言描述 |
| `fileType` | string | 是 | `cp` / `wc` / `vs` / `zip` |
| `vlVersion` | string | 是 | VL 版本，如 `2.91` |
| `interfaceJson` | string | 否 | 组件公开接口结构 JSON 字符串，推荐使用归一化结构 `{props, events, methods}` |
| `metadataJson` | string | 否 | 组件机器可读元数据 JSON 字符串，结构见 §10.1.1 |
| `previewFrameJson` | string\|object | 否 | 模块级预览外框配置；JSON 对象或同构 JSON 字符串，结构见 §10.1.2 |
| `tagsJson` | string | 否 | 标签数组 JSON 字符串 |
| `scenariosJson` | string | 否 | 场景数组 JSON 字符串 |
| `dependsOnJson` | string | 否 | 依赖模块数组 JSON 字符串 |
| `dependenciesJson` | string | 否 | 外部库声明 |
| `envVarsJson` | string | 否 | 环境变量声明 |

请求示例：

```json
{
  "category": "f_chart",
  "family": "common-chart",
  "group": "core-5",
  "navOrder": 10,
  "moduleName": "BarChart",
  "description": "柱状图组件",
  "fileType": "cp",
  "vlVersion": "4.2.5",
  "previewFrameJson": {
    "frame": { "width": 320, "height": 72, "unit": "px" }
  },
  "tagsJson": "[\"chart\",\"bar\"]",
  "scenariosJson": "[\"dashboard\",\"report\"]"
}
```

响应示例：

```json
{
  "status": 0,
  "data": {
    "importName": "f_form_DatePicker_1",
    "version": 1
  }
}
```

注意事项：
- 系统租户发布为系统模块（tid=null），普通租户发布为私有模块。
- `import_name` 由服务端自动生成：`{category}_{moduleName}_{seq}`。
- `family` / `group` / `navOrder` 仅用于导航与浏览排序，不得拼入 `importName`。
- `tagsJson`/`scenariosJson` 未传时，可从 `metadataJson` 中投影生成（`keywords` → tags，`useCases` → scenarios）。
- `dependsOnJson` 需要显式传入，不自动投影。
- `interfaceJson` 与 `metadataJson` 是发布态结构化缓存；它们通常由源码提取生成，不应被视为高于源码的作者真相源。
- `previewFrameJson` 按 `importName` / 模块 id 归属模块记录；它不属于 `metadataJson`，也不得回写为组件源码 `@meta component preview`。
- 上传工具不得从源码 `@meta component preview` 生成 `previewFrameJson`；预览外框由平台预览壳或本地预览壳直接维护并按本接口写入。
- `metadataJson` 不得包含顶层 `preview` 字段、顶层 `layout` 字段，也不得携带源码 `@contract component sizeMode` 内容；服务端入库前应校验并拒绝这些字段。

### 10.1.1 `interfaceJson` / `metadataJson` 结构合同

`interfaceJson` 用于保存组件公开接口结构；`metadataJson` 用于保存组件搜索、IDE 面板与下载说明所需的公开元数据，只承载公开组件合同。

推荐的 `interfaceJson` 归一化结构：

```json
{
  "props": [
    { "name": "value", "type": "STRING", "default": null }
  ],
  "events": [
    { "name": "change", "params": [ { "name": "value", "type": "STRING" } ] }
  ],
  "methods": [
    { "name": "Focus", "params": [] }
  ]
}
```

别名说明：
- 写接口可接受 `properties` 作为 `props` 别名。
- 新客户端应统一按 `props / events / methods` 读写。

`metadataJson` 标准顶层字段为：

- `family`：二级导航分组。
- `group`：三级导航分组。
- `summary`：组件一句话摘要，供搜索结果卡片、下载说明、IDE 面板直接展示。
- `keywords`：搜索关键词数组。
- `useCases`：适用场景列表。
- `notFor`：不适用场景列表。
- `order`：组内排序值，数值越小越靠前。
- `dependsOn`：组件依赖的其他组件 `importName` 数组；若同时传 `dependsOnJson`，两者必须一致。
- `interfaceMeta`：按名字索引的公开 props / methods / events / services 元数据。

`metadataJson` 不允许包含顶层 `preview` 字段或顶层 `layout` 字段。预览外框尺寸统一由模块记录顶层 `previewFrameJson` 维护（见 §10.1.2）；预览输入示例统一写入 `interfaceMeta.*.example`；组件内部宽高适配合同保留在源码 `@contract component sizeMode`，不进入对外元数据。

`metadataJson` 推荐结构示例：

```json
{
  "family": "choice-form",
  "group": "single-choice",
  "summary": "单选下拉菜单组件。",
  "keywords": ["dropdown", "single choice", "select"],
  "useCases": ["表单单选", "筛选项选择"],
  "notFor": ["多选场景"],
  "order": 4,
  "dependsOn": [],
  "interfaceMeta": {
    "props": {
      "placeholder": {
        "description": "占位提示文本。",
        "required": false,
        "example": "请选择"
      },
      "options": {
        "description": "候选项数组。",
        "required": true,
        "control": "json",
        "example": [
          { "label": "男", "value": "male" }
        ]
      },
      "size": {
        "description": "尺寸档位。",
        "required": false,
        "enum": ["small", "medium", "large"],
        "enumLabels": {
          "small": "小",
          "medium": "中",
          "large": "大"
        },
        "example": "medium"
      }
    },
    "methods": {
      "SetValue": {
        "description": "设置当前选中值。",
        "params": {
          "value": {
            "description": "目标选项值。",
            "required": true,
            "nullable": true,
            "example": "male"
          }
        }
      },
      "Focus": {
        "description": "将焦点移动到组件。"
      }
    },
    "events": {
      "Change": {
        "description": "选中值变化时触发。",
        "params": {
          "value": {
            "description": "当前选中值。",
            "required": true,
            "nullable": true,
            "example": "male"
          }
        }
      }
    }
  }
}
```

`interfaceMeta.*` 公共字段说明：

- `description`：面向使用者的说明文字。
- `required`：布尔值；公开 prop 或公开入参是否必填，统一使用 `true` / `false` 表达。
- `nullable`：布尔值；`true` 表示 `null` 是合法且有语义的输入值。
- `example`：公开接口示例值，用于组件目录、IDE、AI 理解和预览壳生成示例输入；不是运行时默认值，也不改变 `required` / `nullable` 语义。
- `enum`：合法值数组。
- `enumLabels`：与 `enum` 一一对应的人类可读标签。
- `control`：可选编辑器 override；默认消费者应按类型、`enum` 和运行时结构推断控件，只有 `textarea` / `json` 这类特殊形式建议显式声明。
- `params`：method / event / service 的入参定义，键为参数名，值结构与 prop 元数据一致，可使用 `description` / `required` / `nullable` / `example` / `enum` 等字段。

消费者约定：
- 本地预览壳、线上预览壳、组件浏览工具和下载后的 IDE，应优先消费 `metadataJson.interfaceMeta`。
- 渲染组件预览时，应读取模块记录顶层 `previewFrameJson` 得到预览外框，再读取 `metadata.interfaceMeta.*.example` 得到示例输入。
- 若服务端同时返回原始字符串和解析后的对象，解析后的对象为推荐读取入口；字符串字段仅作为原始存储值保留。

实现约定：

- parser 和上传工具的公开接口元数据必须在 `metadataJson` 中按 `interfaceMeta.props` / `interfaceMeta.methods` / `interfaceMeta.events` / `interfaceMeta.services` 嵌套输出。
- 解析、上传、保存必须生成 `interfaceMeta` 结构；这是平台 API 与 parser 输出的对齐基线。
- 消费者必须以 `interfaceMeta` 与模块记录顶层 `previewFrameJson` 为标准字段；非标准元数据字段不得作为列表、过滤或编辑器主读取依据。

### 10.1.2 模块级 `previewFrameJson`

`previewFrameJson` 用于描述组件预览外框（frame / viewport / canvas）的尺寸；该信息属于预览壳配置，按 `importName` / 模块 id 归属模块记录，不属于公开组件合同。

`previewFrameJson` 结构：

```json
{
  "frame": {
    "width": 320,
    "height": 72,
    "unit": "px"
  },
  "notes": "可选说明；不参与渲染"
}
```

字段约束：

- `frame.width`：正整数，默认单位 px。
- `frame.height`：正整数，默认单位 px。
- `frame.unit`：当前只允许 `"px"`，可省略，省略时按 `"px"` 处理。
- `notes`：可选说明字符串，不参与渲染。
- 未配置 `previewFrameJson` 时，预览壳使用默认 frame：`600 x 400 px`。

上传与读取约定：

- §10.1 `publishModule`、§10.6 `publishNewVersion` 与 §10.10 `updateModuleMeta` 接口可以接收 `previewFrameJson`，格式为 JSON 对象或同构 JSON 字符串；服务端保存前必须校验 schema。
- §10.2 `getModuleById` 与 §10.3 `getModuleList` 等读接口在返回模块信息时，应返回解析后的 `previewFrameJson`，或返回可解析的同名 JSON 字符串。
- 上传工具不得从源码 `@meta component preview` 生成 `previewFrameJson`。
- `previewFrameJson` 不进入 `metadataJson` / `metadata`，也不进入向量召回画像；它是模块记录顶层独立字段。

### 10.2 获取模块详情
- Name: `getModuleById`
- Method: `POST`
- Path: `/edt/componentfactory/getmodulebyid`

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `importName` | string | 是 | 模块唯一标识 |

请求示例：

```json
{
  "importName": "f_form_DatePicker_1"
}
```

响应示例：

```json
{
  "status": 0,
  "data": {
    "importName": "f_form_DatePicker_1",
    "category": "f_form",
    "family": "date-form",
    "group": "picker",
    "navOrder": 2,
    "moduleName": "DatePicker",
    "description": "日期选择器",
    "versionNum": 2,
    "fileType": "wc",
    "vlVersion": "2.91",
    "status": "published",
    "interfaceJson": "{...}",
    "metadataJson": "{...}",
    "previewFrameJson": {
      "frame": { "width": 320, "height": 72, "unit": "px" }
    },
    "interface": {
      "props": [],
      "events": [],
      "methods": []
    },
    "metadata": {
      "family": "date-form",
      "group": "picker",
      "summary": "日期选择器",
      "keywords": ["date", "picker"],
      "useCases": ["表单日期选择"],
      "notFor": [],
      "order": 0,
      "dependsOn": [],
      "interfaceMeta": {
        "props": {
          "value": {
            "description": "当前选中的日期。",
            "required": false,
            "nullable": true,
            "example": "2026-05-01"
          }
        },
        "methods": {},
        "events": {}
      }
    },
    "tags": "[\"date\",\"range\"]",
    "scenarios": "[\"form\"]",
    "dependsOn": "[]",
    "url": "https://cdn.example.com/factory/system/f_form_DatePicker_1/v2/f_form_DatePicker_1.wc",
    "history": [
      {"version": 1, "changeNote": "initial version", "time": "2026-03-21T10:00:00Z"},
      {"version": 2, "changeNote": "fix range validation", "time": "2026-03-21T12:00:00Z"}
    ]
  }
}
```

注意事项：
- 系统模块 published/deprecated 所有登录用户可读；draft 仅系统租户可读。
- 租户私有模块仅所属租户可读。
- `category` / `family` / `group` / `navOrder` 为导航字段；客户端可展示为 `f_form / date-form / picker / DatePicker`。
- `previewFrameJson` 是模块记录顶层独立字段，不在 `metadata` 内；客户端需要渲染预览时应直接读取它。
- `history` 为解析后的版本历史数组，不是 JSON 字符串。
- `url` 为当前版本文件下载地址。
- 客户端推荐优先读取 `interface` 与 `metadata` 解析对象；`interfaceJson` 与 `metadataJson` 原始字符串仅作为原始存储值保留。

### 10.2.1 获取模块源码内容
- Name: `getModuleContent`
- Method: `POST`
- Path: `/edt/componentfactory/getmodulecontent`

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `importName` | string | 是 | 模块唯一标识 |

请求示例：

```json
{
  "importName": "f_form_DatePicker_1"
}
```

响应示例：

```json
{
  "status": 0,
  "data": {
    "importName": "f_form_DatePicker_1",
    "versionNum": 2,
    "fileType": "wc",
    "mainFile": "f_form_DatePicker_1.wc",
    "content": "<WebComponent-DatePicker>...</WebComponent-DatePicker>",
    "url": "https://cdn.example.com/factory/system/f_form_DatePicker_1/v2/f_form_DatePicker_1.wc"
  }
}
```

注意事项：
- 返回当前有效版本的组件主文件正文。
- `content` 是客户端下载、导入本地或在线预览时的直接正文来源。
- 对 bundle/zip 组件，后续可扩展为返回 envelope 或文件列表；当前最小合同以 `content` 主文件正文为准。

### 10.3 获取模块列表
- Name: `getModuleList`
- Method: `POST`
- Path: `/edt/componentfactory/getmodulelist`

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `category` | string | 否 | 一级分类过滤，用于目录浏览 |
| `family` | string | 否 | 二级导航过滤 |
| `group` | string | 否 | 三级导航过滤 |
| `fileType` | string | 否 | 文件类型过滤：`cp` / `wc` / `vs` / `zip` |
| `contentFormat` | string | 否 | 内容格式过滤 |
| `status` | string | 否 | 状态过滤：`draft`/`published`/`deprecated` |
| `vlVersion` | string | 否 | VL 版本过滤 |
| `source` | string | 否 | `system`/`tenant`/`all`（默认 all） |
| `keyword` | string | 否 | 按 description/import_name 模糊搜索 |
| `page` | number | 是 | 页码，从 1 开始 |
| `pageSize` | number | 是 | 每页条数，推荐 20 |
| `sortBy` | string | 否 | 排序字段，推荐支持 `navOrder` / `moduleName` / `updatedAt` |
| `sortOrder` | string | 否 | `asc` / `desc`，默认 `asc` |

响应示例：

```json
{
  "status": 0,
  "data": {
    "list": [
      {
        "importName": "f_form_DatePicker_1",
        "category": "f_form",
        "family": "date-form",
        "group": "picker",
        "navOrder": 2,
        "moduleName": "DatePicker",
        "description": "日期选择器",
        "mainFile": "f_form_DatePicker_1.wc",
        "fileType": "wc",
        "contentFormat": "single",
        "hasDependencies": false,
        "dependencyCount": 0,
        "vlVersion": "4.2.5",
        "versionNum": 2,
        "status": "published",
        "summary": "支持范围限制的日期选择器",
        "keywords": ["date", "range", "picker"]
      }
    ],
    "total": 42,
    "page": 1
  }
}
```

注意事项：
- 列表接口应投影常用元数据字段，至少包括 `family`、`group`、`navOrder`、`summary`、`keywords`，避免客户端为列表页逐条解析 `metadataJson`。
- `category` / `family` / `group` 用于层级浏览，`fileType` / `contentFormat` / `status` / `vlVersion` 用于工程约束硬过滤。
- 返回列表字段至少应包含 `importName`、`category`、`family`、`group`、`navOrder`、`moduleName`、`description`、`mainFile`、`fileType`、`contentFormat`、`hasDependencies`、`dependencyCount`、`vlVersion`、`versionNum`、`status`。
- `navOrder` 用于同层级排序；不参与组件身份识别。

### 10.4 语义搜索模块
- Name: `searchModules`
- Method: `POST`
- Path: `/edt/componentfactory/searchmodules`

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `query` | string | 是 | 自然语言搜索词 |
| `category` | string | 否 | 一级分类硬过滤 |
| `family` | string | 否 | 二级导航硬过滤 |
| `group` | string | 否 | 三级导航硬过滤 |
| `fileType` | string | 否 | 文件类型硬过滤：`cp` / `wc` / `vs` / `zip` |
| `contentFormat` | string | 否 | 内容格式硬过滤 |
| `status` | string | 否 | 状态硬过滤；默认只返回 `published` |
| `vlVersion` | string | 否 | VL 版本硬过滤 |
| `tags` | [string] | 否 | 标签过滤（AND 逻辑） |
| `scenarios` | [string] | 否 | 场景过滤（OR 逻辑） |
| `tid` | number | 是 | 租户 ID；0 = 仅系统模块，具体值 = 系统 + 该租户 |
| `limit` | number | 是 | 最多返回条数，推荐 5~10 |

请求示例：

```json
{
  "query": "日期选择器",
  "category": "f_form",
  "family": "date-form",
  "group": "picker",
  "fileType": "wc",
  "tags": ["date"],
  "tid": 0,
  "limit": 10
}
```

响应示例：

```json
{
  "status": 0,
  "data": [
    {
      "importName": "f_form_DatePicker_1",
      "versionNum": 1,
      "fileType": "wc",
      "category": "f_form",
      "family": "date-form",
      "group": "picker",
      "navOrder": 2,
      "description": "日期选择器",
      "summary": "支持范围限制的日期选择器",
      "keywords": ["date", "range", "picker"],
      "score": 0.92,
      "url": "https://cdn.example.com/factory/system/f_form_DatePicker_1/v1/f_form_DatePicker_1.wc"
    }
  ]
}
```

注意事项：
- 仅返回 `published` 状态的模块。
- 搜索结果不包含 `history`，只返回模块元数据 + `url`。
- 非系统租户不能查询其他租户的 `tid`。
- 当前 embedding 未接入时，`query` 退化为 ILIKE 文本匹配。
- 推荐执行顺序是先做向量召回或关键词召回，再做 `category` / `family` / `group` / `fileType` / `contentFormat` / `status` / `vlVersion` 等结构化硬过滤。
- `score` 仅用于排序和调试；`family` / `group` / `navOrder` 用于让前端结果页展示结果所在导航层级。
- 搜索画像默认应同时使用 `description`、`tags`、`scenarios`、`metadata.summary`、`metadata.keywords`、`metadata.useCases` 与 `metadata.interfaceMeta` 中的枚举和说明文本。

### 10.5 更新模块状态
- Name: `updateModuleStatus`
- Method: `POST`
- Path: `/edt/componentfactory/updatemodulestatus`

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `importName` | string | 是 | 目标模块标识 |
| `status` | string | 是 | `published` 或 `deprecated` |

状态流转：`draft` → `published` → `deprecated`（不可逆）。

### 10.6 发布新版本
- Name: `publishNewVersion`
- Method: `POST`
- Path: `/edt/componentfactory/publishnewversion`

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `importName` | string | 是 | 目标模块标识 |
| `vlVersion` | string | 否 | 若 VL 版本有变更 |
| `changeNote` | string | 否 | 轻量版本说明，存入 history |

响应示例：

```json
{
  "status": 0,
  "data": {
    "importName": "f_form_DatePicker_1",
    "version": 3
  }
}
```

注意事项：
- 服务端原子递增 `version_num` 并追加 history 记录。
- 需先调用此接口获取新版本号，再调用 `uploadFile` 上传文件。

### 10.7 上传文件
- Name: `uploadFile`
- Method: `POST`
- Path: `/edt/componentfactory/uploadfile`

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `importName` | string | 是 | 目标模块标识 |
| `versionNum` | number | 是 | 目标版本号（必须等于当前版本） |
| `fileContent` | string | 是 | 文件内容（Base64 编码） |

响应示例：

```json
{
  "status": 0,
  "data": {
    "url": "https://cdn.example.com/factory/system/f_form_DatePicker_1/v1/f_form_DatePicker_1.wc"
  }
}
```

注意事项：
- `versionNum` 必须等于模块当前版本号，否则返回 version mismatch 错误。
- 文件写入 S3，返回 CDN URL。

### 10.8 批量注册模块
- Name: `batchPublish`
- Method: `POST`
- Path: `/edt/componentfactory/batchpublish`

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `modules` | [object] | 是 | 模块数组，每个元素结构同 §10.1 |

响应示例：

```json
{
  "status": 0,
  "data": {
    "created": 15,
    "skipped": 3,
    "failed": 0,
    "results": [
      {"moduleName": "DatePicker", "importName": "f_form_DatePicker_1", "status": "created"},
      {"moduleName": "DatePicker", "status": "skipped", "error": "duplicate"}
    ]
  }
}
```

注意事项：
- category + moduleName 完全相同且已存在 published 版本时自动跳过（skipped）。

### 10.9 更新搜索向量
- Name: `updateEmbedding`
- Method: `POST`
- Path: `/edt/componentfactory/updateembedding`

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `importName` | string | 是 | 目标模块标识 |

注意事项：
- 需要 embedding 方法就绪后才可用。
- 从模块的 category、family、group、moduleName、import_name、description、tags、scenarios、metadata_json 拼接文本生成向量。
- `previewFrameJson` 不进入向量拼接源；它仅用于预览壳渲染。
- `updateModuleMeta` 在元数据相关字段变更后会自动尽力刷新该向量；本接口主要用于人工兜底或冷启动初始化。

### 10.10 更新模块元数据
- Name: `updateModuleMeta`
- Method: `POST`
- Path: `/edt/componentfactory/updatemodulemeta`

接口意义：
- 只更新组件工厂模块的导航、描述、标签、场景、公开元数据和模块级预览外框。
- 不创建新版本，不修改 S3 文件内容，不改变 `importName`。
- 适用于修正导航分组、调整标签、更新描述、重排列表顺序、调整模块预览外框。

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `importName` | string | 是 | 目标模块稳定标识 |
| `category` | string | 否 | 一级分类；通常不建议频繁修改 |
| `family` | string | 否 | 二级导航分组 |
| `group` | string | 否 | 三级导航分组 |
| `navOrder` | number | 否 | 同层级排序值 |
| `description` | string | 否 | 模块描述 |
| `tagsJson` | string | 否 | 标签数组 JSON 字符串 |
| `scenariosJson` | string | 否 | 场景数组 JSON 字符串 |
| `metadataJson` | string | 否 | 组件元数据 JSON 字符串，结构见 §10.1.1 |
| `previewFrameJson` | string\|object | 否 | 模块级预览外框配置；结构见 §10.1.2 |

请求示例：

```json
{
  "importName": "f_form_DatePicker_1",
  "family": "date-form",
  "group": "picker",
  "navOrder": 2,
  "description": "日期选择器，支持范围限制",
  "tagsJson": "[\"date\",\"range\",\"picker\"]",
  "previewFrameJson": {
    "frame": { "width": 320, "height": 72, "unit": "px" }
  }
}
```

响应示例：

```json
{
  "status": 0,
  "data": {
    "importName": "f_form_DatePicker_1",
    "updated": true
  }
}
```

注意事项：
- `family` / `group` / `navOrder` 属于导航层字段，更新它们不得改变 `importName`。
- `metadataJson` 中若也存在 `family` / `group` / `order`，服务端应以顶层字段作为列表与过滤的结构化来源。
- `previewFrameJson` 是模块记录顶层独立字段，不与 `metadataJson` 合并存储。
- 服务端在保存 `metadataJson` 前应校验：不允许出现顶层 `preview` 字段、顶层 `layout` 字段，也不允许携带源码 `@contract component sizeMode` 内容。
- 更新成功后，服务端应尽力刷新搜索向量；刷新失败不应导致元数据更新回滚，但应记录可重试状态。

### 10.11 真删除模块
- Name: `deleteModule`
- Method: `POST`
- Path: `/edt/componentfactory/deletemodule`

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `importName` | string | 是 | 目标模块标识 |

请求示例：

```json
{
  "importName": "f_form_DatePicker_1"
}
```

响应示例：

```json
{
  "status": 0,
  "data": {
    "importName": "f_form_DatePicker_1",
    "deleted": true
  }
}
```

权限：
- 系统模块（tid=null）仅系统租户（eid=10000586）可删
- 租户私有模块仅所属租户可删

注意事项：
- 真删除：同时清理 S3 上该模块所有版本/文件类型的全部对象，并物理删除 `factory_modules` 主记录。
- 删除顺序为「先 S3 后 DB」：S3 清理失败时 DB 记录保留，可重试本接口；S3 已清而 DB 删除失败时，重试仍可成功（S3 list 为空、DB delete 兜底完成）。
- 不可逆：无法通过任何接口恢复被删模块的内容、历史与导入引用。
- 调用方应在 UI 层提供二次确认。
- 后续若有项目仍在引用被删模块的 `importName`，引用会成为悬挂引用，由项目侧自行处理。

### 10.12 导航字段存储与索引约定

字段语义：
- `category`：一级分类、主过滤和主存储路径依据。
- `family`：二级浏览导航，用于把同类能力组件归入统一家族。
- `group`：三级浏览导航，用于把一批协同开发、协同验收或共同展示的组件归入一个工作包。
- `navOrder`：同层级排序值，不参与组件身份识别。

最小存储要求：
- 组件工厂模块主表至少持久化 `family`、`group_name`、`nav_order`。
- 组件工厂模块主表持久化 `previewFrameJson` 为模块记录顶层独立字段，不并入 `metadataJson` 列。
- API 字段继续叫 `group`；数据库字段可使用 `group_name`，避免和 SQL 保留字冲突。
- 老记录若没有 `family` / `group` / `navOrder` / `previewFrameJson`，读取时返回空字符串、默认值或空对象，不要求重发旧版本。
- 若系统存在组件工厂搜索向量表或 embedding 存储表，应同步纳入 `category`、`family`、`group_name`、`module_name` 作为结构化过滤字段；`previewFrameJson` 不纳入向量拼接源。

最小索引建议：
- 浏览复合索引：`(category, family, group_name, status)`
- 类型过滤索引：`(file_type, content_format, status, vl_version)`
- 排序辅助索引：`(category, family, group_name, nav_order)`
- 向量搜索可继续使用 embedding 索引，但结构化字段过滤必须能在向量召回后或混合查询中生效。

## 11. 系统文档（SystemDoc）

通用约定：
- 路由前缀：`/edt/systemDoc/`
- 统一方法：`POST`，Content-Type: `application/json`
- 所有接口均需平台登录态（Cookie: `ih5bearer=<jwt>`）
- 读接口：登录即可
- 写接口（`publish`）：额外需要白名单权限
- 旧版 `DocCenter` service 接口已废弃且不再收录于本文档；系统文档相关操作统一以本节 `/edt/systemDoc/*` 定义为准

通用响应格式：

```json
{
  "status": 0,
  "detail": null,
  "data": {}
}
```

- `status`: `0` 表示成功，非 `0` 表示失败
- `detail`: 失败原因描述
- `data`: 成功时的返回数据

### 11.1 发布系统文档版本
- Name: `publishSystemDoc`
- Method: `POST`
- Path: `/edt/systemDoc/publish`

权限：登录态 + 发布白名单

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `key` | string | 是 | 文档唯一标识，如 `VL` |
| `name` | string | 是 | 本次版本对应的文件名，如 `VL_4.0.md` |
| `docVersion` | string | 否 | 人类可读版本号，如 `"4.0"`、`"1.3"` |
| `content` | string | 是 | 文档正文内容 |
| `changeNote` | string | 是 | 版本说明 |

请求示例：

```json
{
  "key": "VL",
  "name": "VL_4.0.md",
  "docVersion": "4.0",
  "content": "# VL 语法规范\n\n...",
  "changeNote": "升级至 v4.0"
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "key": "VL",
    "docId": 1,
    "version": 2
  }
}
```

注意事项：
- `key` 不存在时自动创建文档主记录，并发布为第 1 版。
- 服务端对 `content` 做 SHA-256 去重，与最新版内容完全一致时拒绝创建新版本。
- `docVersion` 必须高于当前版本（语义版本比较），否则拒绝发布。支持大版本升级（4.1 → 5.0）和小版本升级（4.1 → 4.1.1）。
- 同一 `key` 下不允许发布重复的 `docVersion`，否则返回错误。
- 发布成功后，文档主记录的 `name`、`docVersion` 同步更新。

### 11.2 获取系统文档列表
- Name: `getSystemDocList`
- Method: `POST`
- Path: `/edt/systemDoc/list`

权限：登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `keyword` | string | 否 | 按 `key` 或 `name` 模糊搜索 |
| `orderBy` | string | 否 | 排序方式，默认 `createdAt_asc` |
| `page` | number | 是 | 页码，从 `1` 开始 |
| `pageSize` | number | 否 | 每页条数，默认 50，最大 1000 |

`orderBy` 枚举值：

| 值 | 说明 |
|----|------|
| `createdAt_asc` | 按创建时间升序（默认，旧的在前） |
| `createdAt_desc` | 按创建时间降序 |
| `updatedAt_desc` | 按更新时间降序 |
| `key_asc` | 按 key 字母升序 |

请求示例：

```json
{
  "page": 1,
  "pageSize": 50,
  "orderBy": "createdAt_asc"
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "total": 1,
    "list": [
      {
        "id": 1,
        "key": "VL",
        "name": "VL_4.0.md",
        "docVersion": "4.0",
        "description": "",
        "status": "active",
        "createdBy": 10000607,
        "createdAt": "2026-03-24T02:07:20.220741Z",
        "updatedAt": "2026-03-31T10:00:00.000000Z"
      }
    ]
  }
}
```

注意事项：
- 列表不返回正文内容，需要正文时调用 `getSystemDoc` 或 `getSystemDocVersion`。

### 11.3 获取系统文档最新版本
- Name: `getSystemDoc`
- Method: `POST`
- Path: `/edt/systemDoc/get`

权限：登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `key` | string | 是 | 文档唯一标识 |
| `currentVersion` | string | 否 | 客户端当前持有的 `docVersion`，如 `"4.1"` |

请求示例（普通获取）：

```json
{
  "key": "VL"
}
```

请求示例（带版本检查）：

```json
{
  "key": "VL",
  "currentVersion": "4.1"
}
```

响应示例（版本已更新或未传 `currentVersion`）：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "doc": {
      "id": 1,
      "key": "VL",
      "name": "VL_4.1.md",
      "docVersion": "4.1",
      "status": "active",
      "createdBy": 10000607,
      "createdAt": "2026-03-24T02:07:20.220741Z",
      "updatedAt": "2026-03-31T10:00:00.000000Z"
    },
    "version": "4.1",
    "content": "# VL 语法规范\n\n..."
  }
}
```

响应示例（版本未变化）：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "doc": { ... },
    "version": "4.1",
    "upToDate": true
  }
}
```

注意事项：
- 不传 `currentVersion` 时始终返回最新版本正文，行为与旧版完全一致。
- 传 `currentVersion` 且与线上最新 `docVersion` 一致时，跳过 S3 下载，不返回 `content`，额外返回 `"upToDate": true`。适合客户端轮询缓存场景。

### 11.4 获取系统文档版本历史
- Name: `getSystemDocHistory`
- Method: `POST`
- Path: `/edt/systemDoc/history`

权限：登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `key` | string | 是 | 文档唯一标识 |

请求示例：

```json
{
  "key": "VL"
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "key": "VL",
    "docId": 1,
    "versions": [
      {
        "docVersion": "4.0",
        "name": "VL_4.0.md",
        "changeNote": "升级至 v4.0",
        "contentHash": "sha256:xxx",
        "createdBy": 10000607,
        "createdAt": "2026-03-31T10:00:00Z"
      },
      {
        "docVersion": "3.8",
        "name": "VL_3.8.md",
        "changeNote": "初始化文档",
        "contentHash": "sha256:yyy",
        "createdBy": 10000607,
        "createdAt": "2026-03-24T02:07:20Z"
      }
    ]
  }
}
```

注意事项：
- 按发布时间降序返回，不含正文内容。
- 需要指定版本正文时，调用 `getSystemDocVersion`。

### 11.5 获取系统文档指定版本
- Name: `getSystemDocVersion`
- Method: `POST`
- Path: `/edt/systemDoc/getVersion`

权限：登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `key` | string | 是 | 文档唯一标识 |
| `docVersion` | string | 是 | 语义版本号（如 `"3.8"`、`"1.4.3"`） |

请求示例：

```json
{
  "key": "PlatformAPIs",
  "docVersion": "1.4.3"
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "key": "PlatformAPIs",
    "docId": 4,
    "name": "PlatformAPIs_v1.4.3",
    "docVersion": "1.4.3",
    "content": "# PlatformAPIs_v1.4.3\n\n...",
    "changeNote": "新增 updateMeta 接口",
    "createdBy": 10000607,
    "createdAt": "2026-04-14T..."
  }
}
```

注意事项：
- v1.4.4 起，入参从 `version`（number，内部版本序号）改为 `docVersion`（string，语义版本号）。旧的 `version` 参数不再支持。

### 11.6 解析系统文档引用
- Name: `resolveSystemDocRef`
- Method: `POST`
- Path: `/edt/systemDoc/resolveRef`

权限：登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `ref` | string | 是 | 引用字符串，格式：`"KEY"` 或 `"KEY@v<docVersion>"` |

请求示例（最新版本）：

```json
{
  "ref": "PlatformAPIs"
}
```

请求示例（指定版本）：

```json
{
  "ref": "PlatformAPIs@v1.4.3"
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "key": "VL",
    "docId": 1,
    "resolvedVersion": 2,
    "content": "# VL 语法规范\n\n..."
  }
}
```

注意事项：
- `ref` 不带版本号时解析为最新版本；带 `@v<docVersion>` 时解析为指定语义版本（如 `@v1.4.3`）。
- 适合工作流、智能体等动态引用场景。

### 11.7 按 ID 获取系统文档
- Name: `getSystemDocById`
- Method: `POST`
- Path: `/edt/systemDoc/getById`

权限：登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `id` | number | 是 | 文档数字 ID（从 list 接口获取） |
| `currentVersion` | string | 否 | 客户端当前持有的 `docVersion`，如 `"4.1"` |

请求示例：

```json
{
  "id": 1,
  "currentVersion": "4.1"
}
```

响应示例（版本已更新或未传 `currentVersion`）：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "doc": {
      "id": 1,
      "key": "VL",
      "name": "VL_4.1.md",
      "docVersion": "4.1",
      "status": "active",
      "createdBy": 10000607,
      "createdAt": "2026-04-01T00:00:00.000000Z",
      "updatedAt": "2026-04-01T00:00:00.000000Z"
    },
    "version": "4.1",
    "content": "# VL 语法规范\n\n..."
  }
}
```

响应示例（版本未变化）：返回结构同 §11.3，含 `"upToDate": true`，不含 `content`。

注意事项：
- 与 `getSystemDoc`（按 key）行为一致，支持相同的 `currentVersion` 缓存机制。
- `id` 是文档创建时分配的唯一数字标识，不会因版本更新而变化。

### 11.8 删除指定版本
- Name: `deleteVersion`
- Method: `POST`
- Path: `/edt/systemDoc/deleteVersion`

权限：登录态 + 删除白名单（仅限特定 uid）

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `key` | string | 是 | 文档唯一标识 |
| `docVersion` | string | 是 | 要删除的版本号，如 `"4.0"` |

请求示例：

```json
{
  "key": "VL",
  "docVersion": "4.0"
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": null
}
```

注意事项：
- 若删除的是当前最新版本，主记录自动退回到上一版本（即可用作 rollback）。
- 若该文档只剩一个版本，删除后主记录清空但文档主表记录保留。

### 11.9 删除整个文档
- Name: `deleteDoc`
- Method: `POST`
- Path: `/edt/systemDoc/deleteDoc`

权限：登录态 + 删除白名单（仅限特定 uid）

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `key` | string | 是 | 文档唯一标识 |

请求示例：

```json
{
  "key": "VL"
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": null
}
```

注意事项：
- 删除文档主记录及其所有版本记录，不可恢复。

### 11.10 更新文档元数据
- Name: `updateMeta`
- Method: `POST`
- Path: `/edt/systemDoc/updateMeta`

权限：登录态 + 发布白名单

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `key` | string | 条件 | 文档唯一标识，与 `id` 至少传一个 |
| `id` | number | 条件 | 文档数字 ID，与 `key` 至少传一个；两者都传时以 `id` 为准 |
| `name` | string | 条件 | 新文件名，三个可选字段至少传一个 |
| `docVersion` | string | 条件 | 新版本号 |
| `description` | string | 条件 | 新描述 |

请求示例：

```json
{
  "key": "VL",
  "name": "VL_4.2.md",
  "docVersion": "4.2"
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": null
}
```

注意事项：
- 仅更新文档主表的元数据字段，不创建新版本记录、不操作 S3。
- 适用于规范化旧文档命名、修正 docVersion 等场景。
- 只传了的字段会被更新，未传的保持不变。

## 12. 资源中心（Resource Center）

资源中心统一管理六类开发者资源：`component` / `flow` / `doc` / `tool` / `skill` / `plugin`。

核心概念：
- **visibility**：资源可见性。`official`（平台官方）、`private`（用户私有）、`shared`（用户共享）。
- **review_status**：审核状态。`none`（未提审）、`pending`（审核中）、`approved`（已通过）、`rejected`（已驳回）。
- 用户可用资源集合 = `official + shared + own private`。
- 资源内容存储在 S3，小内容可直接存 `content_json` 字段。

### 12.1 资源目录查询
- Name: `catalog`
- Method: `POST`
- Path: `/edt/resource/catalog`

权限：登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `kind` | string | 否 | 资源类型过滤：`component` / `flow` / `doc` / `tool` / `skill` / `plugin` |
| `visibility` | string | 否 | 可见性过滤：`official` / `private` / `shared` |
| `keyword` | string | 否 | 关键词搜索（匹配 title、slug、description） |
| `orderBy` | string | 否 | 排序：`createdAt_asc` / `createdAt_desc`（默认） / `updatedAt_desc` / `title_asc` |
| `page` | int | 否 | 页码，默认 1 |
| `pageSize` | int | 否 | 每页条数，默认 50，最大 1000 |

请求示例：

```json
{
  "kind": "component",
  "keyword": "grid",
  "page": 1,
  "pageSize": 20
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "total": 3,
    "list": [
      {
        "id": 1,
        "kind": "component",
        "slug": "data-grid-pro",
        "title": "Data Grid Pro",
        "description": "高性能数据表格组件",
        "visibility": "shared",
        "reviewStatus": "approved",
        "ownerUid": 10000607,
        "ownerName": "dev-1",
        "version": "2.0",
        "tags": "[\"table\",\"grid\"]",
        "contentFormat": "bundle",
        "contentSize": 45200,
        "fileCount": 5,
        "createdAt": "2026-04-02T21:00:00Z",
        "updatedAt": "2026-04-02T21:00:00Z"
      }
    ]
  }
}
```

注意事项：
- 不传 `visibility` 时，默认返回 `official + shared + 当前用户的 private`。
- 传 `visibility=private` 时仅返回当前用户自己的私有资源。

### 12.2 资源详情
- Name: `detail`
- Method: `POST`
- Path: `/edt/resource/detail`

权限：登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `resourceId` | int | 是 | 资源 ID |

请求示例：

```json
{
  "resourceId": 1
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "item": {
      "id": 1,
      "kind": "component",
      "slug": "data-grid-pro",
      "title": "Data Grid Pro",
      "description": "高性能数据表格组件",
      "visibility": "shared",
      "reviewStatus": "approved",
      "ownerUid": 10000607,
      "ownerName": "dev-1",
      "version": "2.0",
      "tags": "[\"table\",\"grid\"]",
      "meta": "{}",
      "contentFormat": "bundle",
      "contentSize": 45200,
      "fileCount": 5
    },
    "content": "{...}"
  }
}
```

注意事项：
- `content` 字段返回 S3 中 envelope.json 的内容（或 content_json 字段内容）。
- 权限校验：official/shared 所有登录用户可看，private 仅资源所有者和管理员可看。

### 12.3 统一搜索
- Name: `search`
- Method: `POST`
- Path: `/edt/resource/search`

权限：登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `query` | string | 否 | 搜索关键词 |
| `kind` | string | 否 | 资源类型过滤 |
| `limit` | int | 否 | 返回数量，默认 20,最大 100 |

请求示例：

```json
{
  "query": "数据表格",
  "kind": "component",
  "limit": 10
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "results": [
      {
        "resourceId": 1,
        "id": 1,
        "kind": "component",
        "title": "Data Grid Pro",
        "summary": "高性能数据表格组件",
        "slug": "data-grid-pro",
        "tags": "[\"table\",\"grid\"]",
        "ownerName": "dev-1",
        "visibility": "shared",
        "reviewStatus": "approved",
        "version": "2.0",
        "description": "高性能数据表格组件"
      }
    ]
  }
}
```

注意事项：
- 已接入向量搜索（1024 维 embedding），当 `query` 非空时优先使用语义向量召回，支持跨语言检索；若向量服务不可用则自动降级为关键词搜索（ILIKE）。
- 搜索结果自动应用可见性过滤（official + shared + own private）。

### 12.4 发布资源
- Name: `publish`
- Method: `POST`
- Path: `/edt/resource/publish`

权限：登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `kind` | string | 是 | 资源类型：`component` / `flow` / `doc` / `tool` / `skill` / `plugin` |
| `slug` | string | 是 | 资源标识符 |
| `title` | string | 是 | 资源标题 |
| `description` | string | 否 | 资源描述 |
| `version` | string | 否 | 版本号，如 `"1.0"` |
| `visibility` | string | 否 | 可见性，默认 `private`；仅管理员可设为 `official` |
| `contentFormat` | string | 否 | 内容格式：`json`（默认）/ `text` / `bundle` |
| `content` | string | 否 | 资源正文内容（上传到 S3 payload） |
| `contentJson` | string | 否 | 小内容直接存 DB |
| `tagsJson` | string | 否 | 标签 JSON 数组，如 `"[\"table\",\"grid\"]"` |
| `metaJson` | string | 否 | 扩展元数据 JSON |
| `mainFile` | string | 否 | 主文件名（bundle 资源用） |
| `files` | string[] | 否 | 文件列表（bundle 资源用） |
| `fileCount` | int | 否 | 文件数量，默认 1 |

请求示例：

```json
{
  "kind": "doc",
  "slug": "team-style-guide",
  "title": "团队风格指南",
  "description": "前端项目统一代码风格规范",
  "version": "1.0",
  "contentFormat": "text",
  "content": "# 团队风格指南\n\n## 1. 命名规范\n...",
  "tagsJson": "[\"guide\",\"style\"]"
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "id": 5,
    "kind": "doc",
    "slug": "team-style-guide"
  }
}
```

注意事项：
- 同一用户的同 `kind + slug + visibility` 组合视为同一资源，重复发布会更新而非新建。
- 发布时自动上传 envelope.json + payload 到 S3。
- 发布后自动创建/更新检索画像记录。

### 12.5 提交审核
- Name: `submit`
- Method: `POST`
- Path: `/edt/resource/submit`

权限：登录态（仅限资源所有者）

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `resourceId` | int | 是 | 资源 ID |

请求示例：

```json
{
  "resourceId": 5
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": null
}
```

注意事项：
- 只有 `visibility=private` 的资源可以提交审核。
- 提交后 `review_status` 变为 `pending`。
- 已在审核中的资源不可重复提交。

### 12.6 导入资源
- Name: `import`
- Method: `POST`
- Path: `/edt/resource/import`

权限：登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `resourceId` | int | 是 | 资源 ID |

请求示例：

```json
{
  "resourceId": 1
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "item": { "..." },
    "content": "{...}"
  }
}
```

注意事项：
- 返回结构与 detail 一致，供客户端下载资源内容用于本地导入。
- 权限校验同 detail。

### 12.7 我的资源列表
- Name: `mylist`
- Method: `POST`
- Path: `/edt/resource/mylist`

权限：登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `kind` | string | 否 | 资源类型过滤 |
| `reviewStatus` | string | 否 | 审核状态过滤：`none` / `pending` / `approved` / `rejected` |
| `page` | int | 否 | 页码，默认 1 |
| `pageSize` | int | 否 | 每页条数，默认 50，最大 1000 |

请求示例：

```json
{
  "kind": "flow",
  "reviewStatus": "pending",
  "page": 1,
  "pageSize": 20
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "total": 2,
    "list": [...]
  }
}
```

注意事项：
- 仅返回当前登录用户自己的资源。

### 12.8 删除资源
- Name: `delete`
- Method: `POST`
- Path: `/edt/resource/delete`

权限：登录态（资源所有者或管理员）

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `resourceId` | int | 是 | 资源 ID |

请求示例：

```json
{
  "resourceId": 5
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": null
}
```

注意事项：
- 同时删除 S3 内容、检索画像记录、审核记录和主表记录。
- 仅资源所有者或管理员白名单用户可执行。

### 12.9 管理员审核
- Name: `admin/review`
- Method: `POST`
- Path: `/edt/resource/admin/review`

权限：登录态 + 管理员白名单

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `resourceId` | int | 是 | 资源 ID |
| `decision` | string | 是 | 审核决定：`approved` / `rejected` |
| `note` | string | 否 | 审核备注 |

请求示例：

```json
{
  "resourceId": 5,
  "decision": "approved",
  "note": "内容质量合格"
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": null
}
```

注意事项：
- 仅 `review_status=pending` 的资源可被审核。
- `approved`：`visibility` 变为 `shared`，`review_status` 变为 `approved`，S3 内容复制到 shared 路径。
- `rejected`：`visibility` 保持 `private`，`review_status` 变为 `rejected`。
- 每次审核动作都写入 `edten_resource_review` 审核记录表。

### 12.10 管理员待审核列表
- Name: `admin/list`
- Method: `POST`
- Path: `/edt/resource/admin/list`

权限：登录态 + 管理员白名单

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `kind` | string | 否 | 资源类型过滤 |
| `reviewStatus` | string | 否 | 审核状态过滤，默认 `pending` |
| `page` | int | 否 | 页码，默认 1 |
| `pageSize` | int | 否 | 每页条数，默认 50，最大 1000 |

请求示例：

```json
{
  "kind": "component",
  "page": 1,
  "pageSize": 20
}
```

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "total": 1,
    "list": [
      {
        "id": 5,
        "kind": "component",
        "slug": "data-grid-pro",
        "title": "Data Grid Pro",
        "visibility": "private",
        "reviewStatus": "pending",
        "ownerUid": 10000607,
        "ownerName": "dev-1",
        "version": "2.0"
      }
    ]
  }
}
```

注意事项：
- 仅管理员白名单用户可调用。
- 不传 `reviewStatus` 时默认返回 `pending` 状态的资源。

## 13. admin接口

本章收录 edten 新增的 `/edt/admin/*` 管理接口。新 admin 接口使用普通平台登录态识别当前用户，再通过服务端系统管理员 UID 白名单判断是否允许操作；白名单由服务端维护，文档不公开具体 UID。

统一约定：
- 新 admin 接口默认使用 `POST` + JSON body。
- 调用方需携带普通平台登录态，例如 `Cookie: ih5bearer=<token>`。
- 新 admin 接口不依赖旧 `/ih5/admin/auth/login`，也不使用 `ih5admin` Cookie 或旧 admin token。
- 这类接口通常会修改平台核心数据或缓存，自动化调用前必须确认目标参数和影响范围。

### 13.1 清理自定义域名占用
- Name: `admin/work/clearDomain`
- Method: `POST`
- Path: `/edt/admin/work/clearDomain`
- 英文服完整 URL：`https://editor.visuallogic.ai/edt/admin/work/clearDomain`
- 中文服完整 URL：`https://ai.ivx.cn/edt/admin/work/clearDomain`

权限：登录态 + 系统管理员 UID 白名单

接口意义：
- 清理某个自定义域名路径在旧作品上的占用，使该域名路径可以重新被其他项目使用。
- 服务端会把命中的作品域名恢复为系统域名路径，并清理域名路径相关缓存。

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `domain` | string | 条件必填 | 自定义域名。可与 `path` 一起传，例如 `example.com` |
| `path` | string | 条件必填 | 自定义路径。可传 `/a` 或 `a`，服务端会补齐前导 `/` |
| `url` | string | 条件必填 | 完整域名路径。可替代 `domain + path`，例如 `https://example.com/a` 或 `example.com/a` |

请求规则：
- `domain + path` 与 `url` 二选一。
- `domain` 会转为小写。
- `path` 不允许包含 query 或 fragment。
- `domain` 不允许包含 path、query 或 fragment。

请求示例：

```json
{
  "domain": "example.com",
  "path": "/a"
}
```

等价请求：

```json
{
  "url": "https://example.com/a"
}
```

响应字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `domain` | string | 归一化后的域名 |
| `path` | string | 归一化后的路径 |
| `affectedNids` | number[] | 命中的作品 ID 列表 |
| `nodeVxRows` | number | 预览库 `node_vx` 影响行数 |
| `publishedNodeVxRows` | number | 发布库 `node_vx` 影响行数 |
| `versionRows` | number | 预览库 `node_vx_version` 影响行数 |
| `publishedVersionRows` | number | 发布库 `node_vx_version` 影响行数 |
| `cacheKeys` | string[] | 已删除的域名路径缓存 key |
| `cacheEvents` | number | 已发布的缓存失效事件数量 |

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "domain": "example.com",
    "path": "/a",
    "affectedNids": [12000001],
    "nodeVxRows": 1,
    "publishedNodeVxRows": 1,
    "versionRows": 1,
    "publishedVersionRows": 1,
    "cacheKeys": [
      "domain-path:develop:example.com:/a",
      "domain-path:release:example.com:/a"
    ],
    "cacheEvents": 4
  }
}
```

处理效果：
- 命中预览库和发布库中的 `node_vx.domain/path` 与 `node_vx.preview_domain/preview_path`。
- 将命中的自定义域名恢复为空域名，并把路径恢复为系统路径 `/play/{link}`。
- 同步处理对应 `node_vx_version` 记录。
- 删除 `domain-path:develop:{domain}:{path}` 与 `domain-path:release:{domain}:{path}` 缓存。
- 对命中的 `nid` 发布作品、函数、作品配置和作品数据缓存失效事件。

注意事项：
- 这是高权限修复接口，只应在确认域名路径确实被旧项目占用且需要释放时调用。
- 若没有命中任何作品，接口仍会返回成功，影响行数和 `affectedNids` 可用于判断是否实际清理到数据。
- 本接口只清理平台 DB 和缓存中的域名占用，不负责修改 DNS 记录。

### 13.2 强制设置作品发布域名
- Name: `admin/work/setDomain`
- Method: `POST`
- Path: `/edt/admin/work/setDomain`
- 英文服完整 URL：`https://editor.visuallogic.ai/edt/admin/work/setDomain`
- 中文服完整 URL：`https://ai.ivx.cn/edt/admin/work/setDomain`

权限：登录态 + 系统管理员 UID 白名单

接口意义：
- 按 `nid` 强制设置作品发布版 `domain/path`。
- 若目标 `domain/path` 已被其他作品占用，服务端会先把其他作品的同域名占用恢复为系统路径 `/play/{link}`，再把目标 `nid` 写成该发布域名。
- 服务端会清理旧域名、新域名和被转移作品相关缓存。

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `nid` | number | 是 | 目标作品 ID |
| `domain` | string | 条件必填 | 要设置的发布域名。可与 `path` 一起传，例如 `www.example.com` |
| `path` | string | 条件必填 | 要设置的发布路径。根路径传 `/`，也可传 `a` 表示 `/a` |
| `url` | string | 条件必填 | 完整发布域名路径。可替代 `domain + path`，例如 `https://www.example.com/` 或 `www.example.com/a` |

请求规则：
- `domain + path` 与 `url` 二选一。
- 通用字段 `domain/path/url` 只设置发布版域名路径。
- `domain` 会转为小写。
- `path` 不允许包含 query 或 fragment。
- `domain` 不允许包含 path、query 或 fragment。

请求示例：

```json
{
  "nid": 12032776,
  "domain": "www.visuallogic.ai",
  "path": "/"
}
```

等价请求：

```json
{
  "nid": 12032776,
  "url": "https://www.visuallogic.ai/"
}
```

响应字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `nid` | number | 目标作品 ID |
| `published` | object | 归一化后的发布域名路径 |
| `beforePublished` | object | 修改前发布库 `node_vx` 域名状态 |
| `clearedNids` | number[] | 被清理同域名占用的其他作品 ID |
| `clearedRows` | number | 清理其他作品占用影响行数 |
| `publishedNodeVxRows` | number | 发布库 `node_vx` 影响行数 |
| `versionRows` | number | 预览库 `node_vx_version` 中已发布版本影响行数 |
| `publishedVersionRows` | number | 发布库 `node_vx_version` 中已发布版本影响行数 |
| `cacheKeys` | string[] | 已删除的域名路径缓存 key |
| `cacheEvents` | number | 已发布的缓存失效事件数量 |

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "nid": 12032776,
    "published": {
      "domain": "www.visuallogic.ai",
      "path": "/"
    },
    "clearedNids": [12032754],
    "clearedRows": 2,
    "publishedNodeVxRows": 1,
    "versionRows": 1,
    "publishedVersionRows": 1,
    "cacheKeys": [
      "domain-path:develop:www.visuallogic.ai:/",
      "domain-path:release:www.visuallogic.ai:/"
    ],
    "cacheEvents": 8
  }
}
```

处理效果：
- 设置目标作品发布库 `node_vx.domain/path`。
- 同步设置目标作品已发布版本对应的 `node_vx_version.domain/path`。
- 对其他作品中相同 `domain/path` 的占用执行强制清理，恢复为空域名与 `/play/{link}` 系统路径。
- 删除相关 `domain-path:develop:{domain}:{path}` 与 `domain-path:release:{domain}:{path}` 缓存。
- 对目标 `nid` 和被清理的 `nid` 发布作品、函数、作品配置和作品数据缓存失效事件。

注意事项：
- 这是高权限修复接口，会转移域名归属；调用前必须确认目标 `nid` 和目标域名路径。
- 常规调用只设置发布版域名路径，预览域名在当前运维语义中没有独立意义。
- 本接口只修改平台 DB 和缓存，不负责修改 DNS 记录。

### 13.3 强制修改工作组核心信息
- Name: `admin/work/updateGroup`
- Method: `POST`
- Path: `/edt/admin/work/updateGroup`
- 英文服完整 URL：`https://editor.visuallogic.ai/edt/admin/work/updateGroup`
- 中文服完整 URL：`https://ai.ivx.cn/edt/admin/work/updateGroup`

权限：登录态 + 系统管理员 UID 白名单

接口意义：
- 按 `gid` 强制修复工作组核心信息，包括基础展示字段、`info`、归属 `uid`、内含 `nids` 与 `mainNid`。
- 这是数据修复型 admin 接口，不替代普通编辑器工作组保存、作品移动或用户态协作流程。
- 服务端会在事务内更新工作组表和必要的作品归属字段，事务提交后清理 work group、groupInfo、作品、函数、作品配置和作品数据相关缓存。

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `gid` | number | 是 | 目标工作组 ID |
| `title` | string | 否 | 工作组标题；传入则更新，不传保持原值 |
| `description` | string | 否 | 工作组描述；传入则更新，不传保持原值 |
| `cover` | string | 否 | 工作组封面；传入则更新，不传保持原值 |
| `color` | number | 否 | 工作组颜色编号；传入则更新，不传保持原值 |
| `ownerUid` | number | 否 | 工作组归属用户 ID；默认只更新工作组 owner，不修改作品 owner |
| `nids` | number[] | 否 | 强制设置工作组内含作品列表；服务端会去重并排序 |
| `mainNid` | number | 否 | 工作组主作品 ID；非 `0` 时必须包含在最终 `nids` 中 |
| `infoMode` | string | 否 | `merge` 或 `replace`；默认 `merge` |
| `info` | object | 否 | 工作组 `info` JSON；必须保持对象类型 |
| `syncWorkGid` | boolean | 否 | 修改 `nids` 时是否同步 `node_vx.gid` / `node_vx_data.gid`；默认 `true` |
| `syncWorkOwner` | boolean | 否 | 是否把最终 `nids` 对应作品的 `uid` 同步改为 `ownerUid`；默认 `false` |
| `scope` | string | 否 | `preview`、`published` 或 `both`；默认 `preview` |
| `dryRun` | boolean | 否 | 为 `true` 时只返回预计修改范围，不写 DB、不清缓存 |
| `reason` | string | 是 | 修复原因，会进入 admin 日志 |

请求规则：
- `gid` 必须大于 `0`。
- 至少传入一个要修改的字段，不能只传 `gid`。
- `reason` 必填。
- 字段采用 PATCH 语义：字段未传则不修改；字段传空值表示明确写入空值。
- `infoMode:"merge"` 时按顶层 key 合并 `info`；`infoMode:"replace"` 时整体替换 `info`。
- `nids` 为空数组表示把工作组内含作品列表改为空；服务端会同步处理 `mainNid`。
- `mainNid` 非 `0` 时必须包含在最终 `nids` 中，否则返回错误。
- `syncWorkOwner:true` 必须同时传 `ownerUid` 才会修改作品 owner。
- 若同时传 `nids`、`syncWorkOwner:true` 且 `syncWorkGid:false`，服务端会拒绝请求，避免作品 owner 与 gid 形成不一致状态。
- `scope:"published"` 或 `scope:"both"` 会修改发布库对应数据，调用前必须确认确实需要处理发布态。

请求示例：仅修改工作组 owner，不修改组内作品 owner

```json
{
  "gid": 25391,
  "ownerUid": 10006977,
  "reason": "修复工作组归属"
}
```

请求示例：先 dryRun 查看影响范围

```json
{
  "gid": 25391,
  "ownerUid": 10006977,
  "dryRun": true,
  "reason": "确认工作组归属修复影响范围"
}
```

请求示例：强制设置组内作品和主作品

```json
{
  "gid": 25391,
  "nids": [12032776, 12032777],
  "mainNid": 12032776,
  "reason": "修复工作组作品列表"
}
```

请求示例：合并更新 `info`

```json
{
  "gid": 25391,
  "infoMode": "merge",
  "info": {
    "urls": {},
    "serviceMap": {}
  },
  "reason": "修复工作组 info"
}
```

响应字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `success` | boolean | 是否整体成功 |
| `gid` | number | 目标工作组 ID |
| `scope` | string | 本次处理范围 |
| `dryRun` | boolean | 是否为 dryRun |
| `before` | object | 修改前快照 |
| `after` | object | 修改后快照 |
| `affectedNids` | object | 旧作品、新作品、新增作品、移除作品列表 |
| `rows` | object | 各表影响行数 |
| `published` | object | `scope:"both"` 时发布库处理结果 |
| `cache` | object | 缓存事件数量 |
| `warnings` | string[] | 警告信息 |

`before` / `after` 结构：

| 字段 | 类型 | 说明 |
|------|------|------|
| `gid` | number | 工作组 ID |
| `uid` | number | 工作组 owner |
| `title` | string | 标题 |
| `description` | string | 描述 |
| `cover` | string | 封面 |
| `color` | number | 颜色编号 |
| `nids` | number[] | 组内作品列表 |
| `mainNid` | number | 主作品 ID |
| `info` | object | 工作组 info |

`affectedNids` 结构：

| 字段 | 类型 | 说明 |
|------|------|------|
| `old` | number[] | 修改前工作组作品列表 |
| `new` | number[] | 修改后工作组作品列表 |
| `added` | number[] | 新增到该组的作品 |
| `removed` | number[] | 从该组移除的作品 |

`rows` 结构：

| 字段 | 类型 | 说明 |
|------|------|------|
| `group` | number | `node_vx_work_group` 影响行数 |
| `nodeVxSetGid` | number | `node_vx` 设置 gid 行数 |
| `nodeVxDataSetGid` | number | `node_vx_data` 设置 gid 行数 |
| `nodeVxClearGid` | number | `node_vx` 清理 gid 行数 |
| `nodeVxDataClearGid` | number | `node_vx_data` 清理 gid 行数 |
| `nodeVxSetOwner` | number | `node_vx` 设置 owner 行数 |
| `nodeVxDataSetOwner` | number | `node_vx_data` 设置 owner 行数 |

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": {
    "success": true,
    "gid": 25391,
    "scope": "preview",
    "dryRun": false,
    "before": {
      "gid": 25391,
      "uid": 10000607,
      "title": "Example Group",
      "description": "",
      "cover": "",
      "color": 1,
      "nids": [12032776],
      "mainNid": 12032776,
      "info": {}
    },
    "after": {
      "gid": 25391,
      "uid": 10006977,
      "title": "Example Group",
      "description": "",
      "cover": "",
      "color": 1,
      "nids": [12032776],
      "mainNid": 12032776,
      "info": {}
    },
    "affectedNids": {
      "old": [12032776],
      "new": [12032776],
      "added": [],
      "removed": []
    },
    "rows": {
      "group": 1,
      "nodeVxSetGid": 0,
      "nodeVxDataSetGid": 0,
      "nodeVxClearGid": 0,
      "nodeVxDataClearGid": 0,
      "nodeVxSetOwner": 0,
      "nodeVxDataSetOwner": 0
    },
    "cache": {
      "events": 6
    },
    "warnings": []
  }
}
```

处理效果：
- 更新 `node_vx_work_group` 的目标字段，并刷新 `updated_at`。
- 修改 `nids` 且 `syncWorkGid` 为 `true` 时，同步维护 `node_vx.gid` 与 `node_vx_data.gid`。
- `syncWorkOwner` 为 `true` 且传入 `ownerUid` 时，同步维护最终 `nids` 对应的 `node_vx.uid` 与 `node_vx_data.uid`。
- 修改前会校验需要写入的 `nid` 在 `node_vx` 与 `node_vx_data` 中都存在。
- DB 提交后清理 work group cache、groupInfo cache，以及受影响 `nid` 的作品、函数、作品配置和作品数据缓存。

注意事项：
- 这是高权限修复接口，会直接修改核心平台数据；调用前必须先确认目标区服、目标 `gid` 和影响范围。
- 推荐先传 `dryRun:true` 查看 `before`、`after`、`affectedNids` 与 `rows`，确认无误后再执行真实写入。
- 默认 `scope:"preview"`，不会自动修改发布库。
- 默认 `syncWorkOwner:false`，只改工作组 owner，不改组内作品 owner。
- 如果缓存清理失败，接口会返回失败，并在错误中说明 DB 已提交但缓存未完全清理，需要人工补偿。

## 14. 管理后台与内部运维接口（Admin / Internal Ops）

本章基于 `/Users/ivx/Downloads/小工具案例.json` 中的「小工具」案例提取。该案例中同时存在平台 admin 接口、编辑器内部运维接口、旧外部辅助接口和案例私有 custom API；本章只收录可作为 Platform API 口径沉淀的 admin/internal 运维接口。

统一约定：
- 本章接口默认使用 `POST` + JSON body，除非接口历史实现另有说明。
- `/ih5/admin/*` 接口需要管理员登录态，通常通过 `Cookie: ih5admin=<admin-token>` 或当前平台等价管理员会话传递；文档和 agent 运行日志不得写入真实管理员账号、密码或 token。
- `nid`、`gid`、`uid`、`toUid`、`version` 等 ID / 版本字段必须按接口要求使用正确类型；案例调试记录显示，若把 `nid` 作为字符串传给部分 Go 服务，会触发 `cannot unmarshal string into Go struct field ... of type int64`。
- 本章接口多为高权限运维能力，可能改变作品归属、上架状态、屏蔽状态、缓存、数据库结构或数据恢复点；自动化 agent 调用前必须确认任务目标和影响范围。
- 案例中出现的 `https://www.ivx.cn`、`https://latest.ivx.cn`、`https://dev.ivx.cn`、`https://editor.ivx.cn` 代表历史调用环境；新调用方应按当前环境选择 base URL，接口 path 才是稳定识别口径。
- 案例私有接口如 `/api/{nid}/...`、旧外部辅助接口如 `file343434.aiwall.com/v3/*`、第三方支付/微信/地图等应用业务配置，不纳入本章平台通用 API。

### 14.1 管理员登录
- Name: `admin/auth/login`
- Method: `POST`
- Path: `/ih5/admin/auth/login`

权限：管理员账号密码

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `username` | string | 是 | 管理员账号 |
| `password` | string | 是 | 管理员密码 |

响应字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `token` | string | 管理员 token。调用方通常写入 `ih5admin` Cookie 或等价登录态 |

注意事项：
- 不得在文档、项目文件、日志或 agent 输出中保存真实账号密码。
- 若已有管理员会话，优先复用安全会话，不应让 agent 反复处理明文密码。

### 14.2 当前管理员信息
- Name: `admin/auth/info`
- Method: `POST`
- Path: `/ih5/admin/auth/info`

权限：管理员登录态

请求参数：无

响应字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | number | 管理员用户 ID |
| `name` | string | 管理员名称 |
| `email` | string | 邮箱 |
| `phone` | string | 手机号 |
| `createdAt` | string | 创建时间 |
| `updatedAt` | string | 更新时间 |
| `realName` | string | 真实姓名 |
| `lastLogin` | number | 最近登录时间戳 |
| `picture` | string | 头像 |

### 14.3 管理员上架 / 下架版本
- Name: `admin/work/launchVersion`
- Method: `POST`
- Path: `/ih5/admin/work/launchVersion`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `nid` | number | 是 | 作品 ID |
| `version` | string | 是 | 要上架的发布版本；传 `off-shelf` 表示下架 |
| `launchBy` | string | 否 | 操作人标识，历史案例中用于记录发起人 |

响应示例：

```json
{}
```

注意事项：
- 这是 admin 版本入口；普通发布链路仍优先参考 §6.2。
- 下架会改变正式访问入口状态，agent 不得在未确认目标时自动执行。

### 14.4 复制单个作品
- Name: `admin/work/copyWork`
- Method: `POST`
- Path: `/ih5/admin/work/copyWork`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `nid` | number | 是 | 源作品 ID |
| `toUid` | number | 是 | 目标用户 ID |
| `copyData` | boolean | 否 | 是否复制作品数据，默认按平台实现处理 |

响应字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `nid` | number | 新复制出的作品 ID |

注意事项：
- `copyData=false` 只复制作品结构，不复制数据；涉及用户数据迁移时必须明确选择。
- 若源作品不存在或权限/数据不完整，平台可能返回 `dbr: not found` 等内部错误。

### 14.5 复制整组作品
- Name: `admin/work/copyWorkGroup`
- Method: `POST`
- Path: `/ih5/admin/work/copyWorkGroup`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `gid` | number | 是 | 源项目组 ID |
| `toUid` | number | 是 | 目标用户 ID |
| `nids` | number[] | 否 | 指定复制的作品 ID 列表；不传时按平台实现处理 |
| `notCopyData` | boolean | 否 | 是否不复制数据；历史案例中用于控制整组迁移数据策略 |
| `copyLimit` | number | 否 | 历史参数，表示是否复制访问限制等限制配置 |

响应字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `gid` | number | 新项目组 ID |
| `nids` | number[] | 新作品 ID 列表 |

注意事项：
- `nids` / `notCopyData` 与 `copyLimit` 是历史案例中观察到的两组参数形态；新调用方应按当前平台服务实际支持的参数选择，不要混用无关参数。
- 整组复制可能生成多个作品和数据表，调用前必须确认目标用户与复制范围。

### 14.6 设置作品 UA 过滤
- Name: `admin/work/setUAFilter`
- Method: `POST`
- Path: `/ih5/admin/work/setUAFilter`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `nid` | number | 是 | 作品 ID |
| `uaFilter` | string[] | 是 | 允许或限制的 UA 类型列表；案例中出现 `["phone"]` |

响应示例：

```json
{}
```

注意事项：
- `nid` 必须传 number。
- UA 类型枚举以当前平台实现为准，案例只确认 `phone` 曾被使用。

### 14.7 查询作品访问限制
- Name: `admin/work/getLimit`
- Method: `POST`
- Path: `/ih5/admin/work/getLimit`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `nid` | number | 是 | 作品 ID |

响应字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `index` | number | 当前限制值或限制档位；案例中返回过 `300` |

### 14.8 设置作品访问限制
- Name: `admin/work/setLimit`
- Method: `POST`
- Path: `/ih5/admin/work/setLimit`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `nid` | number | 是 | 作品 ID |
| `type` | number | 是 | 限制类型；历史案例中 `0` 用于设置并发/访问限制类值 |
| `value` | number | 是 | 限制值 |

响应示例：

```json
{}
```

注意事项：
- 具体 `type` 枚举需要以当前 admin 服务实现为准；案例只沉淀出参数形态。

### 14.9 设置作品欠费屏蔽
- Name: `admin/work/billingBan`
- Method: `POST`
- Path: `/ih5/admin/work/billingBan`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `nids` | number[] | 是 | 作品 ID 列表 |
| `ban` | boolean | 是 | `true` 表示屏蔽，`false` 表示解除屏蔽 |

响应示例：

```json
{}
```

注意事项：
- 该接口影响作品访问状态，批量调用前必须确认 `nids` 列表无误。

### 14.10 设置用户欠费屏蔽
- Name: `admin/user/billingBan`
- Method: `POST`
- Path: `/ih5/admin/user/billingBan`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `uids` | number[] | 是 | 用户 ID 列表 |
| `ban` | boolean | 是 | `true` 表示屏蔽，`false` 表示解除屏蔽 |

响应示例：

```json
{}
```

注意事项：
- 该接口影响用户维度访问状态，属于高风险批量运维操作。

### 14.11 清理缓存
- Name: `admin/cache/clear`
- Method: `POST`
- Path: `/ih5/admin/cache/clear`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `type` | string | 是 | 缓存类型；案例中出现 `workData` |
| `data` | string | 是 | 缓存定位数据；案例中 `workData` 使用作品 ID 字符串 |

响应示例：

```json
{}
```

注意事项：
- 缓存类型枚举以当前 admin 服务为准。调用前应确认清理范围，避免影响无关作品或环境。

### 14.12 设置短链 / 重定向
- Name: `admin/link/set`
- Method: `POST`
- Path: `/ih5/admin/link/set`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `url` | string | 是 | 短链路径或业务路径 |
| `redirect` | string | 是 | 重定向目标 URL |
| `title` | string | 否 | 标题说明 |

响应示例：

```json
{}
```

注意事项：
- 未登录或登录态无效时，历史服务返回 `code:203` / `请先登陆`。
- `redirect` 应传完整 URL，避免生成不可控跳转。

### 14.13 查询数据库描述
- Name: `admin/db/description`
- Method: `POST`
- Path: `/ih5/admin/db/description`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `dbId` | string | 是 | 数据库 ID |
| `type` | string | 否 | 查询类型；案例中可为空 |

响应字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `alias` | string | 数据库别名 |
| `columns` | array | 字段列表 |
| `config` | object | 数据库配置 |
| `createSql` | string | 建表 SQL |
| `dbId` | string | 数据库 ID |
| `dbName` | string | 数据库名 |
| `indexs` | array | 索引列表 |

`columns[]` 常见字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `name` | string | 字段名 |
| `kind` | string | 字段类型 |
| `index` | string | 索引类型，如 `primary` |
| `order` | number | 字段顺序 |
| `readOnly` | boolean | 是否只读 |
| `internal` | boolean | 是否内部字段 |
| `searchable` | boolean | 是否可检索 |
| `searchType` | string | 检索类型 |

`config` 常见字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `openConfig` | object | 开放配置 |
| `protectEnabled` | boolean | 是否开启保护 |
| `protectUpdateType` | string | 更新保护策略 |
| `protectDeleteType` | string | 删除保护策略 |

### 14.14 解密数据库消息
- Name: `admin/db/decodeMsg`
- Method: `POST`
- Path: `/ih5/admin/db/decodeMsg`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `nid` | number | 是 | 作品 ID |
| `text` | string | 是 | 待解密文本 |

响应字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `text` | string | 解密后的文本；内容可能是 JSON 字符串 |

注意事项：
- 解密结果可能包含业务数据或用户数据，agent 输出时应按最小必要原则摘录，不得整段泄露。

### 14.15 新增自定义数据库表
- Name: `admin/db/addCustomTable`
- Method: `POST`
- Path: `/ih5/admin/db/addCustomTable`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `dbName` | string | 是 | 数据库名 |
| `tableName` | string | 是 | 表名 |
| `instanceId` | number | 是 | 数据库实例 ID |

响应示例：

```json
{}
```

注意事项：
- 该接口会修改数据库结构或数据库注册信息，属于高风险操作。
- 未登录或登录态无效时，历史服务返回 `code:203` / `请先登陆`。

### 14.16 创建扩展 Lambda
- Name: `admin/ext/createLambda`
- Method: `POST`
- Path: `/ih5/admin/ext/createLambda`

权限：管理员登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `eid` | string | 是 | 企业或租户标识 |
| `lang` | string | 是 | Lambda 语言；案例中出现 `js` |
| `seq` | string | 否 | 序号或分片编号，按当前 ext 服务实现解释 |

响应示例：

```json
{}
```

注意事项：
- 创建 Lambda 属于线上资源类操作，agent 执行前必须确认资源归属与语言/序号参数。

### 14.17 数据恢复
- Name: `dblogger/recovery`
- Method: `POST`
- Path: `/ih5/dblogger/recovery`

权限：管理员登录态或等价内部运维登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `dbId` | string | 是 | 数据库 ID |
| `to` | number | 是 | 恢复到的时间点，毫秒时间戳 |

响应示例：

```json
{}
```

注意事项：
- 该接口不在 `/ih5/admin` namespace 下，但案例中作为管理员数据库恢复工具使用。
- 数据恢复可能覆盖或回滚业务数据，属于高风险操作；不得在未获明确授权时调用。

### 14.18 校验作品归属
- Name: `edt/work/testOwner`
- Method: `POST`
- Path: `/edt/work/testOwner`

权限：编辑器登录态或内部登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `nid` | number | 是 | 作品 ID |
| `gid` | number | 是 | 项目组 ID |

响应字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `status` | number | 平台状态码 |
| `detail` | object / null | 错误或附加信息 |
| `data` | boolean | 是否为归属成员 |

响应示例：

```json
{
  "status": 0,
  "detail": null,
  "data": false
}
```

### 14.19 转移作品
- Name: `editor/work/moveWork`
- Method: `POST`
- Path: `/ih5/editor/work/moveWork`

权限：编辑器登录态或内部运维登录态

请求参数：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `nid` | number | 是 | 作品 ID |
| `toUid` | number | 是 | 目标用户 ID |

响应字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `nid` | number | 作品 ID |
| `uid` | number | 新归属用户 ID |
| `title` | string | 作品标题 |
| `workId` | string | 作品 workId |
| `gid` | number | 项目组 ID |
| `version` | string | 当前版本 |
| `domain` | string | 发布域名 |
| `path` | string | 发布路径 |
| `previewPath` | string | 预览路径 |
| `extra` | string | 扩展信息 |

注意事项：
- 若调用方不是作品成员，历史服务返回 `403`，错误信息类似 `您不是案例(nid: ...)的成员`。
- 转移作品会改变归属关系；普通复制场景优先使用 §14.4 / §14.5。

### 14.20 不纳入 Platform API 的案例接口

`/Users/ivx/Downloads/小工具案例.json` 中还包含以下类型接口，本章不作为平台通用 API 收录：

| 类型 | 示例 | 不纳入原因 |
|------|------|------|
| 案例私有 custom API | `/api/10420147/setLimit`、`/api/10256966/refund`、`/api/10256966/wxRefund`、`/api/10256966/costByAdmin` | 绑定特定作品或业务后台，不是平台公共接口 |
| 旧外部辅助接口 | `http://file343434.aiwall.com/v3/setNidLimit`、`http://file343434.aiwall.com/v3/setNidReloc` | 非当前平台统一 namespace，疑似历史运维辅助服务 |
| 模板或业务内部接口 | `/ih5/app/template/innerPublish`、`/ih5/customApi/auth/service` | 与模板/自定义 API 运行时相关，不属于 admin 通用接口 |
| 项目内自建查询接口 | `https://v4rel.h5sys.cn/api/10382731/nodeDetail` | 由具体作品提供，不代表平台 admin API |

如后续确认这些接口属于现行平台公共能力，应另起版本补充到对应正式章节，而不是混入 admin 运维接口。
