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-Typeapplication/yaml。这两个"不走常规 JSON 包装"的契约是本页面最容易踩坑的地方(详见第 8 章排错)。

1.2 核心价值

能力说明对应操作
对象定义导出把单个对象类型的完整定义导出为 YAML 文档(含属性/链接/动作/回写配置)「导出 YAML」
声明式导入把 YAML 文档批量导入为对象类型,支持多对象一次性声明「导入 YAML」
导入结果统计导入后展示 total(总数)/ created(已创建)/ skipped(跳过)/ errors(错误数)四项指标「导入结果」卡片
错误明细每个失败对象单独一行展示「对象 + 错误」,不整体回滚、不阻塞成功对象导入结果错误表格
可编辑文本导入前可自由编辑 YAML,支持复制/粘贴/手工编写YAML 编辑器 textarea
版本一致性导入完成后自动重新拉取对象列表,对象下拉及时反映新对象导入成功后 fetchObjects()

1.3 一句话总结

「YAML 导入导出」是 Foundry 对象类型的外部交换通道:把本体定义变成一份可读、可版本化、可跨环境迁移的 YAML 文档,导出即所见、导入即所建,部分失败也不拖累整体。

2. 访问入口

2.1 路由与菜单

项目
路由 path/foundry/yaml
路由 nameFoundryYAML
侧边栏入口FoundryLayout 侧边栏「YAML 导入导出」
父路由/foundry(组件 FoundryLayout
前端源码action/web/src/views/YAMLPage.vue
路由注册文件action/web/src/router/index.js(约 215-219 行)

访问方式:登录后从 Foundry 左侧菜单点击「YAML 导入导出」,或直接访问 /foundry/yaml

2.2 认证与权限

2.3 端口与 API 前缀

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: 320pxwhite-space: pre
输入校验无前端语法校验;空白文本!yamlText.trim())会使「导入 YAML」置灰

4.4 导入 YAML 按钮

项目说明
位置编辑器下方左侧
含义把编辑器的 YAML 文本导入为对象类型
可用条件:disabled="busy || !yamlText.trim()"
操作效果先清空上一次 importResultPOST /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_tokenAuthorization: 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.gohandleExportObject(format 分支)、handleImportObjects(读 body → ImportObjects)、handleListObjectTypes
foundry/ontology/yaml.goExportObject(对象→YAML 字节)、ImportObjects(两遍扫描导入)、ImportResult/ImportError 结构、YAML 文档结构(OntologyDoc/ObjectYAML/PropertyYAML/LinkYAML/ActionYAML
foundry/ontology/service_impl.goGetObjectType(导出数据源)、CreateObjectType(导入落库)
foundry/server/server.go路由注册与 maxBodyBytes1 << 20,即 1MB)

5.5 关键机制

YAML 文档契约

根结构 apiVersion(固定 zy-foundry/ontology/v1)+ objectTypes 数组。导入时若 apiVersion 不匹配返回"不支持的 apiVersion"错误。对象间引用一律用 name(不直接带内部自增 id),导出时链接的 source/target、动作的 objectType 都翻译为对象 name,保证文档可移植。

两遍扫描导入

第一遍校验所有对象定义(name 必填、属性/链接/动作合法性、共享属性是否存在等),第二遍逐个创建;单个对象失败不整体回滚,仅把该对象计入 skipped 并把错误追加到 result.Errors。重复对象名同样按 skipped 处理。

导入限流

handleImportObjectshttp.MaxBytesReader 限制请求体为 maxBodyBytes(1MB),超限直接拒绝,防止超大 YAML 打满内存。

属性/动作字段映射

属性 YAML 中 sharedProperty 引用共享属性定义(ontology_shared_properties)的 api_name,导入时按 api_name 解析为 shared_property_id 并从共享定义复制默认值;动作 YAML 中 paramSchemavalidationRuleswriteBackConfig 均为 JSON 对象,requiresApproval/idempotencyKeyField 为普通字段。

6. 核心流程详解

6.1 主流程:导出对象定义

  1. 页面挂载执行 fetchObjects(),请求 GET /ontology/objects 填充「选择对象类型」下拉。
  2. 选择一个对象(selectedId 非空),「导出 YAML」按钮由禁用变为可用。
  3. 点击「导出 YAML」→ handleExport()
    • busy=true,请求 GET /ontology/objects/:id/export?format=yaml
    • 成功:yamlText 填入返回的 YAML 文本,alert「导出成功」,busy=false
    • 失败:alert「导出失败:<错误信息>」,busy=false

