# 后台图表业务块设计

## 目标

在现有页面设计器和业务块体系上增加后台业务块能力，并首批提供四个仅后台可用的图表业务块：

- 指标卡
- 折线图
- 柱状图
- 饼图

方案沿用现有业务块注册、布局保存、字段校验和代码生成链路，不建设第二套后台业务块系统，也不升级为通用 BI 或自由大屏设计器。

## 已确认的产品边界

- 采用统一业务块协议扩展方案。
- 图表业务块一期仅用于后台页面。
- 数据源仅支持单表聚合。
- 支持维度、计数、求和、平均值和固定筛选条件。
- 不支持关联表、自定义 SQL、公式脚本、运行时数据源切换和任意 ECharts JSON。
- 首批只实现指标卡、折线图、柱状图和饼图。
- 布局继续使用现有业务块页面和普通页面嵌入区，不增加自由栅格编辑器。
- 每个块只提供整行、半行、三分之一三种宽度。

## 当前基础

项目已有以下可复用能力：

- `FrontendPageDesigner.vue` 同时支持前台和后台页面设计模式。
- 页面布局保存在 `front_project_page_design.layout_json`。
- 业务块定义位于 `business-blocks/*/block.json`。
- `BusinessBlockRegistryService` 负责定义加载、表字段校验和布局校验。
- `BusinessBlockGenerationService` 按业务块实例生成前端组件、API 和后端文件。
- `business-blocks-v1` 支持业务块页面，普通页面支持 `embeddedBlocks.top` 和 `embeddedBlocks.bottom`。
- 后台页面类型已经允许选择业务块页面，但页面入口当前没有向后台设计器传入业务块定义。

因此，本需求的核心不是新增一条独立链路，而是把现有链路扩展为可按使用端和块类型工作。

## 总体架构

```text
后台页面设计器
    |
    | 选择 admin 图表块并配置单表聚合 DSL
    v
front_project_page_design.layout_json
    |
    | 保存时校验表、字段、字段类型、聚合和筛选
    v
BusinessBlockRegistryService
    |
    | 生成 Vue/ECharts、API、Controller、Service、Mapper
    v
BusinessBlockGenerationService
    |
    v
生成后的 Qing 后台运行页面
```

前后台共用业务块注册和生成基础设施，但定义通过 `surfaces` 限制可见范围，模板通过类型区分生成目标。

## 业务块协议 2.0

### 定义扩展

业务块定义增加以下字段：

```json
{
  "code": "admin_line_chart",
  "name": "折线图",
  "category": "数据图表",
  "kind": "chart",
  "surfaces": ["admin"],
  "placements": ["embedded", "page"],
  "chartType": "line",
  "datasetSchema": "single-table-aggregate-v1",
  "version": 2,
  "defaultSpan": 12,
  "templates": []
}
```

字段含义：

- `kind`：块类型。已有业务块归一化为 `business`，图表块为 `chart`。
- `surfaces`：适用端，支持 `frontend`、`admin`。
- `placements`：允许放置到普通页面嵌入区或业务块页面。
- `chartType`：`metric`、`line`、`bar`、`pie`。
- `datasetSchema`：图表使用的数据配置协议。
- `defaultSpan`：默认宽度，取值为 `12`、`6`、`4`。

兼容规则：

- 老定义缺少 `kind` 时按 `business` 处理。
- 老定义缺少 `surfaces` 时按 `["frontend"]` 处理。
- 老定义的单值 `placement` 归一化为 `placements` 数组。
- 老页面和老业务块实例不要求迁移。

### 实例扩展

图表块实例保存在现有页面布局 JSON 中：

```json
{
  "id": "admin_line_chart_001",
  "blockCode": "admin_line_chart",
  "name": "近 30 天销售趋势",
  "span": 12,
  "dataset": {
    "schema": "single-table-aggregate-v1",
    "table": "shop_order",
    "dimension": {
      "field": "create_time",
      "timeUnit": "day"
    },
    "metrics": [
      {
        "field": "amount",
        "aggregate": "sum",
        "alias": "salesAmount",
        "label": "销售额"
      }
    ],
    "filters": [
      {
        "field": "status",
        "operator": "eq",
        "value": "PAID"
      }
    ],
    "dateRange": {
      "field": "create_time",
      "preset": "last_30_days"
    },
    "sort": {
      "by": "dimension",
      "order": "asc"
    },
    "limit": 100
  },
  "display": {
    "showTitle": true,
    "showLegend": true,
    "smooth": true,
    "numberFormat": "decimal",
    "emptyText": "暂无数据"
  }
}
```

现有 `config` 字段继续服务于购物车、公告、轮播等老业务块。图表使用结构化的 `dataset` 和 `display`，避免把嵌套配置塞入 `Map<String, String>`。

## 单表聚合 DSL

### 支持能力

- 一张数据表。
- 最多一个维度字段。
- 一个到三个指标。
- 聚合函数：`count`、`sum`、`avg`。
- 静态筛选条件。
- 日期范围预设。
- 按维度或指标排序。
- 限制返回分组数量。

### 字段类型约束

