# 衍生指标创建与编辑

衍生指标用于在基础指标之上配置同比/环比、最近 N、累计、期末值、业务限定，或通过组合衍生公式引用多个指标。

## 触发场景

- 用户说“创建衍生指标”“做同比/环比”“最近 N 天/周/月”“本月累计/年累计”“期末值”“业务限定”。
- 用户要编辑一个 `subType=DERIVED` 的指标。

## 创建必填信息

| 字段 | 中文 | 说明 |
| --- | --- | --- |
| `projectId` | 主题 ID | 指标所属主题 |
| `subType` | 指标类型 | 固定 `DERIVED` |
| `name` | 指标名称 | 最长 50 |
| `deriveSetting` | 衍生配置 | 见下方细节 |
| `desc` | 业务口径 | 最长 1000 |
| `parentDirId` | 保存至/所属文件夹 | 创建时需要 |
| `businessOwner` | 责任人 | 创建时需要 |
| `publish` | 保存方式 | `true` 上线，`false` 草稿 |

普通衍生还需要：

| 字段 | 中文 | 说明 |
| --- | --- | --- |
| `baseMetricId` | 衍生自/基础指标 ID | 不能是快捷引用指标；先用 `metric search/get` 确认 |

组合衍生不选择单个 `baseMetricId`，核心配置在 `deriveSetting.formula`。

通用可选字段见 `metric-common-fields.md`。`BIZ_CONDITION` 类型必须提供顶层 `filter`。

## 衍生类型

| `advType` / `deriveType` | 中文 | 说明 |
| --- | --- | --- |
| `COMPARATIVE` | 同比/环比 | 需要基础指标有时间维度；配置本期、对比期和输出值类型 |
| `RECENT_X` | 最近 N | 只能衍生自原子指标；如最近 7 天、最近 3 月 |
| `XTD` | 累计计算 | 周/月/季/年至今累计，或近 N 个粒度累计 |
| `LAST` | 期末值 | 按基础指标时间维度取期末值 |
| `BIZ_CONDITION` | 业务限定 | 在基础指标上附加业务筛选，顶层 `filter` 必填 |
| `deriveType=COMPOSITE` | 组合衍生 | 通过 `deriveSetting.formula` 引用多个指标 |

## `deriveSetting` 通用结构

| 字段 | 中文 | 说明 |
| --- | --- | --- |
| `deriveType` | 衍生大类 | 普通衍生为 `ORDINARY`；组合衍生为 `COMPOSITE` |
| `advType` | 衍生方式 | 普通衍生必填；组合衍生提交前可删除 |
| `advValue` | 衍生方式配置 | 普通衍生必填；组合衍生可为 `null` 或不传 |
| `formula` | 组合衍生公式 | 组合衍生必填，使用 `[metric_id]` 引用指标 |

时间粒度 `granularity` 常用值：

| 值 | 中文 |
| --- | --- |
| `DAY` | 日 |
| `WEEK` | 周 |
| `MONTH` | 月 |
| `QUARTER` | 季度 |
| `YEAR` | 年 |
| `NONE` | 历史数据 |
| `YEARBYWEEK` | 年同比按周 |
| `ENDOFMONTH` | 上月末 |
| `ENDOFQUARTER` | 上季度末 |
| `ENDOFYEAR` | 上年末 |

基准日期 `baseline` 常用值：

| 值 | 中文 |
| --- | --- |
| `DATE_RANGE_PARAM_LEFT` | 基于日期筛选：开始日期 |
| `DATE_RANGE_PARAM_RIGHT` | 基于日期筛选：结束日期 |

## 同比/环比 `COMPARATIVE`

用于描述本期时间范围、上期/对比期时间范围、输出值类型。

字段明细：

| 字段 | 中文 | 说明 |
| --- | --- | --- |
| `advValue.advType` | 高级计算类型 | 固定 `COMPARATIVE` |
| `dateFdId` | 日期字段 ID | 来自基础指标时间维度；必填 |
| `dateFdName` | 日期字段名称 | 建议带上 |
| `granularity` | 时间粒度 | `DAY/WEEK/MONTH/QUARTER/YEAR` |
| `window` | 本期时间范围 | Web 默认常见为 `{ "offset": 0 }` |
| `offsetType` | 对比方式粒度 | 如 `MONTH` 月环比、`YEAR` 年同比、`ENDOFMONTH` 上月末 |
| `offset` | 偏移量 | 同环比默认 `1`；自定义偏移可大于 1 |
| `fixedBase` | 固定对比期 | 自定义时间范围时使用 |
| `valueType` | 数值设置 | `VALUE`、`RATE`、`RAWDATA`、`CUSTOM` |
| `growthRateType` | 增长率算法 | `RATE` 时必填：`NORMAL` 或 `ABS` |
| `formulaSetting` | 自定义公式 | `CUSTOM` 时必填，由公式编辑器生成 |

`window` 写法：

