# 原子指标创建与编辑

原子指标用于在单个数据集字段或数据集字段表达式上定义指标口径，例如“订购金额 = SUM(订单金额)”。

## 触发场景

- 用户说“创建原子指标”“基于数据集建指标”“用某个字段求和/计数/平均”。
- 用户给出数据集、字段、聚合方式。
- 用户要编辑一个 `subType=ATOMIC` 的指标。

## 创建必填信息

| 字段 | 中文 | 说明 |
| --- | --- | --- |
| `projectId` | 主题 ID | 指标所属主题 |
| `subType` | 指标类型 | 固定 `ATOMIC` |
| `dsId` | 数据集 ID | 指标来源数据集 |
| `name` | 指标名称 | 最长 50 |
| `desc` | 业务口径 | 最长 1000 |
| `applicableDims` | 适用维度 | 数据集字段 ID 数组，不能为空 |
| `parentDirId` | 保存至/所属文件夹 | 指标目录 ID |
| `businessOwner` | 责任人 | 用户 ID |
| `publish` | 保存方式 | `true` 上线，`false` 草稿 |

计算方式二选一：

| 模式 | 必填字段 | 说明 |
| --- | --- | --- |
| 来源字段 + 聚合方式 | `fdId` + `aggrType` | 最常见；来源字段是数据集字段 ID |
| 表达式 | `formulaField.fdFormula` | 例如 `SUM([fd_amount]) + SUM([fd_tax])`；不要同时传 `fdId/aggrType`；支持范围见 `metric-expression-rules.md` |

## 原子专属可选字段

| 字段 | 中文 | 说明 |
| --- | --- | --- |
| `timeDim` | 时间维度 | 如果适用维度中包含时间字段，优先让用户指定一个时间维度字段 ID |
| `sourceMetricId` | 来源指标 ID | 复制/快捷等高级场景才传 |

通用可选字段见 `metric-common-fields.md`。

## 聚合方式

| `aggrType` | 中文 |
| --- | --- |
| `SUM` | 求和 |
| `MIN` | 最小值 |
| `MAX` | 最大值 |
| `AVG` | 平均值 |
| `CNT` | 计数 |
| `CNT_DISTINCT` | 去重计数 |
| `NUL` | 无处理 |

字段判断：

- 用户只给字段中文名时，先用 `guancli ds get <dsId> --brief` 查字段和 `fdId`。
- 来源字段是维度字段时，常用计数、去重计数、最大值、最小值、无处理。
- 来源字段是指标/数值字段时，常用求和、平均值、最大值、最小值、计数、去重计数。

## 表达式限制

当用户选择“使用表达式”时，先读取 `metric-expression-rules.md`。默认只生成当前前后端明确支持的聚合表达式形态：

- `SUM([fd_id])`、`COUNT([fd_id])`
- 多个聚合项加减：`SUM([fd_a]) - SUM([fd_b])`
- 条件聚合：`SUM(CASE WHEN ... THEN ... ELSE ... END)`

不要把 `AVG/MAX/MIN/FIRST/LAST/ANY_VALUE` 主动写进 `formulaField.fdFormula`；如果用户要平均值、最大值、最小值，优先走“来源字段 + `aggrType`”。表达式中字段必须写 `[fdId]`，不要提交字段中文名。

### 计算表达式确认

遇到计算表达式时，不要直接替用户决定创建原子表达式还是复合指标。先说明两种方案并询问：

- 原子表达式：直接在当前数据集字段上生成 `formulaField.fdFormula`。
- 复合指标：先创建或复用表达式中的基础原子指标，再用 `[metricId]` 公式创建复合指标。

用户选择复合指标后，再拆解基础度量。例如 `SUM([attributed_sales_fd]) / SUM([spend_amount_fd])` 可先创建或复用“归因销售额”“广告花费”两个原子指标，再创建复合指标：

```text
[metric_attributed_sales] / [metric_ad_spend]
```

用户选择原子表达式，或明确要求“数据集字段表达式原子指标”时，才使用 `formulaField.fdFormula`。

## 信息收集

如果用户只说“帮我创建一个原子指标”，先收集：

1. 指标主题和保存目录。
2. 来源数据集和来源字段。
3. 计算方式：来源字段 + 聚合方式，还是表达式；如果是表达式，先询问用户是否改用复合指标。用户选择原子表达式时，再按 `metric-expression-rules.md` 校验支持形态。
4. 指标名称、业务口径、适用维度、时间维度、是否保存并上线。
5. 数据格式：默认、数值、货币、百分比；如需指定，确认小数位、千分位、货币符号/前后缀、数量级单位、百分比是否除以 100。
6. 责任人是否用当前登录用户。
7. 指标业务属性中启用且必填的 `bizAttrs`。

