# 看板与工作台

看板和报表是 Semantic 里的应用，这种应用叫工作台（Workshop 模块）。工作台只存定义：页面、分区、组件和变量。数据在打开时按看的人的权限从本体现算，没有人打开时不计算，也不同步。

> [!NOTE]
> 前提：保存和发布工作台要组织（命名空间 `cell-demo`）的 developer。看的人能看到什么，由他在这个本体上的角色决定。命令行要先运行 `aidc login`。

## 快速上手

先列出本体里的工作台。再预演保存一份定义。最后取一页的数据。

```terminal title="列出工作台"
$ aidc semantic applications list --ontology cell-demo
v3     云费用看板（最新 v3，人保存；替代 0 个旧页面）
  ri.aidc.workshop.cell-demo.module.cloud-costs
```

```terminal title="预演保存"
$ aidc semantic applications save --ontology cell-demo --file cloud-costs.json --dry-run
校验通过：将保存为 v4
```

```terminal title="取一页的数据"
$ aidc semantic applications evaluate ri.aidc.workshop.cell-demo.module.cloud-costs --page overview --filters '{"vendorFilter":{"vendor":"AWS"}}'
v3 · 总览
  filters [filterList]
  kpi [metricCard] 合计（美元）=1842.31
  byVendor [chartXY] 1 个类目
  byMonth [pivotTable]
```

## 为什么只存定义

看板不需要整页 HTML。工作台只存定义，因此有这些好处：

- 数据跟着权限走。看的人读不到某个类型，对应的组件显示「没有权限或数据不存在」，不会泄露数据。
- 数据在打开时才取。自动刷新缺省开启。最短间隔缺省为 10 秒。设置 `settings.autoRefresh.enabled: false` 才会关闭。
- 没有人打开，就不计算，也不同步数据源。

## 定义的结构

定义是一个 JSON 对象。它的顶层字段如下：

| 字段 | 说明 |
| --- | --- |
| `header` | 标题（`title`）和副标题（`subtitle`，可选） |
| `variables` | 变量，最多 100 个。见下文「变量」 |
| `pages` | 页面，1 到 20 个。每页有 `id`、`title` 和 `sections` |
| `settings.autoRefresh` | 自动刷新。打开着、在前台时，注册的对象集一变，就重新取数。最短间隔 10 秒 |

分区（`sections`）决定组件怎么排。分区有这些字段：

- `layout`：子内容的排列方式。可以是 `columns`、`rows`、`flow` 或 `loop`。`loop` 遍历一个结构数组变量，每一项显示一次。
- `widgets`：这一层的组件。
- `sections`：嵌套的子分区。分区最深 4 层。
- `flex`：和同级分区的宽或高的比例，范围是 0.1 到 12。

每一层最多 12 个分区。一页最多 60 个组件。

## 组件

下表列出全部组件，以及它们在定义里的 `type`。

| 组件 | `type` | 做什么 |
| --- | --- | --- |
| Metric Card | `metricCard` | 一组指标数字。每个指标绑定一个变量。最多 12 个指标 |
| Object Table | `objectTable` | 对象列表，可以选列、排序和翻页。最多 30 列，每页最多 200 行。点一行可以打开对象详情页（`openObjectView`） |
| Chart: XY | `chartXY` | 柱状图、横向柱状图或折线图。用 `groupBy` 分类，用 `metric` 计算。`segmentBy` 可以再分段 |
| Chart: Pie | `pieChart` | 饼图最多保留 20 个分类。其余合并为「其他」。最多显示 21 块 |
| Pivot Table | `pivotTable` | 透视表。1 到 2 个行维度，最多 6 个指标。`totals` 可以加合计。`maxRows` 最多 500 |
| Markdown | `markdown` | 一段文字，最多 10,000 个字符 |
| Filter List | `filterList` | 筛选器。输出一个筛选变量，用来过滤对象集。筛选方式有选择、文本、日期区间和数字区间 |
| Data Freshness | `dataFreshness` | 显示数据的新鲜程度。可以用 `objectTypes` 指定类型 |
| Object Set Title | `objectSetTitle` | 按模板显示对象集的标题（`template`，最多 200 个字符） |
| Custom widget | `customWidget` | 嵌入组织的一个应用（widget set），显示它的正式版本。`widgetSet` 是应用的 slug |

## 变量

变量保存对象集、数值、字符串和日期。组件通过变量取数。

| 变量 | `type` | 做什么 |
| --- | --- | --- |
| 对象集 | `objectSet` | 一组对象。`base` 是一个 Object Type，`filter` 是过滤后的对象集。`filters` 套上过滤变量 |
| 对象集过滤 | `objectSetFilter` | 值来自 Filter List。看的人选的筛选，放在网址里 |
| 数值 | `numeric` | 对一个对象集做聚合（`aggregation`），或者取 SQL 的一个值，或者是静态值 |
| 字符串 | `string` | 取 SQL 的一个值，或者是静态值 |
| 日期和时间 | `date`、`timestamp` | 日历区间的边界，例如本月 1 日。按时区取零点 |
| 结构数组 | `structArray` | SQL 的每一行是一个结构，给 `loop` 分区用。最多 10,000 行 |