| 形态 | JSON | 说明 |
| --- | --- | --- |
| 当前粒度本期 | `{ "offset": 0 }` | 当前日/周/月/季/年 |
| 上一个粒度区间 | `{ "offset": 1 }` | 上日/上周/上月等 |
| 相对日期 | `{ "offset": 3 }` | 相对本期前 3 个粒度 |
| 固定区间 | `{ "withMacro": false, "period": ["2026-05-01", "2026-05-31"] }` | 固定日期范围 |
| 时间宏 | `{ "withMacro": true, "period": ["{{{}}}", "{{{}}}"] }` | 使用时间宏表达式 |

数值设置：

| `valueType` | 中文 | 额外字段 |
| --- | --- | --- |
| `VALUE` | 增长值 | 无，含义为本期数据 - 上期数据 |
| `RATE` | 增长率 | `growthRateType` 必填 |
| `RAWDATA` | 对比值 | 无，直接输出上期/对比期数据 |
| `CUSTOM` | 自定义公式 | `formulaSetting` 必填 |

示例：月环比增长率：

```json
"deriveSetting": {
  "deriveType": "ORDINARY",
  "advType": "COMPARATIVE",
  "advValue": {
    "advType": "COMPARATIVE",
    "dateFdId": "<time_fd_id>",
    "dateFdName": "订单日期",
    "granularity": "MONTH",
    "window": { "offset": 0 },
    "offsetType": "MONTH",
    "offset": 1,
    "valueType": "RATE",
    "growthRateType": "NORMAL"
  }
}
```

## 最近 N `RECENT_X`

只能衍生自原子指标。

| 字段 | 中文 | 说明 |
| --- | --- | --- |
| `advValue.advType` | 高级计算类型 | 固定 `RECENT_X` |
| `granularity` | 时间粒度 | `DAY/WEEK/MONTH/QUARTER/YEAR` |
| `baseline` | 基准日期 | `DATE_RANGE_PARAM_LEFT` 或 `DATE_RANGE_PARAM_RIGHT` |
| `baselineOffset` | 起止偏移 | 二元数组 `[startOffset, endOffset]` |
| `baselineOffsetDynamicParameters` | 动态参数偏移 | 如 `[DYNAMIC_PARAMS.N]`，非空时优先于 `baselineOffset` |
| `aggrType` | 计算方式 | 通常 `SUM/MIN/MAX/AVG/CNT/CNT_DISTINCT/NUL` |

偏移示例：

- `[6,0]` + `DAY`：以基准日期为结束，最近 7 天。
- `[29,0]` + `DAY`：最近 30 天。
- `[3,1]` + `MONTH`：基准日期前第 3 个月到前第 1 个月。
- 动态参数：把对应位置的 `baselineOffset` 置为 `-1`，在 `baselineOffsetDynamicParameters` 写 `[DYNAMIC_PARAMS.xxx]`，同时在顶层 `dynamicParameters` 填默认值。

示例：

```json
"deriveSetting": {
  "deriveType": "ORDINARY",
  "advType": "RECENT_X",
  "advValue": {
    "advType": "RECENT_X",
    "granularity": "DAY",
    "baseline": "DATE_RANGE_PARAM_RIGHT",
    "baselineOffset": [6, 0],
    "aggrType": "SUM"
  }
}
```

## 累计计算 `XTD`

用于“周/月/季/年至今累计”或“近 N 个粒度累计”。

| 字段 | 中文 | 说明 |
| --- | --- | --- |
| `advValue.advType` | 高级计算类型 | 固定 `XTD` |
| `granularity` | 累计范围 | `WEEK/MONTH/QUARTER/YEAR/NONE` |
| `baseline` | 基准日期 | `DATE_RANGE_PARAM_LEFT` 或 `DATE_RANGE_PARAM_RIGHT` |
| `baselineOffset` | 自定义累计偏移 | 可选数字；例如 `1` 表示近 2 个粒度累计 |
| `includeCurrentValue` | 是否包含基准日期当前值 | `true/false` |

示例：月累计：

```json
"deriveSetting": {
  "deriveType": "ORDINARY",
  "advType": "XTD",
  "advValue": {
    "advType": "XTD",
    "granularity": "MONTH",
    "baseline": "DATE_RANGE_PARAM_RIGHT",
    "includeCurrentValue": true
  }
}
```

限制：基础指标是复合指标时，不支持部分自定义累计方式；特别是“当年累计”这类年累计自定义配置需谨慎，Web 会拦截不支持组合。

## 期末值 `LAST`

```json
"deriveSetting": {
  "deriveType": "ORDINARY",
  "advType": "LAST",
  "advValue": {
    "advType": "LAST",
    "dateFdId": "<time_fd_id>"
  }
}
```

`dateFdId` 必须来自基础指标时间维度。

## 业务限定 `BIZ_CONDITION`

业务限定必须提供顶层 `filter`。

