# 后台配置 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 配置平台。`