日历区间用日期变量来做。`relativeDateRange` 按指定时间单位计算相对区间，不对齐日历。下面的变量取本月 1 日，按北京时间：

```json
{ "id": "monthStart", "type": "timestamp", "transformation": { "startOf": "month", "timeZoneId": "Asia/Shanghai" } }
```

在对象集过滤里，用 `"{{monthStart}}"` 引用日期变量。

```json
{ "type": "gte", "field": "createdAt", "value": "{{monthStart}}" }
```

SQL 变量的写法见 [SQL 与数据库](sql.md)。

## 保存与校验

保存一份定义，会建一个新版本。没有这个工作台时，保存会新建它。`--dry-run` 只校验，不保存。校验会检查类型、属性和变量引用，并且按你的权限检查。

下面是云费用看板的定义。文件的字段和 API 的请求体一致：

```json
{
  "apiName": "cloud-costs",
  "displayName": "云费用看板",
  "definition": {
    "header": { "title": "云费用看板" },
    "settings": { "autoRefresh": { "enabled": true, "minimumSecondsBetweenRefresh": 60 } },
    "variables": [
      { "id": "vendorFilter", "type": "objectSetFilter", "objectType": "cloudCost" },
      { "id": "costs", "type": "objectSet", "objectSet": { "type": "base", "objectType": "cloudCost" }, "filters": ["vendorFilter"] },
      { "id": "totalUsd", "type": "numeric", "aggregation": { "objectSet": "costs", "metric": { "type": "sum", "field": "costUsd" } } }
    ],
    "pages": [
      {
        "id": "overview",
        "title": "总览",
        "sections": [
          { "widgets": [ { "id": "filters", "type": "filterList", "objectSet": "costs", "output": "vendorFilter", "filters": [ { "property": "vendor", "kind": "select" } ] } ] },
          { "widgets": [ { "id": "kpi", "type": "metricCard", "metrics": [ { "label": "合计（美元）", "variable": "totalUsd" } ] } ] },
          { "layout": "columns", "sections": [
            { "widgets": [ { "id": "byVendor", "type": "chartXY", "chart": "bar", "objectSet": "costs", "groupBy": { "type": "exact", "field": "vendor" }, "metric": { "type": "sum", "field": "costUsd" } } ] },
            { "widgets": [ { "id": "byMonth", "type": "pivotTable", "objectSet": "costs", "rows": [ { "type": "exact", "field": "month" } ], "metrics": [ { "label": "美元", "metric": { "type": "sum", "field": "costUsd" } } ], "totals": true } ] }
          ] }
        ]
      }
    ]
  }
}
```

```bash
aidc semantic applications save --ontology cell-demo --file cloud-costs.json --dry-run   # 先校验
aidc semantic applications save --ontology cell-demo --file cloud-costs.json             # 保存为新版本
```

- `apiName` 小写字母开头，只能用小写字母、数字和连字符，长度是 2 到 63 个字符。
- 定义的大小最多 256 KB。
- 智能体保存时，加 `--as-agent <智能体 id>`。版本会记为智能体保存。
- `--publish` 保存后同时发布。能不能发布，见下文「版本与发布」。

## 取一页的数据

`evaluate` 取一页的数据。每个组件返回一份结果。智能体用这个接口使用工作台，界面也用它。

请求体的字段如下：

- `page`：页面的 id。
- `filters`：筛选变量的值。外层的键是筛选变量的 id，里层是属性和值。值可以是文本，也可以是 `{ "from": "…", "to": "…" }` 区间。
- `version`：要看的版本号。
- `tablePages`：对象表格的页码，从 0 开始。

```bash
curl -X POST "https://www.ai-dc.ai/api/v1/workshop/modules/ri.aidc.workshop.cell-demo.module.cloud-costs/evaluate" \
  -H "Authorization: Bearer $AIDC_API_KEY" -H "Content-Type: application/json" \
  -d '{"page":"overview","filters":{"vendorFilter":{"vendor":"AWS"}}}'
```

## 版本与发布

每次保存是一个新版本。用户看到的是已发布的版本。谁能发布，由组织的审批策略决定。

| 审批策略 | 谁能发布 |
| --- | --- |
| `review`（缺省） | 作者是智能体的版本，要由人在 Semantic 的应用页面上点「发布」 |
| `yolo` | 开发者 Key 也能发布，包括智能体在用的 Key |

```bash
aidc semantic applications versions ri.aidc.workshop.cell-demo.module.cloud-costs      # 列出版本
aidc semantic applications publish ri.aidc.workshop.cell-demo.module.cloud-costs 4      # 发布 v4
```

```terminal title="发布 v4"
$ aidc semantic applications publish ri.aidc.workshop.cell-demo.module.cloud-costs 4
云费用看板：用户现在看到 v4
```

每次发布都写进审计日志。

