积木报表之集成探索
积木报表,是一款免费的企业级Web报表工具,像搭建积木一样在线设计报表!功能涵盖,数据报表、打印设计、图表报表、大屏设计等!
- Web 版报表设计器,类似于excel操作风格,通过拖拽完成报表设计。
- 秉承“简单、易用、专业”的产品理念,极大的降低报表开发难度、缩短开发周期、节省成本、解决各类报表难题。
- 领先的企业级Web报表软件,采用纯Web在线技术,专注于解决企业报表快速制作难题。
永久免费使用
专注于开源,打造“专业 易用 智能”的报表工具
开源宗旨:`功能永久免费、可以商用、代码不开放(大屏设计暂时不提供离线版本)`
为什么集成
系统原本集成了帆软的报表系统,虽然用起来很好用,但是问题是帆软收费,用户需要单独购买帆软的license。正好新项目需要用户自由进行大屏定义的功能,趁这个机会集成一下新的报表系统。
新的报表系统,第一个要求就是要免费,其次,要支持大屏自定义,最后,最好还是开源的。
选了一下,积木报表还算合适。唯一的问题是,不开源。。。
好在是JEECG团队维护的,最近升级维护也算及时,技术栈也比较新,就先用它吧。

集成步骤
因为其不开源,所以首先尝试JimuReport作为单独的服务独立运行,然后通过接口调用积木报表的功能来实现。
具体的步骤,可以参考官网:
http://report.jeecg.com/2078875
第一步:新建一个SpringBoot项目,引入JimuReport依赖
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-spring-boot-starter</artifactId>
<version>1.5.4</version>
</dependency>
第二步:初始化Sql脚本
jimureport.mysql5.7.create.sql
目前只有mysql版本,其他数据库的话需要自己转换。
第三步:排除权限拦截
官方文档里写了下面的,估计应该是集成到一个服务的时候需要,我们是JimuReport要单独跑一个服务,所以这一步不需要。
但我们需要集成我们自己的用户权限到JimuReport。具体见第五步
JimuReport自带权限控制,所以需要放开自己框架对JimuReport请求的权限拦截 ; JeecgBoot修改
org.jeecg.config.shiro.ShiroConfig加入以下代码,其他项目参考修改 ;
//积木报表排除
filterChainDefinitionMap.put("/jmreport/**", "anon");
第四步:访问积木报表
访问地址:{项目前缀}/jmreport/list
运行服务,就可以启动了
最终的Pom文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/>
</parent>
<name>jimureport-wdc</name>
<groupId>com.wdc.jmreport</groupId>
<artifactId>jimureport-wdc</artifactId>
<version>1.5</version>
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun Repository</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>jeecg</id>
<name>jeecg Repository</name>
<url>http://maven.jeecg.org/nexus/content/repositories/jeecg</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<properties>
<jimureport.version>1.5.4</jimureport.version>
<java.version>1.8</java.version>
<minio.version>8.0.3</minio.version>
<!-- DB驱动 -->
<mysql-connector-java.version>8.0.27</mysql-connector-java.version>
</properties>
<dependencies>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- JimuReport -->
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-spring-boot-starter</artifactId>
<version>${jimureport.version}</version>
</dependency>
<!-- SpringBoot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- minio oss-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
<optional>true</optional>
</dependency>
<dependency><!--boot整合mybatis所需要的包-->
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- ============================数据库驱动========================== -->
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
<scope>runtime</scope>
</dependency>
<!-- oracle驱动-->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
<scope>runtime</scope>
</dependency>
<!-- ===需要什么数据库,手工打开注释=== -->
<!-- sqlserver
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc4</artifactId>
<version>4.0</version>
<scope>runtime</scope>
</dependency>-->
<!-- postgresql驱动
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.6</version>
<scope>runtime</scope>
</dependency>-->
<!-- 达梦驱动
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
<version>1.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmDialectForHibernate</artifactId>
<version>5.3</version>
<scope>runtime</scope>
</dependency>-->
<!-- sqlite
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.34.0</version>
<scope>runtime</scope>
</dependency>-->
<!--hsqldb
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.2.8</version>
<scope>runtime</scope>
</dependency>-->
<!--h2
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
<scope>runtime</scope>
</dependency>-->
<!--derby
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbyclient</artifactId>
<version>10.11.1.1</version>
<scope>runtime</scope>
</dependency>-->
<!--db2
<dependency>
<groupId>com.ibm.db2</groupId>
<artifactId>jcc</artifactId>
<version>11.5.0.0</version>
<scope>runtime</scope>
</dependency>-->
<!--神通
<dependency>
<groupId>com.csicit.thirdparty</groupId>
<artifactId>oscar</artifactId>
<version>1.0.1</version>
<scope>runtime</scope>
</dependency>-->
<!--人大金仓
<dependency>
<groupId>kingbase</groupId>
<artifactId>kingbase8</artifactId>
<version>8</version>
<scope>runtime</scope>
</dependency>-->
<!-- ============================数据库驱动========================== -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
第五步:集成用户token权限访问积木报表
作完以上四步,就可以在我们自己的系统里通过url访问访问积木报表了,类似这样:
http://localhost:8085/jmreport/view/740470888715415552
但这样的访问有个问题,就是没有权限控制。集成权限控制需要用我们自己的系统生成token,在调用积木报表时传入token,然后实现积木报表系统接口解析token。具体我们的做法,是用我们系统的活跃sessionID作为token传入:
1.直接创建 JimuReportTokenService 实现 JmReportTokenServiceI接口就行了
package com.wdc.jmreport.service;
import com.alibaba.druid.util.StringUtils;
import com.wdc.jmreport.mapper.SysActiveUserVMapper;
import com.wdc.jmreport.util.TokenUtils;
import org.jeecg.modules.jmreport.api.JmReportTokenServiceI;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.Map;
/**
* 自定义积木报表鉴权(如果不进行自定义,则所有请求不做权限控制)
* 1.自定义获取登录token
* 2.自定义获取登录用户
*/
@Component
public class JimuReportTokenService implements JmReportTokenServiceI {
@Resource
SysActiveUserVMapper sysActiveUserVMapper;
/**
* 通过Token获取登录人用户名
* @param token
* @return
*/
@Override
public String getUsername(String token) {
// 根据token查询oracle对应的用户名
String userName = sysActiveUserVMapper.getUserName(token);
if(StringUtils.isEmpty(userName)){
return null;
}
System.out.println("getUsername============== \t" + userName);
return userName;
}
/**
* Token校验
* @param token
* @return
*/
@Override
public Boolean verifyToken(String token) {
System.out.println("---------verify-----Token---------------\t" + token);
// 缺少token无法访问积木报表系统
if(StringUtils.isEmpty(token)){
return false;
}
String userName = sysActiveUserVMapper.getUserName(token);
// 无法获取到token对应的用户,则无法访问积木报表系统
if(StringUtils.isEmpty(userName)){
return false;
}
return true;
}
/**
* 通过请求获取Token
* @param request
* @return
*/
@Override
public String getToken(HttpServletRequest request) {
String token = TokenUtils.getTokenByRequest(request);
System.out.println("===========getToken==============" + token);
if(null == token){
return null;
}
// 根据token查询oracle对应的用户名
String userName = sysActiveUserVMapper.getUserName(token);
if(StringUtils.isEmpty(userName)){
return null;
}
// 打印请求参数
Map<String, String[]> parameterMap = request.getParameterMap();
parameterMap.keySet().forEach(key->{
// System.out.println(key + "=" + Arrays.toString(parameterMap.get(key)));
});
// 答应请求头参数
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()){
String s = headerNames.nextElement();
// System.out.println(s + "===" +request.getHeader(s));
}
return token;
}
/**
* 自定义请求头
* @return
*/
// @Override
// public HttpHeaders customApiHeader() {
// System.out.println("===========customApiHeader==============");
// HttpHeaders header = new HttpHeaders();
// header.add("custom-header1", "Please set a custom value 1");
// header.add("token", "11111111111");
// return header;
// }
}
具体实现参考:
https://blog.csdn.net/dzh4039443/article/details/126019773
第六步:修改我们自己的系统带上token权限访问积木报表
/**
* 增加一个主窗口
*/
function addMainTab(text, url) {
if(url.indexOf("jmreport/list") != -1){
let sessionId = getCookie("JSESSIONID");
if(!sessionId || !sessionId.JSESSIONID){
showMsg("获取当前用户session失败!");
return;
}
let token = sessionId.JSESSIONID;
url += "&token="+token;
}
if(!mainTabs.tabs("exists", text)) {
mainTabs.tabs('add',{
title: text,
content:'<iframe src="' + url + '" class="easyui-panel" data-options="fit:true,border:false" frameborder="0"></iframe>',
closable:true,
iconCls:'fa fa-check'
});
}else {
mainTabs.tabs("select", text);
//刷新
mainTabs.tabs('getSelected').panel('panel').find('iframe').attr("src", url)
}
}
jimu.html
<#include "/common/head.html"/>
<script type="text/javascript">
$(function() {
let jimuUrl = "${jimuUrl}";
let sessionId = getCookie("JSESSIONID");
if(!sessionId || !sessionId.JSESSIONID){
showMsg("获取当前用户session失败!");
return;
}
let token = sessionId.JSESSIONID;
if(jimuUrl.indexOf("&") != -1 || jimuUrl.indexOf("?") != -1){
$("#jimuModelId").attr("src",jimuUrl+"&token="+token);
}else{
$("#jimuModelId").attr("src",jimuUrl+"?token="+token);
}
});
</script>
<div class="easyui-layout" data-options="fit:true">
<#if jimuUrl?? && jimuUrl != ''>
<iframe src="" id="jimuModelId" class="easyui-panel" data-options="fit:true,border:false" frameborder="0"></iframe>
<#else>
jimuUrl不存在
</#if>
</div>
<script type="text/javascript">
$(function() {
handleAuthDataRule();
});
//扩展js
</script>
<#include "/common/dialogWindow.html"/>
<#include "/common/foot.html"/>
最终效果:


总结
- 我们自己的系统和报表系统是两个
单独分开的服务 - 两个服务通过token传递用户信息
- 集成总体还算是方便、灵活,效果也不错
- 只是免费、非开源系统,将来的维护和稳定性会否是问题,还不好说