@guandata/guanmetric 0.1.0 → 0.1.2

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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## @guandata/guanmetric 0.1.2 - 2026-07-20
4
+
5
+ - 全局安装或升级后自动刷新 AI Skill,减少 CLI 与 Agent 使用说明版本不一致的情况。
6
+ - 随包提供完整的指标管理说明和参考资料。
7
+
8
+ ## @guandata/guanmetric 0.1.1 - 2026-07-10
9
+
10
+ - 增强指标 Excel 标准化能力,支持按原子、复合和多类衍生指标拆分标准模板。
11
+ - 新增汇总 sheet,便于用户总览待创建指标、行状态和问题说明。
12
+ - 优化批量创建前的校验和下一步提示,帮助 Agent 引导用户补齐缺失信息后再创建指标。
13
+
3
14
  ## @guandata/guanmetric 0.1.0 - 2026-07-08
4
15
 
5
16
  - 新增指标写操作 CLI,承接指标创建、编辑、删除、指标主题、指标目录和公共维度管理能力。
package/README.md CHANGED
@@ -9,6 +9,8 @@ npm install -g @guandata/guanmetric
9
9
  guanmetric install-skill
10
10
  ```
11
11
 
12
+ > `npm install -g` / `npm link` 全局安装时会通过 postinstall 自动执行一次 skill 安装/刷新;上述命令用于手动重装或排查。CI 等无需 skill 的环境可设 `GUAN_SKIP_INSTALL_SKILL=1` 跳过。
13
+
12
14
  ## 常用命令
13
15
 
14
16
  ```bash
@@ -23,6 +25,17 @@ guanmetric public-dim create --file public_dim.json --dry-run
23
25
 
24
26
  ## 版本更新
25
27
 
28
+ ### @guandata/guanmetric 0.1.2
29
+
30
+ - 全局安装或升级后自动刷新 AI Skill,减少 CLI 与 Agent 使用说明版本不一致的情况。
31
+ - 随包提供完整的指标管理说明和参考资料。
32
+
33
+ ### @guandata/guanmetric 0.1.1
34
+
35
+ - 增强指标 Excel 标准化能力,支持按原子、复合和多类衍生指标拆分标准模板。
36
+ - 新增汇总 sheet,便于用户总览待创建指标、行状态和问题说明。
37
+ - 优化批量创建前的校验和下一步提示,帮助 Agent 引导用户补齐缺失信息后再创建指标。
38
+
26
39
  ### @guandata/guanmetric 0.1.0
27
40
 
28
41
  - 新增指标写操作 CLI,承接指标创建、编辑、删除、指标主题、指标目录和公共维度管理能力。
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Best-effort post-install hook: refresh the installed AI skill right after
5
+ * a global npm install/upgrade, so users cannot forget to run
6
+ * `guanmetric install-skill` and end up with a stale SKILL.md.
7
+ *
8
+ * Constraints:
9
+ * - MUST never fail the npm install: every failure path still exits 0.
10
+ * - Only runs for global installs (`npm install -g` / `npm link`); a local
11
+ * `npm install` inside a project never touches the user's home dirs.
12
+ * - Escape hatch: GUAN_SKIP_INSTALL_SKILL=1 skips entirely (e.g. CI images
13
+ * that install the CLI but never run an AI agent are skipped by default).
14
+ */
15
+
16
+ "use strict";
17
+
18
+ const { spawnSync } = require("child_process");
19
+ const path = require("path");
20
+
21
+ const CLI_NAME = "guanmetric";
22
+
23
+ function skipReason() {
24
+ if (process.env.GUAN_SKIP_INSTALL_SKILL === "1") return "GUAN_SKIP_INSTALL_SKILL=1";
25
+ if (process.env.npm_config_global !== "true") return "not a global install";
26
+ if (process.env.CI) return "CI environment";
27
+ return "";
28
+ }
29
+
30
+ function main() {
31
+ const reason = skipReason();
32
+ if (reason) {
33
+ console.log(`[${CLI_NAME}] postinstall: skipping automatic install-skill (${reason}).`);
34
+ return;
35
+ }
36
+ console.log(`[${CLI_NAME}] postinstall: refreshing AI skill (${CLI_NAME} install-skill)...`);
37
+ const result = spawnSync(
38
+ process.execPath,
39
+ [path.join(__dirname, "run.js"), "install-skill"],
40
+ { stdio: "inherit", env: process.env, timeout: 180000 }
41
+ );
42
+ if (result.error || result.status !== 0) {
43
+ console.warn(
44
+ `[${CLI_NAME}] postinstall: automatic skill install did not complete` +
45
+ (result.error ? ` (${result.error.message})` : ` (exit ${result.status})`) +
46
+ `; run \`${CLI_NAME} install-skill\` manually to refresh SKILL.md.`
47
+ );
48
+ }
49
+ }
50
+
51
+ try {
52
+ main();
53
+ } catch (err) {
54
+ console.warn(
55
+ `[${CLI_NAME}] postinstall: ${err.message}; run \`${CLI_NAME} install-skill\` manually.`
56
+ );
57
+ }
58
+ process.exit(0);
package/bin/run.js CHANGED
@@ -86,10 +86,36 @@ if (process.argv[2] === "version" && process.argv.length === 3) {
86
86
  process.exit(0);
87
87
  }
