1. 页面概览

1.1 是什么

Notebook 分析页(Notebook · 代码工作簿)是 LightFoundry 在 V5 Stage 3(B3-1)交付的分析协作闭环页面,对标 OpenFoundry notebook-service 的 Notebook / Cell / Session 三段式模型。它以「左目录、右单元格流」的布局,让分析师在一个工作簿里混合编排三类单元格:

单元格支持逐块运行(「运行」)与全量运行(「全量运行」),每次运行结果持久化到 fn_notebook_runs,可随时在「运行历史」中回看每次运行的单元格状态与行数、耗时。SQL 单元格复用 Ontology SQL 执行器(与 SQL 工作台同源),执行时自动注入 RLS/CLS 安全口径。

1.2 核心价值

维度说明
三类块混合编排markdown(说明)+ SQL(取数)+ chart(可视化)在同一个工作簿内按序组织
逐块 / 全量运行单格运行快速验证,全量运行一次性跑完整条分析链路
结果持久化每次运行落 fn_notebook_runs(cell_results JSON),历史可回放
结果表格内嵌SQL 结果直接以内嵌表格展示,chart 单元格在结果上叠加 ECharts 渲染
安全取数SQL 单元格经 Ontology SQL 执行器(安全翻译 + RLS/CLS + 编辑态)执行
chart 不重算chart 单元格引用 SQL 单元格结果集,只做渲染不重复执行查询

1.3 一句话总结

在 Foundry 里把「说明 → 取数 → 出图」串成一个可运行、可回放的分析工作簿——写三类单元格,逐块或全量运行,结果落库留痕。

2. 访问入口

2.1 路由与菜单

2.2 认证与权限

2.3 端口与 API 前缀

3. 界面布局

页面采用左右两栏布局(nb-layout,左侧 240px 固定,右侧自适应):

┌───────────────┬──────────────────────────────────────────────────┐
│ 左栏:工作簿    │ 右栏:当前工作簿                                  │
│ ① 新建输入+新建 │ ② 工具栏:名称输入 + 重命名 + 全量运行            │
│ ③ 工作簿列表    │    + Markdown / + SQL / + 图表 / 运行历史         │
│    (点击切换)  │ ④ 运行历史卡片(可收起)                          │
│                │ ⑤ 单元格流:markdown / sql / chart 卡片            │
└───────────────┴──────────────────────────────────────────────────┘

各板块职责:

未选择工作簿时右栏显示占位文案「从左侧选择或新建一个工作簿开始。」;空工作簿显示「空工作簿,点击上方按钮添加单元格。」

4. 交互元素详解

4.1 工作簿目录(左栏)

元素位置含义操作效果与触发调用
新工作簿名称输入左栏顶部新工作簿名称@keyup.enter 触发「新建」;为空时不提交
新建按钮创建工作簿createNotebook({name, description: ''});成功后提示「工作簿已创建」、清空输入、刷新列表并自动打开新工作簿
工作簿列表项左栏列表工作簿名称 + 更新时间点击调 openNotebook(nb)getNotebook 拉详情);当前项高亮
删除每项右侧删除工作簿confirm「确认删除工作簿「{name}」及其全部单元格/运行记录?」→ 调 deleteNotebook;若删除的是当前工作簿则置空当前,并提示「工作簿已删除」

4.2 工具栏

