当前位置: 首页 > news >正文

苍穹外卖 —— 数据统计和使用Apache_POI库导出Excel报表

一、前言

这是苍穹外卖后端部分最后的一部分内容了,数据统计的难点在于持久层,对于图表的接口依旧看文档写即可,这部分主要还是前端的工作,而报表导出会采用到一个新的库:Apache_POI。

二、数据统计

数据统计我们分为四个表来统计,前面三个表都是以日期作为横坐标,具体值作为纵坐标,所以具体步骤非常相似,我们这里选择订单统计来作为示例详细讲解,因为他最复杂。

1.文档

可以看到请求参数是开始和结束的日期,用的Query参数,要求我们返回一个OrderReportVO即可,所以我们的主要任务还是在持久层查询响应的属性,值得注意的是这几个属性都是String类型的,需要我们用逗号分隔拼接成一个长字符串。

2.Controller

这里我们需要返回一个OrderReportVO,然后接收两个日期参数,所以我们这里使用 @DateTimeFormat(pattern = "yyyy-MM-dd")注解来规定参数格式,便于后续字符串拼接。

/*** 订单统计** @param begin* @param end* @return*/@GetMapping("/ordersStatistics")@ApiOperation("订单统计")public Result<OrderReportVO> ordersStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {log.info("订单统计:{},{}", begin, end);OrderReportVO orderReportVO = reportService.getOrderStatistics(begin, end);return Result.success(orderReportVO);}

3.Service层

接口:

/*** 订单统计* @param begin* @param end* @return*/OrderReportVO getOrderStatistics(LocalDate begin, LocalDate end);

