BI / KPI / 图表引擎
xly 没有内置通用 OLAP / cube 引擎(没有 Mondrian、Saiku、MDX;随包里的 olap4j-1.2.0.jar 没有任何 Java import,属于 classpath 上的历史包袱,见技术栈里的 OLAP4J 说明)。
xly 真正提供的是一套自研的元数据驱动看板 + KPI 层。它和框架其他部分使用同一组原语:gds* 元数据行指向 Sp_* 存储过程,再由通用 Java service 渲染。本页按端到端路径说明它。
三个部分
| 部分 | 入口 | 支撑表 | Service |
|---|---|---|---|
| 图表(卡片、柱线饼仪表盘组件、看板) | FROUNT 的 /indexPage/commonChar 模块;BACK 的图表配置管理页 |
gdsconfigcharmaster(3,006 行)、gdsconfigcharslave(1,951 行) |
CharServiceImpl(2,219 行,是 xlyBusinessService 里最重的类之一) |
| KPI(按员工 / 模块做绩效评分) | BACK / FROUNT 的 KPI 页面,例如 行为KPI项目设置、5.经营KPI分析、异常清除KPI任务表
|
kpimaster(124,524 行)、kpidetail(1,308)、kpimodule(44)、kpimoduleuser、kpimoduleuserday、kpislavel
|
KpiServiceImpl(833 行,位于 xlyBusinessService/.../KPIService/)+ BusinessModelKpiServiceImpl(901 行)+ FlushModleKpiThread(后台刷新) |
| 预置聚合过程 | 由图表元数据行调用 | n/a | 20 个 Sp_chart_* 过程 + 2 个 Sp_KPI_* 过程 + spKPImodule
|
图表:看板如何渲染
xly 中的一个图表就是 gdsconfigcharmaster 中的一行,关键列如下:
| 列 | 作用 |
|---|---|
sId |
图表 ID |
sParentId |
所属模块(gdsmodule.sId) |
sChinese / sEnglish / sBig5
|
图表标题 |
sCharType |
组件类型,见下方分布 |
sProcedureName |
产出图表数据的存储过程 |
sProcedureParam |
存储过程参数 JSON 规格 |
iWidth |
布局跨度(24 列栅格) |
实时 dev DB 中的 sCharType 分布:
sCharType |
数量 | 渲染内容 |
|---|---|---|
Div |
1558 | 容器 / 纯布局块 |
sLabel |
1143 | 单值文本卡片,例如“今日销售额:¥X” |
Progress |
137 | 进度条 |
sPie |
52 | 饼图 |
commonList |
45 | 内嵌数据表格(复用通用 grid) |
sColumnarGroup |
30 | 分组柱状图 |
sColumnar |
28 | 单系列柱状图 |
sBrokenLine |
5 | 折线图 |
sBar |
3 | 横向条形图 |
ColorBlock |
3 | 类热力块的彩色块 |
sGauge |
2 | 仪表盘组件 |
从表行(gdsconfigcharslave)保存多系列 / 多列图表所需的系列或列拆分。
运行时路径:
SPA 打开 /indexPage/commonChar?sModelsId=<看板模块 id>
│
▼
GET /xlyEntry/business/getModelBysId/<id>
→ 返回看板的元数据复合结果
(formData 中包含来自 gdsconfigcharmaster + slave 行的图表布局)
│
▼
对每个图表:
POST /xlyEntry/business/getXxx(CharServiceImpl 方法)
携带图表的 sProcedureName + sProcedureParam
→ CharServiceImpl 通过通用存储过程分发调用对应的 Sp_chart_* 过程
│
▼
SPA 用前端 ECharts 渲染每张卡片;一行图表元数据对应一张卡片
20 个 Sp_chart_* 过程
| 过程 | 计算内容 |
|---|---|
Sp_chart_home_11、Sp_chart_home_13
|
首页看板卡片 |
Sp_chart_TodayOrder、Sp_chart_TodayOrder_hm、Sp_chart_ThisMonthQty、Sp_chart_MonthOrder、Sp_chart_MonthTeamQty
|
当天 / 当月订单数、班组产量 |
Sp_chart_TodayProfit、Sp_chart_MonthProfit、Sp_chart_TodayReceivables、Sp_chart_TodayReceive、Sp_chart_expenses
|
财务:利润、应收、收款、费用汇总 |
Sp_chart_EquipmentLoad、Sp_chart_EquipmentLoad1、Sp_chart_EquipmentLod1、Sp_chart_EquipmentLast、Sp_chart_sMachine_speed、Sp_chart_Bottleneck
|
车间:设备负载、最后运行状态、当前瓶颈 |
Sp_chart_OrderProcess、Sp_chart_WorkOrderProcess
|
订单 / 工单进度时间线 |
每个过程都遵循标准的 (IN sLoginId, IN sBrId, IN sSuId, ...) → result-set 形状,因此通用分发器可以直接调用。多租户作用域也会自然传入:每张图表都会自动按租户过滤。
dev DB 中的预置看板模块
/indexPage/commonChar 是共享路由。dev DB 中有 6 个模块映射到它:
| Module sId | 中文名称 |
|---|---|
19211681019715464089035510 |
销售图表分析 |
19211681019715481435115760 |
财务图表分析 |
19211681019715481435298200 |
生产图表分析 |
19211681019715708435449190 |
销售大数据分析 |
19211681019715708471874620 |
采购大数据分析 |
101251240115015889205266000 |
采购价格分析查询 |
六个模块全部由 gdsconfigcharmaster 行驱动;新增或修改它们不需要改 Java 代码。
KPI 子系统
先消歧。 FROUNT 首页也有一个标题为“KPI监控”的卡片,但它不是本页记录的 KPI。首页卡片是
BusinessModelCenterController.getModelCenter提供的未清任务计数器,读取gdsmodule.bUnTask/sUnType,没有目标值、评分和图表,只是名字容易误导。见运行时页的 KPI 工作中心。下面的kpi*表族才是真正的员工绩效评分层。
kpi* 是独立于图表渲染的按员工绩效评分层。形状如下:
| 表 | 作用 | 实时行数 |
|---|---|---|
kpimaster |
按员工、按期间的 KPI 汇总。它是容量最大的表:每个员工的每次评分事件一行。 | 124,524 |
kpidetail |
支撑每条 kpimaster 汇总的明细行。 |
1,308 |
kpimodule |
KPI 定义:哪些模块 / 指标参与评分。 | 44 |
kpimoduleuser |
每用户 KPI 分配。 | 0(dev DB 未分配) |
kpimoduleuserday |
每用户每日 KPI 桶。 | 1 |
kpislavel |
KPI 等级 / 档位定义。 | 0 |
Java 侧:
-
KpiServiceImpl.java(833 行,位于xlyBusinessService/.../KPIService/):KPI 事件的读写 API。 -
BusinessModelKpiServiceImpl.java(901 行):把业务事件数据计算成 KPI 行的计算层。 -
FlushModleKpiThread.java:后台重算线程。 -
KpimasterCloum.javaenum(xlyPersist):列名常量。
存储过程:
-
Sp_KPI_DetailByEmployee:按员工的明细报表。 -
Sp_KPI_SumByEmployee:按员工的汇总报表。 -
spKPImodule:按模块重算 KPI。
script/客户/ 下也有较大的客户覆盖,例如 script/标版/30100101/spKPImodule.sql 以及多个 Sp_SalesOrder_Kpi* 过程。这与每客户 SQL 覆盖通道一致:需要不同 KPI 规则的客户会交付自己的过程。
自研方案的代价
元数据 + 每图表一个过程的设计与 xly 的数据驱动论点一致,也避免了携带重型 OLAP 引擎。但代价很明确:
-
每张新图表都需要 SQL 作者。 “PM 添加一行元数据”只在工程师已经写好配套
Sp_chart_*过程之后才成立。这里没有聚合构建器、字段选择器或自动生成查询;每个指标都是工程团队手写、评审和维护的存储过程。20 个过程和 11 种图表类型就是当前系统能渲染的全部形状。 - 图表在 OLTP DB 上跑重 SQL。 没有数仓、没有预聚合、没有增量汇总。“今日利润”图表就是在实时交易 schema 上做 SELECT。大客户加载图表时会和录单负载竞争同一个 MySQL 实例。缓存有帮助,但只对命中有效;元数据变更后的第一次加载仍要付完整成本。
-
图表之间没有语义一致性保证。 每个
Sp_chart_*过程自行决定如何计算“月利润”“今日销售额”等指标。两个看似展示同一指标的图表可能因为过程体不同而悄悄不一致。真正的语义层可以避免这个问题,自研模型不能。 - 不能钻取,也不能自由切片分析。 每张图表都是固定查询形状。用户不能自由切换维度,也不能从汇总卡片钻取到底层交易,除非工程师为每条路径再写一个过程。
-
KPI 逻辑会按客户分歧。
script/客户/下的客户会交付自己的spKPImodule和Sp_SalesOrder_Kpi*覆盖;不同客户的 KPI 算法不同,而且代码只存在于该客户 DB 中。这会让“这个 KPI 到底是什么意思”取决于当前连接的是哪个 schema。
这种简单设计足够支撑“展示 xly 一直展示的那 20 张卡片”。如果目标是即席分析或自助报表,它就不够了;那需要一套 xly 当前没有的独立语义层 / 数仓层。
它不是什么
- 不是自助 BI 工具。 客户不能随便指向一张表并拖拽生成图表;新图表需要一个 SQL 存储过程,以及懂得注册元数据行的管理员。
- 不是实时分析基础设施。 图表在缓存 miss 时会直接在 OLTP MySQL schema 上运行过程。没有独立数仓、没有增量聚合管道、没有流式层。大客户的大图表会在实时 DB 上执行重 SQL。
-
不是列存 / OLAP 引擎支撑的分析。
xlyPersist/build.gradle中的olap4jjar 没有任何 Java import,只是 classpath 上的历史包袱。xly 通过 MyBatis 和通用存储过程分发使用 MySQL 的普通行存。