积木报表之集成探索

JimuReport,免费不一定便宜

Posted by 就是我啦 on November 2, 2022

积木报表之集成探索

积木报表,是一款免费的企业级Web报表工具,像搭建积木一样在线设计报表!功能涵盖,数据报表、打印设计、图表报表、大屏设计等!

  • Web 版报表设计器,类似于excel操作风格,通过拖拽完成报表设计。
  • 秉承“简单、易用、专业”的产品理念,极大的降低报表开发难度、缩短开发周期、节省成本、解决各类报表难题。
  • 领先的企业级Web报表软件,采用纯Web在线技术,专注于解决企业报表快速制作难题。
永久免费使用
专注于开源,打造“专业 易用 智能”的报表工具
开源宗旨:`功能永久免费、可以商用、代码不开放(大屏设计暂时不提供离线版本)`

为什么集成

系统原本集成了帆软的报表系统,虽然用起来很好用,但是问题是帆软收费,用户需要单独购买帆软的license。正好新项目需要用户自由进行大屏定义的功能,趁这个机会集成一下新的报表系统。

新的报表系统,第一个要求就是要免费,其次,要支持大屏自定义,最后,最好还是开源的。

选了一下,积木报表还算合适。唯一的问题是,不开源。。。

好在是JEECG团队维护的,最近升级维护也算及时,技术栈也比较新,就先用它吧。

image-20221102112256288

集成步骤

因为其不开源,所以首先尝试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"/>

最终效果:

image-20221102135045901

image-20221102135253673

总结

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