@guandata/guanmetric 0.1.18 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +23 -3
- package/bin/run.js +14 -1
- package/package.json +6 -6
- package/skills/guanmetric/SKILL.md +53 -8
- package/skills/guanmetric/evals/evals.json +117 -0
- package/skills/guanmetric/evals/fixtures/atomic-detail-read-model.json +18 -0
- package/skills/guanmetric/evals/fixtures/confirmed-template-summary.json +12 -0
- package/skills/guanmetric/evals/fixtures/create-result-unknown-state.json +13 -0
- package/skills/guanmetric/evals/forward-eval-2026-09-21.json +62 -0
- package/skills/guanmetric/references/metric-api-contract-examples.json +690 -0
- package/skills/guanmetric/references/metric-api-contract.schema.json +349 -0
- package/skills/guanmetric/references/metric-api-contracts.md +436 -0
- package/skills/guanmetric/references/metric-atomic-mutation.md +11 -0
- package/skills/guanmetric/references/metric-business-dictionary-api-contract-examples.json +53 -0
- package/skills/guanmetric/references/metric-business-dictionary-api-contract.schema.json +26 -0
- package/skills/guanmetric/references/metric-business-dictionary.md +4 -0
- package/skills/guanmetric/references/metric-composite-mutation.md +15 -17
- package/skills/guanmetric/references/metric-derived-mutation.md +14 -16
- package/skills/guanmetric/references/metric-excel-template.md +14 -4
- package/skills/guanmetric/references/metric-flow-generate-example.json +79 -0
- package/skills/guanmetric/references/metric-flow-generate.schema.json +93 -0
- package/skills/guanmetric/references/metric-flow.md +218 -0
- package/skills/guanmetric/references/metric-hierarchy-api-contract-examples.json +171 -0
- package/skills/guanmetric/references/metric-hierarchy-api-contract.schema.json +167 -0
- package/skills/guanmetric/references/metric-hierarchy-api-contracts.md +193 -0
- package/skills/guanmetric/references/metric-mutation.md +13 -9
- package/skills/guanmetric/references/metric-public-dimension-api-contract-examples.json +62 -0
- package/skills/guanmetric/references/metric-public-dimension-api-contract.schema.json +47 -0
- package/skills/guanmetric/references/metric-public-dimension.md +4 -0
- package/skills/guanmetric/references/metric-tree-api-contract-examples.json +277 -0
- package/skills/guanmetric/references/metric-tree-api-contract.schema.json +263 -0
- package/skills/guanmetric/references/metric-tree-api-contracts.md +53 -0
- package/skills/guanmetric/references/metric-tree-mutation.md +31 -3
- package/skills/guanmetric/references/metric-validation-ownership.md +74 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
# 指标创建、编辑、详情和列表 API 契约
|
|
2
|
+
|
|
3
|
+
本文给 Agent 提供可直接参考的真实结构。完整脱敏样例见
|
|
4
|
+
`metric-api-contract-examples.json`,机器可读约束见
|
|
5
|
+
`metric-api-contract.schema.json`。
|
|
6
|
+
|
|
7
|
+
## 证据与适用范围
|
|
8
|
+
|
|
9
|
+
- 2026-09-21 在 test71 使用 `guancli`/`guanmetric` 做了只读查询和隔离测试资源实测。
|
|
10
|
+
- 复合/衍生专用创建、编辑、影响校验和写后回读已使用账号密码认证实测;本轮未使用 PAT 做有权、无权和资源不存在对照,PAT 兼容性仍标记为未验证。
|
|
11
|
+
- 复合指标和六种衍生配置均完成创建;复合、衍生完成原地编辑、详情回读和列表查询;创建/编辑响应均为 `{result:"ok",response:{id}}`。
|
|
12
|
+
- 原子字段模式和表达式模式的详情结构来自真实指标回读;写请求字段同时对照当前 BI 写 DTO 和 Web 对应语义。
|
|
13
|
+
- test71 当前原子创建接口对非空 `applicableDims` 仍返回 `5001 适用维度无效`,因此本文不把该次失败称为成功实测。请求结构仍是当前后端 DTO 与 CLI 的正式契约;在目标环境提交前必须先 dry-run,并以真实创建和回读为最终结论。
|
|
14
|
+
- 所有样例均已脱敏。字段结构、空值形态和读写差异保留;样例 ID 不能直接拿去调用真实环境。
|
|
15
|
+
- 响应允许后端新增字段。Schema 对响应使用 `additionalProperties: true`,Agent 不应因为出现新字段而拒绝解析。
|
|
16
|
+
|
|
17
|
+
## 接口矩阵
|
|
18
|
+
|
|
19
|
+
| 操作 | 方法与路径 | 请求 | 成功响应 |
|
|
20
|
+
| --- | --- | --- | --- |
|
|
21
|
+
| 创建原子 | `POST /api/metric-platform/metrics/atomic-metrics` | `CreateAtomicMetricRO` | mutation success |
|
|
22
|
+
| 编辑原子 | `POST /api/metric-platform/metrics/atomic-metrics/{id}` | `UpdateAtomicMetricRO` | mutation success |
|
|
23
|
+
| 创建复合 | `POST /api/metric-platform/metrics/composite-metrics` | `CreateCompositeMetricRO` | mutation success |
|
|
24
|
+
| 编辑复合 | `POST /api/metric-platform/metrics/composite-metrics/{id}` | `UpdateCompositeMetricRO` | mutation success |
|
|
25
|
+
| 创建衍生 | `POST /api/metric-platform/metrics/derived-metrics` | `CreateDerivedMetricRO` | mutation success |
|
|
26
|
+
| 编辑衍生 | `POST /api/metric-platform/metrics/derived-metrics/{id}` | `UpdateDerivedMetricRO` | mutation success |
|
|
27
|
+
| 指标详情 | `GET /api/metric-platform/metrics/{id}` | 无;草稿加 `?fromDraft=true` | detail response |
|
|
28
|
+
| 指标列表 | `POST /api/metric-platform/metrics` | list request | list response |
|
|
29
|
+
|
|
30
|
+
以上是客户端实际路径。BI 后端路由是去掉 `/api` 后的同路径。写接口分别需要
|
|
31
|
+
`METRIC_PLATFORM` 的 `CREATE` 或 `UPDATE` 权限,详情和列表需要 `READ` 权限。
|
|
32
|
+
|
|
33
|
+
## 原始响应与 CLI canonical 输出
|
|
34
|
+
|
|
35
|
+
本文描述的是 `--raw` 或 `guanmetric fetch` 看到的后端原始 envelope:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"result": "ok",
|
|
40
|
+
"response": {}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`guancli metric get/search -f json` 是供普通机器发现使用的 canonical 输出,外层通常是
|
|
45
|
+
`schemaVersion/command/resultMode/rowCount/data`。canonical 输出会主动省略部分写入字段,
|
|
46
|
+
例如 `formula`、`formulaField`、`deriveSetting`,所以不能用它重建编辑请求。需要编辑时用
|
|
47
|
+
`--raw` 读取完整详情,再按本文映射成写模型。
|
|
48
|
+
|
|
49
|
+
`guanmetric create/edit` 的人类输出可能只展示解包后的 `{"id":"..."}`;这不改变底层 API
|
|
50
|
+
响应契约。
|
|
51
|
+
|
|
52
|
+
## 创建和编辑成功响应
|
|
53
|
+
|
|
54
|
+
三种指标的创建和编辑共用同一响应:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"result": "ok",
|
|
59
|
+
"response": {
|
|
60
|
+
"id": "metric_example_id"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
只收到 HTTP 2xx 但 `result` 不是 `ok`,或 `response.id` 缺失,不能认为创建/编辑成功。
|
|
66
|
+
创建后必须详情回读;上线指标还必须按 `metric-flow.md` 验证状态、权限和查询可用性。
|
|
67
|
+
|
|
68
|
+
## 创建与编辑的公共字段
|
|
69
|
+
|
|
70
|
+
| 字段 | 创建 | 编辑 | 类型 | 说明 |
|
|
71
|
+
| --- | --- | --- | --- | --- |
|
|
72
|
+
| `projectId` | 必填 | 必填 | string | 指标主题 ID |
|
|
73
|
+
| `subType` | 必填 | 必填 | enum | `ATOMIC/COMPOSITE/DERIVED` |
|
|
74
|
+
| `name` | 必填 | 必填 | string | 指标名称 |
|
|
75
|
+
| `engName` | 可选 | 可选 | string | 英文名 |
|
|
76
|
+
| `desc` | 必填 | 必填 | string | 业务口径 |
|
|
77
|
+
| `format` | 可选 | 可选 | object | 数字格式 |
|
|
78
|
+
| `filter` | 可选 | 可选 | object/null | WebFilterData |
|
|
79
|
+
| `bizAttrs` | 可选 | 可选 | array | 业务属性 `{id,values}` |
|
|
80
|
+
| `dynamicParameters` | 可选 | 可选 | array | 动态参数默认值 |
|
|
81
|
+
| `publish` | 必填 | 必填 | boolean | `false` 草稿,`true` 上线/进入审批 |
|
|
82
|
+
| `bizMetricId` | 可选 | 可选 | string | 关联业务指标 |
|
|
83
|
+
| `parentDirId` | 必填 | 不传 | string | 创建位置;编辑不会通过定义接口移动目录 |
|
|
84
|
+
| `businessOwner` | 必填 | 不传 | string | 创建负责人;详情中变成用户对象 |
|
|
85
|
+
|
|
86
|
+
编辑接口接收完整更新写模型,不是 JSON Merge Patch。`guanmetric edit` 可以让用户提供 patch,
|
|
87
|
+
但 CLI 必须先把详情读模型规范化成完整写模型,再合并 patch。
|
|
88
|
+
|
|
89
|
+
## 原子指标
|
|
90
|
+
|
|
91
|
+
### 字段来源模式
|
|
92
|
+
|
|
93
|
+
写模型必须同时提供 `fdId` 和 `aggrType`:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"projectId": "project_demo",
|
|
98
|
+
"subType": "ATOMIC",
|
|
99
|
+
"dsId": "ds_orders",
|
|
100
|
+
"name": "销售额",
|
|
101
|
+
"fdId": "fd_amount",
|
|
102
|
+
"aggrType": "SUM",
|
|
103
|
+
"applicableDims": ["fd_order_date", "fd_region"],
|
|
104
|
+
"timeDim": "fd_order_date",
|
|
105
|
+
"desc": "订单销售额合计",
|
|
106
|
+
"parentDirId": "dir_demo",
|
|
107
|
+
"businessOwner": "user_owner",
|
|
108
|
+
"publish": false
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
详情不返回顶层 `fdId`,而是返回:
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"result": "ok",
|
|
117
|
+
"response": {
|
|
118
|
+
"subType": "ATOMIC",
|
|
119
|
+
"dsInfo": {"dsId": "ds_orders", "name": "订单明细"},
|
|
120
|
+
"fdInfos": [
|
|
121
|
+
{"fdId": "fd_amount", "name": "订单金额", "fdType": "DOUBLE", "metaType": "METRIC"}
|
|
122
|
+
],
|
|
123
|
+
"aggrType": "SUM",
|
|
124
|
+
"applicableDims": [
|
|
125
|
+
{"fdId": "fd_region", "name": "区域", "fdType": "STRING", "metaType": "DIM"}
|
|
126
|
+
],
|
|
127
|
+
"timeDim": {"fdId": "fd_order_date", "name": "订单日期", "fdType": "DATE"}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 表达式模式
|
|
133
|
+
|
|
134
|
+
表达式模式只传 `formulaField.fdFormula`,不能再传 `fdId/aggrType`:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"projectId": "project_demo",
|
|
139
|
+
"subType": "ATOMIC",
|
|
140
|
+
"dsId": "ds_orders",
|
|
141
|
+
"name": "含税销售额",
|
|
142
|
+
"formulaField": {
|
|
143
|
+
"fdFormula": "SUM([fd_amount]) + SUM([fd_tax])"
|
|
144
|
+
},
|
|
145
|
+
"applicableDims": ["fd_order_date", "fd_region"],
|
|
146
|
+
"timeDim": "fd_order_date",
|
|
147
|
+
"desc": "订单金额与税额之和",
|
|
148
|
+
"parentDirId": "dir_demo",
|
|
149
|
+
"businessOwner": "user_owner",
|
|
150
|
+
"publish": false
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
真实详情会同时返回表达式和表达式引用的 `fdInfos`:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"formulaField": {
|
|
159
|
+
"fdFormula": "SUM([fd_amount]) + SUM([fd_tax])",
|
|
160
|
+
"fdType": "DOUBLE"
|
|
161
|
+
},
|
|
162
|
+
"fdInfos": [
|
|
163
|
+
{"fdId": "fd_amount", "name": "订单金额"},
|
|
164
|
+
{"fdId": "fd_tax", "name": "税额"}
|
|
165
|
+
],
|
|
166
|
+
"aggrType": null
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
这里的 `fdInfos` 只是读模型的解析结果,不能把 `fdInfos[0]` 错当成表达式指标的 `fdId`。
|
|
171
|
+
判断顺序必须是:有非空 `formulaField.fdFormula` 就保留表达式模式;否则才从
|
|
172
|
+
`fdInfos[0].fdId` 恢复字段来源模式。
|
|
173
|
+
|
|
174
|
+
## 复合指标
|
|
175
|
+
|
|
176
|
+
请求核心字段:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"projectId": "project_demo",
|
|
181
|
+
"subType": "COMPOSITE",
|
|
182
|
+
"name": "客单价",
|
|
183
|
+
"formula": "[metric_sales] / [metric_order_count]",
|
|
184
|
+
"useExpressionMetricCommonDims": true,
|
|
185
|
+
"applicableDims": ["fd_order_date", "fd_region"],
|
|
186
|
+
"timeDim": "fd_order_date",
|
|
187
|
+
"desc": "销售额除以订单数",
|
|
188
|
+
"parentDirId": "dir_demo",
|
|
189
|
+
"businessOwner": "user_owner",
|
|
190
|
+
"publish": false
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
详情保留 `formula`,并增加 `referencedMetrics`;`applicableDims` 和 `timeDim` 都变成字段对象:
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"formula": "[metric_sales] / [metric_order_count]",
|
|
199
|
+
"referencedMetrics": [
|
|
200
|
+
{
|
|
201
|
+
"id": "metric_sales",
|
|
202
|
+
"name": "销售额",
|
|
203
|
+
"subType": "ATOMIC",
|
|
204
|
+
"status": "PUBLISHED",
|
|
205
|
+
"timeDim": {"fdId": "fd_order_date"}
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
"applicableDims": [
|
|
209
|
+
{"fdId": "fd_region", "name": "区域", "fdType": "STRING", "metaType": "DIM"}
|
|
210
|
+
],
|
|
211
|
+
"timeDim": {"fdId": "fd_order_date", "name": "订单日期"}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## 衍生指标
|
|
216
|
+
|
|
217
|
+
普通衍生请求必须有 `baseMetricId`;组合衍生不传 `baseMetricId`,在
|
|
218
|
+
`deriveSetting.formula` 中引用指标。详情把 `baseMetricId` 展开为 `baseMetric` 对象;组合衍生
|
|
219
|
+
还返回 `referencedMetrics`。
|
|
220
|
+
|
|
221
|
+
六种已实测配置如下,完整请求和详情见 examples JSON。
|
|
222
|
+
|
|
223
|
+
### 对比 `COMPARATIVE`
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"deriveType": "ORDINARY",
|
|
228
|
+
"advType": "COMPARATIVE",
|
|
229
|
+
"advValue": {
|
|
230
|
+
"advType": "COMPARATIVE",
|
|
231
|
+
"dateFdId": "fd_order_date_month",
|
|
232
|
+
"valueType": "VALUE",
|
|
233
|
+
"offset": 1,
|
|
234
|
+
"offsetType": "MONTH",
|
|
235
|
+
"granularity": "MONTH",
|
|
236
|
+
"window": {"offset": 0}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### 最近 N `RECENT_X`
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"deriveType": "ORDINARY",
|
|
246
|
+
"advType": "RECENT_X",
|
|
247
|
+
"advValue": {
|
|
248
|
+
"advType": "RECENT_X",
|
|
249
|
+
"granularity": "DAY",
|
|
250
|
+
"baselineOffset": [6, 0],
|
|
251
|
+
"baseline": "DATE_RANGE_PARAM_RIGHT",
|
|
252
|
+
"aggrType": "SUM",
|
|
253
|
+
"includeCurrentValue": true
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### 累计 `XTD`
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
{
|
|
262
|
+
"deriveType": "ORDINARY",
|
|
263
|
+
"advType": "XTD",
|
|
264
|
+
"advValue": {
|
|
265
|
+
"advType": "XTD",
|
|
266
|
+
"granularity": "MONTH",
|
|
267
|
+
"baseline": "DATE_RANGE_PARAM_RIGHT",
|
|
268
|
+
"includeCurrentValue": true
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### 期末值 `LAST`
|
|
274
|
+
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"deriveType": "ORDINARY",
|
|
278
|
+
"advType": "LAST",
|
|
279
|
+
"advValue": {
|
|
280
|
+
"advType": "LAST",
|
|
281
|
+
"dateFdId": "fd_order_date",
|
|
282
|
+
"calculationMode": "LAST_NON_EMPTY"
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
部分历史详情没有 `calculationMode`。编辑时应保留详情里的真实值;新建时按用户选择传
|
|
288
|
+
`NATURAL_LAST_DAY` 或 `LAST_NON_EMPTY`,不要从字段名猜测。
|
|
289
|
+
|
|
290
|
+
### 业务限定 `BIZ_CONDITION`
|
|
291
|
+
|
|
292
|
+
```json
|
|
293
|
+
{
|
|
294
|
+
"deriveSetting": {
|
|
295
|
+
"deriveType": "ORDINARY",
|
|
296
|
+
"advType": "BIZ_CONDITION",
|
|
297
|
+
"advValue": {"advType": "BIZ_CONDITION"}
|
|
298
|
+
},
|
|
299
|
+
"filter": {
|
|
300
|
+
"type": "condition",
|
|
301
|
+
"value": {
|
|
302
|
+
"dsId": "ds_orders",
|
|
303
|
+
"fdId": "fd_region",
|
|
304
|
+
"name": "区域",
|
|
305
|
+
"fdType": "STRING",
|
|
306
|
+
"metaType": "DIM",
|
|
307
|
+
"filterType": "IN",
|
|
308
|
+
"filterValue": ["华东"]
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
真实回读会补 `filter.not=false`。筛选字段必须属于基础指标的 `applicableDims`。
|
|
315
|
+
|
|
316
|
+
### 组合衍生 `deriveType=COMPOSITE`
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"deriveType": "COMPOSITE",
|
|
321
|
+
"formula": "[metric_sales_recent_7d] + [metric_sales_mtd]"
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## 详情公共结构
|
|
326
|
+
|
|
327
|
+
三类详情至少包含下面的公共语义;字段是否出现受类型、状态、权限和版本影响:
|
|
328
|
+
|
|
329
|
+
| 字段 | 类型 | 说明 |
|
|
330
|
+
| --- | --- | --- |
|
|
331
|
+
| `id` | string | 指标 ID |
|
|
332
|
+
| `projectId/projectName` | string | 主题 |
|
|
333
|
+
| `parentDirId/dirPath` | string/array | 所属目录 |
|
|
334
|
+
| `subType` | enum | 也可能读到只读类型 `SHORTCUT` |
|
|
335
|
+
| `uniformResourceType` | string | `METRIC_ATOMIC/COMPOSITE/DERIVED` 等 |
|
|
336
|
+
| `status` | string | 常见 `PUBLISHED/UNPUBLISHED` |
|
|
337
|
+
| `name/engName/desc` | string | 基础属性 |
|
|
338
|
+
| `businessOwner/creator` | object | 用户读模型,不是写请求中的用户 ID |
|
|
339
|
+
| `applicableDims` | field[] | 字段对象数组,不是 ID 数组 |
|
|
340
|
+
| `timeDim` | field/null | 字段对象,不是 ID |
|
|
341
|
+
| `format/filter/bizAttrs` | object/array/null | 可选定义 |
|
|
342
|
+
| `canManage/canUse` | boolean | 当前用户权限 |
|
|
343
|
+
| `hasDraft` | boolean/null | 草稿标识,部分衍生详情可能缺失或为 null |
|
|
344
|
+
| `statisticsData` | object | 引用、浏览统计,只读 |
|
|
345
|
+
| `metricChartConfig` | object | 图表配置,只读 |
|
|
346
|
+
| `ctime/utime` | string | 时间,只读 |
|
|
347
|
+
|
|
348
|
+
`SHORTCUT` 可能出现在列表和详情,但当前 skill 没有它的创建/编辑契约;遇到时只能读取,
|
|
349
|
+
不要伪造写请求。
|
|
350
|
+
|
|
351
|
+
## 列表请求与响应
|
|
352
|
+
|
|
353
|
+
列表接口是 POST,请求示例:
|
|
354
|
+
|
|
355
|
+
```json
|
|
356
|
+
{
|
|
357
|
+
"projectId": "project_demo",
|
|
358
|
+
"dirId": "dir_demo",
|
|
359
|
+
"status": "PUBLISHED",
|
|
360
|
+
"nameLike": "销售",
|
|
361
|
+
"subTypes": "ATOMIC,COMPOSITE,DERIVED",
|
|
362
|
+
"orderBy": "UTIME_DESC",
|
|
363
|
+
"offset": 0,
|
|
364
|
+
"limit": 20
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
关键点:`subTypes` 是逗号分隔字符串,不是数组。`creator`、`businessOwner` 才是字符串数组。
|
|
369
|
+
分页启用时 `limit` 最大 100。
|
|
370
|
+
|
|
371
|
+
真实响应外形:
|
|
372
|
+
|
|
373
|
+
```json
|
|
374
|
+
{
|
|
375
|
+
"result": "ok",
|
|
376
|
+
"response": {
|
|
377
|
+
"contents": [
|
|
378
|
+
{
|
|
379
|
+
"id": "metric_atomic_sales",
|
|
380
|
+
"projectId": "project_demo",
|
|
381
|
+
"status": "PUBLISHED",
|
|
382
|
+
"subType": "ATOMIC",
|
|
383
|
+
"name": "销售额",
|
|
384
|
+
"desc": "订单销售额合计",
|
|
385
|
+
"dirId": "dir_demo",
|
|
386
|
+
"businessOwner": {"uId": "user_owner", "name": "指标负责人"},
|
|
387
|
+
"creator": {"uId": "user_creator", "name": "创建人"},
|
|
388
|
+
"canManage": true,
|
|
389
|
+
"canUse": true,
|
|
390
|
+
"timeDim": {"fdId": "fd_order_date"},
|
|
391
|
+
"hasDraft": false,
|
|
392
|
+
"favorite": false,
|
|
393
|
+
"approvalStatus": null
|
|
394
|
+
}
|
|
395
|
+
],
|
|
396
|
+
"count": 1,
|
|
397
|
+
"dirPath": [{"dirId": "dir_demo", "dirName": "销售指标"}]
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
不同查询路径/版本可能同时返回 `limit/returnedCount`,也可能返回 `dirPath`。解析器应要求
|
|
403
|
+
`contents/count`,其余作为可选字段。
|
|
404
|
+
|
|
405
|
+
## 读模型到更新写模型映射
|
|
406
|
+
|
|
407
|
+
| 详情读模型 | 更新写模型 | 规则 |
|
|
408
|
+
| --- | --- | --- |
|
|
409
|
+
| `dsInfo.dsId` | `dsId` | 原子指标 |
|
|
410
|
+
| `fdInfos[0].fdId` | `fdId` | 仅字段来源模式 |
|
|
411
|
+
| `aggrType` | `aggrType` | 与 `fdId` 同时存在 |
|
|
412
|
+
| `formulaField.fdFormula` | `formulaField.fdFormula` | 表达式模式优先,丢弃读模型 `fdType` |
|
|
413
|
+
| `applicableDims[].fdId` | `applicableDims[]` | 对象数组压成 ID 数组 |
|
|
414
|
+
| `timeDim.fdId` | `timeDim` | 对象压成 ID;null/缺失则不传 |
|
|
415
|
+
| `businessOwner.uId` | 无 | 更新 DTO 不接收负责人 |
|
|
416
|
+
| `baseMetric.id` | `baseMetricId` | 普通衍生;组合衍生不传 |
|
|
417
|
+
| `formula` | `formula` | 复合指标原样保留 |
|
|
418
|
+
| `deriveSetting` | `deriveSetting` | 深拷贝并保留变体字段 |
|
|
419
|
+
| `filter/format/bizAttrs` | 同名字段 | 保留业务定义 |
|
|
420
|
+
|
|
421
|
+
必须剔除只读键,包括但不限于:`id`、`domId`、`projectName`、`parentDirId`、`dirPath`、
|
|
422
|
+
`businessOwner`、`creator`、`ctime`、`utime`、`canManage`、`canUse`、`statisticsData`、
|
|
423
|
+
`hasDraft`、`metricChartConfig`、`uniformResourceType`、`sourceDsIds`、`publicDimensionMap`、
|
|
424
|
+
`referencedMetrics`、`baseMetric`、`fdInfos`、`dsInfo`、`isLastSupported`。
|
|
425
|
+
|
|
426
|
+
不要维护“详情减掉若干字段后直接提交”的黑名单算法;正确做法是按对应 Update DTO 白名单显式构造。
|
|
427
|
+
|
|
428
|
+
## Agent 使用顺序
|
|
429
|
+
|
|
430
|
+
1. 先判断 `subType` 和具体模式/衍生变体。
|
|
431
|
+
2. 从 examples JSON 取同类型样例,只替换业务值和真实 ID。
|
|
432
|
+
3. 用 schema 检查字段类型,并按类型文档做业务校验。
|
|
433
|
+
4. 创建/编辑先 dry-run;编辑必须详情回读后显式构造更新写模型。
|
|
434
|
+
5. 提交后要求 `result=ok` 且 `response.id` 非空。
|
|
435
|
+
6. 用详情回读验证定义;列表只用于发现和汇总,不能替代详情。
|
|
436
|
+
7. 要求“可用”时继续执行 `metric-flow.md` 的状态、权限和查询验收。
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
原子指标用于在单个数据集字段或数据集字段表达式上定义指标口径,例如“订购金额 = SUM(订单金额)”。
|
|
4
4
|
|
|
5
|
+
创建/编辑请求、真实详情响应和读写映射见 `metric-api-contracts.md`;机器样例见
|
|
6
|
+
`metric-api-contract-examples.json`。不要根据详情响应猜测写模型。
|
|
7
|
+
|
|
5
8
|
## 触发场景
|
|
6
9
|
|
|
7
10
|
- 用户说“创建原子指标”“基于数据集建指标”“用某个字段求和/计数/平均”。
|
|
@@ -186,6 +189,14 @@ guancli metric get <metric_id> --brief
|
|
|
186
189
|
|
|
187
190
|
编辑原子指标时复用 Web 顺序:必要时先口径冲突校验,再影响校验,最后提交。
|
|
188
191
|
|
|
192
|
+
默认编辑模式接收 patch。CLI 会先读取指标详情,将详情读模型规范化为更新写模型(例如
|
|
193
|
+
`fdInfos[0].fdId -> fdId`、维度对象数组 -> 字段 ID 数组),剔除只读字段,再合并 patch。
|
|
194
|
+
提交前必须仍保留一种完整取数方式:`fdId + aggrType` 或
|
|
195
|
+
`formulaField.fdFormula`。无法安全恢复取数字段时直接拒绝提交,不能用缺少取数方式的请求继续保存。
|
|
196
|
+
|
|
197
|
+
原子指标 `edit --replace` 已停用。编辑必须提交 patch,由命令优先读取草稿详情;仅当详情接口以结构化业务状态明确表示草稿不存在时才回退正式详情,再按 Update DTO 白名单构造完整请求。不得把 `guancli metric get --raw` 的详情响应直接回写。
|
|
198
|
+
patch 本身也受同一 Update DTO 白名单约束:`id/status/fdInfos/canUse` 等读模型字段会在读取详情和发请求前被拒绝,`subType` 不能改成非 `ATOMIC`。当 filter 只给出 `fdId` 时,规范化必须使用本次实际选中的草稿或正式详情中的 `applicableDims` 元数据,不能重新读取正式版本覆盖草稿上下文。
|
|
199
|
+
|
|
189
200
|
1. 读取当前详情:
|
|
190
201
|
|
|
191
202
|
```bash
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0",
|
|
3
|
+
"source": {
|
|
4
|
+
"environment": "test153",
|
|
5
|
+
"capturedAt": "2026-09-21",
|
|
6
|
+
"authentication": "account-password",
|
|
7
|
+
"note": "由隔离临时业务字典真实 CRUD 请求采集后脱敏;PAT 未验证,示例 ID 不可用于调用。"
|
|
8
|
+
},
|
|
9
|
+
"createRequest": {"name": "销售额", "desc": "订单实付金额汇总", "dirId": "business_dir_root", "bizAttrs": []},
|
|
10
|
+
"createResponse": {"result": "ok", "response": "business_dictionary_example"},
|
|
11
|
+
"updateRequest": {"name": "含税销售额", "desc": "订单含税实付金额汇总", "dirId": "business_dir_root", "bizAttrs": []},
|
|
12
|
+
"updateResponse": {"result": "ok", "response": "business_dictionary_example"},
|
|
13
|
+
"detailResponse": {
|
|
14
|
+
"result": "ok",
|
|
15
|
+
"response": {
|
|
16
|
+
"id": "business_dictionary_example",
|
|
17
|
+
"domId": "example_domain",
|
|
18
|
+
"dirId": "business_dir_root",
|
|
19
|
+
"name": "含税销售额",
|
|
20
|
+
"desc": "订单含税实付金额汇总",
|
|
21
|
+
"bizAttrs": [],
|
|
22
|
+
"creator": "user_example",
|
|
23
|
+
"creatorName": "示例用户",
|
|
24
|
+
"ctime": "2026-09-21 14:04:11",
|
|
25
|
+
"utime": "2026-09-21 14:04:30",
|
|
26
|
+
"associatedMetrics": []
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"listResponse": {
|
|
30
|
+
"result": "ok",
|
|
31
|
+
"response": {
|
|
32
|
+
"list": [{
|
|
33
|
+
"id": "business_dictionary_example",
|
|
34
|
+
"domId": "example_domain",
|
|
35
|
+
"dirId": "business_dir_root",
|
|
36
|
+
"name": "含税销售额",
|
|
37
|
+
"desc": "订单含税实付金额汇总",
|
|
38
|
+
"bizAttrs": [],
|
|
39
|
+
"creator": "user_example",
|
|
40
|
+
"creatorName": "示例用户",
|
|
41
|
+
"ctime": "2026-09-21 14:04:11",
|
|
42
|
+
"utime": "2026-09-21 14:04:30",
|
|
43
|
+
"associatedMetrics": []
|
|
44
|
+
}],
|
|
45
|
+
"total": 1
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"treeResponse": {
|
|
49
|
+
"result": "ok",
|
|
50
|
+
"response": {"children": [{"dirId": "business_dir_root", "dirName": "根目录", "parentDirId": "", "children": []}]}
|
|
51
|
+
},
|
|
52
|
+
"deleteResponse": {"result": "ok", "response": true}
|
|
53
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://guandata.example/schemas/metric-business-dictionary-api-contract.schema.json",
|
|
4
|
+
"title": "GuanData metric business dictionary API contracts",
|
|
5
|
+
"$defs": {
|
|
6
|
+
"nonEmptyString": {"type": "string", "minLength": 1},
|
|
7
|
+
"bizAttr": {"type": "object", "required": ["id", "values"], "properties": {"id": {"type": "string"}, "values": {"type": "array", "items": {"type": "string"}}}, "additionalProperties": true},
|
|
8
|
+
"writeRequest": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"required": ["name", "desc"],
|
|
11
|
+
"properties": {"name": {"$ref": "#/$defs/nonEmptyString"}, "desc": {"type": "string"}, "dirId": {"type": "string"}, "bizAttrs": {"type": "array", "items": {"$ref": "#/$defs/bizAttr"}}},
|
|
12
|
+
"additionalProperties": false
|
|
13
|
+
},
|
|
14
|
+
"mutationResponse": {"type": "object", "required": ["result", "response"], "properties": {"result": {"const": "ok"}, "response": {"$ref": "#/$defs/nonEmptyString"}}, "additionalProperties": true},
|
|
15
|
+
"dictionary": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["id", "domId", "name", "desc", "bizAttrs", "creator", "creatorName", "ctime", "utime", "associatedMetrics"],
|
|
18
|
+
"properties": {"id": {"type": "string"}, "domId": {"type": "string"}, "dirId": {"type": "string"}, "name": {"type": "string"}, "desc": {"type": "string"}, "bizAttrs": {"type": "array"}, "creator": {"type": "string"}, "creatorName": {"type": "string"}, "ctime": {"type": "string"}, "utime": {"type": "string"}, "associatedMetrics": {"type": "array"}},
|
|
19
|
+
"additionalProperties": true
|
|
20
|
+
},
|
|
21
|
+
"detailResponse": {"type": "object", "required": ["result", "response"], "properties": {"result": {"const": "ok"}, "response": {"$ref": "#/$defs/dictionary"}}, "additionalProperties": true},
|
|
22
|
+
"listResponse": {"type": "object", "required": ["result", "response"], "properties": {"result": {"const": "ok"}, "response": {"type": "object", "required": ["list", "total"], "properties": {"list": {"type": "array", "items": {"$ref": "#/$defs/dictionary"}}, "total": {"type": "number"}}, "additionalProperties": true}}, "additionalProperties": true},
|
|
23
|
+
"treeResponse": {"type": "object", "required": ["result", "response"], "properties": {"result": {"const": "ok"}, "response": {"type": "object", "required": ["children"], "properties": {"children": {"type": "array"}}, "additionalProperties": true}}, "additionalProperties": true},
|
|
24
|
+
"deleteResponse": {"type": "object", "required": ["result", "response"], "properties": {"result": {"const": "ok"}, "response": {"type": "boolean"}}, "additionalProperties": true}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
业务字典在后端接口和代码中也称 `BusinessMetric`,CLI 使用面向产品语义的命令名 `business-dict`,并兼容 `business-metric`、`biz-dict` 别名。
|
|
4
4
|
|
|
5
|
+
构造或解析请求和响应前先读取 `metric-business-dictionary-api-contract-examples.json` 与 `metric-business-dictionary-api-contract.schema.json`。样例于 2026-09-21 在 test153 使用隔离临时业务字典和账号密码认证完成 create、detail/list、edit、delete 后脱敏;PAT 尚未验证。仓库测试会按 Schema 校验真实快照的必需字段和类型,同时允许后端新增可选字段。
|
|
6
|
+
|
|
7
|
+
真实响应外层统一为 `{ "result": "ok", "response": ... }`:创建和编辑成功时 `response` 是业务字典 ID 字符串;详情包含 `id/domId/dirId/name/desc/bizAttrs/creator/creatorName/ctime/utime/associatedMetrics`;搜索返回 `response.list/total`;删除成功返回布尔值。详情是读模型,编辑只白名单提交 `name/desc/dirId/bizAttrs`。
|
|
8
|
+
|
|
5
9
|
## 查询
|
|
6
10
|
|
|
7
11
|
```bash
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
复合指标用于通过公式组合已有指标,例如“客单价 = 销售额 / 订单数”。
|
|
4
4
|
|
|
5
|
+
创建/编辑请求、真实详情响应和读写映射见 `metric-api-contracts.md`;机器样例见
|
|
6
|
+
`metric-api-contract-examples.json`。
|
|
7
|
+
|
|
5
8
|
## 触发场景
|
|
6
9
|
|
|
7
10
|
- 用户说“创建复合指标”“基于多个指标算一个指标”。
|
|
@@ -119,17 +122,18 @@
|
|
|
119
122
|
|
|
120
123
|
## 创建步骤
|
|
121
124
|
|
|
122
|
-
`guanmetric create`
|
|
125
|
+
单个复合指标使用 `guanmetric composite create`;对话式批量创建或公式引用本批指标时,优先按 `metric-flow.md` 使用 `guanmetric flow`。
|
|
123
126
|
|
|
124
127
|
1. 先输出指标创建计划,按 `metric-mutation.md` 的“指标创建计划”要求展示环境、主题、目录、将复用或新建的基础指标、复合公式、共有适用维度/时间维度、格式、业务属性和保存方式。
|
|
125
128
|
2. 用户确认计划后,生成 `/tmp/composite_metric.json`。
|
|
126
|
-
3. 本地/人工检查 JSON、公式引用指标、共有适用维度、必填业务属性;逐个读取引用指标详情,确认 `subType` 仅为 `ATOMIC` 或 `COMPOSITE`。`guanmetric
|
|
129
|
+
3. 本地/人工检查 JSON、公式引用指标、共有适用维度、必填业务属性;逐个读取引用指标详情,确认 `subType` 仅为 `ATOMIC` 或 `COMPOSITE`。`guanmetric composite create` 也会在提交前执行同一类型检查。
|
|
127
130
|
4. 按 `metric-common-fields.md` 向用户摘要并确认。
|
|
128
131
|
5. 用户确认后,不调用原子指标 `save-precheck`;复合指标 Web 创建没有 save-precheck。
|
|
129
132
|
6. 提交真实创建:
|
|
130
133
|
|
|
131
134
|
```bash
|
|
132
|
-
guanmetric
|
|
135
|
+
guanmetric composite create --file /tmp/composite_metric.json --dry-run
|
|
136
|
+
guanmetric composite create --file /tmp/composite_metric.json
|
|
133
137
|
```
|
|
134
138
|
|
|
135
139
|
7. 响应返回指标 ID 后回读:
|
|
@@ -142,26 +146,20 @@ guancli metric get <metric_id> --brief
|
|
|
142
146
|
|
|
143
147
|
复合指标没有原子指标的 save-precheck;主要校验公式或筛选变化是否影响下游指标/卡片。
|
|
144
148
|
|
|
145
|
-
1.
|
|
146
|
-
|
|
147
|
-
```bash
|
|
148
|
-
guancli metric get <metric_id> --raw
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
2. 基于当前详情生成完整编辑 payload,例如 `/tmp/composite_metric_update.json`。至少包含 `projectId`、`subType`、`name`、`formula`、`useExpressionMetricCommonDims`、`applicableDims`、`desc`、`publish`。
|
|
152
|
-
3. 如果修改了 `formula` 或 `filter`,先调用影响校验:
|
|
149
|
+
1. 准备只包含本次变更字段的 patch,例如 `/tmp/composite_metric_patch.json`;不要复制或回写详情读模型。
|
|
150
|
+
2. 先 dry-run。命令会读取当前详情,按复合指标 Update DTO 白名单构造完整写模型并输出影响校验和更新步骤:
|
|
153
151
|
|
|
154
152
|
```bash
|
|
155
|
-
guanmetric
|
|
153
|
+
guanmetric composite edit <metric_id> --file /tmp/composite_metric_patch.json --dry-run
|
|
156
154
|
```
|
|
157
155
|
|
|
158
|
-
|
|
159
|
-
5. 提交真实编辑:
|
|
156
|
+
3. 提交真实编辑。命令自动调用 `validate-update-impact`;存在下游影响时会阻断并返回影响清单,用户确认后才追加 `--confirm`:
|
|
160
157
|
|
|
161
158
|
```bash
|
|
162
|
-
guanmetric
|
|
159
|
+
guanmetric composite edit <metric_id> --file /tmp/composite_metric_patch.json
|
|
160
|
+
guanmetric composite edit <metric_id> --file /tmp/composite_metric_patch.json --confirm
|
|
163
161
|
```
|
|
164
162
|
|
|
165
|
-
|
|
163
|
+
4. 命令保存后自动回读,校验名称、口径、公式及维度等已提交字段;`useExpressionMetricCommonDims=true` 时适用维度由后端按引用指标共有维度重算,不做输入数组逐项相等判断。
|
|
166
164
|
|
|
167
|
-
|
|
165
|
+
编辑命令内部读取后端详情,但只通过白名单生成写模型;`referencedMetrics`、`creator`、`statisticsData`、对象数组 `applicableDims`、对象 `timeDim` 等读模型字段绝不能原样提交。
|