自助报表分析 4:源代码简析

润乾 BI 之所以开源,主要考量是 BI 前端的管理需求多变,固定的产品难以应付,在 DQL 解决了关联数据查询难题后,用户容易基于它开发出真正敏捷的 BI 前端。

代码结构


润乾 BI 运行流程

代码结构:

主要源码简介

浏览器端的 Javascript 源代码都在 {WEB 根目录}/raqsoft/guide/js/ 下,主要的 js 文件有 dqlApi.js、dqlreport.js、query.js、raqsoftApi.js、where.js、common.js。

页面用 JSP 中的 taglib 技术展示,taglib 定义在 {WEB 根目录}/WEB-INF/raqsoftAnalyse.tld,实现 taglib 的 Java 类是 com.raqsoft.guide.tag.AnalyseTag.java,其它一些主要 Java 类有:
com.raqsoft.guide.web.DataSphereServlet.java,请求入口的 Servlet
com.raqsoft.guide.web.dl.ActionResultPage.java,处理大部分 Servlet 请求,主要功能代码在这个类里。
com.raqsoft.guide.web.dl.DfxQuery.java,用内置集算器脚本查询 / 计算不同来源的数据。
com.raqsoft.guide.web.dl.DfxData.java,缓存计算好的数据集。

引入的 jar 主要有:
datalogic.jar,DQL JDBC 驱动,连接 DQL Server 做查询;
esproc-bin-*.jar 集算器,内置了一些集算器脚本实现查询、计算;界面用户也可以自定义脚本做个性化的数据处理;
raqsoftReport.jar 润乾报表。
源代码中会调用这三个工具的一些接口类实现功能,但这三个工具自身的代码不开源。
同时集算器和润乾报表共用了同一个运行环境配置文件,{WEB 根目录}/WEB-INF/raqsoftConfig.xml,里面会定义所有可用的 JDBC 数据源,一些资源路径等。

获得 DQL 元数据
看 ConfigUtil.java 里的代码:

JDBC 查询语句”metadata”,能从 DQL 服务器获得 JSON 格式的整个元数据信息:

{
 "tables": [{"name": "客户","dispName": "客户","type": "0","desc": "",
  "fields": [{
   "name": "客户",
   "type": 2,
   "desc": "",
   "pk": 1,
   "dim": "客户"
   }
   ......
  ],
  "subTables": []
 }
 }],
 "dims": [{"name": "市","table": "市","field": "市",
  "destLevels": [{
   "name": "省","dest": "省. 省","formula": "int(?/100)"
  }, {
   "name": "大区","dest": "大区. 大区","formula": "int(?/10000)"
  }]
 },
 ......
 ],
 "levels": [],
 "annexTables": [
  [{
   "name": "客户","pks": ["客户"]
  }, {
   "name": "VIP 客户","pks": ["VIP 客户"]
  }],
  ......
 ],
 "classTables": [],
 "editStyles": []
}

显示元数据树

把上面元数据信息从 WEB 服务器端传送到浏览器端,query.js 中用一个 JS 控件 zTree 把这些元数据信息整理后,显示为一棵元数据树:

其中的 zNodes 就是整理后的树节点,根据外键关联关系,相应的树节点可以展开出外键表:

依据界面操作生成 DQL 语句

从元数据树上选出一些字段做多维分析:

这些界面操作记录在 JS 变量 olapObj.conf.rpxs 里:

之后用 dqlApi.js 里的 dqlQuery.confUtils.generateDql() 方法按照界面上多维分析操作拼出相应的查询 DQL:

在 generateDql()方法里的最后可以看到,把整理好的 DQL 片段 (dqlSegments) 提交到服务器拼出最终的 DQL 语句:

得到 DQL 语句后,在 dqlreport.js 的 aly.queryData() 方法中,向 WEB 服务器发送查询请求:

用 DQL 查出的数据生成报表

界面上多维分析操作,除了用来生成查询 DQL,同时还用来生成润乾报表模板,用 aly.generateReport() 方法整理出一些报表关键信息,然后请求 WEB 服务器生成报表:

ActionResultPage.java 里 genReport 操作中,把生成好的报表模板存入 session:

最后在 showReport.jsp 中把 session 中的润乾报表模板展示出来即可。

实际上,大部分选项设置都提供了控制标签。像查询中:

	guideConf.queryType = "group";//detail明细查询/group分组查询
	guideConf.analysePage = "raqsoft/guide/jsp/analyse.jsp";//分析界面,用来显示查询结果
	guideConf.fixedTable = '';//只显示某个具体的表
	guideConf.maxDimSize = '5000';//自动生成维显示值时,每个维支持的条数,多于这个的数据会弃用掉
	guideConf.detectLevel = '0';//广义字段自动检测的层数,默认4,0表示让用户在界面上选择
	guideConf.showToolBar = 'yes';//是否显示查询界面上部的工具条
	guideConf.showDataSources = 'yes';//是否显示工具条上的数据源切换
	guideConf.fieldAreaWidth = 350;//树状表字段区域的宽度
	guideConf.emptyReport = "yes";//跳转到分析界面时,是否默认生成明细列表
	guideConf.defaultSaveName = "";//qyx默认的保存名称
	guideConf.filter="default";
	guideConf.autoReloadDimDataOnServer = 'yes';
	guideConf.scanRow=1000;

比如要修改查询页面的默认取数条数:

修改 maxDataSize 就可以:

guideConf.maxDataSize = 1000;

如果想显示 / 隐藏这个取数条数,可以使用:

<script>
	$(document).ready(function(){
		document.title = "<%=GuideMessage.get(request).getMessage("guide.web3")%>";
		//设置取数条数
		$('#maxDataSizeConf').before('<span style="float:right;margin:15px 5px 10px 0;color:#DDD;font-size:14px;">条数据</span>')
			.after('<span style="float:right;margin:15px 0px 10px 5px;color:#DDD;font-size:14px;">查询前</span>')
			.val(guideConf.maxDataSize).css('display','block').css('margin','14px 2px 10px').css('width','60px').css('color','#333').css('text-align','center')
		.keyup(function(){
			try {
				guideConf.maxDataSize = parseInt($(this).val());
			} catch(e) {
				$(this).val("0");
				guideConf.maxDataSize = 0;
			}
			if (guideConf.maxDataSize<0) {
				$(this).val("0");
				guideConf.maxDataSize = 0;
			}
			submitQuery();
			setTimeout(function(){$('#maxDataSizeConf').focus();},200);
		});
		
		//$(‘#maxDataSizeConf’).css(‘display’,‘none’);	//隐藏掉界面设置取数条数
		
	});
<scrpt>

如果想自定义查询按钮,增加如下代码调用 queryApi.submitQuery() 方法:

<input type="button" onclick="queryApi.submitQuery()" style="color:#fe4164;vertical-align:20px;text-decoration: none;" value="自定义查询按钮"/>