757 lines
13 KiB
Markdown
757 lines
13 KiB
Markdown
# 后台配置 SQL 驱动的经营分析方案
|
||
|
||
## 1. 目标
|
||
|
||
本方案满足你明确提出的目标:
|
||
|
||
- 统计定义在后台管理配置
|
||
- H5 端直接按配置展示
|
||
- 取数 SQL 也可以在后台管理配置
|
||
- 后续口径变化时,尽量不改前端代码
|
||
- 同时兼顾 PC 端与 H5 / 企业微信端
|
||
|
||
这套方案的核心思想是:
|
||
|
||
- `前端不写死报表`
|
||
- `后台管理端维护报表定义`
|
||
- `后端读取配置 SQL 执行`
|
||
- `H5 / PC 统一走配置渲染`
|
||
|
||
## 2. 结论先说
|
||
|
||
这个方案是可行的,但建议不要做“完全无限制的任意 SQL 执行”,而要做:
|
||
|
||
- `后台可配置 SQL`
|
||
- `后端受控执行 SQL`
|
||
- `带参数模板`
|
||
- `带权限注入`
|
||
- `带版本管理`
|
||
- `带审核发布`
|
||
|
||
也就是说,不是直接把数据库裸露给后台管理员,而是做一个“`可配置 SQL 报表平台`”。
|
||
|
||
## 3. 适合你的整体架构
|
||
|
||
建议拆成 4 个部分:
|
||
|
||
### 3.1 报表定义后台
|
||
|
||
给管理员使用,用于配置:
|
||
|
||
- 报表名称
|
||
- 适用端
|
||
- 筛选条件
|
||
- 图表类型
|
||
- 指标字段
|
||
- 维度字段
|
||
- SQL 模板
|
||
- 参数说明
|
||
- 发布版本
|
||
|
||
### 3.2 报表执行引擎
|
||
|
||
后端新增一个报表引擎服务,职责是:
|
||
|
||
- 读取报表配置
|
||
- 校验参数
|
||
- 替换 SQL 模板变量
|
||
- 自动追加权限条件
|
||
- 执行 SQL
|
||
- 返回统一结果结构
|
||
|
||
### 3.3 H5 / PC 动态渲染层
|
||
|
||
前端不再写死每个图表,而是:
|
||
|
||
- 先请求报表页面配置
|
||
- 再请求每个组件的数据
|
||
- 根据配置渲染 KPI、柱状图、折线图、饼图、表格
|
||
|
||
### 3.4 配置治理层
|
||
|
||
用于保证 SQL 配置安全可控:
|
||
|
||
- 草稿/已发布版本
|
||
- 审核流
|
||
- 执行日志
|
||
- 回滚版本
|
||
- 性能限制
|
||
|
||
## 4. 最推荐的实现模式
|
||
|
||
你要的是“SQL 可配置”,这里推荐两个级别。
|
||
|
||
### 4.1 级别一:SQL 模板可配置
|
||
|
||
这是最推荐的。
|
||
|
||
后台配置的不是完全自由 SQL,而是带占位符的模板 SQL,例如:
|
||
|
||
```sql
|
||
select
|
||
owner_user_id as ownerUserId,
|
||
sum(amount) as actualAmount
|
||
from crm_opportunity
|
||
where status = 'won'
|
||
and won_at between ${startDate} and ${endDate}
|
||
${dataScopeClause}
|
||
${orgFilterClause}
|
||
group by owner_user_id
|
||
order by actualAmount desc
|
||
```
|
||
|
||
特点:
|
||
|
||
- 可配灵活
|
||
- 后端可控
|
||
- 容易注入权限
|
||
- 易于参数校验
|
||
|
||
### 4.2 级别二:完全自由 SQL
|
||
|
||
不建议默认开放。
|
||
|
||
如果一定要支持,也建议:
|
||
|
||
- 仅超级管理员可用
|
||
- 仅允许 `select`
|
||
- 禁止 `update/delete/insert/drop`
|
||
- 禁止多语句
|
||
- 禁止访问敏感表
|
||
- 强制走只读数据源
|
||
- 强制超时和行数限制
|
||
|
||
## 5. 业务上如何满足“销售完成业绩”和“区域业绩”
|
||
|
||
这两个场景很适合做成后台配置化报表。
|
||
|
||
### 5.1 销售完成业绩
|
||
|
||
建议报表结构:
|
||
|
||
- KPI:
|
||
- 销售目标
|
||
- 实际完成
|
||
- 完成率
|
||
- 同比/环比
|
||
- 图表:
|
||
- 销售排行
|
||
- 趋势图
|
||
- 部门分布
|
||
- 明细:
|
||
- 销售员、目标值、完成值、完成率、区域、趋势
|
||
|
||
### 5.2 区域业绩
|
||
|
||
建议报表结构:
|
||
|
||
- KPI:
|
||
- 区域目标
|
||
- 区域实际
|
||
- 区域完成率
|
||
- 图表:
|
||
- 区域排名
|
||
- 区域趋势
|
||
- 区域构成
|
||
- 明细:
|
||
- 大区、省区、城市、销售数、目标、实际、完成率
|
||
|
||
这两类都可以通过后台 SQL 配置实现。
|
||
|
||
## 6. 推荐的数据模型
|
||
|
||
建议新增以下表。
|
||
|
||
### 6.1 `analytics_report`
|
||
|
||
存报表主定义。
|
||
|
||
建议字段:
|
||
|
||
- `id`
|
||
- `report_code`
|
||
- `report_name`
|
||
- `report_category`
|
||
- `description`
|
||
- `status`
|
||
- `terminal_scope`
|
||
- `default_time_granularity`
|
||
- `visible`
|
||
- `created_by`
|
||
- `updated_by`
|
||
- `created_at`
|
||
- `updated_at`
|
||
|
||
说明:
|
||
|
||
- `terminal_scope` 用于区分 `pc / h5 / both`
|
||
|
||
### 6.2 `analytics_report_version`
|
||
|
||
存报表版本。
|
||
|
||
建议字段:
|
||
|
||
- `id`
|
||
- `report_id`
|
||
- `version_no`
|
||
- `version_status`
|
||
- `change_log`
|
||
- `published_by`
|
||
- `published_at`
|
||
- `config_json`
|
||
- `created_at`
|
||
|
||
说明:
|
||
|
||
- 一个报表允许多个版本
|
||
- H5 默认读已发布版本
|
||
|
||
### 6.3 `analytics_widget`
|
||
|
||
存一个报表下的组件定义。
|
||
|
||
建议字段:
|
||
|
||
- `id`
|
||
- `report_version_id`
|
||
- `widget_code`
|
||
- `widget_name`
|
||
- `widget_type`
|
||
- `title`
|
||
- `subtitle`
|
||
- `layout_json`
|
||
- `props_json`
|
||
- `data_source_id`
|
||
- `sort_order`
|
||
- `visible`
|
||
|
||
### 6.4 `analytics_data_source`
|
||
|
||
存数据源定义,也就是 SQL 配置核心。
|
||
|
||
建议字段:
|
||
|
||
- `id`
|
||
- `source_code`
|
||
- `source_name`
|
||
- `source_type`
|
||
- `dataset_code`
|
||
- `query_sql`
|
||
- `count_sql`
|
||
- `parameter_schema_json`
|
||
- `result_schema_json`
|
||
- `timeout_ms`
|
||
- `max_rows`
|
||
- `cache_ttl_seconds`
|
||
- `enabled`
|
||
- `created_at`
|
||
- `updated_at`
|
||
|
||
### 6.5 `analytics_filter_def`
|
||
|
||
存筛选器定义。
|
||
|
||
建议字段:
|
||
|
||
- `id`
|
||
- `report_version_id`
|
||
- `filter_code`
|
||
- `filter_name`
|
||
- `filter_type`
|
||
- `data_type`
|
||
- `default_value_json`
|
||
- `options_source_type`
|
||
- `options_source_config`
|
||
- `sort_order`
|
||
- `visible`
|
||
|
||
### 6.6 `analytics_sql_publish_log`
|
||
|
||
存 SQL 发布日志。
|
||
|
||
建议字段:
|
||
|
||
- `id`
|
||
- `data_source_id`
|
||
- `version_no`
|
||
- `old_sql`
|
||
- `new_sql`
|
||
- `change_summary`
|
||
- `operator_id`
|
||
- `created_at`
|
||
|
||
### 6.7 `analytics_query_log`
|
||
|
||
存执行日志。
|
||
|
||
建议字段:
|
||
|
||
- `id`
|
||
- `report_code`
|
||
- `widget_code`
|
||
- `data_source_id`
|
||
- `request_user_id`
|
||
- `request_params_json`
|
||
- `final_sql_preview`
|
||
- `duration_ms`
|
||
- `row_count`
|
||
- `success`
|
||
- `error_message`
|
||
- `created_at`
|
||
|
||
## 7. 配置中心怎么设计
|
||
|
||
后台建议做成 5 个页面。
|
||
|
||
### 7.1 报表管理
|
||
|
||
配置:
|
||
|
||
- 报表基本信息
|
||
- 展示端
|
||
- 菜单归属
|
||
- 是否发布
|
||
|
||
### 7.2 页面布局管理
|
||
|
||
配置:
|
||
|
||
- KPI 卡片
|
||
- 图表组件
|
||
- 表格组件
|
||
- H5 是否显示
|
||
- PC 是否显示
|
||
- 顺序与显隐
|
||
|
||
### 7.3 SQL 数据源管理
|
||
|
||
配置:
|
||
|
||
- SQL 模板
|
||
- 参数定义
|
||
- 字段映射
|
||
- 结果预览
|
||
- 缓存策略
|
||
- 执行限制
|
||
|
||
### 7.4 筛选器管理
|
||
|
||
配置:
|
||
|
||
- 时间筛选
|
||
- 区域筛选
|
||
- 销售筛选
|
||
- 部门筛选
|
||
- 自定义枚举
|
||
|
||
### 7.5 发布与回滚
|
||
|
||
配置:
|
||
|
||
- 草稿
|
||
- 预览
|
||
- 发布
|
||
- 回滚历史版本
|
||
|
||
## 8. SQL 配置方式建议
|
||
|
||
这是关键设计点。
|
||
|
||
### 8.1 SQL 模板结构
|
||
|
||
建议使用模板 SQL,不要直接存“拼好的最终 SQL”。
|
||
|
||
示例:
|
||
|
||
```sql
|
||
select
|
||
region_code as regionCode,
|
||
region_name as regionName,
|
||
sum(actual_amount) as actualAmount,
|
||
sum(target_amount) as targetAmount,
|
||
case when sum(target_amount) = 0 then 0
|
||
else round(sum(actual_amount) / sum(target_amount) * 100, 2)
|
||
end as completionRate
|
||
from analytics_sales_performance_fact
|
||
where stat_date between ${startDate} and ${endDate}
|
||
${orgFilterClause}
|
||
${regionFilterClause}
|
||
${salesFilterClause}
|
||
${dataScopeClause}
|
||
group by region_code, region_name
|
||
order by actualAmount desc
|
||
limit ${limit}
|
||
```
|
||
|
||
### 8.2 参数定义
|
||
|
||
每条 SQL 必须配参数定义。
|
||
|
||
示例:
|
||
|
||
```json
|
||
[
|
||
{ "name": "startDate", "type": "date", "required": true },
|
||
{ "name": "endDate", "type": "date", "required": true },
|
||
{ "name": "orgId", "type": "long", "required": false },
|
||
{ "name": "regionCode", "type": "string", "required": false },
|
||
{ "name": "salesUserId", "type": "long", "required": false },
|
||
{ "name": "limit", "type": "int", "required": false, "defaultValue": 50 }
|
||
]
|
||
```
|
||
|
||
### 8.3 建议支持的占位符
|
||
|
||
后端统一支持如下占位符:
|
||
|
||
- `${startDate}`
|
||
- `${endDate}`
|
||
- `${limit}`
|
||
- `${offset}`
|
||
- `${dataScopeClause}`
|
||
- `${orgFilterClause}`
|
||
- `${regionFilterClause}`
|
||
- `${salesFilterClause}`
|
||
|
||
### 8.4 动态条件注入方式
|
||
|
||
例如 `regionCode` 为空时,不拼条件;
|
||
有值时自动生成:
|
||
|
||
```sql
|
||
and region_code = :regionCode
|
||
```
|
||
|
||
不建议把这种逻辑写死在前端。
|
||
|
||
## 9. 前端展示怎么实现
|
||
|
||
虽然 SQL 在后台配置,但前端还是要有统一协议。
|
||
|
||
### 9.1 H5 与 PC 共用配置协议
|
||
|
||
前端请求:
|
||
|
||
- 页面定义
|
||
- 筛选器定义
|
||
- Widget 定义
|
||
- Widget 数据
|
||
|
||
### 9.2 Widget 类型建议
|
||
|
||
支持以下组件:
|
||
|
||
- `kpi`
|
||
- `line`
|
||
- `bar`
|
||
- `stack-bar`
|
||
- `pie`
|
||
- `table`
|
||
- `rank-list`
|
||
|
||
### 9.3 H5 展示规则
|
||
|
||
建议在配置里加上:
|
||
|
||
- `showOnPc`
|
||
- `showOnH5`
|
||
- `h5SortOrder`
|
||
- `pcSortOrder`
|
||
- `h5ChartType`
|
||
- `pcChartType`
|
||
|
||
这样同一个报表可以:
|
||
|
||
- PC 显示表格 + 图表
|
||
- H5 只显示 KPI + Top10
|
||
|
||
### 9.4 H5 页面渲染流程
|
||
|
||
1. 获取报表定义
|
||
2. 获取筛选项定义
|
||
3. 渲染页面骨架
|
||
4. 请求组件数据
|
||
5. 渲染图表
|
||
|
||
## 10. 后端执行引擎设计
|
||
|
||
建议新增一个 `AnalyticsQueryService`。
|
||
|
||
职责:
|
||
|
||
- 加载已发布报表版本
|
||
- 解析筛选条件
|
||
- 校验参数类型
|
||
- 生成最终 SQL
|
||
- 注入权限条件
|
||
- 执行查询
|
||
- 做缓存
|
||
- 记录日志
|
||
|
||
### 10.1 执行流程
|
||
|
||
1. 读取报表配置
|
||
2. 读取组件配置
|
||
3. 读取数据源 SQL 模板
|
||
4. 根据请求参数构造过滤条件
|
||
5. 自动注入数据权限
|
||
6. 生成最终 SQL
|
||
7. 执行查询
|
||
8. 转成统一结果结构
|
||
|
||
### 10.2 统一返回结构
|
||
|
||
建议:
|
||
|
||
```json
|
||
{
|
||
"reportCode": "sales-performance",
|
||
"title": "销售完成业绩",
|
||
"filters": [],
|
||
"widgets": [
|
||
{
|
||
"widgetCode": "sales-rank",
|
||
"widgetType": "bar",
|
||
"title": "销售业绩排名",
|
||
"columns": ["ownerName", "actualAmount"],
|
||
"rows": []
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
## 11. 权限与安全控制
|
||
|
||
这是这个方案能不能上线的关键。
|
||
|
||
### 11.1 禁止直接执行危险 SQL
|
||
|
||
必须限制:
|
||
|
||
- 只允许 `select`
|
||
- 禁止 `insert`
|
||
- 禁止 `update`
|
||
- 禁止 `delete`
|
||
- 禁止 `drop`
|
||
- 禁止 `alter`
|
||
- 禁止多语句执行
|
||
|
||
### 11.2 必须强制数据权限注入
|
||
|
||
即使后台配置 SQL,也不能信任配置人手动写权限条件。
|
||
|
||
必须由后端统一追加:
|
||
|
||
- 当前租户过滤
|
||
- 当前用户数据范围
|
||
- 组织权限范围
|
||
|
||
### 11.3 建议使用只读数据源
|
||
|
||
报表执行引擎单独使用只读数据库账号。
|
||
|
||
### 11.4 超时与数据量限制
|
||
|
||
每条数据源定义都要支持:
|
||
|
||
- 最大执行时长
|
||
- 最大返回行数
|
||
- 最大导出行数
|
||
|
||
### 11.5 SQL 审核
|
||
|
||
建议发布前做 SQL 检查:
|
||
|
||
- 关键词校验
|
||
- 表白名单校验
|
||
- 字段白名单校验
|
||
- explain 成本校验
|
||
|
||
## 12. 针对“销售完成业绩”和“区域业绩”的配置方式
|
||
|
||
### 12.1 销售完成业绩推荐做法
|
||
|
||
建议先沉淀一个业绩事实层表或视图:
|
||
|
||
- `analytics_sales_performance_fact`
|
||
|
||
字段建议:
|
||
|
||
- `stat_date`
|
||
- `sales_user_id`
|
||
- `sales_user_name`
|
||
- `org_id`
|
||
- `org_name`
|
||
- `region_code`
|
||
- `region_name`
|
||
- `target_amount`
|
||
- `actual_amount`
|
||
- `won_amount`
|
||
- `contract_amount`
|
||
- `payment_amount`
|
||
- `adjust_amount`
|
||
|
||
好处:
|
||
|
||
- 前端怎么展示都行
|
||
- 后台 SQL 简单很多
|
||
- 口径变更时只改事实层生成逻辑或切换版本
|
||
|
||
### 12.2 区域业绩推荐做法
|
||
|
||
建议同样通过事实层统一:
|
||
|
||
- 客户区域
|
||
- 项目区域
|
||
- 销售归属区域
|
||
|
||
如果口径会改,可以做多个字段:
|
||
|
||
- `customer_region_code`
|
||
- `project_region_code`
|
||
- `owner_region_code`
|
||
|
||
后台 SQL 通过配置选择用哪个字段聚合。
|
||
|
||
## 13. 推荐的“SQL 可配置”分层
|
||
|
||
为了避免后期越来越乱,建议把 SQL 分成两层。
|
||
|
||
### 13.1 第一层:事实层 SQL
|
||
|
||
由研发维护。
|
||
|
||
职责:
|
||
|
||
- 把复杂业务逻辑沉淀成事实表或视图
|
||
- 处理复杂口径、去重、归属、调整项
|
||
|
||
### 13.2 第二层:展示层 SQL
|
||
|
||
由后台管理配置。
|
||
|
||
职责:
|
||
|
||
- 从事实层表/视图做汇总
|
||
- 做 KPI、排行、趋势、区域分组
|
||
|
||
这是最适合你的方案。
|
||
|
||
原因:
|
||
|
||
- 真正复杂的口径不适合完全交给后台人员写
|
||
- 但展示层统计完全可以后台配置
|
||
- H5 端可以完全动态渲染
|
||
|
||
## 14. 如果你坚持“所有 SQL 都后台可配”
|
||
|
||
也可以做,但我建议加 3 个保护措施。
|
||
|
||
### 14.1 只允许访问白名单对象
|
||
|
||
例如只允许查:
|
||
|
||
- `analytics_*`
|
||
- `crm_*`
|
||
- `work_*`
|
||
- 部分 `sys_*` 维表
|
||
|
||
### 14.2 强制 SQL 发布审核
|
||
|
||
流程建议:
|
||
|
||
- 编辑草稿
|
||
- 测试预览
|
||
- 提交审核
|
||
- 审核通过后发布
|
||
|
||
### 14.3 强制版本切换
|
||
|
||
H5 / PC 永远只读“已发布版本”,不读草稿。
|
||
|
||
## 15. API 设计建议
|
||
|
||
### 15.1 后台管理端
|
||
|
||
- `GET /api/admin/analytics/reports`
|
||
- `POST /api/admin/analytics/reports`
|
||
- `PUT /api/admin/analytics/reports/{id}`
|
||
- `GET /api/admin/analytics/reports/{id}/versions`
|
||
- `POST /api/admin/analytics/data-sources`
|
||
- `PUT /api/admin/analytics/data-sources/{id}`
|
||
- `POST /api/admin/analytics/data-sources/{id}/preview`
|
||
- `POST /api/admin/analytics/reports/{id}/publish`
|
||
|
||
### 15.2 前台 H5 / PC
|
||
|
||
- `GET /api/analytics/reports/{reportCode}`
|
||
- `POST /api/analytics/reports/{reportCode}/query`
|
||
|
||
## 16. 实施步骤建议
|
||
|
||
### 第一阶段
|
||
|
||
目标:
|
||
|
||
- 先把基础平台搭起来
|
||
|
||
内容:
|
||
|
||
- 报表定义表
|
||
- 数据源定义表
|
||
- Widget 定义表
|
||
- H5 动态渲染页
|
||
- PC 动态渲染页
|
||
- SQL 安全校验
|
||
|
||
### 第二阶段
|
||
|
||
目标:
|
||
|
||
- 上线销售完成业绩、区域业绩
|
||
|
||
内容:
|
||
|
||
- 销售业绩事实表
|
||
- 区域业绩事实表
|
||
- 报表管理页
|
||
- SQL 配置预览
|
||
- 发布与回滚
|
||
|
||
### 第三阶段
|
||
|
||
目标:
|
||
|
||
- 真正进入“后台可运营”
|
||
|
||
内容:
|
||
|
||
- 版本对比
|
||
- 审核流
|
||
- 执行监控
|
||
- 性能报警
|
||
- 缓存策略
|
||
|
||
## 17. 最终建议
|
||
|
||
如果你想实现:
|
||
|
||
- 后台管理定义
|
||
- H5 自动展示
|
||
- SQL 可配置
|
||
- 后期口径频繁修改
|
||
|
||
那么我最推荐的是:
|
||
|
||
1. 做一个 `报表定义 + SQL 数据源 + Widget 配置` 的后台管理平台
|
||
2. H5 与 PC 统一走动态渲染
|
||
3. SQL 采用“模板化 + 参数化 + 权限注入 + 发布审核”
|
||
4. 真正复杂的业绩口径优先沉淀为事实层视图或事实表
|
||
5. 展示层 SQL 再交给后台配置
|
||
|
||
一句话总结:
|
||
|
||
`这个方案完全可以做,而且很适合你当前“口径复杂、变化频繁、H5要快速展示”的场景;但最佳实践不是裸放任意 SQL,而是做受控的后台 SQL 配置平台。`
|
||
|