- `count` 可用于整表或任意字段。
- `sum`、`avg` 只能用于数值字段。
- 普通维度支持文本、枚举、数字和日期时间字段。
- `timeUnit` 只能用于日期时间字段。
- 时间粒度一期支持 `day`、`week`、`month`。
- 指标别名在同一块内必须唯一。

### 筛选运算符

一期支持：

- `eq`
- `ne`
- `gt`
- `gte`
- `lt`
- `lte`
- `between`
- `in`
- `is_null`
- `is_not_null`

筛选值必须与字段类型兼容。单个块最多配置十个筛选条件。

### 日期范围

一期提供以下预设：

- 今天
- 最近 7 天
- 最近 30 天
- 本月
- 本年
- 固定起止日期

日期预设在生成后的服务端计算，避免依赖浏览器时间拼接 SQL。

### 数量限制

- 全局最大分组数为 500。
- 折线图默认 100 个分组。
- 柱状图默认 20 个分组。
- 饼图默认 10 个分组，最大 20 个。
- 指标卡不返回维度分组。

## 四种图表块

### 指标卡

- 不要求维度。
- 只允许一个指标。
- 支持前缀、后缀、小数位、标题和辅助说明。
- 一期不计算同比、环比；辅助说明为静态文案。

### 折线图

- 必须配置一个维度。
- 支持一个到三个指标。
- 时间维度可配置日、周、月粒度。
- 展示配置支持平滑曲线、面积填充和图例。

### 柱状图

- 必须配置一个维度。
- 支持一个到三个指标。
- 展示配置支持横向、纵向、堆叠和 Top N。

### 饼图

- 必须配置一个分类维度。
- 只允许一个指标。
- 展示配置支持实心、环形、标签和“其他”分组。

## 页面设计器

### 素材面板

后台模式请求 `scope=admin` 的业务块定义，只展示 `surfaces` 包含 `admin` 的定义。

图表块归入“数据图表”分类：

- 指标卡
- 折线图
- 柱状图
- 饼图

前台模式不显示这四个块。现有前台业务块也不会因为本需求自动出现在后台素材池。

### 放置方式

图表块支持：

- 普通后台页面顶部业务块区。
- 普通后台页面底部业务块区。
- 后台业务块页面。

块实例支持拖拽排序和三档宽度：

- `span=12`：整行。
- `span=6`：半行。
- `span=4`：三分之一。

画布使用 12 列 CSS Grid 自动换行，不提供任意坐标、任意缩放和断点级布局编辑。

### 属性面板

选中图表块后，右侧分为三个页签。

“数据”页签：

- 标题
- 数据表
- 维度字段
- 时间粒度
- 指标字段
- 聚合方式
- 筛选条件
- 日期范围
- 排序
- Top N 或分组上限

“展示”页签：

- 图例
- 颜色主题
- 数值格式
- 图表类型特有选项
- 空数据文案

“权限”页签：

- 显示“继承所在后台页面”。
- 一期不允许单个块覆盖页面权限。
- 当前生成目标若只有后台登录保护，则块继承该登录保护；本需求不额外建设块级 RBAC。

### 预览

设计器内的即时预览使用稳定的样例数据，只验证图形、标题、图例、格式和布局。

配置变化后 400 毫秒防抖刷新当前块。真实数据库结果在保存并生成运行预览后验证。本期不增加设计器直接执行聚合 SQL 的接口。

这样可以避免设计阶段访问未初始化的数据源，也减少预览查询对数据库的压力。

## API 调整

### 业务块定义

现有接口调整为：

```text
GET /front/project/{projectId}/business-blocks?scope=admin
```

行为：

- 校验项目归属。
- `scope` 归一化为 `frontend` 或 `admin`。
- 只返回 `surfaces` 包含当前 scope 的定义。
- 前端 `listBusinessBlocks(projectId, scope)` 显式传入当前页面设计范围。

`PageDesignerView.vue` 不再通过“后台传空数组”隐藏业务块，而是传入后端过滤后的定义。

### 页面保存

继续使用现有页面设计保存接口，不新增业务块实例表：

```text
PUT /front/project/{projectId}/page-designs/{designId}?scope=admin
```

保存时增加图表 DSL 校验。任一图表块无效时，整个页面设计保存失败，并返回块名称和具体配置项。

### 运行时数据接口

每个生成的图表块实例提供只读接口：

```text
GET /business-blocks/{instanceCode}/data
```

响应协议：

```json
{
  "dimensions": [
    {
      "key": "2026-06-01",
      "label": "06-01"
    }
  ],
  "series": [
    {
      "key": "salesAmount",
      "name": "销售额",
      "values": [12800.5]
    }
  ],
  "summary": {
    "value": 86420,
    "count": 328
  }
}
```

指标卡读取 `summary`；折线图和柱状图读取 `dimensions` 与 `series`；饼图把维度和第一组指标映射为分类占比。

## 后端校验

`BusinessBlockRegistryService` 增加以下职责：

- 按 `surface` 过滤定义。
- 归一化协议 1.0 和 2.0 定义。
- 校验图表块只能出现在后台页面。
- 校验数据表存在于项目数据库设计。
- 校验维度、指标、筛选和日期字段存在。
- 校验聚合函数与字段类型匹配。
- 校验图表类型需要的维度和指标数量。
- 校验运算符、筛选值、排序字段和 limit。
- 校验 `span` 只能为 `12`、`6`、`4`。

