1. 页面概览

1.1 是什么

指标管理(MetricsPage.vue,路由 /foundry/metrics)是 LightFoundry 的「指标语义层」管理界面,对标 Palantir Foundry 的 Metric 语义层能力。它把「业务指标」从物理 SQL 中解放出来:先定义实体(Entity),再在实体下声明维度(Dimension)度量(Measure),最后把「度量 + 维度 + 过滤条件 + 口径」组合成一条指标(Metric)。查询时前端只提交指标名与参数,由后端 OOL 翻译引擎生成物理 SQL,并对用户做 RLS/CLS 安全注入后到真实数据源执行。

页面采用四个 Tab 组织全链路:指标目录(Catalog 卡片浏览口径)、实体模型(实体 + 维度/度量维护)、指标定义(指标 CRUD + 状态/版本)、指标查询(维度分组 + 过滤 + limit 结果表格)。四个 Tab 对应四套后端 API,全部走同一个 client.js axios 实例(baseURL /api/v1,Bearer aip_token)。

这是一个「深水区」页面:指标查询不是简单表格查询,而是聚合 + 维度分组 + 时间范围过滤 + 编辑态叠加的组合翻译流程。理解后端 QueryMetric 的七步翻译(取定义 → 合并筛选 → OOL 翻译 → 分页 → 编辑态叠加判定 → 执行 → 组装口径)是排查查询异常的关键。

1.2 核心价值

维度说明
口径统一指标定义一次、多处查询复用,formula 口径字段在目录/查询结果中完整回显,杜绝各报表各算各的
三层语义建模Entity(grain 键)→ Dimension/Measure(维度/聚合值)→ Metric(业务计算),与 dbt MetricFlow / OSI 四层拆分对齐
查询即翻译前端只发指标名 + 参数,OOOL 翻译引擎负责生成参数化 SQL 并注入 RLS/CLS,前端不可绕过安全闸门
状态与版本指标状态 draft → active → deprecated 全生命周期;每次编辑更新版本自动 +1,目录随状态联动
多类型派生simple / ratio / derived / cumulative 四类指标分派,ratio/derived 用分子分母两个度量拼装口径

1.3 一句话总结

在 Foundry 里「把指标当作一等公民」:先建实体、再挂维度/度量,最后把口径存成指标,查询时后端 OOL 翻译 + 安全注入 + 编辑态叠加,前端负责建模与展示。

2. 访问入口

2.1 路由与菜单

2.2 认证与权限

2.3 端口与 API 前缀

3. 界面布局

页面从上到下为:页头 → 操作结果提示条 → Tab 切换条 → 当前 Tab 内容区。

┌──────────────────────────────────────────────────────────────┐
│ ① 页头:标题「指标管理」                                       │
│ ② 操作结果提示条(alert 成功/失败,可「关闭」)                 │
│ ③ Tab 切换条:[指标目录] [实体模型] [指标定义] [指标查询]       │
│ ④ 内容区(随 Tab 切换)                                        │
│    ├─ 指标目录:指标卡片网格(grid auto-fill minmax 340px)     │
│    ├─ 实体模型:左「实体列表」(300px) + 右「实体详情」双栏        │
│    │    ├─ 新建实体表单 / 实体详情(维度表 + 度量表 + 行内编辑)  │
│    ├─ 指标定义:状态筛选 + 新建指标 + 创建/编辑表单 + 指标表格    │
│    └─ 指标查询:查询表单(指标/limit/offset/时间范围/维度/过滤) │
│         + 查询结果卡片(指标元信息 + 结果表格)                  │
└──────────────────────────────────────────────────────────────┘

各板块职责:

4. 交互元素详解

4.1 全局元素

位置元素含义默认值/必填操作效果触发的后端调用
页顶操作结果提示条 + 「关闭」按钮反馈最近一次操作结果点击清空 alert.message
页顶Tab 条「指标目录/实体模型/指标定义/指标查询」切换四块功能区默认 指标目录切换 activeTab,内容区重渲染各 Tab 数据源在 onMounted 预加载

4.2 指标目录 Tab

位置元素含义默认值/必填操作效果触发的后端调用
目录卡片指标卡片单条指标的口径卡片只读展示
卡片头状态徽标draft/active/deprecated 状态样式随状态
目录卡片字段:display_name || name(标题)、name · 实体 <entity> · v<version>(元信息)、口径 formula(code)、度量/维度标签、description

4.3 实体模型 Tab

