基于springboot+mybatis,数据库mysql
模板样例:
效果样例:
1.pom文件
cn.afterturn
easypoi-base
4.1.0
org.jfree
jcommon
1.0.24
org.jfree
jfreechart
1.5.0
org.springframework.boot
spring-boot-starter-freemarker
2.JfreeUtil 文件(用于生成图表,更多图表样式等前往官网查找)
package com.bonc.huanghai.manage.util;
import cn.afterturn.easypoi.entity.ImageEntity;
import lombok.extern.slf4j.Slf4j;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.StandardChartTheme;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.DatasetRenderingOrder;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.chart.renderer.category.StackedBarRenderer;
import org.jfree.chart.renderer.category.StandardBarPainter;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DefaultPieDataset;
import org.springframework.util.Assert;
import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
/**
* @ClassName:JfreeUtil
* @author:莫须有
* @Description: 生成word文档中的图表(JPG)
* @create:2020/2/13 19:22
* @Version1.0
*/
@Slf4j
public class JfreeUtil {
/**
* 临时文件存储,也可以设置读取配置文件
*/
private static String tempImgPath = "/data/app/server/weixin/tempJfree.jpeg";
/**
* 横纵轴等字体
*/
private static Font font = new Font("宋体", Font.PLAIN, 12);
/**
* 颜色循环列表
*/
private static final Paint[] COLORS = { new Color(65, 105, 255),new Color(0, 191, 255),
new Color(30, 144, 223),new Color(30, 70, 255),
new Color(0, 0, 200), new Color(0, 0, 150)
};
/**
* 将图片转化为字节数组
* @return 字节数组
*/
private static byte[] imgToByte(){
File file = new File(tempImgPath);
byte[] buffer = null;
FileInputStream fis = null;
ByteArrayOutputStream bos = null;
try {
fis = new FileInputStream(file);
bos = new ByteArrayOutputStream(1000);
byte[] b = new byte[1000];
int n;
while ((n = fis.read(b)) != -1) {
bos.write(b, 0, n);
}
buffer = bos.toByteArray();
} catch (IOException e) {
log.error(e.getMessage());
}finally {
try {
if(fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bos!= null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//删除临时文件
file.delete();
}
return buffer;
}
/**
* 生成主题样式,及避免乱码
* @return standardChartTheme
*/
private static StandardChartTheme getChartTheme() {
// 处理中文乱码
// 创建主题样式
StandardChartTheme standardChartTheme = new StandardChartTheme("CN");
//设置标题字体
standardChartTheme.setExtraLargeFont(new Font("宋体", Font.BOLD, 15));
//设置图例的字体
standardChartTheme.setRegularFont(new Font("宋体", Font.PLAIN, 12));
//设置轴向的字体
standardChartTheme.setLargeFont(new Font("宋体", Font.PLAIN, 12));
return standardChartTheme;
}
/**
* 生成饼图
* @param title 标题
* @param data 数据集 {String,Number}
* @param width 图片宽度
* @param height 图片高度
* @return 图像对象
*/
public static ImageEntity pieChart(String title, List
2.DownReportUtil 文件(下载生成的文件)
package com.bonc.huanghai.manage.util;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.activation.MimetypesFileTypeMap;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
/**
* @author:莫须有
* @Description: 下载文件工具类
* @create:2020/3/9 9:35
* @Version1.0
*/
public class DownReportUtil {
/**
* 下载文件,可以下载后删除
* @param pathname
* @param fileName
* @throws IOException
*/
public static void download(String pathname, String fileName) throws IOException {
// 读取filename
File file = new File(pathname);
BufferedInputStream bis = null;
OutputStream outputStream = null;
if (file.isFile() && file.exists()) {
try {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletResponse response = requestAttributes.getResponse();
// 设置信息给客户端不解析
String type = new MimetypesFileTypeMap().getContentType(fileName);
// 设置contenttype,即告诉客户端所发送的数据属于什么类型
response.setHeader("Content-type", type);
// 设置编码
String encoded = URLEncoder.encode(fileName, "utf-8");
// 设置扩展头,当Content-Type 的类型为要下载的类型时 , 这个信息头会告诉浏览器这个文件的名字和类型。
response.setHeader("Content-Disposition", "attachment;filename=" + encoded);
// 发送给客户端的数据
outputStream = response.getOutputStream();
byte[] buff = new byte[1024];
bis = new BufferedInputStream(new FileInputStream(file));
//office打开异常
/* int i = bis.read(buff);
while (i != -1) {
outputStream.write(buff, 0, buff.length);
outputStream.flush();
i = bis.read(buff);
}*/
//office打开正常,但是QQ浏览器下载会异常
int len = 0;
while ((len = bis.read(buff)) >= 0) {
outputStream.write(buff, 0, len);
outputStream.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭流
try {
if (bis != null) {
bis.close();
}
if (outputStream != null) {
outputStream.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
// 文件删除
file.delete();
}
}
}
}
2.WordUtil(用于生成最终word)
package com.bonc.huanghai.manage.util;
import cn.afterturn.easypoi.word.WordExportUtil;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.util.Assert;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Map;
/**
* @ClassName:WordUtil
* @author:莫须有
* @Description: 导出word 只支持docx
* @create:2020/2/13 19:23
* @Version1.0
*/
public class WordUtil {
/**
* 导出word
* 第一步生成替换后的word文件,只支持docx
* 第二步下载生成的文件
* 第三步删除生成的临时文件
* 模版变量中变量格式:{{foo}}
*
* @param templatePath word模板地址
* @param temDir 生成临时文件存放地址
* @param fileName 文件名
* @param params 替换的参数
*/
public static void exportWord(String templatePath, String temDir, String fileName, Map params) {
Assert.notNull(templatePath, "模板路径不能为空");
Assert.notNull(temDir, "临时文件路径不能为空");
Assert.notNull(fileName, "导出文件名不能为空");
Assert.isTrue(fileName.endsWith(".docx"), "word导出请使用docx格式");
if (!temDir.endsWith("/")) {
temDir = temDir + File.separator;
}
File dir = new File(temDir);
if (!dir.exists()) {
dir.mkdirs();
}
try {
XWPFDocument doc = WordExportUtil.exportWord07(templatePath, params);
String tmpPath = temDir + fileName;
FileOutputStream fos = new FileOutputStream(tmpPath);
doc.write(fos);
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.bonc.huanghai.manage.service.report.impl;
import cn.afterturn.easypoi.entity.ImageEntity;
import com.bonc.huanghai.manage.model.count.Count;
import com.bonc.huanghai.manage.model.ticket.TicketInfo;
import com.bonc.huanghai.manage.model.tourist.TouristOrigin;
import com.bonc.huanghai.manage.util.DownReportUtil;
import com.bonc.huanghai.manage.util.JfreeUtil;
import com.bonc.huanghai.manage.util.WordUtil;
import com.bonc.huanghai.manage.mapper.count.ITouristPortraitMapper;
import com.bonc.huanghai.manage.mapper.count.LyricalCountMapper;
import com.bonc.huanghai.manage.mapper.oa.TicketMapper;
import com.bonc.huanghai.manage.service.report.ReportService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import java.io.File;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @ClassName:ReportServiceImpl
* @author:莫须有
* @Description: 导出报表
* @create:2020/3/2 15:47
* @Version1.0
*/
@Service
@Primary
public class ReportServiceImpl implements ReportService {
private static final String PERCENT_SIGN = "%";
private static final String TEM_DIR = "tjs/tjs-manage/src/main/resources/templates/download/";
private static final String TEMPLATE_PATH = "templates/数据报告模板.docx";
private static final String DOCX = ".docx";
private static final String DATA_ERROR = "(暂无数据)";
private static final String DATA_PICTURE_ERROR = "(图片中数据暂无)";
private static final String DATA_FIGURE_ERROR = "0";
private static final Integer WIDTH = 550;
private static final Integer HEIGHT = 400;
@Autowired
TicketMapper ticketMapper;
@Autowired
ITouristPortraitMapper iTouristPortraitMapper;
@Autowired
LyricalCountMapper lyricalCountMapper;
/**
* 生成百分比
* @param proportionDouble
* @return
*/
private String proportionInit(double proportionDouble) {
String format = new DecimalFormat("0").format(BigDecimal.valueOf(proportionDouble).multiply(new BigDecimal(100)));
String str = format + PERCENT_SIGN;
return str;
}
/**
* 导出景区数据分析报告
* @param title
* @param beginDate
* @param endDate
* @return
*/
@Override
public String downloadReport(String title, String beginDate, String endDate) {
HashMap map = new HashMap<>(40);
if (title == null || title.length() == 0){
title = "景区_"+beginDate+"-"+endDate+"_数据分析报告";
}
//标题,起止时间
map.put("title", title);
map.put("beginDate", beginDate);
map.put("endDate", endDate);
// 获取当前时间
SimpleDateFormat sdf = new SimpleDateFormat();
sdf.applyPattern("yyyy年MM月dd日");
Date date = new Date();
map.put("date", sdf.format(date));
//游客画像分析
//省内外游客客流量
map.putAll(provincialSourceToReporttPic(beginDate, endDate));
//国内游客画像,省外游客
map.putAll(outofprovinceSourceRankingPic(beginDate, endDate));
//国内游客画像,省内游客
map.putAll(provincialSourceRankingPic(beginDate, endDate));
//游客年龄段分布
map.putAll(ageProportionPic(beginDate, endDate));
//游客男女性别分布
map.putAll(sexProportionPic(beginDate, endDate));
//游客交通方式分析
map.putAll(transportationProportionPic(beginDate, endDate));
//游客消费潜力分析
map.putAll(consumptionLevelPic(beginDate, endDate));
//车辆来源分析
map.putAll(carOriginPic(beginDate, endDate));
//门票汇总分析
map.putAll(ticketPic(beginDate, endDate));
//舆情分析
map.putAll(lyricalPic(beginDate, endDate));
//导出word
String fileName = title + DOCX;
WordUtil.exportWord(TEMPLATE_PATH, TEM_DIR, fileName, map);
//下载文件
try {
DownReportUtil.download(TEM_DIR + fileName, fileName);
} catch (Exception e) {
e.printStackTrace();
}
File file = new File(TEM_DIR + fileName);
// 文件删除
if (file.isFile() && file.exists()) {
file.delete();
}
return "OK";
}
/**
* 游客画像分析
* 省内外游客客流量
*
* @param beginDate
* @param endDate
* @return map (2)
*/
private HashMap provincialSourceToReporttPic(String beginDate, String endDate) {
HashMap map = new HashMap<>(2);
//省内外+未知等的总游客数量
int sumOfAllTourist = 0;
try {
/*
存放进柱状图的数据集
*/
List provincialSourceToReportPicData = new ArrayList();
/*
存放数据库取出的数据
*/
List provincialSourceToReport = iTouristPortraitMapper.provincialSourceToReport(beginDate, endDate);
/*
将数据库取出的数据放入柱状图的数据集
*/
for (TouristOrigin data : provincialSourceToReport) {
if (data.getInProvince() == 0) {
provincialSourceToReportPicData.add(new Object[]{data.getCount(), "省外", data.getDate()});
}
if (data.getInProvince() == 1) {
provincialSourceToReportPicData.add(new Object[]{data.getCount(), "省内", data.getDate()});
}
sumOfAllTourist = sumOfAllTourist + data.getCount();
}
/*
判断数据是否为空或0,若为空或零则直接当做异常处理
*/
if (provincialSourceToReport == null || provincialSourceToReport.size()==0 || sumOfAllTourist ==0) {
throw new Exception();
}
/*
生成柱状图的图片
*/
ImageEntity provincialSourceToReporttPic = JfreeUtil.stackedBarChart("省内外游客流量", "", "游客数量", provincialSourceToReportPicData, WIDTH, HEIGHT);
/*
放入文档对应的map集合中
*/
map.put("provincialSourceToReporttPic", provincialSourceToReporttPic);
map.put("游客总计", sumOfAllTourist);
}catch (Exception e){
e.printStackTrace();
map.put("provincialSourceToReporttPic", DATA_PICTURE_ERROR);
map.put("游客总计", DATA_FIGURE_ERROR);
}
return map;
}