错误消息包含业务块名称和配置项，例如：

```text
折线图“近 30 天销售趋势”的指标字段 amount 不是数值类型，不能使用 SUM
```

## 代码生成

### 前端

生成项目增加 ECharts 依赖，并提供共享图表工具：

- 图表初始化与销毁。
- 窗口尺寸变化时 resize。
- 统一加载、空数据和错误状态。
- 统一主题颜色与数值格式。

每个图表块仍按现有业务块模式生成独立 Vue 组件和 API 文件。该方式会增加文件数量，但与当前生成机制一致，实例隔离清楚，也便于预览单个生成文件。

### 后端

每个实例生成只读的 Controller、Service 和 Mapper 文件。

生成 SQL 规则：

- 表名和字段名只能来自已校验的项目数据库元数据。
- 聚合函数只能来自固定枚举。
- MySQL 时间维度使用固定的 `DATE_FORMAT` 模板。
- 筛选值通过 MyBatis 参数绑定。
- 排序只能引用生成后的维度或指标别名。
- 不接受客户端传入表名、字段名、SQL 片段、聚合函数或排序表达式。

生成的接口位于后台认证保护范围内，并继承所在页面当前具备的后台访问边界。

## 权限与安全

- 图表块不能放入前台页面。
- 块不拥有独立权限，继承后台页面和后台登录态。
- 设计器保存前做元数据白名单校验。
- 生成阶段再次校验，防止绕过页面保存直接生成。
- 运行时只接受生成代码中预定义的查询，不接受动态查询结构。
- 最大分组数和筛选数量受限。
- 空值和数值聚合使用安全的默认处理，避免前端出现 `NaN`。

块级 RBAC、部门数据权限和用户可编辑筛选器不属于一期。后续若项目增加稳定的页面权限模型，再由图表块继承该模型，不单独发明另一套权限协议。

## 错误处理

### 设计时

- 必填配置缺失时在右侧表单直接标红。
- 字段被删除或类型发生变化时，页面标记为“需要修复”。
- 无效页面不能保存，也不能生成预览。
- 错误定位到具体块和具体字段。

### 运行时

- 空结果展示块级空状态。
- 当前接口失败时只让当前块显示错误和重试按钮。
- 一个图表块失败不影响同页其他块。
- 组件卸载时销毁 ECharts 实例和 resize 监听。

## 数据库与兼容性

- 不新增数据库表。
- 不修改 `front_project_page_design` 表结构。
- 新配置继续存入 `layout_json`。
- 老页面布局保持原样可读。
- 老业务块定义通过归一化逻辑继续工作。
- 前台业务块生成结果不因本需求改变。

## 测试

### 前端

- 后台页面能获取并展示四个图表块。
- 前台页面不展示后台图表块。
- 拖入、选择、删除、排序和宽度切换正确更新布局 JSON。
- 图表配置面板按图表类型显示正确字段。
- 类型不匹配和必填缺失显示块级错误。
- 样例数据预览支持加载、空数据和错误状态。
- 旧业务块页面和普通页面嵌入块行为不回归。

### 后端

- 业务块定义能按 scope 过滤。
- 老定义能被正确归一化。
- 表、字段、字段类型、聚合、筛选、时间粒度、排序和 limit 校验完整。
- 后台图表块不能保存到前台页面。
- 无效布局不能进入代码生成。

### 生成器

- 四种图表生成正确的 Vue、API、Controller、Service 和 Mapper。
- 生成项目包含 ECharts 依赖。
- SQL 只包含白名单标识符和固定函数。
- 筛选值使用参数绑定。
- 统一响应能被四种组件正确消费。
- 多块页面按 `span` 正确排列。
- 单块接口失败不会影响其他组件。

## 分阶段落地

### 第一阶段：协议和可见范围

- 扩展业务块定义 DTO。
- 增加定义归一化和 scope 过滤。
- 修改业务块列表 API 和后台设计器传参。
- 保证旧定义与旧页面测试通过。

### 第二阶段：图表设计器

- 增加图表块实例模型和单表聚合 DSL。
- 增加专用图表配置面板。
- 增加三档宽度和 12 列布局。
- 增加样例数据即时预览。

### 第三阶段：校验和代码生成

- 完成 DSL 后端校验。
- 增加 ECharts 共享运行组件。
- 增加四个图表块定义和生成模板。
- 生成只读聚合接口。

### 第四阶段：验证和发布

- 前后端自动化测试。
- 使用包含数值、枚举、日期字段的订单表做运行预览。
- 验证前台业务块、后台 CRUD 页面和老项目兼容性。
- 文档中说明一期限制和后续扩展边界。

## 后续扩展

协议稳定后可以逐项增加：

- 表格统计块。
- 排行榜块。
- 待办列表块。
- 审核工作台块。
- 页面级公共筛选器。
- 关联表数据集。

这些能力不进入本期，也不预留可执行 SQL 或脚本入口。