元素含义操作效果与触发调用
名称输入框当前工作簿名称草稿(nbNameDraft修改后点「重命名」生效
重命名保存名称修改updateNotebook(id, {name: nbNameDraft.trim()});提示「已重命名」并刷新列表
全量运行顺序运行全部单元格runAll(id)(120s 超时);运行中按钮文案变「运行中...」且禁用;完成后提示「全量运行完成」
+ Markdown新增 markdown 单元格addCell(id, {cell_type:'markdown', content:'# 新说明'}),随后 refreshCells()
+ SQL新增 sql 单元格addCell(id, {cell_type:'sql', content:'SELECT * FROM '}),随后 refreshCells()
+ 图表新增 chart 单元格addCell(id, {cell_type:'chart', chart_config:{cell_id: 第一个 SQL 单元格 id 或 '', chart_type:'bar'}}),随后 refreshCells()
运行历史运行历史面板开关showRuns 取反;显示时标题「运行历史(最近 N 次)」

4.3 单元格卡片

每张单元格卡片头部(cell-head)包含:类型徽标(ct-badge,markdown 紫 / sql 蓝 / chart 橙)、#ord 序号、最近运行结果摘要(status · row_count 行 · elapsed_ms ms,有结果才显示),右侧三个按钮:

按钮含义操作效果与调用
保存保存单元格编辑内容updateCell(id, cellId, {cell_type, content, chart_config?});提示「单元格已保存」
运行运行该单元格先自动 saveCell(cell) 保存再调 runCell(id, cellId)(120s);chart 单元格运行后 nextTick 渲染 ECharts;随后刷新运行历史
删除删除该单元格confirm「确认删除该单元格?」→ 调 deleteCell(id, cellId),清空其本地结果并 refreshCells()

4.4 三类单元格正文

类型正文区说明
markdown<textarea rows="4">(源码态)+ .md-preview(富文本预览态)占位符「# 标题\n支持 markdown 文本」;单元格内「查看源码 / 富文本预览」按钮切换两种态;预览经 marked 解析 + DOMPurify.sanitize 清洗后 v-html 渲染,展示态不写入数据、不落库
sql<textarea rows="5"> + 结果区占位符「SELECT name, amount FROM order WHERE amount > 100 LIMIT 10」;有结果时显示 ResultTable(列排序 / 分页 / 行号),若有 error 以红字提示
chart配置下拉 + chart-host + ResultTable见下

chart 单元格配置区(chart-cfg):

4.5 运行历史表格

含义
时间run.created_atfmtTimeT→空格、截 19 位)
创建者run.created_by
单元格Object.keys(run.cell_results || {}).length(本次运行涉及的单元格数)
状态每个单元格一个 status-badgesuccessstatus-active 绿徽标,其余(error)用 status-error 红徽标

5. 后端关联

5.1 API 客户端

5.2 端点表(前缀 /api/v1,均挂 protected)

方法路径请求体超时用途
GET/notebooks60s工作簿列表
POST/notebooks{name, description}60s创建工作簿
GET/notebooks/:id60s工作簿详情(含 cells)
PUT/notebooks/:id{name?, description?}(指针语义)60s更新工作簿
DELETE/notebooks/:id60s删除工作簿(级联 cells/runs)
GET/notebooks/:id/cells60s单元格列表(ord 升序)
POST/notebooks/:id/cells{cell_type, content, chart_config?}60s添加单元格
PUT/notebooks/:id/cells/:cid{cell_type?, content?, chart_config?}60s更新单元格
DELETE/notebooks/:id/cells/:cid60s删除单元格
POST/notebooks/:id/cells/:cid/run{}120s运行单格
POST/notebooks/:id/run{}120s全量运行(落 fn_notebook_runs)
GET/notebooks/:id/runs?limit=查询参数 limit60s运行历史

5.3 响应结构(JSON 示例)

工作簿详情 GET /notebooks/:id

{ "code": 0, "data": {
  "notebook": { "id": "...", "rid": "notebook://foundry/...", "name": "销售分析",
                "description": "", "owner": "550e8400-...",
                "created_at": "...", "updated_at": "..." },
  "cells": [
    { "id": "...", "notebook_id": "...", "ord": 1, "cell_type": "markdown",
      "content": "# 标题", "created_at": "...", "updated_at": "..." },
    { "id": "...", "ord": 2, "cell_type": "sql",
      "content": "SELECT name, amount FROM order WHERE amount > 100 LIMIT 10" },
    { "id": "...", "ord": 3, "cell_type": "chart",
      "chart_config": "{ \"cell_id\": \"...\", \"chart_type\": \"bar\" }" }
  ]
} }

单格运行 POST /notebooks/:id/cells/:cid/run

{ "code": 0, "data": {
  "status": "success", "elapsed_ms": 42, "row_count": 10,
  "columns": ["name", "amount"], "rows": [["张三", 1200], ["李四", 980]]
} }

全量运行 POST /notebooks/:id/run

{ "code": 0, "data": {
  "id": "run-uuid", "notebook_id": "...", "created_by": "550e8400-...",
  "cell_results": {
    "cell-id-2": { "status": "success", "elapsed_ms": 40, "row_count": 10,
                   "columns": [...], "rows": [...] },
    "cell-id-3": { "status": "success", "elapsed_ms": 1, "row_count": 10,
                   "columns": [...], "rows": [...], "chart_type": "bar", "source_cell": "cell-id-2" }
  }
} }