6.2 主流程:导入 YAML

  1. 在编辑器粘贴或编写 YAML 文本(也可先导出再修改)。
  2. 点击「导入 YAML」→ handleImport()
    • busy=trueimportResult=null(清掉上次结果);
    • POST /ontology/objects/import,body 为 yamlText 原始文本,headers: {'Content-Type': 'application/yaml'}
    • 成功:importResult = data.data,alert「导入完成」,随后 fetchObjects() 刷新对象下拉;
    • 失败:alert「导入失败:<错误信息>」。
  3. 下方「导入结果」卡片展示统计;有错误时渲染错误明细表格。

6.3 分支:部分失败语义

当 YAML 声明多个对象时,后端两遍扫描保证:created + skipped = totalskipped 中每个对象对应一条 errors 条目(对象级错误,不整体回滚)。已创建的对象照常生效,用户可修正错误对象后再次导入同一份文档(已存在的对象按 skipped 处理,不会重复创建——这也是 created/skipped 拆分的意义)。

6.4 状态流转

场景用户动作结果
导出成功点「导出 YAML」编辑器出现 YAML 文本,alert「导出成功」
导入成功点「导入 YAML」结果卡片出现,alert「导入完成」,下拉刷新
部分失败同上统计中 skipped/errors 非零,错误表格列出明细
全量失败同上created=0errors 列出全部失败对象,alert 仍显示「导入完成」
请求失败同上alert「导入失败:<错误信息>」,结果卡片不出现

7. 权限与安全

8. 常见问题与排错

问题 1:导入报 400/解析错误,提示"不支持的 apiVersion"

现象:粘贴的 YAML 导入失败,alert 显示"导入失败:...不支持的 apiVersion ..."。

原因:YAML 文档根结构缺失或 apiVersion 不是 zy-foundry/ontology/v1

排查步骤

  1. 检查文档第一行是否包含 apiVersion: zy-foundry/ontology/v1
  2. 确认根层级只有一个 objectTypes 数组(对象列表);
  3. 从「导出 YAML」得到一份正确文档作对照,逐字段比对;
  4. 用 YAML 解析器(如在线工具)校验文档语法。

问题 2:导入后统计显示跳过(skipped)但无错误表格

现象:结果卡片中"跳过"非零,但没有错误明细。

原因:跳过的对象可能因重复对象名被跳过(后端重复名按 skipped 处理且错误明细可能并入校验错误),或对象的 name 为空(错误条目的 object 字段为空字符串)。

排查步骤

  1. 检查 YAML 中是否有重复的对象 name;
  2. 检查每个对象的 name 是否非空("对象 name 必填"错误);
  3. 修正后重新导入同一文档(已创建对象会按 skipped 跳过,不会重复)。

问题 3:导入返回 413/请求失败,body 过大

现象:alert 显示"导入失败:...",Network 面板显示请求被拒(400/413)。

原因:YAML 文档超过后端限制 maxBodyBytes(1MB),或 Content-Type 未被识别。

排查步骤

  1. 检查 Network 面板中该请求的请求体大小;
  2. 拆分为多个对象分批导入;
  3. 确认请求头 Content-Type: application/yaml 已设置(前端源码固定设置);
  4. 若走 curl 手测,注意 body 用文件方式传避免 shell 转义破坏 YAML。

问题 4:导出后编辑器是 JSON 而非 YAML

现象:点击「导出 YAML」后,textarea 里是 {...} JSON 文本而非 YAML。

原因:后端返回了非字符串响应(如 format 参数未生效或后端版本差异),前端兜底分支 JSON.stringify(res.data, null, 2) 触发。

排查步骤

  1. 确认请求 URL 带 ?format=yaml(DevTools Network 检查);
  2. 直接 curl GET /api/v1/ontology/objects/:id/export?format=yaml 观察 Content-Type 与响应体;
  3. 若后端返回标准 {code:0} 包装,说明 format 未识别,检查后端版本。

9. 已知缺陷与边界

缺陷/边界说明
无前端 YAML 语法校验编辑器不做语法高亮/校验,语法错误依赖后端返回
无格式切换页面固定导出 format=yaml;json/owl/ttl/shacl 需通过 API 或「本体导出」相关页面
导入不可增量更新已存在同名对象按 skipped 跳过,不支持同名对象的定义覆盖/升级(升级需走版本/草稿流程)
无下载按钮导出结果仅进 textarea,文件落盘需用户手动复制保存
1MB body 上限超大对象定义或批量导入受限,需分批
下拉无 change 处理器选中对象仅解锁「导出 YAML」,不会自动触发任何请求(避免误解)