开源数据应用平台Lumina:从报表到业务闭环的一站式解决方案

发布时间:2026/10/3 18:33:57
开源数据应用平台Lumina:从报表到业务闭环的一站式解决方案
最近一个多月我每天睡得挺晚但不是在赶报表而是在写自己的开源数据应用平台 Lumina。做数据这行的人都知道最内耗的不是分析本身而是无穷无尽的取数、清洗、口径核对。工具越来越多真正留给“思考数据”的时间却越来越少。Lumina 就是在这种憋屈里长出来的它想让你交付的不再是一张静态图表而是一个同事每天都会主动打开、能自己过滤、能看出异常、能直接导出结果的数据应用。现在是开源项目也配了在线 Demo下面我把设计思路、踩坑过程和实际玩法一次说清楚。如果你是被报表需求淹埋的分析师或者想给团队搭一个自助分析工具但不想从零写前端的数据开发这篇文章应该对你有用。Demo 入口放在文末建议你先去点两下再回来看文字体验会更直观。1. 为什么我认定“报表工具”解决不了当下的问题1.1 业务要的不是一张图而是一个结果过去四年我做过不少内部数据产品最常见的一个需求是运营说想看“分渠道转化趋势”于是我们搭一张折线图。过两周运营说能不能加个日期筛选我们加上。再过两周运营说要按地区拆分我们又加一个下拉框。半年后这张“简单图表”变成了一个带五个筛选条件、三张图、两张明细表的复杂页面。但运营真正想要的其实是自己打开一个页面圈定时间和渠道看到排行榜发现转化异常然后一键把候选名单导出来发给人跟进。这两件事的差别很大。传统 BI 报表解决的是“把数据画出来”而数据应用解决的是“让业务围着数据完成一个闭环动作”。Lumina 从一开始就定位后者。它不是一个报表工具而是一个能让你把数据源、指标、筛选条件、表格组件、图表组件和权限规则拼装成一个小型业务系统的平台。1.2 报表和数据应用的差别到底是什么我习惯用下面这张对比表来解释 Lumina 的定位维度传统报表数据应用使用方式打开看看打开后用起来生命周期做完发布就完事业务持续迭代表单持续演化交互深度筛选、下钻筛选、下钻、标记、批量操作、外部跳转口径管理每个报表单独维护指标一处定义全平台复用权限要求多数只读行级、列级、操作级交付形式图表 URL嵌入业务系统的组件/页面/API看清这个差别之后再看“开源数据应用平台”这八个字就顺了。Lumina 想完成的事是给数据团队一个快速构建数据应用的底座接上数据配好指标拖一拖页面Ok 了给业务方一个能真正用起来的东西。1.3 Lumina 到底在做哪一层很多朋友问我和 BI 平台、低代码平台的边界在哪。我的理解是BI 偏向“看”低代码偏向“随便搭一个表单”而 Lumina 卡在“数据很重、交互要轻”这个中间层。它替你处理连接数据源、定义指标口径、控制权限、渲染图表和表格、暴露查询 API 这些脏活你只需要关注业务怎么用数。这一层恰恰是团队里最容易重复造轮子的地方。我见过太多团队自己写一个后端接口再配一个前端图表页换一个业务又重复来一遍。Lumina 想提供一种机制数据和指标定义一次应用层随意组合。2. 架构上让我最得意的三个决定2.1 不做中心化数仓而是把查询下推给源库平台刚设计的时候我差点掉进一个坑做一个自带存储的数据平台把所有数据先抽进来再让用户查询。但这么做会背上昂贵的存储和同步成本而且数据实时性也差。最后我定了一个原则Lumina 不存业务数据只存元数据。数据仍留在它该在的地方Lumina 通过连接器把查询下推到源数据库执行。这句话翻译成人话就是你的数据在 MySQL、PostgreSQL、ClickHouse 或者 SQLite 里Lumina 不复制一份而是在你配置好的连接地址之上生成查询计划交给源库去算再把结果返回出来。这样做有几个实打实的好处不需要额外搭一套同步链路省了开发和维护成本。用户查到的永远是实时数据不会被“T1 同步”卡喉。大数据量场景下可以依赖源库的分布式计算能力。当然代价也清楚你没法在 Lumina 里做大范围跨源 join。Lumina 的思路是鼓励你建好宽表再接入这也是大多数数据工程团队的常规操作。目前内置的连接器有 SQLite、MySQL、PostgreSQL、ClickHouse、CSV 文件。SQLite 主要用来自测和本地体验因为零依赖Demo 环境跑得飞快。2.2 语义层口径一处定义处处复用第二个关键决定是做语义层。我把它理解为“把指标和维度做一次集中抽象”。假设销售额这个指标口径是 sum(amount * discount)如果在十个页面里各写一遍早晚会有一处漏掉折扣导致对不上数。Lumina 的做法是在数据源配置阶段就把指标定义好之后所有应用、所有图表共享同一份指标定义。配置大概是这个样子datasource: retail_order table: dm_order_daily metrics: - name: sales_amount expr: sum(amount * discount) desc: 实际销售额折扣后 - name: order_count expr: count(distinct order_id) desc: 订单数按订单ID去重 dimensions: - name: channel field: channel_name - name: region field: region - name: order_date field: dt type: date配置一次之后搭建页面的时候只需要声明“我要展示 sales_amount 和 order_count按 channel 维度分组”Lumina 的查询引擎会从语义层自动生成 SQL。口径只有一份定义改一处所有应用跟着变这几乎是所有数据团队最刚的需求。2.3 页面即配置一张 JSON 描述整个应用第三个决定可能最反直觉Lumina 的应用页面不是代码写出来的而是用一份描述性 JSON 配置渲染出来的。一个应用就是一个 JSON 文档里面声明了布局、筛选条件、组件类型、绑定的指标和数据源。比如一个最简单的销售看板长这样{ app: sales_dashboard, title: 销售运营看板, layout: [ { type: filter, fields: [order_date, channel, region] }, { type: chart, chart_type: line, metrics: [sales_amount], dimension: order_date, granularity: week }, { type: table, metrics: [sales_amount, order_count], dimensions: [channel], page_size: 20 } ], permission: { role: sales_analyst } }渲染器拿到这份 JSON 之后自动切查询、自动画图、自动生成表格和筛选器。这意味着什么意味着 Lumina 的应用天然适合嵌入到你们已有的系统里。你不需要引导用户去一个新的站点看页面直接把组件嵌到你们公司的运营后台一个 iframe 或一段脚本就能跑起来。这也是 Lumina 能叫“数据应用平台”而不是“数据报表平台”的原因。页面即配置配置即 API应用可以被组装。3. 从零跑通第一个 Lumina 应用我亲手实测的步骤3.1 准备数据源调整成宽表思维我第一次测试时用了销售订单数据建议你也这么干。不要先想着把所有表都接进来而是先把业务需要的数据整理成一张宽表一行记录代表一个维度组合下的聚合值比如按日期渠道地区聚合的订单指标。在 Lumina 的数据源配置里我新建一个连接器指向 ClickHouse 里的 dm_order_daily 表。如果你本地测试SQLite 是最快的建一个本地文件数据库导入几千行 CSV 就够用了。Lumina 会自动读取表结构识别字段类型和可空性。这一步里第一个值得养成的习惯日期字段必须单独定义格式因为后面做按天、按周、按月聚合都依赖它。我的踩坑经历后文会细说。3.2 在语义层定义指标和维度数据源连上之后进入指标配置页。很多人问这一步是不是只是写 SQL我的回答是是也不完全是。写 SQL 只是定义了计算逻辑在这里你同时还把这个指标的口径、单位、默认聚合方式、是否可下钻注册成了元数据。我把前面那个 yaml 里的配置贴进去保存后系统会自动校验字段是否存在于表里比如 amount 列是否存在、channel_name 是否可空。校验的好处是业务应用跑挂了通常在你保存配置的那一刻就会暴露而不是等用户打开页面才发现。配置完成之后可以立刻在“查询调试”里试一下选择销售指标按渠道分组看看返回结果是不是和直接在 SQL 客户端里跑一致。这一步能筛掉 90% 的口径错误。3.3 拖出一个页面绑定交互组件页面配置这一步我一开始担心拖拽搭建会不会太低效。实际体验发现配置系统中用 JSON 描述比拖拽更高效。拖拽编辑器做给业务自己改还可以但数据团队给业务搭东西速度是第一位的。我在 Lumina 中新建了一个 app命名为 sales_dashboard添加了三个筛选组件order_date、channel、region一个趋势图和一个明细表。趋势图的维度选 order_date粒度选 week这样看到的是按周的销售额走势。表格里同时放 sales_amount 和 order_count便于对比客单价。组件绑定完保存应用立即生成一个可访问的 URL。这个 URL 我直接发给同事测试他们不需要登录后台打开就能用。从这里开始它就不只是一张报表而是一个可以被业务点来点去的小应用。3.4 发布、嵌入和权限控制发布之前我还做了一件事配置权限。Lumina 支持最简单的“页面级可见”和更细的“行级权限”。页面级可见是指定某个角色能看到这个应用行级权限则是指定每个用户只能看到自己负责的地区数据。配置方式也不复杂在应用权限里写一条规则即可permission: allow: [sales_analyst, sales_manager] row_rule: region ${user.region}${user.region} 是登录上下文变量Lumina 会在执行查询时自动替换成当前用户的属性值从查询层面保证数据隔离。这一点比我之前用过的很多商业化 BI 更直接因为它是内建能力而不是靠前端偷偷隐藏行。最后我把这个应用的 URL 嵌到公司内部运营后台的菜单里。整个流程下来从接数据到嵌入大约二十分钟。这也是我第一次意识到数据交付的时间单位不应该停留在“周”而是应该以“小时”计。4. 测试期间最想吐槽的三个坑4.1 80 万行让渲染器直接卡死排查了半天第一次用自己的平台跑真实业务数据时我给一个明细表配置了 80 万行返回。结果用户一进页面浏览器直接僵住。我第一反应是查询太慢了于是打开数据库慢查询日志发现 ClickHouse 执行只花了 0.4 秒问题根本不在数据库。继续排查我先确认网络传输数据量80 万行每行十几个字段大约 30MB 的 JSON。浏览器要一次性接收并渲染 30MB JSON还要做 DOM 渲染卡住几乎是必然。定位到方向之后我从三个层面修复表格组件默认进行服务端分页每次只取当前页的 20 条。汇总指标比如总计销售额单独走聚合查询不依赖明细返回。限制默认导出条数导出超过 1 万行时走异步任务后台生成好文件再通知用户。改完之后同样是 80 万行的表页面秒开。这个坑给我最大的教训是平台必须内建“大数据量保护机制”不能依赖使用者的自觉。对一个给业务用的数据应用来说性能抖动会直接摧毁信任。4.2 日期聚合的边界一周七天和自然周不是一回事第二个坑发生在“按周聚合”上。我配置趋势图时选了 order_date 字段粒度选 week。本意是按照自然周周一到周日聚合但第一次跑出来的数据每周一的数据单独成了一段周日的数据也异常稀疏。我花了不少时间才发现是时区问题库里存的是 UTC 时间业务上希望按北京时间划分“自然日”和“自然周”。Lumina 默认用数据库会话时区做聚合导致 UTC 下的零点被映射成了北京时间的早上 8 点日期归属偏了一天有些周一只剩下半天数据。定位到根因后我的处理方式是在数据源连接配置里强制指定业务的时区datasource: type: clickhouse timezone: Asia/Shanghai date_column: dt同时在语义层把 order_date 标记为日期类型字段聚合粒度统一到“业务日业务周”。之后再跑数据分布就符合直觉了。这里想提醒大家所有带时间分析的平台时区都必须提前定好而且应该由平台统一处理而不是丢给源库各自发挥。时区问题看起来小一旦数据错了业务方会直接质疑你的整个平台。4.3 行级权限和排序冲突第三个坑稍微隐蔽。给某个应用配置了行级权限region ${user.region}之后表格里有一个排序功能用户点击表头“按销售额降序”。我的实现是直接在 SQL 里 order by sales_amount desc但行级权限的过滤条件和排序之间出现了执行顺序问题常规逻辑是先过滤再排序结果没错但在一个特殊场景里——用户同时看到了共享的总计行和本区域明细行——总计行参与排序后位置被插到明细行中间观感非常乱。排查链路如下先复现稳定路径发现总计行是一个“虚拟行”它不属于任何 region行级权限规则自动把它过滤掉了。也就是说业务用户点了排序后看到的根本不是一个完整表格而是缺失总计的表格。最后我把虚拟汇总逻辑从销售明细查询中抽出来单独走一次聚合查询单独渲染在表格底部不再和明细行混排。这彻底解决了权限和排序的冲突。这些事情如果不是真跑起来看设计文档是发现不了的。所以我一直建议所有做数据平台的人一定要拿真实业务数据、真实用户行为去压测而不是只对着样例数据验证功能。5. 在线 Demo 怎么玩才有价值5.1 入口和演示数据场景在线 Demo 我放到了文末打开就是 Lumina 的默认工作区不需要注册登录。内置了三套演示数据集分别对应三个业务场景零售销售分析订单明细 门店维度的日汇总表可以试筛选、试图表、试导出。用户留存分析按用户活跃日期表计算次日/7日/30日留存重点看指标定义和自定义公式。营销投放分析渠道投放费用与转化数据的宽表适合测试行级权限和排序交互。我建议你先打开“零售销售分析”因为这个数据集的字段更贴近日常能很快理解“指标 维度 筛选”的建模逻辑。菜单结构很简洁左侧是数据源中间是应用列表右上角是新建应用的按钮。第一次使用不用手软随便点。5.2 三个值得动手试的功能第一是“自定义指标”。在 Demo 里打开任意一个应用进指标配置页加上一个公式比如sales_amount / order_count保存后回到图表页立刻能选择这个新指标。你会感受到“口径一处定义处处复用”的效率。第二是“行级权限模拟”。Demo 内置了一个测试角色切换按钮切换成华东销售代表后你会发现所有地区数据被自动剥走只留下华东的数据而且图表和导出结果同样被过滤。这个功能建议你反复试几次能直观理解平台层面的数据隔离逻辑。第三是“嵌入预览”。Demo 里有一个应用详情页的“嵌入预览”按钮点击后新页面只渲染应用本身不显示任何平台外壳。模拟的就是把应用嵌入你们公司内部系统的效果。很多刚接触的朋友到这个环节都会说一句原来嵌入这么轻。5.3 Demo 没做到和暂时做不到的事说点实话Demo 环境为了稳定后端只接了一个 SQLite 数据库查询性能上肯定没法代表生产环境跑在 ClickHouse 上的表现。另外Demo 没有开放多用户注册所以协作和审批流目前看不到。如果你想完整体验最靠谱的方式是下载源码本地跑起来三分钟就能起一个单机实例再把 Excel 或 CSV 导进去试。6. 开源状态与未来半年规划6.1 现在开源到了什么程度Lumina 目前的代码量不算大后端加前端加起来大约两万行出头。仓库里已经包含完整的单机可运行版本支持 SQLite / PostgreSQL / MySQL内置了语义层、应用渲染器、权限系统和查询 API。具体来说你现在 clone 下来能直接用到的功能包括数据源管理、指标定义、应用配置与渲染、筛选器、图表组件、表格组件、行级权限、嵌入模式。也可以直接用仓库里提供的 Docker Compose 一键起服务。仓库地址是 github.com/lanr项目名就叫 Lumina。文档我抽了两个晚上专门整理过包括快速开始、配置详解和 API 说明。如果你是第一次接触这种“数据应用”概念建议先从 README 里的“五分钟上手”看起。6.2 暂时不开源什么有一块代码我暂时没有完全开源企业版里针对多集群、多租户的调度与审计模块。这不是理念上想封闭而是这块涉及大量生产环境的稳定性细节如果仓促开源但没人维护反而是种负担。内部的架构文档和接口设计我会逐步放出来等社区讨论成熟之后再调整。6.3 我希望社区从哪里切入比起“帮我写代码”我更希望听到的是“我在 XX 场景下用了发现 XX 不好用”。这个反馈对数据平台的价值远大于代码贡献。加入社区之后你可以参与以下方向新增连接器比如 Doris、StarRocks 这类国内使用率很高的 OLAP 引擎。完善可视化组件库比如漏斗图、地图组件、透视表。补齐权限体系与企业目录SSO / LDAP的对接。完善应用间的页面跳转和参数传递机制。现在的开源项目很多都追求“一键安装哪都能跑”Lumina 也确实做了这个体验但我更想让它变成一个有生命力的工程而不是代码仓库里的一堆静态文件。每次收到真实使用反馈我都会在心里把它放大成一个具体的改进点然后排进迭代计划。最后再分享一点我自己的体会。搭建 Lumina 的这段时间最快乐的并不是“我终于能做出一个平台”而是每当有人跑来跟我说“这个应用解决了我们的问题”的时候那种感觉和以前发出去一张报表却长期没人打开完全不同。Demo 入口就在下边如果你看完愿意留言告诉我哪里顺手、哪里别扭我会认真看每一条。在线 Demolumina-demo.vercel.app仓库地址github.com/lanr