## 权限

看板的权限分四个方面：版本、数据、自动刷新和公开访问。

- **版本**：成员只能看到已发布的版本。开发者可以看草稿，也可以看任一版本。
- **数据**：数据按类型判断。看的人读不到某个类型，组件显示「没有权限或数据不存在」，不显示数据。
- **自动刷新**：自动刷新只注册页面上能读的对象集。`objectSets` 可以指定注册哪些对象集变量。
- **公开访问**：应用或组织的 Applications 设为 Open to Internet 后，应用的发布版不用登录就能只读。只能读到这个应用用到的类型。嵌在应用里的 widget set 一起放行，也是只读。
- **Loop 报告的全屏链接**：`/semantic/<组织>/objects/loopReport/<slug>?embedded=true&media=html` 跟随组织的 Applications 的开放程度。改回 Group，同一个链接就要登录。

工作台也是资源。谁能看它、怎样分享它，见 [访问与安全](auth.md)。

## 网页地址与分享

工作台的网页地址是 `https://www.ai-dc.ai/semantic/<组织>/applications/<apiName>`。筛选、翻页和切换页面的状态都在网址里。把网址发给别人，对方打开看到的是自己权限下的数据。

## 命令行

这一节列出工作台的命令。

| 命令 | 做什么 |
| --- | --- |
| `aidc semantic applications list --ontology <cell-…>` | 列出本体里的工作台 |
| `aidc semantic applications save --ontology <cell-…> --file <应用.json>` | 保存一个新版本。加 `--dry-run` 只校验 |
| `aidc semantic applications show <rid>` | 看一个版本的页面。加 `--version <N>` 看指定版本 |
| `aidc semantic applications versions <rid>` | 列出最近 100 个版本，要 developer |
| `aidc semantic applications publish <rid> <版本号>` | 发布一个版本 |
| `aidc semantic applications evaluate <rid>` | 取一页的数据。加 `--page` 和 `--filters` |

全部参数见 [参考 · 访问、自动化与用量](cli-govern.md#看板aidc-semantic-applications)。

## API

工作台调用下面这些端点。凭证是开发者 Key 或应用票据。

| 方法 | 路径 | 做什么 |
| --- | --- | --- |
| GET | `/api/v1/workshop/modules?ontology={命名空间}` | 列出本体里的工作台 |
| POST | `/api/v1/workshop/modules?ontology={命名空间}` | 保存一个新版本。`dryRun=true` 只校验 |
| GET | `/api/v1/workshop/modules/{rid}` | 读一个版本。`?version=N` 指定版本号 |
| GET | `/api/v1/workshop/modules/{rid}/versions` | 列出版本 |
| POST | `/api/v1/workshop/modules/{rid}/versions/{版本号}/publish` | 发布一个版本。`dryRun=true` 只校验 |
| POST | `/api/v1/workshop/modules/{rid}/evaluate` | 取一页的数据。请求体见上文「取一页的数据」 |

## 限制

下表列出工作台的上限。

| 项目 | 上限 |
| --- | --- |
| 页面数 | 20 页 |
| 分区的嵌套深度 | 4 层 |
| 每页的组件数 | 60 个 |
| 变量数 | 100 个 |
| 指标卡的指标数 | 12 个 |
| 对象表格的列数 | 30 列 |
| 对象表格每页的行数 | 200 行 |
| 柱状图的分类数 | 100 个 |
| 饼图的块数 | 最多保留 20 个分类。其余合并为「其他」。最多显示 21 块 |
| 透视表的行数 | 500 行 |
| Filter List 的筛选数 | 10 个 |
| 定义的大小 | 256 KB |
| 自动刷新的最短间隔 | 10 秒 |

## 常见错误

按错误或现象查。

| 错误或现象 | 原因 | 怎么办 |
| --- | --- | --- |
| 组件显示「没有权限或数据不存在」 | 看的人读不到这个组件用的类型 | 请 Owner 把这个类型分享给看的人。见 [访问与安全](auth.md) |
| `--dry-run` 校验失败 | 类型、属性或变量引用有误，或超过了上限 | 按返回的问题逐条修改定义 |
| 智能体保存的版本没有上线 | 审批策略是 `review` | 请人在 Semantic 的应用页面上点「发布」 |
| 打开着的看板没有刷新 | 显式关闭了自动刷新，或间隔太长 | 检查是否设置了 `settings.autoRefresh.enabled: false`。开启自动刷新，并缩短间隔 |
| 组件显示的数据不是最新的 | 数据已过期，还没有同步 | 在页面上点「立即同步」，最多等 20 秒 |

## 下一步

- [访问与安全](auth.md)：谁能看到看板里的数据。
- [定义本体](ontology.md)：看板读的 Object Type 和 Action。
- [自动化](workflow.md)：数据变化后，自动运行工作流。
- [SQL 与数据库](sql.md)：SQL 变量的写法。
- [参考 · 访问、自动化与用量](cli-govern.md#看板aidc-semantic-applications)：全部看板命令。