88
88
 
89
+
90
+ // 定位 npm 自带的 npx-cli.js 并用 node 直接执行(与 guanskill 保持一致),
91
+ // 避免 Windows 上 spawn npx.cmd 的两类问题:shell:true 不做参数 quoting
92
+ // (全局安装路径含空格时会被拆参),以及新版 Node 禁止无 shell 的 .cmd
93
+ // spawn(CVE-2024-27980)。postinstall 场景下 npm_execpath 必然可用。
94
+ function resolveNpxInvocation() {
95
+ const executableDir = path.dirname(process.execPath);
96
+ const candidates = [];
97
+ if (process.env.npm_execpath) {
98
+ candidates.push(path.join(path.dirname(process.env.npm_execpath), "npx-cli.js"));
99
+ }
100
+ candidates.push(path.join(executableDir, "node_modules", "npm", "bin", "npx-cli.js"));
101
+ candidates.push(path.resolve(executableDir, "..", "node_modules", "npm", "bin", "npx-cli.js"));
102
+ candidates.push(path.resolve(executableDir, "..", "lib", "node_modules", "npm", "bin", "npx-cli.js"));
103
+ const npxCliPath = candidates.find((candidate) => fs.existsSync(candidate));
104
+ if (npxCliPath) {
105
+ return { command: process.execPath, argsPrefix: [npxCliPath] };
106
+ }
107
+ if (process.platform === "win32") {
108
+ throw new Error("Cannot locate npm/bin/npx-cli.js for safe Windows execution");
109
+ }
110
+ return { command: "npx", argsPrefix: [] };
111
+ }
112
+
89
113
  if (process.argv[2] === "install-skill") {
90
114
  const pkgRoot = path.join(__dirname, "..");
91
115
  const extraArgs = process.argv.slice(3);
92
116
  const args = [
117
+ // --yes: postinstall 等非交互环境下 npx 需要免确认下载 skills CLI
118
+ "--yes",
93
119
  "skills",
94
120
  "add",
95
121
  pkgRoot,
@@ -100,7 +126,12 @@ if (process.argv[2] === "install-skill") {
100
126
  ...extraArgs,
101
127
  ];
102
128
  console.log("Installing guanmetric to AI coding assistants...");
103
- const result = spawnSync("npx", args, { stdio: "inherit", env: process.env, shell: true });
129
+ const npxInvocation = resolveNpxInvocation();
130
+ const result = spawnSync(npxInvocation.command, [...npxInvocation.argsPrefix, ...args], {
131
+ stdio: "inherit",
132
+ env: process.env,
133
+ shell: false,
134
+ });
104
135
  if (result.error) throw result.error;
105
136
  const status = result.status || 0;
106
137
  if (status === 0) installBuddySkills(pkgRoot, "guanmetric");
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@guandata/guanmetric",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "观远 BI 指标写操作工具 - 指标创建编辑删除、指标目录、公共维度、Excel 标准化",
5
5
  "bin": {
6
6
  "guanmetric": "bin/run.js"
7
7
  },
8
8
  "scripts": {
9
+ "postinstall": "node bin/postinstall.js",
9
10
  "build": "node scripts/build.js && node scripts/sync-skill.js",
10
11
  "changelog": "node ../../scripts/generate-release-changelog.js .",
11
12
  "check-changelog": "node ../../scripts/check-release-changelog.js .",
@@ -21,7 +21,21 @@ guanmetric public-dim create --file public_dim.json --dry-run
21
21
  guanmetric fetch POST /api/metric-platform/metrics/composite-metrics --file composite_metric.json
22
22
  ```
23
23
 
24
- 批量创建指标时,必须先执行 `guanmetric template normalize`,把标准 Excel 返回给用户确认;确认前不生成最终 payload,不提交真实创建。
24
+ 批量创建指标时,必须先生成固定结构的标准 Excel 返回给用户确认;确认前不生成最终 payload,不提交真实创建。
25
+
26
+ 如果用户提供的是客户原始 Excel/CSV,而不是已经接近标准模板的文件,agent 必须先自行读取并理解表格语义,产出一个语义映射中间表(建议命名为 `*.ai_mapped.xlsx`),再执行 `guanmetric template normalize` 生成最终标准模板。不要直接把客户原始 Excel 丢给 CLI 后交付结果;CLI 不负责穷举客户列名或业务表述。CLI 继续负责固定模板结构、必填项标记、业务属性配置读取、自动填充说明、行状态、问题说明、高亮样式、列宽自适应、基础校验和 report 输出。
27
+
28
+ 如果用户没有提供 Excel/CSV,而是给出一段文字、需求描述、粘贴的半结构化表格、截图转写内容或其它非文件信息,也必须先由 agent 理解语义并整理成同样的 `*.ai_mapped.xlsx` 中间表,再执行 `guanmetric template normalize` 生成标准模板。信息不足时不要直接放弃;应尽量补出可判断的指标行,并在标准模板的 `问题说明` 中列出缺失的主题、目录、数据集、字段、聚合方式、公式、衍生自、适用维度、时间维度或业务属性,让用户在返回的标准模板中修改后重新上传。
29
+
30
+ 如果 report 的 `notes` 提示“疑似业务语义列被丢弃但复合/衍生 sheet 为空”,说明 agent 跳过了语义映射步骤,必须返回重新读取原始 Excel、拆分原子/复合/衍生后再标准化,不要把全原子模板交给用户。
31
+
32
+ 如果 report 的 `notes` 提示“疑似衍生语义”、某个衍生 sheet 为空,或“所有衍生指标 sheet 均为空”,说明用户输入或业务口径中可能包含同比/环比、最近 N/时间窗口、累计/历史累计、期末值、业务限定等衍生语义,但 agent 没有产出对应衍生模板行。必须回到语义映射步骤重新识别并补充对应类型的衍生指标;不要把衍生指标缺类的模板直接交付给用户。
33
+
34
+ 标准 Excel 第一张固定为 `汇总` sheet,用于给用户总览本次待创建指标总数、按类型计数、指标名称、指标类型、所在模板、关键来源、行状态和问题说明;创建 payload 仍以各指标类型模板 sheet 为事实源,不从 `汇总` sheet 创建。
35
+
36
+ 生成复合指标模板时,如果 `计算公式` 引用的 `[指标名]` 不在原子指标模板中,agent 必须补充同名原子指标行,保证创建时先有基础原子指标,再创建复合指标。
37
+
38
+ 生成衍生指标模板时,如果 `衍生自` 指标不是已明确存在的线上指标,而是本批也要创建的指标,必须在标准模板中补充对应的原子指标或复合指标行;信息不足时先补行并在该基础指标行的 `问题说明` 中列出缺失的数据集、来源字段、聚合方式、计算公式、适用维度等。不要等到创建计划阶段才临时新增基础指标。
25
39
 
26
40
  `fetch` 只用于指标平台写入接口尚未封装为专用命令的场景,例如复合指标/衍生指标创建、编辑和影响校验;常规原子指标、主题、目录、公共维度优先使用专用命令。
27
41
 
@@ -0,0 +1,225 @@
1
+ # 原子指标创建与编辑
2
+
3
+ 原子指标用于在单个数据集字段或数据集字段表达式上定义指标口径,例如“订购金额 = SUM(订单金额)”。
4
+
5
+ ## 触发场景
6
+
7
+ - 用户说“创建原子指标”“基于数据集建指标”“用某个字段求和/计数/平均”。
8
+ - 用户给出数据集、字段、聚合方式。
9
+ - 用户要编辑一个 `subType=ATOMIC` 的指标。
10
+
11
+ ## 创建必填信息
12
+
13
+ | 字段 | 中文 | 说明 |
14
+ | --- | --- | --- |
15
+ | `projectId` | 主题 ID | 指标所属主题 |
16
+ | `subType` | 指标类型 | 固定 `ATOMIC` |
17
+ | `dsId` | 数据集 ID | 指标来源数据集 |
18
+ | `name` | 指标名称 | 最长 50 |
19
+ | `desc` | 业务口径 | 最长 1000 |
20
+ | `applicableDims` | 适用维度 | 数据集字段 ID 数组,不能为空 |
21
+ | `parentDirId` | 保存至/所属文件夹 | 指标目录 ID |
22
+ | `businessOwner` | 责任人 | 用户 ID |
23
+ | `publish` | 保存方式 | `true` 上线,`false` 草稿 |
24
+
25
+ 计算方式二选一:
26
+
27
+ | 模式 | 必填字段 | 说明 |
28
+ | --- | --- | --- |
29
+ | 来源字段 + 聚合方式 | `fdId` + `aggrType` | 最常见;标准 Excel 填 `来源字段` 和 `聚合方式`;来源字段是数据集字段 ID |
30
+ | 表达式 | `formulaField.fdFormula` | 标准 Excel 填 `字段表达式`;例如 `SUM([fd_amount]) + SUM([fd_tax])`;不要同时传 `fdId/aggrType`;支持范围见 `metric-expression-rules.md` |
31
+
32
+ ## 原子专属可选字段
33
+
34
+ | 字段 | 中文 | 说明 |
35
+ | --- | --- | --- |
36
+ | `timeDim` | 时间维度 | 如果适用维度中包含时间字段,优先让用户指定一个时间维度字段 ID |
37
+ | `sourceMetricId` | 来源指标 ID | 复制/快捷等高级场景才传 |
38
+
39
+ 通用可选字段见 `metric-common-fields.md`。
40
+
41
+ ## 聚合方式
42
+
43
+ | `aggrType` | 中文 |
44
+ | --- | --- |
45
+ | `SUM` | 求和 |
46
+ | `MIN` | 最小值 |
47
+ | `MAX` | 最大值 |
48
+ | `AVG` | 平均值 |
49
+ | `CNT` | 计数 |
50
+ | `CNT_DISTINCT` | 去重计数 |
51
+ | `NUL` | 无处理 |
52
+
53
+ 字段判断:
54
+
55
+ - 用户只给字段中文名时,先用 `guancli ds get <dsId> --brief` 查字段和 `fdId`。
56
+ - 来源字段是维度字段时,常用计数、去重计数、最大值、最小值、无处理。
57
+ - 来源字段是指标/数值字段时,常用求和、平均值、最大值、最小值、计数、去重计数。
58
+
59
+ ## 表达式限制
60
+
61
+ 当用户选择“使用表达式”时,先读取 `metric-expression-rules.md`。默认只生成当前前后端明确支持的聚合表达式形态:
62
+
63
+ - `SUM([fd_id])`、`COUNT([fd_id])`
64
+ - 多个聚合项加减:`SUM([fd_a]) - SUM([fd_b])`
65
+ - 条件聚合:`SUM(CASE WHEN ... THEN ... ELSE ... END)`
66
+
67
+ 不要把 `AVG/MAX/MIN/FIRST/LAST/ANY_VALUE` 主动写进 `formulaField.fdFormula`;如果用户要平均值、最大值、最小值,优先走“来源字段 + `aggrType`”。表达式中字段必须写 `[fdId]`,不要提交字段中文名。
68
+
69
+ ### 计算表达式确认
70
+
71
+ 遇到计算表达式时,不要直接替用户决定创建原子表达式还是复合指标。先说明两种方案并询问:
72
+
73
+ - 原子表达式:直接在当前数据集字段上生成 `formulaField.fdFormula`。
74
+ - 复合指标:先创建或复用表达式中的基础原子指标,再用 `[metricId]` 公式创建复合指标。
75
+
76
+ 用户选择复合指标后,再拆解基础度量。例如 `SUM([attributed_sales_fd]) / SUM([spend_amount_fd])` 可先创建或复用“归因销售额”“广告花费”两个原子指标,再创建复合指标:
77
+
78
+ ```text
79
+ [metric_attributed_sales] / [metric_ad_spend]
80
+ ```
81
+
82
+ 用户选择原子表达式,或明确要求“数据集字段表达式原子指标”时,才使用 `formulaField.fdFormula`。
83
+
84
+ ## 信息收集
85
+
86
+ 如果用户只说“帮我创建一个原子指标”,先收集:
87
+
88
+ 1. 指标主题和保存目录。
89
+ 2. 来源数据集和来源字段。
90
+ 3. 计算方式:来源字段 + 聚合方式,还是表达式;如果是表达式,先询问用户是否改用复合指标。用户选择原子表达式时,再按 `metric-expression-rules.md` 校验支持形态。
91
+ 4. 指标名称、业务口径、适用维度、时间维度、是否保存并上线。
92
+ 5. 数据格式:默认、数值、货币、百分比;如需指定,确认小数位、千分位、货币符号/前后缀、数量级单位、百分比是否除以 100。
93
+ 6. 责任人是否用当前登录用户。
94
+ 7. 指标业务属性中启用且必填的 `bizAttrs`。
95
+
96
+ ## JSON 模板:字段聚合
97
+
98
+ ```json
99
+ {
100
+ "projectId": "<metric_project_id>",
101
+ "subType": "ATOMIC",
102
+ "dsId": "<ds_id>",
103
+ "name": "订购金额",
104
+ "engName": "order_amount",
105
+ "fdId": "<source_fd_id>",
106
+ "aggrType": "SUM",
107
+ "applicableDims": ["<dim_fd_id_1>", "<dim_fd_id_2>"],
108
+ "timeDim": "<time_dim_fd_id>",
109
+ "format": {
110
+ "formatType": "CURRENCY",
111
+ "decimalPlaces": 2,
112
+ "useThousandsSeparator": true,
113
+ "prefix": "¥",
114
+ "prefixUnit": null,
115
+ "showPrefixUnit": true,
116
+ "suffix": ""
117
+ },
118
+ "desc": "统计订单明细中的订购金额总和,用于分析销售规模。",
119
+ "parentDirId": "<metric_dir_id>",
120
+ "businessOwner": "<user_id>",
121
+ "publish": false
122
+ }
123
+ ```
124
+
125
+ 没有时间维度时删除 `timeDim`;没有英文名称时删除 `engName`;未指定数据格式时删除 `format` 或传 `{}`。
126
+
127
+ ## JSON 模板:表达式
128
+
129
+ ```json
130
+ {
131
+ "projectId": "<metric_project_id>",
132
+ "subType": "ATOMIC",
133
+ "dsId": "<ds_id>",
134
+ "name": "含税销售额",
135
+ "formulaField": {
136
+ "fdFormula": "SUM([fd_amount]) + SUM([fd_tax])"
137
+ },
138
+ "applicableDims": ["<dim_fd_id_1>", "<dim_fd_id_2>"],
139
+ "format": {
140
+ "formatType": "NUMBER",
141
+ "decimalPlaces": 2,
142
+ "useThousandsSeparator": true,
143
+ "prefixUnit": null,
144
+ "showPrefixUnit": true,
145
+ "prefix": "",
146
+ "suffix": ""
147
+ },
148
+ "desc": "销售金额与税额之和。",
149
+ "parentDirId": "<metric_dir_id>",
150
+ "businessOwner": "<user_id>",
151
+ "publish": false
152
+ }
153
+ ```
154
+
155
+ ## 创建步骤
156
+
157
+ 1. 先输出指标创建计划,按 `metric-mutation.md` 的“指标创建计划”要求展示环境、主题、目录、来源数据集/字段、聚合方式或表达式、适用维度/时间维度、格式、业务属性和保存方式。
158
+ 2. 用户确认计划后,生成 `/tmp/atomic_metric.json`。
159
+ 3. 先 dry-run:
160
+
161
+ ```bash
162
+ guanmetric create --file /tmp/atomic_metric.json --dry-run
163
+ ```
164
+
165
+ 4. 按 `metric-common-fields.md` 向用户摘要并确认。
166
+ 5. 如果 `publish=true`,先跑 save-precheck:
167
+
168
+ ```bash
169
+ guanmetric fetch POST /api/metric-platform/metrics/atomic-metrics/save-precheck --stdin < /tmp/atomic_metric.json
170
+ ```
171
+
172
+ 6. 如果返回 `conflicts` 且非空,展示冲突指标、冲突口径、适用维度差异,等待用户二次确认。
173
+ 7. 校验通过或二次确认后提交:
174
+
175
+ ```bash
176
+ guanmetric create --file /tmp/atomic_metric.json -f json
177
+ ```
178
+
179
+ 8. 响应返回指标 ID 后回读:
180
+
181
+ ```bash
182
+ guancli metric get <metric_id> --brief
183
+ ```
184
+
185
+ ## 编辑步骤
186
+
187
+ 编辑原子指标时复用 Web 顺序:必要时先口径冲突校验,再影响校验,最后提交。
188
+
189
+ 1. 读取当前详情:
190
+
191
+ ```bash
192
+ guancli metric get <metric_id> -f json
193
+ ```
194
+
195
+ 2. 生成 patch 或完整 payload,先 dry-run:
196
+
197
+ ```bash
198
+ guanmetric edit <metric_id> --file /tmp/atomic_metric_update.json --dry-run
199
+ ```
200
+
201
+ 3. 如果 `publish=true`,调用 save-precheck。编辑态请求体需要带 `metricId`:
202
+
203
+ ```bash
204
+ guanmetric fetch POST /api/metric-platform/metrics/atomic-metrics/save-precheck --stdin < /tmp/atomic_metric_update_precheck.json
205
+ ```
206
+
207
+ 其中 precheck JSON 等于编辑 payload 加:
208
+
209
+ ```json
210
+ { "metricId": "<metric_id>" }
211
+ ```
212
+
213
+ 4. 如果存在 `conflicts`,展示后等待二次确认。
214
+ 5. 调用编辑影响校验:
215
+
216
+ ```bash
217
+ guanmetric fetch POST /api/metric-platform/metrics/atomic-metrics/<metric_id>/validate-update-impact --stdin < /tmp/atomic_metric_update.json
218
+ ```
219
+
220
+ 6. 如果返回 `hasChanged=true` 且 `affectedMetrics` 或 `affectedCards` 非空,展示影响并等待二次确认。
221
+ 7. 提交真实编辑:
222
+
223
+ ```bash
224
+ guanmetric edit <metric_id> --file /tmp/atomic_metric_update.json -f json
225
+ ```
@@ -0,0 +1,168 @@
1
+ # 指标通用配置
2
+
3
+ 本文记录原子指标、复合指标、衍生指标共享的写操作规则、通用字段和业务属性处理方式。处理任一指标创建/编辑任务时,都应先读取本文,再读取对应类型文档。
4
+
5
+ ## 环境与查询
6
+
7
+ - 默认使用用户当前已登录环境,不询问“创建到哪个 profile”。可先执行 `guancli auth status` 确认登录态。
8
+ - 只有用户明确指定其它环境时,才统一追加 `--profile <profile>`。
9
+ - 用户只给中文名称时,先查询并展示候选;查不到或有多个候选再向用户确认。
10
+ - 常用查询:
11
+ - 指标主题:`guancli metric project [keyword] -f json`
12
+ - 指标目录:`guancli metric tree -f json`
13
+ - 指标:`guancli metric search <keyword> -f json`
14
+ - 指标详情:`guancli metric get <metric_id> --brief`
15
+ - 数据集:`guancli ds search <keyword>`、`guancli ds get <dsId> --brief`
16
+
17
+ ## 通用字段
18
+
19
+ | 字段 | 中文 | 适用范围 | 说明 |
20
+ | --- | --- | --- | --- |
21
+ | `projectId` | 主题 ID | 创建/编辑 | 指标所属主题 |
22
+ | `subType` | 指标类型 | 创建/编辑 | `ATOMIC`、`COMPOSITE`、`DERIVED` |
23
+ | `name` | 指标名称 | 创建/编辑 | 最长 50;仅中文、字母、数字、下划线 |
24
+ | `engName` | 英文名称 | 可选 | 最长 50;仅字母、数字、下划线 |
25
+ | `desc` | 业务口径 | 创建/编辑 | 最长 1000;必须能解释业务含义 |
26
+ | `format` | 数据格式 | 可选 | 指标数值展示格式;省略或传 `{}` 表示默认格式 |
27
+ | `parentDirId` | 保存至/所属文件夹 | 创建必填 | 指标目录 ID |
28
+ | `businessOwner` | 责任人 | 创建必填 | 用户 ID,默认可用当前登录用户 |
29
+ | `publish` | 保存方式 | 创建/编辑 | `true` 保存并上线;`false` 保存为草稿 |
30
+ | `bizMetricId` | 关联业务字典 ID | 可选 | 关联后业务口径/业务属性通常会同步 |
31
+ | `bizAttrs` | 业务属性 | 条件必填 | 由环境中的指标属性配置决定 |
32
+ | `filter` | 业务限定/筛选 | 可选或条件必填 | 业务限定衍生指标必填 |
33
+ | `dynamicParameters` | 动态参数默认值 | 可选 | 表达式或最近 N 配置引用动态参数时使用 |
34
+
35
+ ## 数据格式 `format`
36
+
37
+ `format` 是指标数值的展示格式,创建和编辑原子指标、复合指标、衍生指标时都可设置。保存指标时写入 Web 表单使用的 UI 配置,不要写转换后的 d3 `specifier`、Excel format 或预览接口返回值。
38
+
39
+ 省略 `format` 或传 `{}` 表示使用默认格式。用户没有明确要求时,不要为了补全字段强行设置格式;用户提到“保留两位小数”“显示百分比”“人民币”“万元”“不要千分位”“加后缀”等,再按下面字段生成。
40
+
41
+ | 字段 | 中文 | 说明 |
42
+ | --- | --- | --- |
43
+ | `formatType` | 格式类型 | `NUMBER` 数值、`CURRENCY` 货币、`PERCENTAGE` 百分比、`ADVANCED` 高级自定义 |
44
+ | `decimalPlaces` | 小数位数 | `null` 表示 0 位;也可传 `1` 到 `15` |
45
+ | `useThousandsSeparator` | 是否使用千分位 | `true/false` |
46
+ | `prefix` | 前缀/货币符号 | 货币常用 `¥`、`$`、`€`、`£`;后端兼容旧字段 `currencySymbol`,新建优先写 `prefix` |
47
+ | `suffix` | 后缀 | 如 `元`、`人`、`单`、`次`;没有则省略或传空字符串 |
48
+ | `prefixUnit` | 数量级单位 | `null` 无,`K`、`M`、`万`、`亿` 或 `auto` 自动 |
49
+ | `showPrefixUnit` | 是否展示数量级单位 | 数值/货币格式可用;`prefixUnit=auto` 时应为 `true` |
50
+ | `divideDataBy` | 百分比是否除以 100 | 百分比格式可用;`1` 适合底层值是比例小数如 `0.85`,`100` 适合底层值已是百分数如 `85` |
51
+ | `customFormat` | 高级自定义格式 | `formatType=ADVANCED` 时使用;除非用户给出现成格式串,否则不要主动生成 |
52
+ | `suffixSizePercent` | 后缀字号比例 | 部分卡片展示场景使用,指标创建/编辑通常不需要 |
53
+
54
+ 常用模板:
55
+
56
+ ```jsonc
57
+ // 默认格式:省略 format 或传 {}
58
+ "format": {}
59
+
60
+ // 数值:千分位,保留 2 位小数
61
+ "format": {
62
+ "formatType": "NUMBER",
63
+ "decimalPlaces": 2,
64
+ "useThousandsSeparator": true,
65
+ "prefixUnit": null,
66
+ "showPrefixUnit": true,
67
+ "prefix": "",
68
+ "suffix": ""
69
+ }
70
+
71
+ // 货币:人民币,千分位,保留 2 位小数
72
+ "format": {
73
+ "formatType": "CURRENCY",
74
+ "decimalPlaces": 2,
75
+ "useThousandsSeparator": true,
76
+ "prefix": "¥",
77
+ "prefixUnit": null,
78
+ "showPrefixUnit": true,
79
+ "suffix": ""
80
+ }
81
+
82
+ // 百分比:底层值为 0.85 时展示为 85.00%
83
+ "format": {
84
+ "formatType": "PERCENTAGE",
85
+ "decimalPlaces": 2,
86
+ "divideDataBy": 1,
87
+ "useThousandsSeparator": false,
88
+ "prefix": "",
89
+ "suffix": ""
90
+ }
91
+
92
+ // 百分比:底层值为 85 时展示为 85.00%
93
+ "format": {
94
+ "formatType": "PERCENTAGE",
95
+ "decimalPlaces": 2,
96
+ "divideDataBy": 100,
97
+ "useThousandsSeparator": false,
98
+ "prefix": "",
99
+ "suffix": ""
100
+ }
101
+
102
+ // 数值:按万元展示
103
+ "format": {
104
+ "formatType": "NUMBER",
105
+ "decimalPlaces": 2,
106
+ "useThousandsSeparator": true,
107
+ "prefixUnit": "万",
108
+ "showPrefixUnit": true,
109
+ "prefix": "",
110
+ "suffix": "元"
111
+ }
112
+ ```
113
+
114
+ ## 业务属性 `bizAttrs`
115
+
116
+ `bizAttrs` 是管理员配置的指标自定义业务属性,不是固定字段。创建或编辑指标前读取当前环境配置:
117
+
118
+ ```bash
119
+ guanmetric fetch GET /api/metric-attr-config
120
+ ```
121
+
122
+ 响应关键字段:
123
+
124
+ | 字段 | 中文 | 说明 |
125
+ | --- | --- | --- |
126
+ | `id` | 属性 ID | 写入 `bizAttrs[].id` |
127
+ | `name` | 属性名称 | 用于向用户提问 |
128
+ | `description` | 属性描述 | 可作为输入提示 |
129
+ | `valueType` | 属性值类型 | `ENUM_SINGLE`、`ENUM_MULTIPLE`、`TEXT`、`TEXT_AREA` |
130
+ | `defaultValue` | 默认值 | 用户未指定且允许默认时可采用 |
131
+ | `optionValue` | 枚举选项 | 枚举单选/多选必须从这里选择 |
132
+ | `required` | 是否必填 | `true` 且 `enabled=true` 时必须收集 |
133
+ | `enabled` | 是否启用 | 只处理启用的属性 |
134
+
135
+ 处理规则:
136
+
137
+ - 只展示和写入 `enabled=true` 的属性;其中 `required=true` 是必填项。
138
+ - `ENUM_SINGLE`:`values` 只能有一个值,且必须来自 `optionValue`。
139
+ - `ENUM_MULTIPLE`:`values` 可以有多个值,且都必须来自 `optionValue`。
140
+ - `TEXT`:写入 `values[0]`,Web 单项最大 50 个字符。
141
+ - `TEXT_AREA`:写入 `values[0]`,Web 单项最大 200 个字符。
142
+ - 如果关联了业务字典 `bizMetricId`,业务口径和业务属性通常由业务字典同步;仍需确认最终 payload 中 `desc` 和 `bizAttrs` 已有值。
143
+
144
+ 示例:
145
+
146
+ ```json
147
+ "bizAttrs": [
148
+ { "id": "attr_region", "values": ["华东"] },
149
+ { "id": "attr_tags", "values": ["销售", "核心"] },
150
+ { "id": "attr_owner_note", "values": ["用于经营分析月报"] }
151
+ ]
152
+ ```
153
+
154
+ ## 保存与确认
155
+
156
+ - 先输出“指标创建计划”让用户 review;计划确认前不生成最终提交 JSON,不执行真实创建。
157
+ - 用户确认计划后,再生成 JSON 文件并做 dry-run 或人工摘要,不直接提交。
158
+ - 计划/摘要至少包含:当前环境、主题、目录、指标类型、名称、核心计算方式、依赖顺序、复用/新建指标、适用维度/时间维度、数据格式、业务属性、保存方式。
159
+ - 如果适用维度中存在时间字段但时间维度为空,计划必须标记为待确认,不能继续提交。
160
+ - 用户确认后按类型执行 save-precheck 或 validate-update-impact。
161
+ - 如果校验返回冲突或影响下游资源,展示冲突指标、受影响指标、受影响卡片,并等待二次确认。
162
+ - 写入成功后回读 `guancli metric get <metric_id> --brief`。
163
+
164
+ ## 发布状态判断
165
+
166
+ - 用户说“先别上线”“保存草稿”:`publish=false`。
167
+ - 用户说“直接上线”“发布”:`publish=true`,提交前提醒会创建或更新在线指标。
168
+ - 不要把 `publish=true` 自动当成用户已经确认所有冲突;冲突/影响校验仍需要单独二次确认。