UReport是一款基于单元格迭代模型的纯Java中式报表引擎。它架构于Spring之上,因此与企业应用具有良好的集成能力。UReport提供了基于Eclipse插件与基于网页的两种报表模版设计方式,采用类Excel报表模版设计风格,简单、易上手,可在不编程的情况下完成绝大多数报表模版的设计工作。
UReport的主要作用体现在以下几个方面:
- 报表设计:UReport支持简单、复杂报表的设计,能够很好地嵌合实际业务需求。无论是通过纯SQL还是配置的方式,都可以实现针对不同人员的报表管理。
- 数据实时处理:UReport能够根据实时数据自动调整参数,以达到最佳效果。这种自动调整参数的能力使得模型能够快速响应并精确预测,同时减少人工参与的次数,提升模型的投入产出比。
- 参数优化:UReport是一种强大的参数优化技术,它能够帮助用户改进模型参数,最大化模型性能,并减少人工参与,提高模型的运行效率。通过UReport的帮助,用户可以更快地实现模型的优化,提升性能,加快产品开发进度,从而节省大量时间和金钱。
首先看一下整体目录:
最终显示结果:
一:需要引入相关依赖pom.xml:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.crwl</groupId>
<artifactId>ureport</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring-cloud.version>2.1.1.RELEASE</spring-cloud.version>
<flowable.version>6.5.0</flowable.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>2.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>1.16.20</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.10</version>
</dependency>
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>activerecord</artifactId>
<version>4.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>com.bstek.ureport</groupId>
<artifactId>ureport2-console</artifactId>
<version>2.2.9</version>
</dependency>
<dependency>
<groupId>com.bstek.ureport</groupId>
<artifactId>ureport2-core</artifactId>
<version>2.2.9</version>
</dependency>
</dependencies>
</project>
二:配置resources:
1. application.yaml
server:
port: 9090
servlet:
context-path: /pro
spring:
http:
encoding:
force: true
enabled: true
charset: UTF-8
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/j2eedb?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowMultiQueries=true&nullNamePatternMatchesAll=true
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
resources:
static-locations: classpath:/,classpath:/static/
2. config目录下config.properties:
#ureport.fileStoreDir=D:/myfile/ureportfiles
ureport.disableFileProvider=true
ureport.fileToDbStoreDir=D:/ureportDbfiles
ureport.disableFileDbProvider=false
ureport.contextPath=/pro
3. config目录下context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<import resource="classpath:ureport-console-context.xml" />
<bean id="propertyConfigurer" parent="ureport.props">
<property name="locations">
<list>
<value>classpath:config/config.properties</value>
</list>
</property>
</bean>
<bean id="ureport.fielToDataBaseProvider" class="com.report.provider.FileToDatabaserProvider">
<property name="fileStoreDir" value="${ureport.fileToDbStoreDir}"></property>
<property name="disabled" value="${ureport.disableFileDbProvider}"></property>
</bean>
</beans>
三:配置数据源DataSourceConfig.java
package com.report.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.report.model._MappingKit;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.CaseInsensitiveContainerFactory;
import com.jfinal.plugin.activerecord.dialect.MysqlDialect;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Primary
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
/**
* 设置数据源代理
*/
@Bean
public TransactionAwareDataSourceProxy transactionAwareDataSourceProxy() {
TransactionAwareDataSourceProxy transactionAwareDataSourceProxy = new TransactionAwareDataSourceProxy();
transactionAwareDataSourceProxy.setTargetDataSource(druidDataSource());
return transactionAwareDataSourceProxy;
}
/**
* 设置ActiveRecord
*/
@Bean
public ActiveRecordPlugin activeRecordPlugin() {
ActiveRecordPlugin arp = new ActiveRecordPlugin(transactionAwareDataSourceProxy());
arp.setDialect(new MysqlDialect());
arp.setContainerFactory(new CaseInsensitiveContainerFactory(true));//忽略大小写
arp.setShowSql(true);
arp.getEngine().setToClassPathSourceFactory();
//arp.addSqlTemplate("sql/all.sql");
_MappingKit.mapping(arp);
arp.start();
System.out.println("调用Jfinal ActiveRecordPlugin 成功");
return arp;
}
}
四:配置返回结果类Result.java:
package com.report.dto;
import com.report.enums.ResultEnum;
import java.io.Serializable;
public class Result implements Serializable {
/**
*
*/
private static final long serialVersionUID = 3337439376898084639L;
/**
* 处理状态
*/
private Integer code;
/**
* 处理信息
*/
private String msg;
private String serverID;
/**
* 返回值
*/
private Object data;
private int total;
private Object rows;
/**
* 成功,传入data(使用最多)
*
* @param data
* @return
*/
public static Result success(Object data) {
return Result.success(data,"请求成功!");
}
/**
* 成功,传入data(使用最多)
* @param msg
* @return
*/
public static Result success(String msg) {
Result result = new Result();
result.setCode(ResultEnum.SUCCESS.getCode());
result.setMsg(msg);
return result;
}
/**
* 成功,传入rows和total
* @param rows
* @param total
* @return
*/
public static Result success(Object rows,int total) {
Result result = new Result();
result.setCode(ResultEnum.SUCCESS.getCode());
result.setMsg("请求成功!");
result.setRows(rows);
result.setTotal(total);
return result;
}
/**
* 成功,传入data 和 msg
* @param data
* @param msg
* @return
*/
public static Result success(Object data, String msg) {
Result result = new Result();
result.setCode(ResultEnum.SUCCESS.getCode());
result.setMsg(msg);
result.setData(data);
return result;
}
/**
* 失败
* @return
*/
public static Result error() {
return Result.error("请求失败!");
}
/**
* 失败 传入 msg
* @param msg
* @return
*/
public static Result error(String msg) {
return Result.error(msg,ResultEnum.FAILURE);
}
public static Result error(String msg ,ResultEnum resultEnum){
Result result = new Result();
result.setCode(resultEnum.getCode());
result.setMsg(msg);
return result;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
if(null != this.data && this.data.getClass().getName().equals("com.crwl.commonserver.dto.CurrUser")){
this.data = null;
}
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getServerID() {
return serverID;
}
public void setServerID(String serverID) {
this.serverID = serverID;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public Object getRows() {
return rows;
}
public void setRows(Object rows) {
this.rows = rows;
}
@Override
public String toString() {
return "Result{" +
"code=" + code +
", msg='" + msg + '\'' +
", serverID='" + serverID + '\'' +
", data=" + data +
", total=" + total +
", rows=" + rows +
'}';
}
}
五:返回枚举类ResultEnum.java:
package com.report.enums;
/**
* 返回状态
*/
public enum ResultEnum {
/**
* 200 OK //客户端请求成功
* 400 Bad Request //客户端请求有语法错误,不能被服务器所理解
* 401 Unauthorized //请求未经授权,这个状态代码必须和 WWW-Authenticate 报头域一起使用
* 403 Forbidden //服务器收到请求,但是拒绝提供服务
* 404 Not Found //请求资源不存在,eg:输入了错误的 URL
* 500 Internal Server Error //服务器发生不可预期的错误
* 503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
*/
SUCCESS(200, "操作成功"),ERROR(500,"操作失败"), FAILURE(404, "请求的网页不存在"),INVALID(503,"服务不可用"),LOGINOVERTIME(1000,"登录超时");
private ResultEnum(Integer code, String data) {
this.code = code;
this.data = data;
}
private Integer code;
private String data;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
六:BaseReport.java
package com.report.model.base;
import com.jfinal.plugin.activerecord.IBean;
import com.jfinal.plugin.activerecord.Model;
import java.util.Date;
@SuppressWarnings({"serial", "unchecked"})
public abstract class BaseReport<M extends BaseReport<M>> extends Model<M> implements IBean {
public M setId(Integer id) {
set("id", id);
return (M)this;
}
public Integer getId() {
return getInt("id");
}
public M setRptCode(String rptCode) {
set("rpt_code", rptCode);
return (M)this;
}
public String getRptCode() {
return getStr("rpt_code");
}
public M setRptName(String rptName) {
set("rpt_name", rptName);
return (M)this;
}
public String getRptName() {
return getStr("rpt_name");
}
public M setRptType(Integer rptType) {
set("rpt_type", rptType);
return (M)this;
}
public Integer getRptType() {
return getInt("rpt_type");
}
public M setUreportName(String ureportName) {
set("ureport_name", ureportName);
return (M)this;
}
public String getUreportName() {
return getStr("ureport_name");
}
public M setRptUrl(String rptUrl) {
set("rpt_url", rptUrl);
return (M)this;
}
public String getRptUrl() {
return getStr("rpt_url");
}
public M setRemark(String remark) {
set("remark", remark);
return (M)this;
}
public String getRemark() {
return getStr("remark");
}
public M setSort(Integer sort) {
set("sort", sort);
return (M)this;
}
public Integer getSort() {
return getInt("sort");
}
public M setStatus(Integer status) {
set("status", status);
return (M)this;
}
public Integer getStatus() {
return getInt("status");
}
public M setCreateUser(String createUser) {
set("create_user", createUser);
return (M)this;
}
public String getCreateUser() {
return getStr("create_user");
}
public M setCreateDate(Date createDate) {
set("create_date", createDate);
return (M)this;
}
public Date getCreateDate() {
return getDate("create_date");
}
public M setUpdateUser(String updateUser) {
set("update_user", updateUser);
return (M)this;
}
public String getUpdateUser() {
return getStr("update_user");
}
public M setUpdateDate(Date updateDate) {
set("update_date", updateDate);
return (M)this;
}
public Date getUpdateDate() {
return getDate("update_date");
}
}
七:_MappingKit.java
package com.report.model;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
public class _MappingKit {
public static void mapping(ActiveRecordPlugin arp) {
arp.addMapping("ur_report", "id", Report.class);
}
}
八:Report.java
package com.report.model;
import com.report.model.base.BaseReport;
/**
* Generated by JFinal.
*/
@SuppressWarnings("serial")
public class Report extends BaseReport<Report> {
public static final Report dao = new Report().dao();
}
九:DsProvider.java
package com.report.provider;
import com.bstek.ureport.definition.datasource.BuildinDatasource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
//提供ureport 内置数据源连接
@Component("dsScoreSys")
public class DsProvider implements BuildinDatasource {
@Autowired
private DataSource dataSource;
@Override
public String name() {
return "内置数据源";
}
@Override
public Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
}
十: FileToDatabaserProvider.java
package com.report.provider;
import com.bstek.ureport.exception.ReportException;
import com.bstek.ureport.provider.report.ReportFile;
import com.bstek.ureport.provider.report.ReportProvider;
import com.report.model.Report;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import java.io.*;
import java.util.*;
public class FileToDatabaserProvider implements ReportProvider {
private String prefix="fileToDb:";
private String fileStoreDir;
private String disabled;
@Override
public InputStream loadReport(String file) {
if(StringUtils.isNotEmpty(file)){
String[] arr = file.split("@");
Report report = null;
if(null != arr && arr.length==2){
report = Report.dao.findById(Integer.parseInt(arr[1]));
file = report.getUreportName();
}else{
report = Report.dao.findFirst(" select * from ur_report t where t.ureport_name=?", file);
}
if(null != report) {
if (file.startsWith(prefix)) {
file = file.substring(prefix.length(), file.length());
}
String fullPath = fileStoreDir + "/" + file;
try {
return new FileInputStream(fullPath);
} catch (FileNotFoundException e) {
throw new ReportException(e);
}
}else{
throw new ReportException("报表文件不存在");
}
}else{
throw new ReportException("报表文件不存在");
}
}
@Override
public void deleteReport(String file) {
Report report = Report.dao.findFirst(" select * from ur_report t where t.ureport_name=?",file);
if(null != report){
if(file.startsWith(prefix)){
file=file.substring(prefix.length(),file.length());
}
String fullPath=fileStoreDir+"/"+file;
File f=new File(fullPath);
if(f.exists()){
f.delete();
}
report.delete();
}
}
@Override
public List<ReportFile> getReportFiles() {
List<Report> reportList = Report.dao.find("select * from ur_report t where t.rpt_type=2 ");
File file=new File(fileStoreDir);
List<ReportFile> list=new ArrayList<>();
// if(reportList != null) {
// for(Report reportStore:reportList) {
// ReportFile reportFile = new ReportFile(reportStore.getRptName(),reportStore.getCreateDate());
// list.add(reportFile);
// }
// }
// return list;
for(File f:file.listFiles()){
Calendar calendar=Calendar.getInstance();
calendar.setTimeInMillis(f.lastModified());
Report report = null;
for(int i=0; i<reportList.size();i++){
Report r = reportList.get(i);
String reportName = r.getUreportName();
if(StringUtils.isNotEmpty(reportName)){
reportName = reportName.substring(prefix.length(), reportName.length());
if(f.getName().equals(reportName)){
report = r;
}
}
}
if(null != report){
list.add(new ReportFile(f.getName(),calendar.getTime()));
// list.add(new ReportFile(report.getId(),f.getName(),calendar.getTime()));
}
}
Collections.sort(list, new Comparator<ReportFile>(){
@Override
public int compare(ReportFile f1, ReportFile f2) {
return f2.getUpdateDate().compareTo(f1.getUpdateDate());
}
});
return list;
}
@Override
public void saveReport(String file, String content) {
try {
if(StringUtils.isNotEmpty(file)) {
String[] arr = file.split("@");
Report report = null;
if(null != arr && arr.length==2){
report = Report.dao.findById(Integer.parseInt(arr[0]));
file = arr[1];
}else{
report = Report.dao.findFirst(" select * from ur_report t where t.ureport_name=?", file);
if (report == null) {
report = new Report();
}
}
report.setUreportName(file);
report.setRptUrl("ureport/preview?_u=" + file);
if (file.startsWith(prefix)) {
file = file.substring(prefix.length(), file.length());
}
String fullPath = fileStoreDir + "/" + file;
FileOutputStream outStream = null;
try {
outStream = new FileOutputStream(new File(fullPath));
IOUtils.write(content, outStream, "utf-8");
} catch (Exception ex) {
throw new ReportException(ex);
} finally {
if (outStream != null) {
try {
outStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
report.setUpdateDate(new Date());
if (null != report.getId()) {
report.update();
} else {
report.setCreateDate(new Date());
report.setRptType(2);
report.save();
}
}else{
throw new ReportException("报表文件不存在");
}
} catch (Exception e) {
throw new ReportException(e);
}
}
@Override
public String getName() {
return "数据库文件系统";
}
@Override
public boolean disabled() {
return false;
}
@Override
public String getPrefix() {
return prefix;
}
public void setFileStoreDir(String fileStoreDir) {
this.fileStoreDir = fileStoreDir;
}
public void setDisabled(String disabled) {
this.disabled = disabled;
}
}
十一:ReportServiceImpl.java
package com.report.service.impl;
import com.report.model.Report;
import com.report.service.ReportService;
import com.jfinal.plugin.activerecord.Page;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
@Service
public class ReportServiceImpl implements ReportService {
private final String table = "ur_report";
@Override
public Page<Report> getPageList(Integer currentPage, Integer pageSize, String rptName, String rptType) {
StringBuilder sql = new StringBuilder();
sql.append(" from "+ table + " t where 1=1 ");
if(StringUtils.isNotEmpty(rptName)){
sql.append(" and instr(t.rpt_name,'"+rptName +"')>0 ");
}
if(StringUtils.isNotEmpty(rptType)){
sql.append(" and t.rpt_type="+rptType);
}
sql.append(" order by t.sort desc ");
Page<Report> pageList = Report.dao.paginate(currentPage,pageSize,"select t.* ",sql.toString());
return pageList;
}
}
十二:ReportService.java
package com.report.service;
import com.report.model.Report;
import com.jfinal.plugin.activerecord.Page;
public interface ReportService {
/***
* 获取表格数据
* @param currentPage
* @param pageSize
* @param rptName
* @param rptType
* @return
*/
Page<Report> getPageList(Integer currentPage, Integer pageSize, String rptName, String rptType);
}
十三:主类UreportApplication.java
package com.report;
import com.bstek.ureport.console.UReportServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* 入口类
*/
@SpringBootApplication()
@EnableTransactionManagement
@ImportResource("classpath:config/context.xml")
@ComponentScan(basePackages = {"com.report.*"})
public class UreportApplication {
public static void main(String[] args) {
SpringApplication.run(UreportApplication.class, args);
}
//ureport报表
@Bean
public ServletRegistrationBean buildUReprtServlet(){
return new ServletRegistrationBean(new UReportServlet(),"/ureport/*");
}
}
最后贴上表设计: