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

苍穹外卖Day12 | Apache POI、导出Excel报表、HttpServletResponse、工作台

目录

工作台

1. 需求分析和设计

2. 代码导入

3. 功能测试

Apache POI

1. 介绍

2. 入门案例

导出运营数据Excel报表

1. 需求分析和设计

​编辑

​编辑

2. 代码开发

一、参数传递链路:从 Controller 到 Service

二、HttpServletResponse 的核心作用(为何要传递它?)

1. 提供 “输出流”:将 Excel 数据写入响应

3. 功能测试


先开前端nginx,再开redis,cpolar内网穿透(用于语音播报),最后springboot

工作台

1. 需求分析和设计

2. 代码导入

admin/WorkSpaceController

package com.sky.controller.admin;import com.sky.result.Result;
import com.sky.service.WorkspaceService;
import com.sky.vo.BusinessDataVO;
import com.sky.vo.DishOverViewVO;
import com.sky.vo.OrderOverViewVO;
import com.sky.vo.SetmealOverViewVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.time.LocalTime;/*** 工作台*/
@RestController
@RequestMapping("/admin/workspace")
@Slf4j
@Api(tags = "工作台相关接口")
public class WorkSpaceController {@Autowiredprivate WorkspaceService workspaceService;/*** 工作台今日数据查询* @return*/@GetMapping("/businessData")@ApiOperation("工作台今日数据查询")public Result<BusinessDataVO> businessData(){//获得当天的开始时间LocalDateTime begin = LocalDateTime.now().with(LocalTime.MIN);//获得当天的结束时间LocalDateTime end = LocalDateTime.now().with(LocalTime.MAX);BusinessDataVO businessDataVO = workspaceService.getBusinessData(begin, end);return Result.success(businessDataVO);}/*** 查询订单管理数据* @return*/@GetMapping("/overviewOrders")@ApiOperation("查询订单管理数据")public Result<OrderOverViewVO> orderOverView(){return Result.success(workspaceService.getOrderOverView());}/*** 查询菜品总览* @return*/@GetMapping("/overviewDishes")@ApiOperation("查询菜品总览")public Result<DishOverViewVO> dishOverView(){return Result.success(workspaceService.getDishOverView());}/*** 查询套餐总览* @return*/@GetMapping("/overviewSetmeals")@ApiOperation("查询套餐总览")public Result<SetmealOverViewVO> setmealOverView(){return Result.success(workspaceService.getSetmealOverView());}
}

WorkSpaceService

package com.sky.service;import com.sky.vo.BusinessDataVO;
import com.sky.vo.DishOverViewVO;
import com.sky.vo.OrderOverViewVO;
import com.sky.vo.SetmealOverViewVO;
import java.time.LocalDateTime;public interface WorkspaceService {/*** 根据时间段统计营业数据* @param begin* @param end* @return*/BusinessDataVO getBusinessData(LocalDateTime begin, LocalDateTime end);/*** 查询订单管理数据* @return*/OrderOverViewVO getOrderOverView();/*** 查询菜品总览* @return*/DishOverViewVO getDishOverView();/*** 查询套餐总览* @return*/SetmealOverViewVO getSetmealOverView();}

WorkSpaceServiceImpl

package com.sky.service.impl;import com.sky.constant.StatusConstant;
import com.sky.entity.Orders;
import com.sky.mapper.DishMapper;
import com.sky.mapper.OrderMapper;
import com.sky.mapper.SetmealMapper;
import com.sky.mapper.UserMapper;
import com.sky.service.WorkspaceService;
import com.sky.vo.BusinessDataVO;
import com.sky.vo.DishOverViewVO;
import com.sky.vo.OrderOverViewVO;
import com.sky.vo.SetmealOverViewVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.Map;@Service
@Slf4j
public class WorkspaceServiceImpl implements WorkspaceService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate UserMapper userMapper;@Autowiredprivate DishMapper dishMapper;@Autowiredprivate SetmealMapper setmealMapper;/*** 根据时间段统计营业数据* @param begin* @param end* @return*/public BusinessDataVO getBusinessData(LocalDateTime begin, LocalDateTime end) {/*** 营业额:当日已完成订单的总金额* 有效订单:当日已完成订单的数量* 订单完成率:有效订单数 / 总订单数* 平均客单价:营业额 / 有效订单数* 新增用户:当日新增用户的数量*/Map map = new HashMap();map.put("begin",begin);map.put("end",end);//查询总订单数Integer totalOrderCount = orderMapper.countByMap(map);map.put("status", Orders.COMPLETED);//营业额Double turnover = orderMapper.sumByMap(map);turnover = turnover == null? 0.0 : turnover;//有效订单数Integer validOrderCount = orderMapper.countByMap(map);Double unitPrice = 0.0;Double orderCompletionRate = 0.0;if(totalOrderCount != 0 && validOrderCount != 0){//订单完成率orderCompletionRate = validOrderCount.doubleValue() / totalOrderCount;//平均客单价unitPrice = turnover / validOrderCount;}//新增用户数Integer newUsers = userMapper.countByMap(map);return BusinessDataVO.builder().turnover(turnover).validOrderCount(validOrderCount).orderCompletionRate(orderCompletionRate).unitPrice(unitPrice).newUsers(newUsers).build();}/*** 查询订单管理数据** @return*/public OrderOverViewVO getOrderOverView() {Map map = new HashMap();map.put("begin", LocalDateTime.now().with(LocalTime.MIN));map.put("status", Orders.TO_BE_CONFIRMED);//待接单Integer waitingOrders = orderMapper.countByMap(map);//待派送map.put("status", Orders.CONFIRMED);Integer deliveredOrders = orderMapper.countByMap(map);//已完成map.put("status", Orders.COMPLETED);Integer completedOrders = orderMapper.countByMap(map);//已取消map.put("status", Orders.CANCELLED);Integer cancelledOrders = orderMapper.countByMap(map);//全部订单map.put("status", null);Integer allOrders = orderMapper.countByMap(map);return OrderOverViewVO.builder().waitingOrders(waitingOrders).deliveredOrders(deliveredOrders).completedOrders(completedOrders).cancelledOrders(cancelledOrders).allOrders(allOrders).build();}/*** 查询菜品总览** @return*/public DishOverViewVO getDishOverView() {Map map = new HashMap();map.put("status", StatusConstant.ENABLE);Integer sold = dishMapper.countByMap(map);map.put("status", StatusConstant.DISABLE);Integer discontinued = dishMapper.countByMap(map);return DishOverViewVO.builder().sold(sold).discontinued(discontinued).build();}/*** 查询套餐总览** @return*/public SetmealOverViewVO getSetmealOverView() {Map map = new HashMap();map.put("status", StatusConstant.ENABLE);Integer sold = setmealMapper.countByMap(map);map.put("status", StatusConstant.DISABLE);Integer discontinued = setmealMapper.countByMap(map);return SetmealOverViewVO.builder().sold(sold).discontinued(discontinued).build();}
}

SetmealMapper

package com.sky.mapper;import com.github.pagehelper.Page;
import com.sky.annotation.AutoFill;
import com.sky.dto.SetmealPageQueryDTO;
import com.sky.entity.Setmeal;
import com.sky.entity.SetmealDish;
import com.sky.enumeration.OperationType;
import com.sky.vo.DishItemVO;
import com.sky.vo.SetmealVO;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.util.List;
import java.util.Map;@Mapper
public interface SetmealMapper {/*** 新增套餐* @param setmeal*/@AutoFill(OperationType.INSERT)void insert(Setmeal setmeal);/*** 根据分类id查询套餐的数量* @param id* @return*/@Select("select count(id) from setmeal where category_id = #{categoryId}")Integer countByCategoryId(Long id);/*** 分页查询* @param setmealPageQueryDTO* @return*/Page<SetmealVO> pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);/*** 根据id查询套餐* @param id* @return*/@Select("select * from setmeal where id = #{id}")Setmeal getById(Long id);/*** 根据id删除套餐* @param setmealId*/@Delete("delete from setmeal where id = #{id}")void deleteById(Long setmealId);/*** 根据id查询套餐和套餐菜品关系* @param id* @return*/SetmealVO getByIdWithDish(Long id);/*** 根据id修改套餐** @param setmeal*/@AutoFill(OperationType.UPDATE)void update(Setmeal setmeal);/*** 动态条件查询套餐* @param setmeal* @return*/List<Setmeal> list(Setmeal setmeal);/*** 根据套餐id查询菜品选项* @param setmealId* @return*/@Select("select sd.name, sd.copies, d.image, d.description " +"from setmeal_dish sd left join dish d on sd.dish_id = d.id " +"where sd.setmeal_id = #{setmealId}")List<DishItemVO> getDishItemBySetmealId(Long setmealId);/*** 根据条件统计套餐数量* @param map* @return*/Integer countByMap(Map map);}
 <select id="countByMap" resultType="java.lang.Integer">select count(id) from setmeal<where><if test="status != null">and status = #{status}</if><if test="categoryId != null">and category_id = #{categoryId}</if></where></select>

