1. 页面概览
1.1 是什么
对象查询页面(ObjectQueryPage.vue)让用户以「选择对象 → 勾选字段 → 配置过滤/排序 → 执行」的方式对本体对象做结构化查询,结果以二维表格(行 × 列)渲染。它走的是 POST /objects/:id/query 语义查询接口:
- 对象驱动:先从对象类型下拉选择目标对象(如订单),页面自动加载该对象的属性清单、链接与动作信息;
- 字段多选:查询列从对象属性中勾选,不选 = 全部列;
- 过滤动态行:任意添加「字段 + 算子 + 值」过滤条件行,支持 eq/ne/gt/gte/lt/lte/like/contains/in/is_null 十种算子;
- 排序分页:可指定排序字段、降序开关与 limit;
- 安全兜底:查询由后端翻译(白名单校验防注入)+ RLS/CLS 只读注入 + 真实数据源执行,前端不可绕过;
- 编辑态可见:若对象存在「编辑态叠加」(草稿修改未合入),被覆盖的属性列会带「已编辑」标记,结果叠加最新编辑值。
本页与「SQL 工作台」形成互补:SQL 工作台用 FROM 对象名 写自由 SQL,本页则是表单化的 OOL 查询构造器,把对象查询语法(字段/过滤算子/排序/limit/offset/params)显式暴露给用户操作。
1.2 核心价值
| 维度 | 说明 |
|---|---|
| 表单化 OOL | 无需写 SQL,下拉+勾选+动态行即可构造对象查询 |
| 十种过滤算子 | eq/ne/gt/gte/lt/lte/like/contains/in/is_null 覆盖常规条件 |
| 安全闸门 | 后端白名单校验 + RLS/CLS 只读注入 + 参数绑定,防注入防越权 |
| 编辑态可见 | 未合入的草稿修改会叠加到结果并用「已编辑」标记说明 |
| 二维结果 | 行 × 列表格直接呈现,与后端 columns/rows 结构一一对应 |
1.3 一句话总结
2. 访问入口
2.1 路由与菜单
- 路由:
/foundry/query - 路由名称:
FoundryQuery - 菜单位置:Foundry 左侧边栏「对象查询」(
FoundryLayout.vue菜单第 14 项,位于「语义检索」之后、「审计管理」之前) - 源码文件:
action/web/src/views/ObjectQueryPage.vue(369 行)
2.2 认证与权限
- 需要登录:是(路由
meta.requiresAuth = true) - 登录方式:AIP 统一登录,
localStorage.aip_token作为 Bearer Token 自动附带 - 页面本身无角色限制,但查询结果受三层约束:对象级
ontology:<对象名>:read(无读权限直接 403)、行级 RLS 过滤、列级 CLS 隐藏(无读权限列置 null);后端查询入口还会把当前用户注入params.user_id供注入判定 - 404 排错:若访问
/foundry/query出现 404,先确认 Foundry 后端(端口 18081)已启动、前端路由已注册(router/index.js第 257~261 行)
2.3 端口与 API 前缀
- 后端端口:18081(Foundry)
- 前端 API 前缀:
/api/v1(client.js,超时 30 秒) - 注意:执行查询的路径是
/objects/:id/query(不带/ontology前缀);加载对象列表与详情则用/ontology/objects与/ontology/objects/:id
3. 界面布局
页面纵向分为 5 个板块(从上到下):
┌──────────────────────────────────────────────────────────────┐ │ ① 页头:标题「对象语义查询」 │ │ ② 操作结果提示条(alert,失败红,可关闭) │ │ ③ 选择对象卡片 │ │ ├─ 对象类型 * 下拉(显示名(name · #id)) │ │ └─ 对象信息(display_name · 表 xxx · 数据源 #N) │ │ ④ 查询配置卡片(选中对象后出现) │ │ ├─ 查询字段 fields 多选(不选=全部列,计数标签) │ │ ├─ 过滤条件 filters(+ 过滤条件 动态行) │ │ └─ 排序字段 order_by / limit / 降序 order_desc 复选框 │ │ └─ [查询] 按钮 │ │ ⑤ 查询结果卡片(共 N 行 × M 列 + 已编辑列标记) │ └──────────────────────────────────────────────────────────────┘
各板块职责:
- ① 页头:标识本页能力。
- ② 提示条:通用 alert,加载失败等场景提示。
- ③ 选择对象:唯一入口,选中对象即触发
loadDetail拉取属性清单。 - ④ 查询配置:字段多选、过滤动态行、排序与分页;仅在对象详情加载成功后显示。
- ⑤ 查询结果:二维表格 + 「已编辑」列标记;查询出错时单独红条显示
queryError。
4. 交互元素详解
4.1 对象类型下拉
| 属性 | 值 |
|---|---|
| 控件 | <select v-model.number=selectedObjectId> |
| 数据来源 | GET /api/v1/ontology/objects(onMounted(fetchObjects) 拉取,data.data 数组) |
| 占位项 | 「选择对象类型」(value=null,disabled) |
| 选项格式 | display_name || name(name · #id),value 为 object_type.id |
| 交互效果 | @change="loadDetail" 选中即加载详情;切换对象时清空上次结果与错误 |
4.2 对象信息展示
选中对象后 loadDetail 调 GET /ontology/objects/:id,data.data 存为 detail,展示「display_name || name · 表 base_table || '-' · 数据源 #data_source_id」;未选中时显示灰色「选择对象后自动加载属性清单...」。loadDetail 还会把 queryForm.limit 兜底为 100(limit || 100)。
4.3 查询字段 fields 多选
| 属性 | 值 |
|---|---|
| 控件 | 每个属性一个 checkbox(.row-check),标签为 p.name + 灰色 p.data_type |
| 数据来源 | detail.properties(对象详情属性数组) |
| 计数 | 标题右侧蓝色计数 {{ queryForm.fields.length }} |
| 语义 | 不选 = 全部列(请求体传空数组 fields: []);选中的属性名进入 fields |
| 空态 | 对象无属性时显示「该对象暂无属性。」 |
4.4 过滤条件 filters 动态行
| 属性 | 值 |
|---|---|
| 添加按钮 | 「+ 过滤条件」(.sub-title 右侧小按钮),push 一条 {field:'', op:'eq', value:''} |
| 行结构 | 字段下拉 + 算子下拉 + 值输入框 + 「删除」按钮 |
| 字段下拉 | 选项为 detail.properties[].name,占位「字段」(disabled 空值) |
| 算子下拉 | filterOps 十种:eq, ne, gt, gte, lt, lte, like, contains, in, is_null |
| 值输入框 | placeholder「值(in 用逗号分隔)」;算子为 is_null 时输入框禁用(无需值) |
| 删除按钮 | 「删除」调 removeRow(filters, i) 从数组移除该行 |
| 空态 | 无过滤条件时显示「暂无过滤条件。」 |
| 提交处理 | 只提交 f.field 非空的行;op !== 'is_null' 时带 value,is_null 省略 value |
4.5 排序与分页
| 字段 | 控件 | 说明 |
|---|---|---|
| 排序字段 order_by | <select> | 选项为属性名,空值「不排序」 |
| limit | <input type=number min=1> | 默认 100,空值提交时兜底 100 |
| 降序 order_desc | checkbox | 勾选后请求体 order_desc: true |
4.6 「查询」按钮
| 属性 | 值 |
|---|---|
| 按钮文字 | 「查询」(查询中变「查询中...」并禁用) |
| 前置校验 | 未选对象时提示「请先选择对象类型」 |
| 请求体 | {fields, filters, order_by, order_desc, limit, offset: 0, params: {}} |
| 失败 | queryError 红条显示「查询失败:<error>」,结果清空 |
| 成功 | queryResult = {columns, rows, editedFields},queried = true |
4.7 查询结果表格
| 元素 | 说明 |
|---|---|
| 标题 | 「查询结果」+「共 N 行 × M 列」 |
| 表头 | queryResult.columns 逐列渲染;列在 editedFields 中时追加「已编辑」黄色小标签(title「该列叠加了编辑态未合并的值」) |
| 单元格 | formatCell(cell):null 显示空串,对象/数组转 JSON,其余 String() |
| 空行 | rows.length === 0 时渲染跨列灰字「无查询结果」 |
| 未查询态 | !queryResult && queried 时显示「请先选择对象并配置查询条件,然后点击"查询"。」 |
5. 后端关联
5.1 API 客户端
client.js:baseURL: /api/v1,超时 30 秒;请求拦截器附Authorization: Bearer <aip_token>;响应拦截器 401 清理并跳/login。- 页面直接使用
apiClient.get('/ontology/objects')、apiClient.get('/ontology/objects/:id')、apiClient.post('/objects/:id/query', body),无独立 API 文件。
5.2 端点表
| 方法 | 路径 | 请求体 | 说明 |
|---|---|---|---|
| GET | /ontology/objects | — | 对象类型列表(data.data 数组,含 object_type) |
| GET | /ontology/objects/:id | — | 对象详情(data.data,含 object_type/properties) |
| POST | /objects/:id/query | OOL 查询请求体 | 执行对象语义查询(路径不带 /ontology) |
| POST | /ontology/objects/:id/query | 同左 | 语义等价别名路径 |
查询请求体(后端 objectQueryRequest):
{
"fields": ["amount", "status"],
"filters": [
{ "field": "amount", "op": "gt", "value": 1000 },
{ "field": "status", "op": "in", "value": ["paid", "shipped"] },
{ "field": "deleted_at", "op": "is_null" }
],
"order_by": "created_at",
"order_desc": true,
"limit": 100,
"offset": 0,
"traverse": [],
"params": {}
}
5.3 响应结构
统一包装 {code: 0, data: ...};data 即 domain.QueryResult:
{
"code": 0,
"data": {
"columns": ["id", "amount", "status", "created_at"],
"rows": [
[1, 2500.0, "paid", "2026-08-29T10:00:00Z"],
[2, 300.0, "shipped", "2026-08-28T09:00:00Z"]
],
"edited_fields": ["status"]
}
}
columns:查询结果列名数组(属性 api_name 或linkName.propName关联列);rows:二维数组,每行与columns一一对应;edited_fields:编辑态叠加(第 1 层)发生、被覆盖的属性 api_name 列表,叠加未发生时为空/缺省。
5.4 关联模块表
| 后端包 | 职责 |
|---|---|
foundry/semantic | SemanticQueryService.Execute:OOL 翻译(白名单校验 + RLS/CLS 注入)→ 连接器真实执行 → 编辑态叠加 |
foundry/server/semantic_handlers.go | handleObjectQuery:解析对象 id、注入 params.user_id、调用 Execute |
foundry/ontology | 对象类型/属性/链接元数据、GetLatestEditValues 编辑态读取 |
platform/connector | 数据源连接器执行物理查询 |
platform/auth | RLS/CLS 注入与对象级 ontology:<name>:read 授权 |
5.5 关键机制
OOL 过滤算子:后端语义包定义算子全集为 eq / ne / not_eq(ne 别名)/ gt / gte / lt / lte / like / contains / starts_with / in / is_null / and / or / not;前端页面暴露其中十种(eq/ne/gt/gte/lt/lte/like/contains/in/is_null)。in 的值可为标量/切片,页面提示「in 用逗号分隔」。值一律参数化绑定,不拼接进 SQL。
安全注入:handleObjectQuery 把 req.Params["user_id"] = currentUserID(c);语义服务 Translate 阶段做属性白名单校验(字段必须是本体属性集合成员,含 linkName.propName 关联属性),再经 ApplyRLS/ApplyCLS 注入只读安全口径。
对象级可见性:查询入口经 authorizeObjectRead 校验 ontology:<对象名>:read,无读权限返回 403 明确错误(不静默放行);内部调度路径或 apps 组件加载(APP-06 已知路径)可跳过。
编辑态叠加:applyEditOverlay 按主对象行 id 批量取 LoadEditValues 最新值覆盖源表列(仅主对象 t0 属性,traverse/link 关联对象列为后续层);被覆盖的属性写入 edited_fields 并剥离注入的主键列,保证输出列契约稳定。
Traverse 遍历式扩展:请求体 traverse 支持沿 link 进入相邻对象再过滤(then_filters/select 限相邻对象属性,禁点号引用,可嵌套 ≤3 层)。本页前端未暴露 traverse 编辑 UI,但请求体已透传空数组,供 API 调用方直接使用。
6. 核心流程详解
6.1 主流程:执行一次对象查询
- 页面加载
onMounted(fetchObjects)→GET /ontology/objects填充对象下拉; - 选择对象 →
loadDetail→GET /ontology/objects/:id→ 渲染属性多选/过滤字段/排序字段,limit 兜底 100; - 勾选查询字段(不选=全部列),按需点「+ 过滤条件」添加过滤行并选算子填值;
- 选择排序字段、勾选降序、设置 limit;
- 点「查询」→
POST /objects/:id/query(body 见 5.2)→ 后端翻译+注入+执行; - 结果渲染二维表格,
edited_fields中的列显示「已编辑」标签。
6.2 分支流程:is_null 过滤
算子选 is_null 时值输入框禁用且提交时省略 value(后端 is_null 不需要值)。例:过滤已删除行用 {field: "deleted_at", op: "is_null"}。
6.3 分支流程:in 多值过滤
算子选 in 时值按逗号分隔输入,如 paid, shipped。注意:前端把值作为字符串提交(out.value = f.value),若需数值列表由后端语义解析处理;逗号分隔是页面提示约定。
6.4 结果导出(数据如何落盘)
本页当前不提供内置导出按钮——结果以二维表格在页面上渲染。若需要将结果导出为 CSV 落地,可采用以下途径:
- 手动复制:选中表格内容复制到剪贴板,粘贴到表格软件/CSV 文件中(列顺序即
columns,单元格对象/数组为 JSON 文本); - 复用 SQL 工作台「导出 CSV」:把同样的字段/过滤条件写成
SELECT ... FROM 对象名 ...,在SqlWorkbenchPage.vue点「导出 CSV」下载(其导出逻辑对 rows 逐行join(',')并 BOM 前缀处理中文); - API 直接调用:
POST /objects/:id/query返回结构化{columns, rows},可被脚本/低代码应用直接消费,再自行序列化为 CSV/JSON; - 审计页导出:若查询结果来自审计场景,可参考「审计管理」页的「导出 CSV」交互。
6.5 编辑态叠加语义
当对象存在未合入的草稿修改(如 Action 测试/应用写入的编辑态),查询结果会叠加最新编辑值:被覆盖的属性列带「已编辑」标签;未 SELECT 该属性的列不受影响;聚合场景下编辑态叠加会回退为行级+内存聚合(超 10000 行标 approximate),保证行级与聚合一致。
7. 权限与安全
- 认证:axios 拦截器附带 Bearer Token;401 统一清理并跳登录。
- 对象级可见性:无
ontology:<对象名>:read权限时查询直接 403(不静默放行);admin 通配自动通过。 - RLS/CLS 只读注入:翻译后的物理 SQL 注入行级(RLS)与列级(CLS)安全;无读权限属性列置 null/隐藏,不整行拒绝。
- 防注入:字段/算子白名单校验(非本体属性成员直接拒绝)、值参数化绑定(
?占位符)、标识符合法性校验,前端无法构造任意 SQL。 - 写防护:本页为纯只读查询;写操作必须走 Action 唯一写路径。
8. 常见问题与排错
8.1 点「查询」提示「查询失败:...」
- 现象:结果区上方红条提示查询失败 + 后端 error 文案。
- 原因:请求被后端拒绝。常见:对象 id 非法(路径参数解析失败)、对象不存在(404
object type not found)、字段不在属性白名单、无读权限(403ontology:<name>:read)、数据源不可达。 - 排查步骤:① Network 面板看
POST /objects/:id/query状态码与{error};② 403 检查当前用户对对象/属性的读权限(换 admin 对比);③ 400/422 检查 fields 是否误填了非属性名(含大小写、含点号引用);④ 5xx 检查后端日志与数据源连通性。
8.2 「已编辑」列标记出现了但值「不新」
- 现象:某列带「已编辑」标签,但看到的值仍是旧值。
- 原因:编辑态叠加只覆盖「主对象 t0 属性」,且仅当该属性被 SELECT 且主键列参与查询时生效;关联对象(traverse/link)列为后续层不叠加。
- 排查步骤:① 确认字段列表包含该属性且包含主键属性;② 确认该对象确实存在未合入的草稿(可在「Action 测试」/应用写入侧验证);③ 查看请求体是否意外带了
traverse;④ 聚合场景确认是否命中 approximate 标注。
8.3 查询结果只有一行「无查询结果」
- 现象:表格只显示「无查询结果」跨列灰字。
- 原因:
rows为空——过滤条件过严或数据源该对象确实无数据。若带过滤条件,通常是is_null/in写法与数据不匹配。 - 排查步骤:① 逐步删除过滤行重查,定位是哪个条件筛空;②
in检查逗号分隔与数值类型(数值字段用gt/eq而非 in 的字符串值);③like/contains确认算子语义(like用%通配还是子串由后端定义);④ 确认为空数据而非权限问题(换 admin 对比)。
8.4 字段下拉/过滤字段为空
- 现象:选中对象后「查询配置」卡片未出现,或字段列表为空。
- 原因:
GET /ontology/objects/:id失败(详情未加载),或对象确实无属性。 - 排查步骤:① 顶部 alert 是否提示「加载对象详情失败」;② Network 检查详情请求状态码;③ 查看对象是否有
properties(可在「本体工作台」确认);④ 切换其他对象验证是否通用问题。
8.5 limit 填写无效(如 0 或负数)
- 现象:limit 填 0 或负数,查询仍返回大量数据或报错。
- 原因:前端
v-model.number接收数字但 min 仅为提示;提交时limit: queryForm.limit || 100会把 0/空 兜底为 100,负数则原样提交。 - 排查步骤:① 确认输入框值非空;② 负数 limit 后端按默认/钳制处理,尽量填 1~1000 之间的正数;③ 需要大量数据可调大 limit(后端有钳制上限)。
9. 已知缺陷与边界
| 项 | 说明 |
|---|---|
| 无内置导出按钮 | 本页结果以二维表格呈现,未提供「导出 CSV」按钮(见 6.4 的替代途径) |
| 前端未暴露 traverse | 请求体透传 traverse: [],但页面没有遍历式编辑 UI,需 API 调用方自行构造 |
| 无 offset 编辑控件 | 请求体固定 offset: 0,前端不提供翻页 |
| 过滤值统一字符串 | 前端把过滤 value 作为字符串提交,数值语义由后端按列类型解析 |
| 编辑态仅主对象叠加 | traverse/link 关联对象列的编辑态为后续层,不叠加 |
| 关联列显示 | linkName.propName 关联字段若勾选会出现在 columns,单元格对象/数组转 JSON 文本 |
后端文件
action/products/foundry/semantic/query.go(OOL 翻译、Filter/QueryRequest/TraverseQuery、Execute、applyEditOverlay)action/products/foundry/server/semantic_handlers.go(handleObjectQuery/executeObjectQuery)action/products/foundry/server/server.go(对象查询路由与安全注入组装)action/platform/domain/query_result.go(QueryResult{columns, rows, edited_fields})
项目文档
action/wiki/upgrade-v5/dev-story/stage-1.md(B1-5 编辑态叠加)action/wiki/changelog/2026-08-29-2-v5-stage1.md(Stage 1 交付说明)action/wiki/frontend-intro-v5/markdown/foundry/index.md(Foundry 页面清单)