## JSON 模板：字段聚合

```json
{
  "projectId": "<metric_project_id>",
  "subType": "ATOMIC",
  "dsId": "<ds_id>",
  "name": "订购金额",
  "engName": "order_amount",
  "fdId": "<source_fd_id>",
  "aggrType": "SUM",
  "applicableDims": ["<dim_fd_id_1>", "<dim_fd_id_2>"],
  "timeDim": "<time_dim_fd_id>",
  "format": {
    "formatType": "CURRENCY",
    "decimalPlaces": 2,
    "useThousandsSeparator": true,
    "prefix": "￥",
    "prefixUnit": null,
    "showPrefixUnit": true,
    "suffix": ""
  },
  "desc": "统计订单明细中的订购金额总和，用于分析销售规模。",
  "parentDirId": "<metric_dir_id>",
  "businessOwner": "<user_id>",
  "publish": false
}
```

没有时间维度时删除 `timeDim`；没有英文名称时删除 `engName`；未指定数据格式时删除 `format` 或传 `{}`。

## JSON 模板：表达式

```json
{
  "projectId": "<metric_project_id>",
  "subType": "ATOMIC",
  "dsId": "<ds_id>",
  "name": "含税销售额",
  "formulaField": {
    "fdFormula": "SUM([fd_amount]) + SUM([fd_tax])"
  },
  "applicableDims": ["<dim_fd_id_1>", "<dim_fd_id_2>"],
  "format": {
    "formatType": "NUMBER",
    "decimalPlaces": 2,
    "useThousandsSeparator": true,
    "prefixUnit": null,
    "showPrefixUnit": true,
    "prefix": "",
    "suffix": ""
  },
  "desc": "销售金额与税额之和。",
  "parentDirId": "<metric_dir_id>",
  "businessOwner": "<user_id>",
  "publish": false
}
```

## 创建步骤

1. 先输出指标创建计划，按 `metric-mutation.md` 的“指标创建计划”要求展示环境、主题、目录、来源数据集/字段、聚合方式或表达式、适用维度/时间维度、格式、业务属性和保存方式。
2. 用户确认计划后，生成 `/tmp/atomic_metric.json`。
3. 先 dry-run：

```bash
guancli metric create --file /tmp/atomic_metric.json --dry-run
```

4. 按 `metric-common-fields.md` 向用户摘要并确认。
5. 如果 `publish=true`，先跑 save-precheck：

```bash
guancli fetch POST /api/metric-platform/metrics/atomic-metrics/save-precheck --stdin < /tmp/atomic_metric.json
```

6. 如果返回 `conflicts` 且非空，展示冲突指标、冲突口径、适用维度差异，等待用户二次确认。
7. 校验通过或二次确认后提交：

```bash
guancli metric create --file /tmp/atomic_metric.json -f json
```

8. 响应返回指标 ID 后回读：

```bash
guancli metric get <metric_id> --brief
```

## 编辑步骤

编辑原子指标时复用 Web 顺序：必要时先口径冲突校验，再影响校验，最后提交。

1. 读取当前详情：

```bash
guancli metric get <metric_id> -f json
```

2. 生成 patch 或完整 payload，先 dry-run：

```bash
guancli metric edit <metric_id> --file /tmp/atomic_metric_update.json --dry-run
```

3. 如果 `publish=true`，调用 save-precheck。编辑态请求体需要带 `metricId`：

```bash
guancli fetch POST /api/metric-platform/metrics/atomic-metrics/save-precheck --stdin < /tmp/atomic_metric_update_precheck.json
```

其中 precheck JSON 等于编辑 payload 加：

```json
{ "metricId": "<metric_id>" }
```

4. 如果存在 `conflicts`，展示后等待二次确认。
5. 调用编辑影响校验：

```bash
guancli fetch POST /api/metric-platform/metrics/atomic-metrics/<metric_id>/validate-update-impact --stdin < /tmp/atomic_metric_update.json
```

6. 如果返回 `hasChanged=true` 且 `affectedMetrics` 或 `affectedCards` 非空，展示影响并等待二次确认。
7. 提交真实编辑：

```bash
guancli metric edit <metric_id> --file /tmp/atomic_metric_update.json -f json
```