DishMapper

 /*** 根据条件统计菜品数量* @param map* @return*/Integer countByMap(Map map);
<select id="countByMap" resultType="java.lang.Integer">select count(id) from dish<where><if test="status != null">and status = #{status}</if><if test="categoryId != null">and category_id = #{categoryId}</if></where></select>

3. 功能测试

Apache POI

1. 介绍

Apache POI 的全称是 Poor Obfuscation Implementation(意为 “简陋的混淆实现”)。这个名称源于其最初的开发背景 —— 早期它主要用于解析微软 Office 文件格式,而这些格式在当时并未完全公开,且存在一定的 “混淆” 特性,开发者通过逆向工程等方式逆向解析格式并实现兼容,因此得名 “简陋的混淆实现”。

2. 入门案例

package com.sky.test;import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.*;/*** 使用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页中创建行对象,下标从0开始XSSFRow row = sheet.createRow(1);// 在这一行的第几个创建cell单元格、写入内容,结果可以不接收row.createCell(0).setCellValue("姓名");row.createCell(2).setCellValue("城市");// 通过输出流将内存中的Excel文件写入到磁盘FileOutputStream out = new FileOutputStream(new File("D:\\info.xlsx"));excel.write(out);//关闭资源out.close();excel.close();}/*** 通过POI获取Excel文件中的内容*/public static void read() throws Exception {InputStream in = new FileInputStream(new File("D:\\info.xlsx"));// 读取磁盘上已经存在的Excel文件XSSFWorkbook excel = new XSSFWorkbook(in);// 读取excel文件中的第一个sheet页XSSFSheet sheet = excel.getSheetAt(0);// 获取sheet中最后一行的行号(有文字内容的)int lastRowNum = sheet.getLastRowNum();for (int i = 1; i <= lastRowNum; i++){// 获取某一行XSSFRow row = sheet.getRow(i);// 获得单元格对象String cellValue1 = row.getCell(0).getStringCellValue();String cellValue2 = row.getCell(2).getStringCellValue();System.out.println(cellValue1 + " " + cellValue2);}// 关闭资源in.close();excel.close();}public static void main(String[] args) throws Exception {//write();read();}
}

