# 复合指标创建与编辑

复合指标用于通过公式组合已有指标，例如“客单价 = 销售额 / 订单数”。

## 触发场景

- 用户说“创建复合指标”“基于多个指标算一个指标”。
- 用户描述的口径是已有指标之间的四则运算。
- 用户给出数据集字段计算表达式后，确认希望先创建/复用基础原子指标，再创建复合指标。
- 用户要编辑一个 `subType=COMPOSITE` 的指标。

## 创建必填信息

| 字段 | 中文 | 说明 |
| --- | --- | --- |
| `projectId` | 主题 ID | 指标所属主题 |
| `subType` | 指标类型 | 固定 `COMPOSITE` |
| `name` | 指标名称 | 最长 50 |
| `formula` | 计算公式 | 使用已有指标 ID，形如 `[metric_sales] / [metric_orders]` |
| `useExpressionMetricCommonDims` | 是否沿用公式指标共有维度 | Web 默认 `true` |
| `applicableDims` | 适用维度 | 引用指标的共有维度字段 ID 数组；手动选择时不能为空 |
| `desc` | 业务口径 | 最长 1000 |
| `parentDirId` | 保存至/所属文件夹 | 创建时需要 |
| `businessOwner` | 责任人 | 创建时需要 |
| `publish` | 保存方式 | `true` 上线，`false` 草稿 |

专属可选字段：

| 字段 | 中文 | 说明 |
| --- | --- | --- |
| `timeDim` | 时间维度 | 最终适用维度中存在时间字段时选择一个 |

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

## 公式与引用指标

复合指标公式在最终 payload 中使用指标 ID，而不是指标中文名：

```text
[metric_sales_amount] / [metric_order_count]
```

处理规则：

- 涉及复合指标 `formula` 时，先读取 `metric-expression-rules.md`。
- 先用 `guancli metric search <关键词> -d <projectId> -f json` 查询公式中的指标，再用 `guancli metric get <metric_id> --brief` 核对名称、类型、状态和适用维度。
- 如果用户只提供数据集字段级公式，先询问是否要创建复合指标。用户确认后，再识别公式里的基础度量，创建或复用基础原子指标，并把公式改写为 `[metricId]`。
- 公式只能引用数值型指标；不要引用当前正在编辑的指标本身。
- 公式支持 `+`、`-`、`*`、`/`、括号、数字和 `[指标ID]`；不要主动生成函数、窗口函数或 SQL 片段。
- 公式中的 `[xxx]` 会被后端解析为上游指标 ID，并参与循环引用校验。
- 如果用户用中文名描述公式，先转换成 ID 公式，再把“中文公式”和“ID 公式”都展示给用户确认。

用户确认创建复合指标后的字段公式改写示例：

| 用户口径 | 建模方式 |
| --- | --- |
| `SUM([销售额字段]) / COUNT([订单ID字段])` | 先建“销售额”“订单数”原子指标，再建复合指标 `[metric_sales] / [metric_order_count]` |
| `SUM([归因销售额字段]) / SUM([广告花费字段])` | 先建“归因销售额”“广告花费”原子指标，再建复合指标 ROAS |
| `SUM([点击量字段]) / SUM([曝光量字段])` | 先建“点击量”“曝光量”原子指标，再建复合指标 CTR |
| `COUNT(CASE WHEN ... THEN 1 ELSE NULL END) / COUNT([ID字段])` | 先判断分子是否应建为条件计数原子指标或业务限定衍生指标，再创建复合比率指标 |

## 适用维度

复合指标的适用维度来自公式中引用指标的共有维度：

- `useExpressionMetricCommonDims=true`：沿用表达式中指标的共用维度。需要根据引用指标详情整理共有维度；如果没有共有维度，Web 会报“计算公式来源指标无共有适用维度，请检查”。
- `useExpressionMetricCommonDims=false`：从共有维度中手动选择 `applicableDims`，至少选择一个。
- 如果最终适用维度中有时间类型字段，需要选择一个 `timeDim`。
- 不要把某个上游指标独有的维度写进 `applicableDims`。

## 信息收集

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

1. 指标主题和保存目录。
2. 公式中要引用哪些已有指标；中文名先搜索确认唯一指标。
3. 计算公式，转换成 `[metric_id]` 形式。
4. 是否沿用共有维度；如果手动选择，确认具体维度和时间维度。
5. 数据格式：默认、数值、货币、百分比；如需指定，确认小数位、千分位、货币符号/前后缀、数量级单位、百分比是否除以 100。
6. 指标名称、业务口径、是否保存并上线。
7. 责任人和必填 `bizAttrs`。

## JSON 模板

```json
{
  "projectId": "<metric_project_id>",
  "subType": "COMPOSITE",
  "name": "客单价",
  "engName": "avg_order_value",
  "formula": "[<sales_amount_metric_id>] / [<order_count_metric_id>]",
  "useExpressionMetricCommonDims": true,
  "applicableDims": ["<common_dim_fd_id_1>", "<common_dim_fd_id_2>"],
  "timeDim": "<time_dim_fd_id>",
  "format": {
    "formatType": "NUMBER",
    "decimalPlaces": 2,
    "useThousandsSeparator": true,
    "prefixUnit": null,
    "showPrefixUnit": true,
    "prefix": "",
    "suffix": ""
  },
  "desc": "销售金额除以订单数，用于衡量平均每笔订单贡献的销售额。",
  "parentDirId": "<metric_dir_id>",
  "businessOwner": "<user_id>",
  "publish": false
}
```

没有时间维度时删除 `timeDim`；没有英文名称时删除 `engName`；未指定数据格式时删除 `format` 或传 `{}`。如需业务属性，按 `metric-common-fields.md` 补充 `bizAttrs`。

## 创建步骤

`guancli metric create` 当前是原子指标专用 endpoint；复合指标创建使用 `guancli fetch`。

1. 先输出指标创建计划，按 `metric-mutation.md` 的“指标创建计划”要求展示环境、主题、目录、将复用或新建的基础指标、复合公式、共有适用维度/时间维度、格式、业务属性和保存方式。
2. 用户确认计划后，生成 `/tmp/composite_metric.json`。
3. 本地/人工检查 JSON、公式引用指标、共有适用维度、必填业务属性。
4. 按 `metric-common-fields.md` 向用户摘要并确认。
5. 用户确认后，不调用原子指标 `save-precheck`；复合指标 Web 创建没有 save-precheck。
6. 提交真实创建：

```bash
guancli fetch POST /api/metric-platform/metrics/composite-metrics --stdin < /tmp/composite_metric.json
```

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

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

## 编辑步骤

复合指标没有原子指标的 save-precheck；主要校验公式或筛选变化是否影响下游指标/卡片。

1. 读取当前详情：

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

2. 基于当前详情生成完整编辑 payload，例如 `/tmp/composite_metric_update.json`。至少包含 `projectId`、`subType`、`name`、`formula`、`useExpressionMetricCommonDims`、`applicableDims`、`desc`、`publish`。
3. 如果修改了 `formula` 或 `filter`，先调用影响校验：

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

4. 如果返回 `hasChanged=true` 且 `affectedMetrics` 或 `affectedCards` 非空，展示受影响指标和卡片，等待用户二次确认。
5. 提交真实编辑：

```bash
guancli fetch POST /api/metric-platform/metrics/composite-metrics/<metric_id> --stdin < /tmp/composite_metric_update.json
```

6. 保存后回读确认。