实现类:这个就比较复杂了,我们慢慢来解析。

 /*** 订单统计** @param begin* @param end* @return*/@Overridepublic OrderReportVO getOrderStatistics(LocalDate begin, LocalDate end) {//当前集合用于存放从begin到end范围内地每天的日期List<LocalDate> dateList = new ArrayList<>();dateList.add(begin);while (!begin.equals(end)) {//日期计算,计算指定日期的后一天对应的日期begin = begin.plusDays(1);dateList.add(begin);}//存放每天的订单总数List<Integer> orderCountList = new ArrayList<>();//存放每天的有效订单数List<Integer> validOrderCountList = new ArrayList<>();//遍历dateList集合,查询每天的有效订单数和订单总数for (LocalDate date : dateList) {//查询每天的订单总数 select count(id) from orders where order_time and order_time < ?LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);Integer orderCount = getOrderCount(beginTime, endTime, null);//查询每天的有效订单数 select count(id) from orders where order_time and order_time < ? and status = 5Integer validOrderCount = getOrderCount(beginTime, endTime, Orders.COMPLETED);orderCountList.add(orderCount);validOrderCountList.add(validOrderCount);}//计算时间区间内的订单总数量Integer totalOrderCount = orderCountList.stream().reduce(Integer::sum).get();//计算时间区间内的有效订单数量Integer validOrderCount = validOrderCountList.stream().reduce(Integer::sum).get();//计算订单完成率Double orderCompletionRate = 0.0;if (totalOrderCount != 0) {orderCompletionRate = validOrderCount.doubleValue() / totalOrderCount;}return OrderReportVO.builder().dateList(StringUtils.join(dateList, ",")).orderCountList(StringUtils.join(orderCountList, ",")).validOrderCountList(StringUtils.join(validOrderCountList, ",")).totalOrderCount(totalOrderCount).validOrderCount(validOrderCount).orderCompletionRate(orderCompletionRate).build();}/*** 根据条件统计订单数量** @param begin* @param end* @param status* @return*/private Integer getOrderCount(LocalDateTime begin, LocalDateTime end, Integer status) {Map map = new HashMap();map.put("begin", begin);map.put("end", end);map.put("status", status);return orderMapper.countByMap(map);}

首先看第一个属性dateList,我们通过日期的加减,可以获得每天的日期,尽管最后是需要传回一个字符串,但是我们这里还是选择先存到一个日期列表中去(后面可以用工具类一次性转化为长字符串)。

第二个属性orderCountList(每天的订单数)我们将通过从持久层查询得到,我们这里是通过一个Map来向持久层查找数据的,相当于查询的参数是:1.开始日期 2.结束日期 3.订单状态。

传入的是开始日期的刚开始的时间,比如我想传入11月16日,那么我在这里就会传入11月16日0时0分000000001秒    ,儿结束日期就是最后快结束的时间,比如11月16日23时59分59.9999999秒。我们将这个步骤单独封装成了一个private方法便于后续复用。

而对于第三个属性validOrderCountList(每天有效的订单数) ,我们当然只统计完成了的订单,派送中的未接单的我们不会统计,所以要求订单的状态是Orders.COMPLETED。

第四个属性orderCompletionRate 就很简单了,订单完成率 = 有效订单数 / 总订单数

至于总订单数总有效订单数,我们就用处理。

  • orderCountList:存储了每天订单数量的列表(如每天的订单数分别为 10、20、30 等)。
  • stream():将列表转换为流,以便进行函数式操作。
  • reduce(Integer::sum)
    • reduce 是流的归约操作,用于将流中的元素合并为一个结果。
    • Integer::sum 是方法引用,等价于 (a, b) -> a + b表示对两个整数进行求和。
    • 该操作会依次将流中的元素累加,最终得到所有天的订单数量总和。
  • get()因为 reduce 返回的是 Optional<Integer>(防止流为空时出现异常),这里通过 get() 获取最终的整数值。

最后我们创建一个OrderReportVO,用工具类将集合转化为长字符串

4.持久层

Mapper:

/*** 根据动态条件统计订单数量** @param map* @return*/Integer countByMap(Map map);

映射文件:

<select id="countByMap" resultType="java.lang.Integer">select count(id) from orders<where><if test="begin != null">and order_time &gt; #{begin}</if><if test="end != null">and order_time &lt; #{end}</if><if test="status != null">and status = #{status}</if></where></select>

三、Apache_POI快速入门

POI总的来说就是获取一个Excel对象,然后获取每一行的对象,最后获取每个单元个格的对象,我们在单元格中写入想写的数据即可。

public class POITest {/*** 通过POI创建Excel文件并且写入文件内容*/public static void write() throws Exception {//在内存中创建一个Excel文件XSSFWorkbook excel = new XSSFWorkbook();//在Excel文件中创建一个Sheet页XSSFSheet sheet = excel.createSheet("info");//在sheet页中创建行对象,rownumber是从0开始XSSFRow row = sheet.createRow(1);//创建单元格并写入内容row.createCell(1).setCellValue("姓名");row.createCell(2).setCellValue("城市");//创建一个新行row = sheet.createRow(2);//创建单元格并写入内容row.createCell(1).setCellValue("印东升");row.createCell(2).setCellValue("成都");//创建一个新行row = sheet.createRow(3);//创建单元格并写入内容row.createCell(1).setCellValue("张宇");row.createCell(2).setCellValue("万州");//通过输出流将内存中的Excel文件写入到磁盘FileOutputStream out = new FileOutputStream(new File("D:\\info.xlsx"));excel.write(out);excel.close();out.close();}public static void main(String[] args) throws Exception {//write();read();}/*** 通过POI读取Excel文件** @throws Exception*/public static void read() throws Exception {FileInputStream in = new FileInputStream(new File("D:\\info.xlsx"));//读取磁盘中已经存在的Excel文件XSSFWorkbook excel = new XSSFWorkbook(in);//读取Excel文件中的第一个Sheet页XSSFSheet sheet = excel.getSheet("info");//获取sheet页中最后一行的行号int lastRowNum = sheet.getLastRowNum();for (int i = 1; i <= lastRowNum; i++) {//获得某一行XSSFRow row = sheet.getRow(i);//获得单元格对象String cellValue1 = row.getCell(1).getStringCellValue();String cellValue2 = row.getCell(2).getStringCellValue();System.out.println(cellValue1 + " " + cellValue2);}//关闭资源excel.close();in.close();}
}

四、导出Excel报表

这里一个用于将数据导出为Excel的库。我们这里就不写快速入门了,直接边写功能边讲。

1.文档

这里是不需要参数的,也不需要返回响应。

2.Controller

由于参数和响应都没有,所以Controller异常简单。

/*** 导出Excel报表** @param response*/@GetMapping("/export")@ApiOperation("导出Excel报表")public void export(HttpServletResponse response) {log.info("导出Excel报表");reportService.exportBusinessData(response);}

2.Service层

接口:

 /*** 导出Excel报表* @param response*/void exportBusinessData(HttpServletResponse response);

实现类:

@Overridepublic void exportBusinessData(HttpServletResponse response) {//1.查询数据库,获取营业数据---30天LocalDate dataBegin = LocalDate.now().minusDays(30);LocalDate dataEnd = LocalDate.now().minusDays(1);LocalDateTime begin = LocalDateTime.of(dataBegin, LocalTime.MIN);LocalDateTime end = LocalDateTime.of(dataEnd, LocalTime.MIN);BusinessDataVO businessDataVO = workspaceService.getBusinessData(begin, end);//2.通过POI将数据写入到excel文件中InputStream in = this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx");try {//基于模板文件创建一个新的Excel文件XSSFWorkbook excel = new XSSFWorkbook(in);//获取表格文件Sheet页XSSFSheet sheet = excel.getSheet("Sheet1");//填充数据--时间sheet.getRow(1).getCell(1).setCellValue("时间:" + dataBegin + " 至 " + dataEnd);XSSFRow row = sheet.getRow(3);row.getCell(2).setCellValue(businessDataVO.getTurnover());row.getCell(4).setCellValue(businessDataVO.getOrderCompletionRate());row.getCell(6).setCellValue(businessDataVO.getNewUsers());row = sheet.getRow(4);row.getCell(2).setCellValue(businessDataVO.getValidOrderCount());row.getCell(4).setCellValue(businessDataVO.getUnitPrice());//填充明细数据for (int i = 0; i < 30; i++) {//查询某一天的营业数据LocalDate date = dataBegin.plusDays(i);LocalDateTime beginDate = LocalDateTime.of(date, LocalTime.MIN);LocalDateTime endDate = LocalDateTime.of(date, LocalTime.MAX);BusinessDataVO businessDataVO2 = workspaceService.getBusinessData(beginDate, endDate);row = sheet.getRow(i+7);row.getCell(1).setCellValue(date.toString());row.getCell(2).setCellValue(businessDataVO2.getTurnover());row.getCell(3).setCellValue(businessDataVO2.getValidOrderCount());row.getCell(4).setCellValue(businessDataVO2.getOrderCompletionRate());row.getCell(5).setCellValue(businessDataVO2.getUnitPrice());row.getCell(6).setCellValue(businessDataVO2.getNewUsers());}//3.通过输出流将Excel文件下载到客户端浏览器ServletOutputStream out = response.getOutputStream();excel.write(out);//关闭资源out.close();excel.close();in.close();} catch (IOException e) {throw new RuntimeException(e);}}

这里需要传回一个响应参数(类似Servlet),让浏览器下载报表。

如果想导入一个报表模板就需要InputStream,当然,想下载下来就用一个OutputStream就行了(ServletOutputStream),这里注释写得很详细,就不过多赘述了。

http://www.dtcms.com/a/618324.html

相关文章:

  • 昆明好的网站制作网站价格评估 优帮云
  • 如何查询SQL Server数据库服务器的IP地址
  • 开源:开源协议从入门到落地
  • 网站域名要怎样规划佛山做外贸网站案例
  • 网站建设找导师蓝林月租网站空间
  • 2025 IntelliJ IDEA 2025最新免费安装教程
  • Numpy数值分析库实验
  • 游戏常用运行库丨免费纯净丨多系统支持丨自动推荐安装
  • git-拉取代码报错update ref failed ref ‘ORIG HEAD‘
  • 手机网站模板 html5西安搬家公司电话号码大全
  • 资源优化排名网站哈尔滨企业网站模板建站
  • 基于扩散模型与流模型的跨分辨率流场映射方法研究
  • 【Linux日新月异(十)】CentOS 7 文件系统结构深度解剖:从根到叶的完整指南
  • linux服务-rsync+inotify文件同步-ssh
  • 保障房建设网站首页游戏策划
  • 深度学习杂草分割系统1:数据集说明(含下载链接)
  • 超分辨率重建(Super-Resolution, SR)
  • 高端品牌网站建设注意事项制作ppt的基本做法
  • 2025 年 Redis 面试天花板
  • component-富文本实现(WangEditor)
  • 烟台城乡住房建设厅网站网站alt标签
  • win11上使用Workbench备份mysql数据库
  • B站评论数据采集:基于Requests的智能爬虫实战
  • 信息学与容斥
  • 网易云音乐评论数据采集:基于Requests的智能爬虫实战
  • 网站空间登录网站建设模式有哪些内容
  • VSCode 中快捷键的使用:(大小写转换快捷键、自动补全函数注释快捷键、代码和注释自动缩进快捷键)
  • 使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 25--数据驱动--参数化处理 Excel 文件 2
  • SpringCloud微服务笔记
  • 广告公司网站官网安徽网站建设流程