13 KiB
后台配置 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,例如:
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
存报表主定义。
建议字段:
idreport_codereport_namereport_categorydescriptionstatusterminal_scopedefault_time_granularityvisiblecreated_byupdated_bycreated_atupdated_at
说明:
terminal_scope用于区分pc / h5 / both
6.2 analytics_report_version
存报表版本。
建议字段:
idreport_idversion_noversion_statuschange_logpublished_bypublished_atconfig_jsoncreated_at
说明:
- 一个报表允许多个版本
- H5 默认读已发布版本
6.3 analytics_widget
存一个报表下的组件定义。
建议字段:
idreport_version_idwidget_codewidget_namewidget_typetitlesubtitlelayout_jsonprops_jsondata_source_idsort_ordervisible
6.4 analytics_data_source
存数据源定义,也就是 SQL 配置核心。
建议字段:
idsource_codesource_namesource_typedataset_codequery_sqlcount_sqlparameter_schema_jsonresult_schema_jsontimeout_msmax_rowscache_ttl_secondsenabledcreated_atupdated_at
6.5 analytics_filter_def
存筛选器定义。
建议字段:
idreport_version_idfilter_codefilter_namefilter_typedata_typedefault_value_jsonoptions_source_typeoptions_source_configsort_ordervisible
6.6 analytics_sql_publish_log
存 SQL 发布日志。
建议字段:
iddata_source_idversion_noold_sqlnew_sqlchange_summaryoperator_idcreated_at
6.7 analytics_query_log
存执行日志。
建议字段:
idreport_codewidget_codedata_source_idrequest_user_idrequest_params_jsonfinal_sql_previewduration_msrow_countsuccesserror_messagecreated_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”。
示例:
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 必须配参数定义。
示例:
[
{ "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 为空时,不拼条件;
有值时自动生成:
and region_code = :regionCode
不建议把这种逻辑写死在前端。
9. 前端展示怎么实现
虽然 SQL 在后台配置,但前端还是要有统一协议。
9.1 H5 与 PC 共用配置协议
前端请求:
- 页面定义
- 筛选器定义
- Widget 定义
- Widget 数据
9.2 Widget 类型建议
支持以下组件:
kpilinebarstack-barpietablerank-list
9.3 H5 展示规则
建议在配置里加上:
showOnPcshowOnH5h5SortOrderpcSortOrderh5ChartTypepcChartType
这样同一个报表可以:
- PC 显示表格 + 图表
- H5 只显示 KPI + Top10
9.4 H5 页面渲染流程
- 获取报表定义
- 获取筛选项定义
- 渲染页面骨架
- 请求组件数据
- 渲染图表
10. 后端执行引擎设计
建议新增一个 AnalyticsQueryService。
职责:
- 加载已发布报表版本
- 解析筛选条件
- 校验参数类型
- 生成最终 SQL
- 注入权限条件
- 执行查询
- 做缓存
- 记录日志
10.1 执行流程
- 读取报表配置
- 读取组件配置
- 读取数据源 SQL 模板
- 根据请求参数构造过滤条件
- 自动注入数据权限
- 生成最终 SQL
- 执行查询
- 转成统一结果结构
10.2 统一返回结构
建议:
{
"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_datesales_user_idsales_user_nameorg_idorg_nameregion_coderegion_nametarget_amountactual_amountwon_amountcontract_amountpayment_amountadjust_amount
好处:
- 前端怎么展示都行
- 后台 SQL 简单很多
- 口径变更时只改事实层生成逻辑或切换版本
12.2 区域业绩推荐做法
建议同样通过事实层统一:
- 客户区域
- 项目区域
- 销售归属区域
如果口径会改,可以做多个字段:
customer_region_codeproject_region_codeowner_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/reportsPOST /api/admin/analytics/reportsPUT /api/admin/analytics/reports/{id}GET /api/admin/analytics/reports/{id}/versionsPOST /api/admin/analytics/data-sourcesPUT /api/admin/analytics/data-sources/{id}POST /api/admin/analytics/data-sources/{id}/previewPOST /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 可配置
- 后期口径频繁修改
那么我最推荐的是:
- 做一个
报表定义 + SQL 数据源 + Widget 配置的后台管理平台 - H5 与 PC 统一走动态渲染
- SQL 采用“模板化 + 参数化 + 权限注入 + 发布审核”
- 真正复杂的业绩口径优先沉淀为事实层视图或事实表
- 展示层 SQL 再交给后台配置
一句话总结:
这个方案完全可以做,而且很适合你当前“口径复杂、变化频繁、H5要快速展示”的场景;但最佳实践不是裸放任意 SQL,而是做受控的后台 SQL 配置平台。