效果如下

导出运营数据Excel报表

1. 需求分析和设计

点击导出按钮下载Excel文件

2. 代码开发

在sky-server/resources/template下面拷贝报表excel模版

ReportController

/*** 导出运营数据报表* @param response*/@GetMapping("/export")@ApiOperation("导出运营数据报表")public void export(HttpServletResponse response){reportService.exportBusinessData(response);}

结合 Controller 层的 export 方法和 Service 层的 exportBusinessData 方法来看,整个调用链路中传递的核心参数是 HttpServletResponse 对象,它是连接 “接口响应” 和 “Excel 下载” 的关键,具体传递逻辑和作用如下:

一、参数传递链路:从 Controller 到 Service

整个过程中只有一个核心参数 HttpServletResponse,传递路径非常清晰:

  1. Controller 层接收并转发参数
    当客户端(如浏览器、Postman)访问 GET /export 接口时,Spring MVC 框架会自动创建 HttpServletResponse 对象(无需开发者手动创建),并将其作为参数注入到 export 方法中。
    随后,export 方法直接将这个 response 对象传递给 Service 层的 exportBusinessData 方法,没有新增或修改其他参数。

  2. Service 层接收并使用参数
    exportBusinessData 方法接收 response 对象后,基于它完成 Excel 文件的下载响应逻辑(这是该参数的核心作用,也是整个报表导出功能的关键)。

二、HttpServletResponse 的核心作用(为何要传递它?)

response 对象的本质是 “服务器对客户端的 HTTP 响应载体”,在 exportBusinessData 方法中,它的作用完全服务于 “Excel 下载”,具体体现在 3 个关键步骤:

1. 提供 “输出流”:将 Excel 数据写入响应

Service 层通过 POI 生成 Excel 工作簿(XSSFWorkbook)后,需要将 Excel 的二进制数据传递到客户端。此时通过 response.getOutputStream() 可以获取 Servlet 输出流(ServletOutputStream),这个流是 “服务器 Excel 数据” 到 “客户端下载” 的桥梁:

// 从 response 中获取输出流
ServletOutputStream out = response.getOutputStream();
// 将 Excel 数据写入流,最终传递到客户端
excel.write(out);

ReportService

 /*** 导出运营数据报表* @param response*/void exportBusinessData(HttpServletResponse response);

ReportServiceImpl