位置元素含义默认值/必填操作效果触发的后端调用
左栏实体列表项实体(显示名 + name + 表 + 数据源 #)点击选中并加载详情GET /metrics/entities/:id
左栏「新建实体」/「收起」按钮展开/收起新建实体表单切换 showEntityForm
新建表单名称(api_name)*实体 api_name必填,如 customer_entity文本输入
新建表单显示名称中文显示名可空,如 客户实体文本输入
新建表单对象类型关联 ontology 对象类型下拉可空(默认 disabled 占位「选择对象类型」)选项来自 GET /ontology/objects
新建表单数据源 ID *实体查询数据源必填,默认 1数字输入
新建表单基础表实体基础物理表可空,如 customer文本输入
新建表单主时间维度primary_time_dimension可空,如 order_date文本输入
新建表单描述实体描述可空文本域
新建表单「创建」/「提交中...」提交实体前置校验名称/数据源非空校验失败 alert;成功关闭表单并选中新实体POST /metrics/entities
新建表单「取消」关闭表单showEntityForm = false
详情-维度维度表格name/display_name/source_property/time_grain/时间维度只读
详情-维度「+ 添加维度」按钮展开行内维度表单showDimForm = true
行内维度维度名/显示名/来源属性/时间粒度下拉/时间维度勾选维度字段维度名必填;时间维度勾选则粒度必填粒度选项 year/quarter/month/week/day
行内维度「保存」/「保存中...」+「取消」提交/放弃维度成功后刷新详情POST /metrics/entities/:id/dimensions
详情-度量度量表格name/display_name/expression/agg_type/type/source_property只读
详情-度量「+ 添加度量」按钮展开行内度量表单showMeasureForm = true
行内度量度量名/显示名/聚合下拉/类型下拉/来源属性/表达式/分子/分母度量字段度量名必填;ratio 类型须填分子分母聚合选项 sum/avg/count/min/max;类型 simple/ratio/derived/cumulative;类型为 ratio 时多出分子分母两个输入框
行内度量「保存」+「取消」提交/放弃度量成功后刷新详情POST /metrics/entities/:id/measures

4.4 指标定义 Tab

位置元素含义默认值/必填操作效果触发的后端调用
顶部状态筛选下拉按状态过滤指标列表默认 全部;选项 全部/draft/active/deprecatedchange 即刷新列表GET /metrics?status=
顶部「新建指标」/「收起表单」按钮展开/收起创建表单切换 showMetricForm
表单名称(api_name)*指标 api_name创建必填,编辑时 disabled(不可改)文本输入
表单显示名称指标显示名可空,如 营收文本输入
表单状态指标状态默认 draft;选项 draft/active/deprecated下拉
表单实体 *指标关联实体必填选择后触发 onMetricEntityChange() 加载该实体的度量/维度选项GET /metrics/entities/:id
表单度量(measure)*指标引用的度量必填(选项来自实体)选择
表单版本(创建时)初始版本号默认 1,编辑时 disabled数字输入
表单负责人 owner指标负责人可空,如 zhangsan文本输入
表单同义词 synonyms业务同义词可空,逗号分隔,如 收入,销售额文本输入
表单描述指标口径描述可空文本域
表单维度多选该指标允许的维度(勾选)选项来自实体维度名未选实体时提示「请先选择实体」
表单过滤条件(可选)+「+ 过滤条件」默认筛选条件列表每行 field/op/value;op 为下拉选择,取后端语义层算子字面量 eq/ne/not_eq/gt/gte/lt/lte/like/contains/starts_with/in/is_null,另含「自定义…」档可手填(后端新增算子时不被锁死,非法值由后端拦截)
表单「删除」按钮(过滤条件行)移除一行removeRow(filters, i)
表单「保存」/「保存中...」+「取消」提交/放弃前置校验名称/实体/度量创建走 POST、编辑走 PUT(版本自动 +1),成功刷新列表与目录POST /metrics / PUT /metrics/:id
列表指标表格ID/名称/显示名称/实体/度量(#id)/维度/状态/版本只读
列表「编辑」按钮打开编辑表单异步加载实体选项后恢复原选择GET /metrics/entities/:id(编辑表单内)
列表「删除」按钮软删指标confirm 二次确认「确定删除指标"xx"吗?」成功刷新列表与目录DELETE /metrics/:id

4.5 指标查询 Tab

位置元素含义默认值/必填操作效果触发的后端调用
查询表单指标 *查询指标下拉必填选择后触发 onQueryMetricChange 加载维度选项GET /metrics/:id / GET /metrics/entities/:id(维度缺失时)
查询表单limit返回行数上限默认 100数字输入
查询表单offset分页偏移默认 0数字输入
查询表单时间维度名(可选)time_dimension可空,如 order_date文本输入
查询表单开始时间(可选)time_start可空,如 2026-01-01文本输入
查询表单结束时间(可选)time_end可空,如 2026-12-31文本输入
查询表单维度多选分组维度该指标维度名勾选即入 dimensions
查询表单过滤条件(可选)+「+ 过滤条件」查询过滤同指标定义表单
查询表单「查询」/「查询中...」发起查询未选指标时禁用三时间项任一填写才附带 time_range;成功渲染结果卡片POST /metrics/query
结果卡片指标元信息名称/实体/聚合/状态/formula只读
结果卡片结果表格列名 + 行数据无结果时显示「无查询结果」

5. 后端关联

5.1 API 客户端

action/web/src/api/client.js

5.2 端点表

方法路径请求体要点超时
GET/metrics/catalog30s
GET/metricsquery: status(draft/active/deprecated,可空)30s
POST/metrics{name, display_name, description, measure_id, entity_id, dimensions, filters, synonyms, owner, status, version}30s
GET/metrics/:id30s
PUT/metrics/:id{display_name, description, dimensions, filters, synonyms, owner, status}(name/measure_id/entity_id 不可改)30s
DELETE/metrics/:id无(软删:status=deprecated)30s
POST/metrics/query{name, dimensions, filters, time_range{start,end,time_dimension}, limit, offset, user_id}30s
GET/metrics/entities30s
POST/metrics/entities{name, display_name, description, object_type_id, base_table, data_source_id, primary_time_dimension}30s
GET/metrics/entities/:id30s
POST/metrics/entities/:id/dimensions{name, display_name, source_property, time_grain, is_time_dimension}30s
POST/metrics/entities/:id/measures{name, display_name, expression, agg_type, type, numerator, denominator, source_property}30s
GET/ontology/objects无(对象类型下拉数据源)30s

5.3 响应结构

统一成功包装 {code: 0, data: ...}(server 包 okData);失败包装 {code, error}errorResponse,code 为错误码、status 对应 HTTP 状态码)。

指标目录 GET /metrics/catalog

{
  "code": 0,
  "data": {
    "metrics": [
      {
        "name": "revenue",
        "display_name": "营收",
        "entity": "sales_entity",
        "entity_id": 1,
        "metric_type": "simple",
        "measures": ["revenue_measure"],
        "dimensions": ["order_date", "region"],
        "formula": "SUM(amount)",
        "synonyms": "收入,销售额",
        "data_source_id": 1,
        "owner": "zhangsan",
        "version": 2,
        "status": "active"
      }
    ]
  }
}

实体详情 GET /metrics/entities/:id

{
  "code": 0,
  "data": {
    "entity": { "id": 1, "name": "sales_entity", "display_name": "销售实体", "object_type_id": 3, "base_table": "orders", "data_source_id": 1, "primary_time_dimension": "order_date" },
    "dimensions": [ { "name": "order_date", "display_name": "下单日期", "source_property": "order_date", "time_grain": "day", "is_time_dimension": true } ],
    "measures": [ { "name": "revenue_measure", "display_name": "营收度量", "expression": "SUM(amount)", "agg_type": "sum", "type": "simple" } ]
  }
}

指标查询 POST /metrics/query

{
  "code": 0,
  "data": {
    "columns": ["order_date", "metric"],
    "rows": [["2026-08-01", 12500.5]],
    "metric": {
      "name": "revenue",
      "display_name": "营收",
      "entity": "sales_entity",
      "formula": "SUM(amount)",
      "agg_type": "sum",
      "type": "simple",
      "metric_type": "simple",
      "dimensions": ["order_date"],
      "status": "active",
      "edited_overlay": false,
      "edit_overlay": ""
    }
  }
}
前端 resultColumns 计算兼容 columns 两种形态:数组直接使用;对象则 Object.keys(columns) 取键名。

5.4 关联模块表

后端包/文件职责
products/foundry/server/semantic_handlers.go指标/实体/维度/度量/目录/查询 HTTP handler(含语义检索与对象查询)
products/foundry/metric/models.go四层模型:MetricEntity / MetricDimension / MetricMeasure / MetricDefinition(表 metric_entities / metric_dimensions / metric_measures / foundry_metrics)
products/foundry/metric/service.go实体/度量/指标 CRUD 与版本管理
products/foundry/metric/query.goQueryMetric 七步翻译 + GetMetricCatalog
products/foundry/metric/catalog.go指标目录组装(口径 formula 分派)
products/foundry/semantic/ool.goOOL 翻译(聚合翻译 + RLS/CLS 注入 + 属性级只读判定)
products/foundry/semantic/edit_overlay.go编辑态聚合叠加(B1-5)
products/foundry/ontology对象类型(GET /ontology/objects
platform/connector真实数据源连接器(runnerFor 经 provider 获取)

5.5 关键机制

6. 核心流程详解

6.1 从零到可查:指标建模主流程

  1. 建实体:进入「实体模型」Tab → 点「新建实体」→ 填写名称(api_name)、显示名称、对象类型(可选,来自 GET /ontology/objects)、数据源 ID、基础表、主时间维度、描述 → 点「创建」。成功后自动选中新实体并加载详情。
  2. 加维度:详情「维度」区点「+ 添加维度」→ 填维度名/显示名/来源属性/时间粒度;若勾选「时间维度」则粒度必填(year/quarter/month/week/day)→ 点「保存」。维度名是后续分组与多选的 key。
  3. 加度量:详情「度量」区点「+ 添加度量」→ 填度量名/显示名/聚合(sum/avg/count/min/max)/类型(simple/ratio/derived/cumulative)/来源属性/表达式(如 SUM(amount));类型为 ratio 时补充分子(numerator)与分母(denominator)→ 点「保存」。
  4. 建指标:切到「指标定义」Tab → 「新建指标」→ 填名称(api_name)、显示名、状态(默认 draft)、实体(必选,选中即加载该实体度量/维度选项)、度量(必选)、版本、owner、同义词(逗号分隔)、描述;勾选允许维度;按需追加过滤条件 → 「保存」。创建成功后列表与目录同时刷新。
  5. 发版:在表单把状态改为 active 再保存(或创建时直接 active),目录卡片即展示 active 徽标。
  6. 查询:切到「指标查询」Tab → 选指标 → 勾选维度 → 填 limit/offset(默认 100/0)→ 可选时间维度名与开始/结束时间 → 「查询」→ 查看结果表格与口径 formula。

6.2 指标编辑与版本流

6.3 指标删除(软删)流

指标列表「删除」→ confirm('确定删除指标"xx"吗?')DELETE /metrics/:id → 后端置 status=deprecated → 刷新列表与目录。删除后指标仍可被目录展示(带 deprecated 徽标),但查询接口直接拒绝执行。

6.4 指标查询分支语义

6.5 指标类型分派

类型口径 formula查询翻译
simplemeasure.Expression(如 SUM(amount)单聚合表达式 OOL 翻译
ratio分子表达式 / 分母表达式两个聚合分别翻译后 (num)/(den) 拼装;优先定义层 numerator_measure_id/denominator_measure_id 引用,缺省回退 measure 层 numerator/denominator
derived两度量派生表达式同 ratio 拼装;带 time_window(移动平均等)标 P2 拒绝;须定义分子/分母度量引用
cumulativecumulative(<表达式>)基础聚合须 sum/count,按时间维度分组后 SUM(...) OVER (ORDER BY time ...) 累计;trailing 窗口 P2 拒绝

7. 权限与安全

8. 常见问题与排错

8.1 查询报「metric "xx" 已停用(deprecated),不可查询」

8.2 查询报「时间维度 "order_date" 未定义或非时间维度」

8.3 查询结果只有一列「metric」或列结构奇怪

8.4 实体/指标保存时按钮一直「保存中...」或卡住

8.5 指标目录为空但指标定义列表有数据

9. 已知缺陷与边界

缺陷/边界说明影响
cumulative 基础聚合受限仅支持 sum/count;avg/min/max 非可加聚合标 P2cumulative 指标查询报 400
cumulative/derived 时间窗口 P2time_window(trailing 累计/移动平均)本版不支持带窗口的指标查询报 400
derived 仅两度量派生须定义 numerator_measure_id/denominator_measure_id,更多表达式不支持建模受限
编辑态叠加仅 simpleratio/derived/cumulative 不叠加,edited_overlay=false 如实标注聚合值可能与行级不一致(编辑态下)
指标详情 O(n) 过滤handleGetMetric 无 GetByID,按 ListMetrics 全量过滤指标量大时详情接口较慢
维度多选以字符串为 key指标维度多选用维度 name 字符串,重名实体维度会混淆同名维度跨实体选择异常
过滤 op 已收敛为下拉op 改为后端算子字面量下拉(含「自定义…」兜底档);field/value 仍为自由文本、无类型校验非法或自定义算子值由后端白名单拦截(unsupported filter op %q
无时间过滤不附带 time_range三项全空时请求体省略 time_range功能正常,但接口语义需文档化
删除为软删DELETE 指标置 deprecated,历史查询仍可见该状态目录仍展示 deprecated 指标

注:查询结果 NULL 原样展示的问题已于 2026-09-06 修复(formatCell 将 null/undefined/纯空白统一显示为「NULL」,与数据集预览页文案一致)。