🎯 本章核心问题
数据大屏(Dashboard)的核心矛盾是什么?
| 维度 | 传统方案的问题 | 我们的解决方案 |
|---|---|---|
| 性能 | 每次刷新都调 LLM,3-5 秒延迟 | 运行时只执行 SQL,200ms 内完成 |
| 成本 | 刷新 100 次 = 调用 100 次 LLM API | 只在设计时调 1 次 LLM,后续零成本 |
| 可维护性 | 硬编码 UI 布局,改需求要改代码 | 配置驱动,修改 JSON 即可生效 |
| 灵活性 | 固定模板,无法自定义布局 | Grid 网格系统 + 拖拽交互 |
📐 架构总览:两阶段分离模型
数据大屏两阶段分离架构
核心思想
设计时调 LLM 写配置,运行时仅执行 SQL
为什么这种设计是革命性的?
类比理解:
就像预制菜 vs 现炒:
- 设计时 = 厨师精心烹饪一道菜(调用 LLM 生成配置)
- 运行时 = 微波炉加热即可食用(直接读取配置 + 执行 SQL)
传统方案的致命缺陷:
1 | |
我们的方案:
1 | |
🎨 一、阶段一:设计时 —— LLM 驱动的配置生成
1.1 用户输入 → 结构化配置
app/services/dashboard_service.py
1 | |
1.2 DashboardConfig 数据结构定义
app/schemas/dashboard.py(schemas/dashboard.py)
1 | |
1.3 实际案例:从自然语言到完整配置
用户输入:
1 | |
LLM 生成的配置(存储在数据库中):
每个 Widget 预存完整 SQL(已折行展示)
💡 关键观察
- 每个 Widget 都有独立的、完整的 SQL — 这是运行时能脱离 LLM 的前提
- 坐标使用 Grid 系统(x/y/w/h)— 支持拖拽调整位置和大小
- chart_options 是可选的 — 允许高级用户微调图表样式
⚡ 二、阶段二:运行时 —— 高效的数据获取与渲染
2.1 加载 Dashboard 配置
app/api/dashboards.py(api/dashboards.py)
1 | |
2.2 并发查询所有 Widget 数据
app/api/dashboards.py(api/dashboards.py)
1 | |
🎯 为什么并发如此重要?
假设有 5 个 Widget,每个 SQL 执行需要 100ms:
| 方式 | 耗时计算 | 总时间 |
|---|---|---|
| 串行执行 | 100ms × 5 = 500ms | 较慢 |
| 并发执行 | max(100ms, 100ms, …) = ~100ms | 快 5 倍 |
2.3 前端渲染引擎
views/dashboard/DashboardView.vue
1 | |
2.4 自动刷新机制
1 | |
🗄️ 三、数据模型设计
3.1 ER 图
1:N 关系:一个大屏包含多个 Widget
3.2 SQLAlchemy 模型
app/models/dashboard.py(models/dashboard.py)
1 | |
🔄 四、两阶段对比总结
4.1 完整生命周期
设计时一次性生成 vs 运行时无限刷新
4.2 性能与成本对比矩阵
| 指标 | 传统方案(每次调 LLM) | 我们的方案(两阶段分离) |
|---|---|---|
| 首次加载 | 3000-5000ms | 3000-5000ms(含 LLM) |
| 刷新延迟 | 3000-5000ms | 150-250ms(纯 SQL) |
| 单日成本(100 次刷新) | ~$0.50 | $0.005(仅首次) |
| 月度成本(30天) | ~$15 | $0.15 |
| 可扩展性 | 受限于 LLM QPS | 仅受限于 MySQL 连接池 |
| 离线可用 | ❌ 需要 LLM 服务在线 | ✅ 配置已本地持久化 |
4.3 适用场景判断
| 场景 | 是否适合两阶段架构? | 原因 |
|---|---|---|
| ✅ 实时监控大屏 | 强烈推荐 | 高频刷新,成本敏感 |
| ✅ 固定报表模板 | 推荐 | SQL 稳定,无需频繁调整 |
| ⚠️ 探索式分析 | 可选 | 可能需要频繁修改配置 |
| ❌ 完全动态场景 | 不推荐 | 每次 SQL 都不同,无法预编译 |
🎯 五、最佳实践与设计模式
✅ 我们做到了什么
- 真正的解耦:UI 渲染与业务逻辑完全分离
- 声明式编程:通过 JSON 描述界面,而非命令式代码
- 性能极致优化:运行时零 AI 成本,响应速度提升 15 倍+
- 热更新能力:修改数据库配置即可实时生效
- 前端组件化:基于 Vue 动态组件实现灵活的 Widget 系统
📚 最佳实践清单
- 使用 Pydantic 定义严格的 Config Schema(确保 LLM 输出格式正确)
- 每个 Widget 的 SQL 必须独立、完整(不能依赖其他 Widget 的结果)
- 使用
asyncio.gather()并发查询(而非 for 循环串行) - 前端使用 CSS Grid 或绝对定位实现自由布局
- 定时刷新时暂停页面隐藏状态(节省资源)
- 错误隔离:单个 Widget 查询失败不影响整体展示
- 配置版本控制:记录每次修改的时间戳和操作人
🚀 进阶优化方向
- 增量更新:只刷新变化的 Widget(通过 SQL Hash 对比)
- WebSocket 推送:替代轮询,实现真正的实时更新
- SQL 缓存层:对于聚合类查询,缓存 TTL 内的结果
- 多数据源支持:同一个大屏可以组合不同数据库的数据
相关代码文件:
app/services/dashboard_service.py(dashboard_service.py) — 大屏服务核心逻辑app/models/dashboard.py(models/dashboard.py) — 数据模型定义app/schemas/dashboard.py(schemas/dashboard.py) — Pydantic 配置 Schemaapp/api/dashboards.py(api/dashboards.py) — RESTful API 接口- DashboardView.vue — 前端大屏视图
- components/dashboard/*.vue — Widget 组件库
下一篇文章将深入 前端拖拽交互系统的实现细节 —— HTML5 Drag API、Grid 布局、mousedown 移动/缩放(碰撞检测为规划能力)!
敬请期待!🚀