1. 页面概览
1.1 是什么
「YAML 导入导出」页面是 LightFoundry 本体工作台中负责对象类型声明式导入导出的页面。Foundry 的对象类型定义(对象名、属性、链接、动作、回写配置等)可以序列化为一份 YAML 文档(对外稳定契约格式),通过本页面一键导出成文本、或从文本导入并批量重建对象类型。
页面源码位于 action/web/src/views/YAMLPage.vue,共约 170 行,是三个页面中结构最简单的一个:上半部是「选择对象类型 + 导出 YAML」按钮,中部是 YAML 文本编辑区(textarea),下部是导入结果统计卡片。
顶部注释块对功能的概括非常精炼:选择对象 → 导出定义(GET export?format=yaml)到 textarea;textarea 输入 YAML → 导入(POST /ontology/objects/import),显示 created/skipped/errors。值得注意的是导出接口返回的是原始 YAML 文本,而非 {code:0} 包装的 JSON;而导入接口的请求体是原始 YAML 文本、Content-Type 为 application/yaml。这两个"不走常规 JSON 包装"的契约是本页面最容易踩坑的地方(详见第 8 章排错)。
1.2 核心价值
| 能力 | 说明 | 对应操作 |
|---|---|---|
| 对象定义导出 | 把单个对象类型的完整定义导出为 YAML 文档(含属性/链接/动作/回写配置) | 「导出 YAML」 |
| 声明式导入 | 把 YAML 文档批量导入为对象类型,支持多对象一次性声明 | 「导入 YAML」 |
| 导入结果统计 | 导入后展示 total(总数)/ created(已创建)/ skipped(跳过)/ errors(错误数)四项指标 | 「导入结果」卡片 |
| 错误明细 | 每个失败对象单独一行展示「对象 + 错误」,不整体回滚、不阻塞成功对象 | 导入结果错误表格 |
| 可编辑文本 | 导入前可自由编辑 YAML,支持复制/粘贴/手工编写 | YAML 编辑器 textarea |
| 版本一致性 | 导入完成后自动重新拉取对象列表,对象下拉及时反映新对象 | 导入成功后 fetchObjects() |
1.3 一句话总结
2. 访问入口
2.1 路由与菜单
| 项目 | 值 |
|---|---|
| 路由 path | /foundry/yaml |
| 路由 name | FoundryYAML |
| 侧边栏入口 | FoundryLayout 侧边栏「YAML 导入导出」 |
| 父路由 | /foundry(组件 FoundryLayout) |
| 前端源码 | action/web/src/views/YAMLPage.vue |
| 路由注册文件 | action/web/src/router/index.js(约 215-219 行) |
访问方式:登录后从 Foundry 左侧菜单点击「YAML 导入导出」,或直接访问 /foundry/yaml。
2.2 认证与权限
- 路由
meta.requiresAuth: true;API 认证使用aip_token(localStorage),由client.js请求拦截器自动附加Authorization: Bearer <aip_token>。 - 页面不区分角色,但导出与导入都属于本体定义级读写:导出只读无副作用;导入是创建型写操作,会把 YAML 中声明的对象类型实际写入本体库。在多用户环境中建议先导出到文本备份、人工 review 后再导入。
- 401 时客户端拦截器清理
aip_token/aip_username并跳转/login。
2.3 端口与 API 前缀
- Foundry 后端端口:18081;Vite 开发服务器把
/api前缀代理到该后端。 - API 基础前缀:
/api/v1(client.js的baseURL)。 - 本页请求落在
/api/v1/ontology/...,属于 Foundry 本体域。
3. 界面布局
本页为三张纵向卡片流:
┌─────────────────────────────────────────────────────┐
│ YAML 导入导出 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ [alert 提示条(出现时展示,右上角"关闭")] │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 选择对象类型 [下拉框] [导出 YAML] │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 本体定义(YAML) │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ textarea(等宽字体,min-height 320px) │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ │ [导入 YAML] [清空] │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─ 导入结果(importResult 存在时展示)──────────────┐ │
│ │ 总数 N 已创建 N 跳过 N 错误 N │ │
│ │ 对象 | 错误(错误表格,可选) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
| 板块 | 职责 |
|---|---|
| 页面标题区 | 固定显示「YAML 导入导出」 |
| Alert 提示条 | 展示「导出成功」「导入完成」「导出失败」「导入失败」等异步操作结果 |
| 对象类型选择卡片 | 下拉选择要导出的对象类型;「导出 YAML」按钮在未选对象或忙碌时置灰 |
| 本体定义(YAML)卡片 | 标题 + 等宽字体 YAML 编辑器 + 「导入 YAML」「清空」两个按钮 |
| 导入结果卡片 | 仅导入成功后出现:四格统计 + 可选错误明细表格 |
4. 交互元素详解
4.1 选择对象类型下拉框
| 项目 | 说明 |
|---|---|
| 位置 | 第一张卡片内,与「导出 YAML」按钮同行 |
| 含义 | 选择要导出定义的对象类型 |
| 必填/默认 | 必选;初始占位项「请选择」(option :value="null" disabled) |
| 选项文案 | {{ item.object_type.name }}(v{{ item.object_type.version }})(比版本历史页少状态字段) |
| 操作效果 | 绑定 selectedId;注意本页下拉没有 @change 处理器,选中只影响「导出 YAML」按钮的可用性 |
| 触发的后端调用 | 无(选中等导出才发请求);下拉数据来自挂载时 GET /ontology/objects |
4.2 导出 YAML 按钮
| 项目 | 说明 |
|---|---|
| 位置 | 选择卡片内右侧 |
| 含义 | 把选中对象类型的定义导出为 YAML 文本并填入编辑器 |
| 可用条件 | :disabled="!selectedId || busy"(未选对象或忙碌时禁用) |
| 操作效果 | 请求 GET /ontology/objects/:id/export?format=yaml,把返回的原始 YAML 字符串写入 yamlText;成功后 alert「导出成功」 |
| 触发的后端调用 | GET /ontology/objects/:id/export(query format=yaml) |
| 细节 | 前端先判断 typeof res.data === 'string',是字符串直接使用,否则 JSON.stringify(res.data, null, 2) 兜底(防御非文本响应) |
4.3 本体定义(YAML)编辑器
| 项目 | 说明 |
|---|---|
| 位置 | 第二张卡片内 |
| 含义 | YAML 文本编辑区,可编辑、可粘贴、可手工编写 |
| 属性 | v-model="yamlText";spellcheck="false";placeholder="在此输入 YAML,或点击上方导出获取..." |
| 样式 | 等宽字体(Fira Code / Cascadia Code / Consolas),min-height: 320px,white-space: pre |
| 输入校验 | 无前端语法校验;空白文本(!yamlText.trim())会使「导入 YAML」置灰 |
4.4 导入 YAML 按钮
| 项目 | 说明 |
|---|---|
| 位置 | 编辑器下方左侧 |
| 含义 | 把编辑器的 YAML 文本导入为对象类型 |
| 可用条件 | :disabled="busy || !yamlText.trim()" |
| 操作效果 | 先清空上一次 importResult,POST /ontology/objects/import(body 为原始 YAML 文本,Content-Type: application/yaml);成功后把响应的 data.data(导入结果)写入 importResult、alert「导入完成」,并自动 fetchObjects() 刷新对象下拉 |
| 触发的后端调用 | POST /ontology/objects/import(Content-Type: application/yaml,body 限 1MB) |
4.5 清空按钮
| 项目 | 说明 |
|---|---|
| 位置 | 编辑器下方右侧 |
| 含义 | 清空编辑器文本 |
| 操作效果 | yamlText = ''(纯前端,无后端调用) |
| 注意 | 清空后「导入 YAML」因文本为空而置灰 |
4.6 导入结果卡片
| 项目 | 说明 |
|---|---|
| 显示条件 | v-if="importResult"(首次导入成功后出现) |
| 统计项 | total 总数 / created 已创建 / skipped 跳过 / (importResult.errors || []).length 错误数 |
| 错误表格 | importResult.errors 非空时渲染,表头「对象 / 错误」,行内对象列显示 e.object、错误列显示 e.error(红色) |
| 语义 | 每个统计项均为后端 ImportResult 的字段:total 文档声明的对象总数、created 成功创建数、skipped 跳过数(含校验失败与重复对象)、errors 明细数组 |
5. 后端关联
5.1 API 客户端
| 项目 | 值 |
|---|---|
| 客户端文件 | action/web/src/api/client.js |
| baseURL | /api/v1(Vite 代理到 Foundry 后端 18081) |
| 超时 | 30000ms |
| 请求拦截器 | localStorage aip_token → Authorization: Bearer <aip_token> |
| 响应拦截器 | HTTP 401 → 清 token 并跳 /login |
| 导出 | export default apiClient |
5.2 端点表
| 方法 | 路径 | 请求体 | 说明 |
|---|---|---|---|
| GET | /ontology/objects | — | 对象类型列表(下拉数据源) |
| GET | /ontology/objects/:id/export | — | 导出对象定义;query format 支持 yaml(默认)/json/owl/ttl/shacl |
| POST | /ontology/objects/import | 原始 YAML 文本(Content-Type application/yaml,限 1MB) | 批量导入对象类型,返回 ImportResult |
format=yaml 时后端用 c.Data(http.StatusOK, "application/yaml; charset=utf-8", data) 直接返回 YAML 字节(非 {code:0} 包装);format=json 时返回 okData(detail) 标准包装;owl/ttl/shacl 返回 Turtle 文本。5.3 响应结构
导入成功响应(POST /ontology/objects/import):
{
"code": 0,
"data": {
"total": 3,
"created": 2,
"skipped": 1,
"errors": [
{ "object": "order", "error": "链接 \"xx\" 目标对象 \"yy\" 未创建" }
]
}
}
导出成功响应(format=yaml)为原始 YAML 文本,形如:
apiVersion: zy-foundry/ontology/v1
objectTypes:
- name: customer
displayName: 客户
description: 客户档案
category: sales
dataSourceId: 1
baseTable: customers
pkColumn: customer_id
properties:
- name: customer_id
displayName: 客户ID
dataType: number
mappedColumn: customer_id
isPrimaryKey: true
ordinal: 1
links: []
actions:
- name: update_customer_credit
objectType: customer
requiresApproval: false
5.4 关联模块表
| 后端包/文件 | 职责 |
|---|---|
foundry/server/ontology_handlers.go | handleExportObject(format 分支)、handleImportObjects(读 body → ImportObjects)、handleListObjectTypes |
foundry/ontology/yaml.go | ExportObject(对象→YAML 字节)、ImportObjects(两遍扫描导入)、ImportResult/ImportError 结构、YAML 文档结构(OntologyDoc/ObjectYAML/PropertyYAML/LinkYAML/ActionYAML) |
foundry/ontology/service_impl.go | GetObjectType(导出数据源)、CreateObjectType(导入落库) |
foundry/server/server.go | 路由注册与 maxBodyBytes(1 << 20,即 1MB) |
5.5 关键机制
YAML 文档契约
根结构 apiVersion(固定 zy-foundry/ontology/v1)+ objectTypes 数组。导入时若 apiVersion 不匹配返回"不支持的 apiVersion"错误。对象间引用一律用 name(不直接带内部自增 id),导出时链接的 source/target、动作的 objectType 都翻译为对象 name,保证文档可移植。
两遍扫描导入
第一遍校验所有对象定义(name 必填、属性/链接/动作合法性、共享属性是否存在等),第二遍逐个创建;单个对象失败不整体回滚,仅把该对象计入 skipped 并把错误追加到 result.Errors。重复对象名同样按 skipped 处理。
导入限流
handleImportObjects 用 http.MaxBytesReader 限制请求体为 maxBodyBytes(1MB),超限直接拒绝,防止超大 YAML 打满内存。
属性/动作字段映射
属性 YAML 中 sharedProperty 引用共享属性定义(ontology_shared_properties)的 api_name,导入时按 api_name 解析为 shared_property_id 并从共享定义复制默认值;动作 YAML 中 paramSchema、validationRules、writeBackConfig 均为 JSON 对象,requiresApproval/idempotencyKeyField 为普通字段。
6. 核心流程详解
6.1 主流程:导出对象定义
- 页面挂载执行
fetchObjects(),请求GET /ontology/objects填充「选择对象类型」下拉。 - 选择一个对象(
selectedId非空),「导出 YAML」按钮由禁用变为可用。 - 点击「导出 YAML」→
handleExport():busy=true,请求GET /ontology/objects/:id/export?format=yaml;- 成功:
yamlText填入返回的 YAML 文本,alert「导出成功」,busy=false; - 失败:alert「导出失败:<错误信息>」,
busy=false。
6.2 主流程:导入 YAML
- 在编辑器粘贴或编写 YAML 文本(也可先导出再修改)。
- 点击「导入 YAML」→
handleImport():busy=true,importResult=null(清掉上次结果);POST /ontology/objects/import,body 为yamlText原始文本,headers: {'Content-Type': 'application/yaml'};- 成功:
importResult = data.data,alert「导入完成」,随后fetchObjects()刷新对象下拉; - 失败:alert「导入失败:<错误信息>」。
- 下方「导入结果」卡片展示统计;有错误时渲染错误明细表格。
6.3 分支:部分失败语义
当 YAML 声明多个对象时,后端两遍扫描保证:created + skipped = total,skipped 中每个对象对应一条 errors 条目(对象级错误,不整体回滚)。已创建的对象照常生效,用户可修正错误对象后再次导入同一份文档(已存在的对象按 skipped 处理,不会重复创建——这也是 created/skipped 拆分的意义)。
6.4 状态流转
| 场景 | 用户动作 | 结果 |
|---|---|---|
| 导出成功 | 点「导出 YAML」 | 编辑器出现 YAML 文本,alert「导出成功」 |
| 导入成功 | 点「导入 YAML」 | 结果卡片出现,alert「导入完成」,下拉刷新 |
| 部分失败 | 同上 | 统计中 skipped/errors 非零,错误表格列出明细 |
| 全量失败 | 同上 | created=0,errors 列出全部失败对象,alert 仍显示「导入完成」 |
| 请求失败 | 同上 | alert「导入失败:<错误信息>」,结果卡片不出现 |
7. 权限与安全
- 认证:
aip_tokenBearer 认证;/ontology/...全部挂载在protected路由组。 - 数据级安全:导出仅返回本体定义(不含业务数据),无行级敏感数据外泄风险;但对象定义中可能含
isPII标记、synonyms等元信息,导出文档对外分发时需注意脱敏与权限控制。 - 写操作防护:导入是创建型操作,前端以「导入 YAML」按钮置灰(文本为空时禁用)+
busy忙碌锁防护重复提交;后端以 1MB body 上限、两遍扫描校验、对象级失败隔离兜底,部分失败不影响已创建对象。 - 审计:导入的每个对象通过
CreateObjectType落库,版本快照机制自动生成版本记录,可回溯谁在何时创建了什么对象。
8. 常见问题与排错
问题 1:导入报 400/解析错误,提示"不支持的 apiVersion"
现象:粘贴的 YAML 导入失败,alert 显示"导入失败:...不支持的 apiVersion ..."。
原因:YAML 文档根结构缺失或 apiVersion 不是 zy-foundry/ontology/v1。
排查步骤:
- 检查文档第一行是否包含
apiVersion: zy-foundry/ontology/v1; - 确认根层级只有一个
objectTypes数组(对象列表); - 从「导出 YAML」得到一份正确文档作对照,逐字段比对;
- 用 YAML 解析器(如在线工具)校验文档语法。
问题 2:导入后统计显示跳过(skipped)但无错误表格
现象:结果卡片中"跳过"非零,但没有错误明细。
原因:跳过的对象可能因重复对象名被跳过(后端重复名按 skipped 处理且错误明细可能并入校验错误),或对象的 name 为空(错误条目的 object 字段为空字符串)。
排查步骤:
- 检查 YAML 中是否有重复的对象 name;
- 检查每个对象的
name是否非空("对象 name 必填"错误); - 修正后重新导入同一文档(已创建对象会按 skipped 跳过,不会重复)。
问题 3:导入返回 413/请求失败,body 过大
现象:alert 显示"导入失败:...",Network 面板显示请求被拒(400/413)。
原因:YAML 文档超过后端限制 maxBodyBytes(1MB),或 Content-Type 未被识别。
排查步骤:
- 检查 Network 面板中该请求的请求体大小;
- 拆分为多个对象分批导入;
- 确认请求头
Content-Type: application/yaml已设置(前端源码固定设置); - 若走 curl 手测,注意 body 用文件方式传避免 shell 转义破坏 YAML。
问题 4:导出后编辑器是 JSON 而非 YAML
现象:点击「导出 YAML」后,textarea 里是 {...} JSON 文本而非 YAML。
原因:后端返回了非字符串响应(如 format 参数未生效或后端版本差异),前端兜底分支 JSON.stringify(res.data, null, 2) 触发。
排查步骤:
- 确认请求 URL 带
?format=yaml(DevTools Network 检查); - 直接 curl
GET /api/v1/ontology/objects/:id/export?format=yaml观察 Content-Type 与响应体; - 若后端返回标准
{code:0}包装,说明format未识别,检查后端版本。
9. 已知缺陷与边界
| 缺陷/边界 | 说明 |
|---|---|
| 无前端 YAML 语法校验 | 编辑器不做语法高亮/校验,语法错误依赖后端返回 |
| 无格式切换 | 页面固定导出 format=yaml;json/owl/ttl/shacl 需通过 API 或「本体导出」相关页面 |
| 导入不可增量更新 | 已存在同名对象按 skipped 跳过,不支持同名对象的定义覆盖/升级(升级需走版本/草稿流程) |
| 无下载按钮 | 导出结果仅进 textarea,文件落盘需用户手动复制保存 |
| 1MB body 上限 | 超大对象定义或批量导入受限,需分批 |
| 下拉无 change 处理器 | 选中对象仅解锁「导出 YAML」,不会自动触发任何请求(避免误解) |