```json
"deriveSetting": {
  "deriveType": "ORDINARY",
  "advType": "BIZ_CONDITION",
  "advValue": {
    "advType": "BIZ_CONDITION"
  }
},
"filter": {
  "combineType": "AND",
  "conditions": []
}
```

`filter` 结构复杂时，优先参考同环境已有指标详情或由 Web 配置后复制 payload，不凭空拼复杂筛选。

## 组合衍生 `COMPOSITE`

组合衍生通过公式引用多个指标，不选择单个 `baseMetricId`：

```json
"deriveSetting": {
  "deriveType": "COMPOSITE",
  "formula": "[<metric_id_1>] / [<metric_id_2>]"
}
```

引用规则：

- 涉及组合衍生 `deriveSetting.formula` 时，先读取 `metric-expression-rules.md`。
- 公式支持 `+`、`-`、`*`、`/`、括号、数字和 `[指标ID]`；不要主动生成函数、窗口函数或 SQL 片段。
- 可引用原子指标。
- 可引用普通衍生里的 `RECENT_X`、`XTD`、`LAST`。
- 不要引用复合指标、组合衍生、快捷引用指标或当前正在编辑的指标。
- 组合衍生需要公式引用指标存在共有时间维度；没有共有时间维度时 Web 会提示不可配置衍生指标。

## 基础指标选择规则

- 普通衍生可衍生自原子指标或复合指标；不能衍生自快捷引用指标。
- `RECENT_X` 只能衍生自原子指标。
- `COMPARATIVE` 和 `LAST` 需要基础指标有时间维度。
- `COMPARATIVE` 可基于最近 N、累计、期末值、组合衍生继续衍生。
- `BIZ_CONDITION` 可基于非业务限定的任意其它衍生指标。

## 信息收集

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

1. 指标主题和保存目录。
2. 衍生类型：同比/环比、最近 N、累计、期末值、业务限定、组合衍生。
3. 普通衍生的基础指标，或组合衍生要引用的指标与公式。
4. 时间粒度、时间偏移、是否包含当天、数值设置等衍生配置。
5. 业务限定的筛选条件 `filter`。
6. 数据格式：默认沿用/复制基础指标格式，或指定为数值、货币、百分比；如需指定，确认小数位、千分位、货币符号/前后缀、数量级单位、百分比是否除以 100。
7. 指标名称、业务口径、是否保存并上线。
8. 责任人和必填 `bizAttrs`。

衍生指标常见做法：

- 用户说“跟基础指标格式一样”时，先 `guancli metric get <baseMetricId> -f json` 读取基础指标 `format` 并复制。
- 同比/环比增长率、转化率、占比类指标通常使用 `PERCENTAGE`。
- 最近 N、累计、期末值通常复制基础指标 `format`；如果用户没有明确要求，也可以省略 `format` 使用默认。

## JSON 模板：最近 7 天

```json
{
  "projectId": "<metric_project_id>",
  "subType": "DERIVED",
  "name": "最近7天销售额",
  "engName": "sales_amount_recent_7d",
  "baseMetricId": "<atomic_metric_id>",
  "deriveSetting": {
    "deriveType": "ORDINARY",
    "advType": "RECENT_X",
    "advValue": {
      "advType": "RECENT_X",
      "granularity": "DAY",
      "baseline": "DATE_RANGE_PARAM_RIGHT",
      "baselineOffset": [6, 0],
      "aggrType": "SUM"
    }
  },
  "format": {
    "formatType": "CURRENCY",
    "decimalPlaces": 2,
    "useThousandsSeparator": true,
    "prefix": "￥",
    "prefixUnit": null,
    "showPrefixUnit": true,
    "suffix": ""
  },
  "desc": "统计筛选日期结束日前最近7天的销售额。",
  "parentDirId": "<metric_dir_id>",
  "businessOwner": "<user_id>",
  "publish": false
}
```

## JSON 模板：组合衍生

```json
{
  "projectId": "<metric_project_id>",
  "subType": "DERIVED",
  "name": "组合衍生客单价",
  "deriveSetting": {
    "deriveType": "COMPOSITE",
    "formula": "[<recent_sales_metric_id>] / [<recent_order_metric_id>]"
  },
  "format": {
    "formatType": "NUMBER",
    "decimalPlaces": 2,
    "useThousandsSeparator": true,
    "prefixUnit": null,
    "showPrefixUnit": true,
    "prefix": "",
    "suffix": ""
  },
  "desc": "使用多个衍生指标组合计算客单价。",
  "parentDirId": "<metric_dir_id>",
  "businessOwner": "<user_id>",
  "publish": false
}
```

## 创建步骤

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

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

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

7. 响应返回指标 ID 后回读确认。

## 编辑步骤

衍生指标没有原子指标的 save-precheck；主要校验基础指标、衍生配置或筛选变化是否影响下游指标/卡片。

1. 读取当前详情：

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

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

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

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

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

6. 保存后回读确认。
