1. 页面概览

1.1 是什么

「版本历史」页面是 LightFoundry 本体工作台(Foundry)中负责对象类型版本管理的页面。Foundry 的本体对象(如 customer、order、product)并不是"改一次就立刻生效"的扁平定义,而是遵循一条 draft(草稿)→ review(评审中)→ merged(已合入) 的版本状态机演进。每次正式修改都会生成一个新的版本记录,页面以垂直时间线的形式展示某个对象类型的全部历史版本,并在每个版本旁按状态渲染对应的操作按钮。

本页面的源码位于 action/web/src/views/VersionHistoryPage.vue,共约 380 行,属于典型的"单页 + 列表 + 弹窗"结构:顶部一个「选择对象类型」下拉框,选中后下方渲染该对象的版本时间线;时间线每条目按版本状态(draft / review / merged)显示不同的可用操作;所有需要展示的后端分析结果(合并前影响分析、合并后影响、回滚影响)统一在「影响分析弹窗」中呈现。

页面顶部注释块概括了三件事:选择对象 → 版本时间线;对 draft 执行 review(提交评审)、对 review 版本合入前调用 /impact 接口做影响分析;对历史 merged 版本执行 rollback(回滚)。这决定了本页面是 Foundry 版本治理的"操作台":它不负责创建/编辑对象定义(那是「本体工作台」与草稿编辑入口的事),而是负责推动版本状态流转、审批把关、合入与回滚

1.2 核心价值

能力说明对应操作
版本时间线一眼看清对象类型从诞生到现在的全部版本,含版本号、状态、基线版本、创建人与时间选择对象后自动加载
评审流转草稿提交评审,推动 draft → review「提交评审」
审批闭环review 且配置了审批策略时,可通过/驳回,驳回自动退回草稿(P0-5)「通过」/「驳回」
合入前影响分析合入 review 版本前先调 /impact 展示对象引用清单,降低误合入风险点击「合入」自动触发
历史回滚从任意历史 merged 版本重建主表并生成新版本,实现"后悔药"「回滚到此版本」
审计可追溯每个版本记录 created_bycreated_at,合入/回滚返回影响报告时间线元信息

1.3 一句话总结

「版本历史」是 Foundry 对象类型定义的安全变更闸门:草稿先行、评审把关、合入前看影响、历史可回滚,所有流转在版本时间线上一目了然。

2. 访问入口

2.1 路由与菜单