5.4 关联模块

后端包 / 表职责
foundry/notebook/models.goNotebook(fn_notebooks)、Cell(fn_cells)、NotebookRun(fn_notebook_runs)、ChartConfigCellResult
foundry/notebook/rest.goREST handler:notebooks / cells CRUD + run / runAll / runs
foundry/notebook/service.go业务服务:RunCell / RunAll / ListRuns
Ontology SQL 执行器SQL 单元格经 query.OntologySQLService.Execute(注入的 SQLExecutor 接口)执行,复用安全翻译 + RLS/CLS + 编辑态
foundry/server/server.gonotebook.RegisterRoutes(protected, s.notebookSvc) 挂载(notebookSvc 经 NewOntologySQLAdapter 接 SQL 执行器)

5.5 关键机制

6. 核心流程详解

6.1 新建工作簿主流程

  1. 左栏输入新名称(或直接回车)→ 点「新建」。
  2. createNotebook({name, description: ''}) → 提示「工作簿已创建」→ fetchNotebooks() 刷新列表 → openNotebook(data.data) 自动打开。
  3. 打开后 getNotebook 拉详情,将每个单元格的 chart_config JSON 解析为 chartConfigCellId / chartType(前端编辑态字段),并 loadRuns() 加载运行历史。

6.2 添加与编辑单元格流程

  1. 点「+ Markdown / + SQL / + 图表」→ handleAddCell(type)
    • 构造默认 content(markdown # 新说明、sql SELECT * FROM 、chart 空);
    • chart 单元格默认 chart_config = { cell_id: sqlCells[0]?.id || '', chart_type: 'bar' }
    • addCell 后将新单元格 push 进本地 current.cells,再调 refreshCells() 与后端对齐。
  2. 编辑单元格内容(textarea 双向绑定 cell.content),点「保存」调 updateCell
  3. 点「运行」单格:先 saveCell(cell) 保存当前编辑,再 runCell,结果写入 cellResults[cell.id],chart 单元格 nextTickrenderChart

6.3 全量运行流程

  1. 点「全量运行」(handleRunAll)→ running=true,按钮变「运行中...」。
  2. runAll(id):后端按 ord 顺序执行全部单元格(SQL 取数、chart 渲染配置传递),落 fn_notebook_runs
  3. 前端将 run.cell_results 整体写入 cellResults,随后 refreshCells() 对齐单元格列表,nextTick 后为每个 chart 单元格执行 renderChart,最后 loadRuns() 刷新历史并提示「全量运行完成」。

6.4 运行历史回看

  1. 点工具栏「运行历史」展开面板(默认收起)。
  2. loadRuns()listRuns(id, limit=0) 拉取全部运行记录;每行展示时间 / 创建者 / 单元格数 / 各单元格状态徽标。
  3. 单元格数取 Object.keys(run.cell_results || {}).length;状态按每个 cell 的 status 分别打徽标。

6.5 删除分支流程

7. 权限与安全

8. 常见问题与排错

8.1 添加 / 删除 / 全量运行时报「listCells is not defined」(已知缺陷)

8.2 新建工作簿后右侧仍是空

8.3 SQL 单元格运行后显示红字错误

8.4 图表单元格不渲染

8.5 运行历史不刷新

9. 已知缺陷与边界

项目说明
chart 单元格不重算 SQLchart 仅引用 SQL 单元格结果集渲染;被引用 SQL 改后须先「运行」该 SQL 单元格再运行 chart
单格运行回退缓存独立运行 chart 单元格时回退最近一次 run 的缓存结果,可能与当前 SQL 单元格最新结果不一致
markdown 预览已改富文本2026-09-13 起 .md-previewmarked 解析 + DOMPurify.sanitize 清洗后 v-html 渲染(标题/列表/表格/代码块),并提供「查看源码 / 富文本预览」切换;清洗为必需步骤(防 XSS)。展示态仅前端状态、不落库
运行历史仅展示摘要历史表只展示状态徽标与单元格数,不含每格的详细数据(详细结果在当期运行后内存中)
无工作簿级共享/协作工作簿归属个人 owner,无团队共享、评论、版本对比能力
运行超时边界runCell / runAll 前端 120s 超时,超大查询可能超时中断

注:listCells 未导入缺陷已于 2026-09-06 修复(源码 import 已补 listCells),添加/删除/全量运行不再抛 ReferenceError