/*** 导出运营数据报表* @param response*/public void exportBusinessData(HttpServletResponse response) {//1. 查询数据库,获取营业数据--查询最近30天的运营数据LocalDate dateBegin = LocalDate.now().minusDays(30);LocalDate dateEnd = LocalDate.now().minusDays(1);//今天的可能还会变动// 查询概览数据BusinessDataVO businessDataVO = workspaceService.getBusinessData(LocalDateTime.of(dateBegin, LocalTime.MIN), LocalDateTime.of(dateEnd, LocalTime.MAX));//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("时间:" + dateBegin + "至" + dateEnd);// 第四行第三、五、七个单元格--营业额、订单完成率、新增用户数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 = dateBegin.plusDays(i);// 查询某一天的数据BusinessDataVO businessData = workspaceService.getBusinessData(LocalDateTime.of(date,LocalTime.MIN), LocalDateTime.of(date, LocalTime.MAX));// 获得某一行row = sheet.getRow(7+i);row.getCell(1).setCellValue(date.toString());row.getCell(2).setCellValue(businessData.getTurnover());row.getCell(3).setCellValue(businessData.getValidOrderCount());row.getCell(4).setCellValue(businessData.getOrderCompletionRate());row.getCell(5).setCellValue(businessData.getUnitPrice());row.getCell(6).setCellValue(businessData.getNewUsers());}// 3. 通过输出流将Excel文件下载到客户端浏览器ServletOutputStream out = response.getOutputStream();excel.write(out);// 关闭资源out.close();excel.close();} catch (IOException e) {e.printStackTrace();}}

3. 功能测试


文章转载自:

http://8D6xbwqz.gnmhy.cn
http://wOvAzxJp.gnmhy.cn
http://YrApDN4o.gnmhy.cn
http://oHkonYYL.gnmhy.cn
http://Pjp6FOEj.gnmhy.cn
http://jqbZ302A.gnmhy.cn
http://ybIturya.gnmhy.cn
http://4XnfI2WZ.gnmhy.cn
http://o53omnv2.gnmhy.cn
http://um17E6m5.gnmhy.cn
http://wt2wwbfQ.gnmhy.cn
http://WBgzbRhr.gnmhy.cn
http://DmFvdf0S.gnmhy.cn
http://zstCBISe.gnmhy.cn
http://7TD3ZI01.gnmhy.cn
http://i7D26vS5.gnmhy.cn
http://4YVAbfMq.gnmhy.cn
http://woP5jy9L.gnmhy.cn
http://xO5i2XWC.gnmhy.cn
http://ClgXwe4G.gnmhy.cn
http://DuAKh9EZ.gnmhy.cn
http://ltv8BaVT.gnmhy.cn
http://QpAJeVTD.gnmhy.cn
http://1esMlISm.gnmhy.cn
http://Gv5I4JAQ.gnmhy.cn
http://gp02U3wI.gnmhy.cn
http://EzdjxKis.gnmhy.cn
http://tPMKfJ5M.gnmhy.cn
http://gRzgSXxV.gnmhy.cn
http://HHEPYCp5.gnmhy.cn
http://www.dtcms.com/a/372095.html

相关文章:

  • 《Go小技巧易错点100例》第三十八篇
  • Conda 包管理器与环境管理使用指南
  • 笔记本、平板如何成为电脑拓展屏?向日葵16成为副屏功能一键实现
  • OpenHarmony 显示能效管理组件:掌控屏幕亮灭与亮度的核心利器
  • SQLite的基本操作
  • 第五课 C#语言基本元素概览,初始类型,变量与方法,算法简介
  • 【系统分析师】第12章-关键技术:软件架构设计(核心总结)
  • Lightdash:一个免费开源的自助式BI平台
  • Claude Code 使用教程
  • UML(统一建模语言)
  • Android开发-常用布局
  • Spring Cloud Gateway 进行集群化部署
  • EmbodiedOneVision——类似π0.5集成了离散自回归解码与连续流匹配去噪:单个模型中完成具身推理、动作生成
  • Paper reading - 03. Speech sequencing in the human precentral gyrus
  • Spring事务失效的常见陷阱与解决方案
  • 现代C++:现代C++?
  • ZSet
  • Linux初级篇
  • MySQL集群高可用架构——组复制 (MGR)
  • MySQL Cluster核心优缺点
  • RestTemplate使用 | RestTemplate设置http连接池参数
  • 01OpenCV简介
  • 美股市场股票数据API对接文档
  • Coze源码分析-资源库-删除插件-前端源码-核心接口与工具
  • 【深度学习】重采样(Resampling)
  • http接口幂等性
  • 无重复字符的最长子串
  • 架构思维:架构师视角的 FullGC 治理
  • pytest(1):fixture从入门到精通
  • Logstash中http_poller插件的用法