bi-engine.md 9.18 KB

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)、kpimoduleuserkpimoduleuserdaykpislavel 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_11Sp_chart_home_13 首页看板卡片
Sp_chart_TodayOrderSp_chart_TodayOrder_hmSp_chart_ThisMonthQtySp_chart_MonthOrderSp_chart_MonthTeamQty 当天 / 当月订单数、班组产量
Sp_chart_TodayProfitSp_chart_MonthProfitSp_chart_TodayReceivablesSp_chart_TodayReceiveSp_chart_expenses 财务:利润、应收、收款、费用汇总
Sp_chart_EquipmentLoadSp_chart_EquipmentLoad1Sp_chart_EquipmentLod1Sp_chart_EquipmentLastSp_chart_sMachine_speedSp_chart_Bottleneck 车间:设备负载、最后运行状态、当前瓶颈
Sp_chart_OrderProcessSp_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.java enum(xlyPersist):列名常量。

存储过程:

  • Sp_KPI_DetailByEmployee:按员工的明细报表。
  • Sp_KPI_SumByEmployee:按员工的汇总报表。
  • spKPImodule:按模块重算 KPI。

script/客户/ 下也有较大的客户覆盖,例如 script/标版/30100101/spKPImodule.sql 以及多个 Sp_SalesOrder_Kpi* 过程。这与每客户 SQL 覆盖通道一致:需要不同 KPI 规则的客户会交付自己的过程。

自研方案的代价

元数据 + 每图表一个过程的设计与 xly 的数据驱动论点一致,也避免了携带重型 OLAP 引擎。但代价很明确:

  1. 每张新图表都需要 SQL 作者。 “PM 添加一行元数据”只在工程师已经写好配套 Sp_chart_* 过程之后才成立。这里没有聚合构建器、字段选择器或自动生成查询;每个指标都是工程团队手写、评审和维护的存储过程。20 个过程和 11 种图表类型就是当前系统能渲染的全部形状。
  2. 图表在 OLTP DB 上跑重 SQL。 没有数仓、没有预聚合、没有增量汇总。“今日利润”图表就是在实时交易 schema 上做 SELECT。大客户加载图表时会和录单负载竞争同一个 MySQL 实例。缓存有帮助,但只对命中有效;元数据变更后的第一次加载仍要付完整成本。
  3. 图表之间没有语义一致性保证。 每个 Sp_chart_* 过程自行决定如何计算“月利润”“今日销售额”等指标。两个看似展示同一指标的图表可能因为过程体不同而悄悄不一致。真正的语义层可以避免这个问题,自研模型不能。
  4. 不能钻取,也不能自由切片分析。 每张图表都是固定查询形状。用户不能自由切换维度,也不能从汇总卡片钻取到底层交易,除非工程师为每条路径再写一个过程。
  5. KPI 逻辑会按客户分歧。 script/客户/ 下的客户会交付自己的 spKPImoduleSp_SalesOrder_Kpi* 覆盖;不同客户的 KPI 算法不同,而且代码只存在于该客户 DB 中。这会让“这个 KPI 到底是什么意思”取决于当前连接的是哪个 schema。

这种简单设计足够支撑“展示 xly 一直展示的那 20 张卡片”。如果目标是即席分析或自助报表,它就不够了;那需要一套 xly 当前没有的独立语义层 / 数仓层。

它不是什么

  • 不是自助 BI 工具。 客户不能随便指向一张表并拖拽生成图表;新图表需要一个 SQL 存储过程,以及懂得注册元数据行的管理员。
  • 不是实时分析基础设施。 图表在缓存 miss 时会直接在 OLTP MySQL schema 上运行过程。没有独立数仓、没有增量聚合管道、没有流式层。大客户的大图表会在实时 DB 上执行重 SQL。
  • 不是列存 / OLAP 引擎支撑的分析。 xlyPersist/build.gradle 中的 olap4j jar 没有任何 Java import,只是 classpath 上的历史包袱。xly 通过 MyBatis 和通用存储过程分发使用 MySQL 的普通行存。