项目
路由 path/foundry/versions
路由 nameFoundryVersions
侧边栏入口FoundryLayout 侧边栏「版本历史」(action/web/src/views/FoundryLayout.vue 菜单项)
父路由/foundry(组件 FoundryLayoutmeta.title: 'Foundry'
前端源码action/web/src/views/VersionHistoryPage.vue
路由注册文件action/web/src/router/index.js(约 209-213 行)

访问方式:登录后在 Foundry 左侧菜单点击「版本历史」,或直接访问 /foundry/versions。路由注册于 Foundry 布局的子路由下,因此必须先进入 /foundry 布局,URL 才能被正确匹配;直接访问完整路径同样有效。

2.2 认证与权限

2.3 端口与 API 前缀

3. 界面布局

本页布局为纵向卡片流,文字框图如下:

┌─────────────────────────────────────────────────────┐
│  版本历史                                             │
│  ┌─────────────────────────────────────────────────┐ │
│  │ [alert 提示条(出现时展示,右上角"关闭")]          │ │
│  └─────────────────────────────────────────────────┘ │
│  ┌─────────────────────────────────────────────────┐ │
│  │ 选择对象类型  [下拉框:对象名(v版本 · 状态)]      │ │
│  └─────────────────────────────────────────────────┘ │
│  ┌─ 状态卡片(三选一)───────────────────────────────┐ │
│  │ A. 加载中...(loading=true)                     │ │
│  │ B. 该对象暂无版本记录。(versions 为空)           │ │
│  │ C. 版本时间线(timeline)                        │ │
│  │    ● v3 [merged] base v2 · user · 2026-08-30    │ │
│  │      [回滚到此版本]                              │ │
│  │    ● v2 [review]  base v1 · user · 2026-08-30   │ │
│  │      [合入] [通过] [驳回]                         │ │
│  │    ● v1 [draft]   base - · system · 2026-08-30  │ │
│  │      [提交评审]                                  │ │
│  └─────────────────────────────────────────────────┘ │
│  ┌─ 影响分析弹窗(impactModal.visible 时全屏蒙层)───┐ │
│  │  标题 + 关闭  │  <pre> 影响分析 JSON </pre>      │ │
│  └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
板块职责
页面标题区固定显示「版本历史」,说明当前所在页面
Alert 提示条展示所有异步操作的成败信息,按类型着色(alert-info / alert-success / alert-error),右上角「关闭」按钮可手动清除
选择对象类型卡片唯一的入口表单:下拉列出全部对象类型,选项文案为「对象名(v版本号 · 状态)」,选中即触发版本加载
状态卡片根据 loadingversions 长度三选一渲染:加载中占位 / 空态提示 / 版本时间线
版本时间线每个版本一条时间线条目:左侧彩色圆点(dot-draft/dot-review/dot-merged),右侧版本信息与操作按钮区;渲染层分页(每页 10/20/50 条 + 上一页/下一页)
渲染层分页条时间线下方:每页条数下拉(10/20/50 条)+「上一页 / 第 P / 总 T 页 / 下一页」;顶部常驻提示说明「版本列表接口无服务端分页、一次全量拉取」
影响分析弹窗全屏遮罩 + 居中卡片,pre 区块按 JSON 格式化展示后端返回的影响分析报告,右上角「关闭」或点击遮罩空白处关闭

4. 交互元素详解

4.1 选择对象类型下拉框

项目说明
位置页面顶部第一个卡片内
含义选择要查看版本历史的对象类型
必填/默认必选;初始为占位项「请选择」(option :value="null" disabled
选项文案{{ item.object_type.name }}(v{{ item.object_type.version }} · {{ item.object_type.status }})
操作效果@change="handleSelectObject":立即加载该对象的版本列表(loading=true 期间显示"加载中...")
触发的后端调用GET /ontology/objects/:id/versions

下拉选项数据来自 GET /ontology/objects,取响应的 data.data 数组,每个元素含 object_type 对象(含 idnameversionstatus)。

4.2 提交评审(draft 专属)

项目说明
可见条件版本状态为 draftv-if="v.status === 'draft'"
含义把草稿版本提交为评审中状态
操作效果成功后 alert 显示「版本 v{n} 已提交评审」(alert-success),并刷新版本列表
触发的后端调用POST /ontology/drafts/:vid/reviewvid 为该版本的内部 id)
状态变化draftreview;若草稿内容包含 requires_approval 动作,后端会在该版本上记录 approval_policy,前端据此显示「通过」/「驳回」按钮
附加行为操作期间 busy=true,所有按钮置灰防止连点

4.3 合入(review 专属)

项目说明
可见条件版本状态为 review
含义把评审中版本合入为主表最新版本
操作效果点击后先调用 /impact 做合并前影响分析并弹出,然后调用 merge 接口,合入成功后再次弹出「合入结果(影响分析)」并刷新列表
触发的后端调用POST /ontology/objects/:id/impact(预检)→ POST /ontology/drafts/:vid/merge
状态变化reviewmerged;若配置了审批策略且 approve 票数不足,merge 会被后端拒绝(错误码 APPROVAL_REQUIRED,HTTP 409)
注意「合入」与「通过」的区别:通过只写一条 approve 审批记录、状态保持 review只有「合入」才真正把版本变为 merged 并重建主表

4.4 通过 / 驳回(review 且带审批策略时显示)

项目说明
可见条件v.status === 'review' && v.approval_policy(审批闭环,P0-5)
含义对评审中版本投通过/驳回票,用于满足审批策略门禁
前置交互两个按钮都会先用 window.prompt('审批意见(可选)', '') 收集意见(可为空),再 confirm 二次确认(「通过」提示"确定通过版本 v{n} 的审批吗?";「驳回」提示"确定驳回版本 v{n} 吗?将退回草稿状态。")
操作效果通过:alert「版本 v{n} 已通过审批」;驳回:alert「版本 v{n} 已驳回,退回草稿」;随后刷新版本列表
触发的后端调用POST /ontology/objects/:id/versions/:vid/approve(body {comment})/POST /ontology/objects/:id/versions/:vid/reject(body {comment}
状态变化通过:状态保持 review(直至合入);驳回:状态退回 draft
审批后端有自批拦截:若 approval_policy.self_approval=false 且审批人(当前登录用户)正是版本创建人,返回错误码 APPROVAL_DENIED(HTTP 409),提示"self-approval not allowed by approval policy"。

4.5 回滚到此版本(merged 专属)

项目说明
可见条件版本状态为 merged
含义以该历史版本为基线重建主表,并生成一个新版本
前置交互confirm('确定回滚到 v{n} 吗?将重建主表并生成新版本。') 二次确认
操作效果成功后 alert「回滚成功」,并弹出「回滚结果(影响分析)」报告
触发的后端调用POST /ontology/objects/:id/rollback(body {target_version: v.version}
约束后端要求 target_version > 0,且目标版本必须是 merged 状态,否则返回 INVALID_VERSION_STATE(HTTP 409)

4.6 影响分析弹窗与 Alert 提示条

元素说明
影响分析弹窗impactModal = {visible, title, content};合入预检标题为「合并前影响分析(对象引用清单)」,合入结果标题为「合入结果(影响分析)」,回滚标题为「回滚结果(影响分析)」;内容为 JSON.stringify(data, null, 2) 格式化的报告;「关闭」按钮或点击遮罩(@click.self)均可关闭
Alert 提示条alert = {message, type}showAlert(msg, type) 统一封装;成功用 alert-success、失败用 alert-error、信息用 alert-info;右上角「关闭」把 message 置空

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 时清空 aip_token/aip_username,若非登录页则 window.location.href = '/login'
导出export default apiClient(本页 import apiClient from '../api/client.js'

5.2 端点表

方法路径请求体说明
GET/ontology/objects列对象类型(支持 ?status= 过滤)
GET/ontology/objects/:id/versions版本历史列表(版本号降序)
POST/ontology/drafts/:vid/reviewdraft → review 状态流转
POST/ontology/objects/:id/impact对象引用影响分析(合并前预检)
POST/ontology/drafts/:vid/mergereview → merged,返回影响分析报告
POST/ontology/objects/:id/versions/:vid/approve{approver_name?, comment?}审批通过,写 approve 审批记录,状态保持 review
POST/ontology/objects/:id/versions/:vid/reject{approver_name?, comment?}审批驳回,写 reject 记录,状态退回 draft
POST/ontology/objects/:id/rollback{target_version: int}从历史 merged 版本重建主表

注::id 支持数字内部 id,也支持 api_name / rid(object-type:<name> 前缀会被剥离后按 name 查询),见服务端 resolveObjectID

5.3 响应结构

所有成功响应统一封装为 {code: 0, data: ...}okData);失败响应为 {code, error, ...}

版本历史列表(GET /ontology/objects/:id/versions)的 data 为数组,元素为:

{
  "id": 7,
  "version": 3,
  "status": "merged",
  "base_version": 2,
  "created_by": "admin",
  "created_at": "2026-08-30T10:00:00Z",
  "approval_policy": { "min_approvals": 1, "self_approval": false }
}

影响分析报告(POST /ontology/objects/:id/impact、merge、rollback 的 data.impact)结构:

{
  "object_type_id": 2,
  "object_type_name": "order",
  "impacts": [
    { "type": "link", "id": "5", "name": "customer_orders", "detail": "link \"customer_orders\" (1 → 2)source 引用" },
    { "type": "action", "id": "9", "name": "update_order_status", "detail": "action \"update_order_status\" belongs to object type" },
    { "type": "edit", "id": "2", "name": "order", "detail": "1 条进行中的编辑记录(ontology_edits)" }
  ]
}

影响项 type 取值:link / action / pipeline / lineage / metric / report / editdetail 中会出现"source 引用"、"target 引用"、"表未就绪"等后缀说明。

5.4 关联模块表

后端包/文件职责
foundry/server/ontology_handlers.gohandleListVersionshandleSubmitReviewhandleApproveVersionhandleRejectVersionhandleMergeDrafthandleRollbackhandleAnalyzeImpact
foundry/ontology/version.go版本状态机核心(CreateDraft/SubmitReview/Approve/Reject/Merge/Rollback/ListVersions)、ApprovalPolicy、错误码 INVALID_VERSION_STATE/APPROVAL_REQUIRED/APPROVAL_DENIED
foundry/ontology/impact.goAnalyzeImpact 影响分析(扫描链接/动作/管道/血缘/指标/编辑六类引用)
foundry/ontology/repository.go版本快照、合并、回滚到版本的持久层(RollbackToVersion
foundry/server/server.go路由注册(/ontology/... 全部在 protected 组)

5.5 关键机制

版本状态机(draft → review → merged)

转移触发接口条件与语义
draft → reviewPOST /ontology/drafts/:vid/review仅 draft 可转移;记录 approval_policy(快照中有 requires_approval 动作时需过审)
review → mergedPOST /ontology/drafts/:vid/merge仅 review 可合入;需先过审批门禁;冲突判定为"聚合真冲突"
review → draftPOST .../versions/:vid/reject仅 review 可驳回,写 reject 审批记录
review → reviewPOST .../versions/:vid/approveapprove 审批记录,状态保持 review
merged →(新 merged)POST .../rollback从历史 merged 快照重建主表,生成新版本

聚合真冲突判定

merge 时聚合 (base_version, latest] 区间所有 merged 版本的 change_diff,与草稿 diff 按 (kind, api_name) 求交,op ∈ {modify, delete} 即冲突;落后但无真冲突时允许"非冲突自动合并"(不再"落后即拒绝")。

审批门禁

approve 数 ≥ min_approvals 才允许 merge(不足返回 APPROVAL_REQUIRED);self_approval=false 时禁止版本创建人给自己批准(返回 APPROVAL_DENIED)。

回滚语义

target_version 必须为正整数且目标版本为 merged;回滚重建主表并生成一个新版本,返回影响分析报告,便于回滚后评估影响面。

6. 核心流程详解

6.1 主流程:查看版本时间线

  1. 页面挂载时执行 fetchObjects(),请求 GET /ontology/objects,把返回的对象类型列表填充进下拉框。
  2. 用户在「选择对象类型」下拉中选择一个对象,触发 handleSelectObject()
    • loading=true,渲染"加载中..."卡片;
    • 请求 GET /ontology/objects/:id/versions
    • 成功把 data.data 写入 versionsloading=false,渲染时间线;失败 alert「加载版本失败:<错误信息>」。切换对象时页码复位为第 1 页。
    • 版本数组为一次全量返回(后端无 limit/offset、无 total),时间线之上按 pageSize(10/20/50,默认 20)做渲染层分页pagedVersions = versions.slice((page-1)*pageSize, page*pageSize),合入/回滚刷新后页码自动钳制回合法范围。
  3. 若该对象没有任何版本记录,显示"该对象暂无版本记录。"。

时间线每条目渲染:版本号 v{n}(加粗)、状态徽标 status-badge(按状态着色)、元信息 base v{base_version || '-'} · {created_by || 'system'} · {formatTime(created_at)}formatTime 把 ISO 时间串的 T 替换为空格并截取前 19 位。

6.2 分支流程:评审与合入

6.3 分支流程:回滚

  1. merged 版本点击「回滚到此版本」→ confirm「确定回滚到 v{n} 吗?将重建主表并生成新版本。」
  2. POST /ontology/objects/:id/rollback,body {target_version: v.version}
  3. 成功 alert「回滚成功」并弹出「回滚结果(影响分析)」;随后刷新版本列表,可在时间线顶部看到新生成的 merged 版本。

6.4 状态机终态语义

终态含义可执行操作
draft草稿,可继续编辑(本页不可编辑)提交评审
review评审中,等待审批/合入合入;带审批策略时通过/驳回
merged已合入主表,成为主表权威定义回滚到此版本
所有异步写操作期间 busy=true 全局置灰按钮,防止并发重复提交;操作完成后统一 refreshVersions() 保证时间线与后端一致。

7. 权限与安全

8. 常见问题与排错

问题 1:下拉框空白,加载对象列表失败

现象:进入页面后「选择对象类型」下拉没有选项,顶部 alert 显示"加载对象列表失败:..."。

原因GET /ontology/objects 请求失败,常见于后端未启动、token 失效或网络代理未配置。

排查步骤

  1. 确认 Foundry 后端(18081)已启动:curl http://localhost:18081/api/v1/ontology/objects 观察响应;
  2. 打开浏览器 DevTools Network,检查该请求的状态码与响应体 error 字段;
  3. 若返回 401,重新登录获取 aip_token(localStorage)后刷新页面;
  4. 确认 Vite 代理配置中 /api 前缀正确转发到 18081。

问题 2:点击「合入」报 409,提示审批票数不足

现象:版本处于 review 状态且带 approval_policy,点击「合入」后 alert 显示"合入失败:approvals ... APPROVAL_REQUIRED"。

原因:版本配置了审批策略(如 min_approvals: 1),但当前 approve 审批记录数不足;或该版本由本人提交且 self_approval=false,本人"通过"被自批拦截(APPROVAL_DENIED)。

排查步骤

  1. 先点击「通过」补齐审批记录(非创建人账号);
  2. 若「通过」时报 APPROVAL_DENIED,换一个非创建人账号登录操作;
  3. 再回到本页点击「合入」。

问题 3:点击「回滚到此版本」后报错或时间线没变化

现象:confirm 后 alert 显示"回滚失败:...",或回滚后版本列表没有出现新版本。

原因target_version 非法(≤0)、目标版本非 merged、或后端重建主表失败。

排查步骤

  1. 确认回滚的版本状态徽标是 merged(绿色),draft/review 版本不可回滚;
  2. 检查 alert 中的具体错误码:INVALID_VERSION_STATE 表示状态非法,需核对目标版本;
  3. 查看后端日志确认主表重建 SQL 是否执行成功;
  4. 刷新页面重新拉取版本列表确认最新状态。

问题 4:影响分析弹窗无法关闭

现象:弹窗一直显示,点「关闭」无反应。

原因:本弹窗的关闭按钮与遮罩点击均依赖 impactModal.visible = false;若连续触发(如合入预检弹窗弹出后未关又点了别的按钮),状态可能被覆盖。

排查步骤

  1. 先关闭当前弹窗,观察是否还有第二层弹窗;
  2. 检查浏览器控制台是否有 Vue 渲染错误;
  3. 刷新页面即可复位(弹窗为纯前端状态,无后端副作用)。

9. 已知缺陷与边界

缺陷/边界说明
分页为渲染层(后端无服务端分页)GET /ontology/objects/:id/versionslimit/offset 参数(ontology_handlers.go:189-201ontology/repository.go:501-512 全量 Find),响应无 total。已补渲染层分页(10/20/50 条每页 + 上/下一页 + 常驻诚实提示),仍一次全量拉取;服务端真分页需后端补分页契约
审批人员体系未接入ApprovalPolicy.required_reviewers 为骨架字段,人员体系后续接入;当前仅按 min_approvals 计数
无草稿编辑入口本页只负责状态流转;创建/编辑草稿需通过其他入口(如对象编辑相关页面)
回滚不可再回滚回滚生成的是新版本,若想回到回滚前状态需再对更早版本回滚
状态机严格跨状态操作一律 409,无强制覆盖能力(有意识的安全设计)

注:影响分析弹窗互相覆盖问题已于 2026-09-06 修复(合入改为先弹影响分析、经用户确认后才真正执行 merge;合入结果以只读弹窗在确认后另屏展示,不再与确认弹窗互相覆盖)。