前言
用于记录苍穹外卖Day10、Day11、Day12的学习
Day10 订单状态定时处理 来电提醒 客户催单
订单状态定时处理
Spring Task
Spring Task是一个任务调度工具,可以按照约定的时间自动执行某个代码逻辑(定时自动执行某段Java代码)
cron表达式:
cron表达式其实就是一个字符串,通过cron表达式可以定义任务触发的时间。
构成规则:分为6或7个域,用空格隔开,每个域代表一个含义。从左到右依次为秒、分钟、小时、日、月、周、年(可选)
cron在线生成器:https://cron.qqe2.com
Spring Task使用步骤:
- 导入坐标spring-context
- 启动类添加注解@EnableScheduling开启任务调度
- 自定义定时任务类
需求开发
存在的问题:
- 下单后未支付,订单一直处于”待支付“状态
- 用户收货后管理端未点击完成按钮,订单一直处于”派送中“状态
只需自定义个任务处理类来定时处理即可:
//定时任务类,定时处理订单状态
@Component
@Slf4j
public class OrderTask {
@Autowired
private OrderMapper orderMapper;
//处理下单后未支付超时的情况
@Scheduled(cron = "0 * * * * ? *")//每分钟触发一次
// @Scheduled(cron="0/5 * * * * ?")
public void processTimeOut(){
log.info("定时处理下单未支付的订单");
//当前时间减15分钟
LocalDateTime localDateTime = LocalDateTime.now().plusMinutes(-15);
List<Orders> list = orderMapper.getByStatusAndOrderTimeLT(Orders.PENDING_PAYMENT, localDateTime);
if(list!=null&&list.size()>0){
for (Orders orders : list) {
orders.setStatus(Orders.CANCELLED);
orders.setConsignee("订单超时,自动取消");
orders.setCancelTime(LocalDateTime.now());
orderMapper.update(orders);
}
}
}
//处理一直处于派送中,没有完成的订单
@Scheduled(cron = "0 0 1 * * ?")//每天凌晨一点触发
// @Scheduled(cron="0/10 * * * * ?")
public void processDeliveryOrder(){
log.info("定时处理一直在派送的订单");
LocalDateTime localDateTime = LocalDateTime.now().plusMinutes(-60);
List<Orders> list = orderMapper.getByStatusAndOrderTimeLT(Orders.DELIVERY_IN_PROGRESS, localDateTime);
if(list!=null&&list.size()>0){
for (Orders orders : list) {
orders.setStatus(Orders.COMPLETED);
orderMapper.update(orders);
}
}
}
}
来电提醒
WebSocket
WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工通信–浏览器和服务器只需完成一次握手,两者之间即可建立持久性的连接,并进行双向数据传输。
WebSocket和HTTP对比:
- HTTP是短连接;WebSocket是长连接
- HTTP是单向通信,基于请求响应模型;WebSocket支持双向通信。
- WebSocket和HTTP都是基于TCP协议
应用场景:
- 视频弹幕
- 实况更新
- 网页聊天
需求开发
实现思路:
- 通过WebSocket实现管理端页面和服务端保持长连接
- 当客户支付后,调用WebSocket的相关API从服务端向客户端推送消息
- 客户端解析服务端发送的消息,判断是来电提醒还是客户催单,并进行相应的语音播报
- 约定服务端向客户端发送的消息的数据格式为JSON,字段包括:type(消息类型,1为来单提醒、2为客户催单)、orderId、content(消息内容)
这里我们只需要在支付成功后提示管理端即可,在OrderServiceImpl的paySuccess方法中:
//通过WebSocket向客户端浏览器推送数据
Map map=new HashMap();
map.put("type",1);
map.put("orderId",ordersDB.getId());
map.put("content","订单号:"+outTradeNo);
String Json= JSON.toJSONString(map);
webSocketServer.sendToAllClient(Json);
注意:启动项目的时候看看你是否连接上WebSocket,如果没连接上可能是因为自己修改过端口号的问题,将端口号改回80或者改下前端代码即可。
客户催单
实现思路和来电提醒差不多。当用户在客户端点击催单按钮时,发起请求
- OrderController
@GetMapping("/reminder/{id}")
@ApiOperation("客户催单")
public Result reminder(@PathVariable Long id){
orderService.reminder(id);
return Result.success();
}
- OrderServiceImpl
public void reminder(Long id) {
// 根据id查询订单
Orders orders1= orderMapper.getById(id);
// 校验订单是否存在
if (orders1 == null) {
throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
}
//通过WebSocket向客户端浏览器推送数据
Map map=new HashMap();
map.put("type",2);
map.put("orderId",id);
map.put("content","订单号:"+orders1.getNumber());
String Json= JSON.toJSONString(map);
webSocketServer.sendToAllClient(Json);
}
Day11 数据统计-图形报表
效果如下所示:
Apache ECharts
Apache ECharts是一款基于JavaScript的数据可视化图表库,提供直观、生动、可交互、可个性化定制的数据可视化图表。简单来说,它就是一款数据可视化工具。我们只需大致知道它是干啥的,它是在前端使用的,后端开发中我们使用不到。
营业额统计
业务规则:
- 营业额指订单状态为已完成的订单金额合计
- 基于可视化报表的折线图展示营业额数据,x轴为日期,y轴为营业额
- 根据时间选择区间,展示每天的营业额数据
ReportController:注意时间的数据格式
@RestController
@Slf4j
@RequestMapping("/admin/report")
@Api(tags = "数据统计相关接口")
public class ReportController {
@Autowired
private ReportService reportService;
@GetMapping("/turnoverStatistics")
@ApiOperation("营业额统计")
public Result<TurnoverReportVO> turnoverStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){
log.info("营业额数据统计:{},{}",begin,end);
TurnoverReportVO turnoverReportVO=reportService.getTurnoverStatistics(begin,end);
return Result.success(turnoverReportVO);
}
}
ReportServiceImpl:
这里我的实现方法与课程中略有不同,可以参考一下
public TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end) {
TurnoverReportVO turnoverReportVO=new TurnoverReportVO();
//1.封装日期数据
//先去得到一个日期集合,包含begin到end中的所有日期
List<LocalDate> dateList=new ArrayList<>();
dateList.add(begin);
while(!begin.equals(end)){
begin=begin.plusDays(1);
dateList.add(begin);
}
//将集合转成字符串的同时在每个元素间加一个逗号
String dateList1=StringUtils.join(dateList,",");
turnoverReportVO.setDateList(dateList1);
//2.封装营业额数据
//查询对应日期的订单的总营业额
List<Double> moneyList=new ArrayList<>();
for (LocalDate localDate : dateList) {
//根据日期查询状态为已完成的订单的营业额
//00:00:00
LocalDateTime beginTime=LocalDateTime.of(localDate, LocalTime.MIN);
//23:59:59
LocalDateTime endTime=LocalDateTime.of(localDate, LocalTime.MAX);
Map map=new HashMap();
map.put("begin",beginTime);
map.put("end",endTime);
map.put("status", Orders.COMPLETED);
Double money=orderMapper.getSumByMap(map);
if(money==null){
money=0.0;
}
moneyList.add(money);
}
String moneyList1=StringUtils.join(moneyList,",");
turnoverReportVO.setTurnoverList(moneyList1);
return turnoverReportVO;
}
用户统计
ReportController:
@GetMapping("/userStatistics")
@ApiOperation("用户统计")
public Result<UserReportVO> userStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin, @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){
log.info("用户统计:{},{}",begin,end);
UserReportVO userReportVO=reportService.getUserStatistics(begin,end);
return Result.success(userReportVO);
}
ReportServiceImpl:
//用户数据统计
public UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {
UserReportVO userReportVO=new UserReportVO();
//1.封装日期数据
//先去得到一个日期集合,包含begin到end中的所有日期
List<LocalDate> dateList=new ArrayList<>();
dateList.add(begin);
while(!begin.equals(end)){
begin=begin.plusDays(1);
dateList.add(begin);
}
//将集合转成字符串的同时在每个元素间加一个逗号
String dateList1=StringUtils.join(dateList,",");
userReportVO.setDateList(dateList1);
//2.封装用户总量数据
List<Integer> totalList=new ArrayList<>();
//3.封装新增用户数量数据
List<Integer> newList=new ArrayList<>();
for (LocalDate localDate : dateList) {
//查询用户表createTime这一天的用户的总量
//00:00:00
LocalDateTime beginTime=LocalDateTime.of(localDate, LocalTime.MIN);
//23:59:59
LocalDateTime endTime=LocalDateTime.of(localDate, LocalTime.MAX);
Map map=new HashMap();
map.put("end",endTime);
Integer total=userMapper.countByMap(map);
if(total==null){
total=0;
}
totalList.add(total);
map.put("begin",beginTime);
Integer today= userMapper.countByMap(map);
if(today==null){
today=0;
}
newList.add(today);
}
String userList1=StringUtils.join(totalList,",");
userReportVO.setTotalUserList(userList1);
String list1=StringUtils.join(newList,",");
userReportVO.setNewUserList(list1);
return userReportVO;
}
订单统计
业务规则:
- 两条折线,一条代表总订单数,另一条代表有效订单数(状态为已完成的订单)
- 展示订单总数、有效订单数、订单完成率数据
ReportController:
@GetMapping("/ordersStatistics")
@ApiOperation("订单统计")
public Result<OrderReportVO> orderStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin, @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){
log.info("订单统计:{},{}",begin,end);
OrderReportVO orderReportVO=reportService.getOrderReportStatistics(begin,end);
return Result.success(orderReportVO);
}
ReportServiceImpl:
@Override
public OrderReportVO getOrderReportStatistics(LocalDate begin, LocalDate end) {
OrderReportVO orderReportVO=new OrderReportVO();
//1.封装日期数据
List<LocalDate> dateList=new ArrayList<>();
dateList.add(begin);
while(!begin.equals(end)){
begin=begin.plusDays(1);
dateList.add(begin);
}
//将集合转成字符串的同时在每个元素间加一个逗号
String dateList1=StringUtils.join(dateList,",");
orderReportVO.setDateList(dateList1);
//2.订单总数
List<Integer> totalOrder=new ArrayList<>();
//3.有效订单数
List<Integer> realOrder=new ArrayList<>();
//每天的订单总数以及有效订单数
for (LocalDate localDate : dateList) {
//00:00:00
LocalDateTime beginTime=LocalDateTime.of(localDate, LocalTime.MIN);
//23:59:59
LocalDateTime endTime=LocalDateTime.of(localDate, LocalTime.MAX);
Map map=new HashMap();
map.put("begin",beginTime);
map.put("end",endTime);
Integer total=orderMapper.getByMap(map);
if(total==null){
total=0;
}
totalOrder.add(total);
map.put("status",Orders.COMPLETED);
Integer real=orderMapper.getByMap(map);
if(real==null){
real=0;
}
realOrder.add(real);
}
String totalOrder1=StringUtils.join(totalOrder,",");
String realOrder1=StringUtils.join(realOrder,",");
//计算时间区间内的订单总数量
Integer sum=0;
for (Integer integer : totalOrder) {
sum+=integer;
}
//计算时间区间内的有效订单数量
Integer real=0;
for (Integer integer : realOrder) {
real+=integer;
}
//计算订单完成率
double orderCompletionRate=0.0;
if (sum!=0) {
orderCompletionRate= (double) real /sum;
}
orderReportVO.setOrderCompletionRate(orderCompletionRate);
orderReportVO.setOrderCountList(totalOrder1);
orderReportVO.setValidOrderCountList(realOrder1);
orderReportVO.setTotalOrderCount(sum);
orderReportVO.setValidOrderCount(real);
System.out.println(orderReportVO);
return orderReportVO;
}
销量排名统计
ReportController:
@GetMapping("/top10")
@ApiOperation("销量排名top10")
public Result<SalesTop10ReportVO> top10(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin, @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){
log.info("销量排名top10:{},{}",begin,end);
SalesTop10ReportVO salesTop10ReportVO=reportService.getTop10(begin,end);
return Result.success(salesTop10ReportVO);
}
ReportServiceImpl:
public SalesTop10ReportVO getTop10(LocalDate begin, LocalDate end) {
SalesTop10ReportVO salesTop10ReportVO=new SalesTop10ReportVO();
//00:00:00
LocalDateTime beginTime=LocalDateTime.of(begin, LocalTime.MIN);
//23:59:59
LocalDateTime endTime=LocalDateTime.of(end, LocalTime.MAX);
List<GoodsSalesDTO> goodsSalesDTOList=orderMapper.getSalesTop10(beginTime,endTime);
//遍历取出DTO中的numa和number放到对应的集合中去
List<String> nameList=new ArrayList<>();
List<String> numberList=new ArrayList<>();
for (GoodsSalesDTO goodsSalesDTO : goodsSalesDTOList) {
nameList.add(goodsSalesDTO.getName());
numberList.add(String.valueOf(goodsSalesDTO.getNumber()));
}
String nameLists=StringUtils.join(nameList,",");
String numberLists=StringUtils.join(numberList,",");
salesTop10ReportVO.setNameList(nameLists);
salesTop10ReportVO.setNumberList(numberLists);
return salesTop10ReportVO;
}
OrderMapper.xml:
分析一下这里的SQL语句,因为我们要根据订单状态(对应orders表中的status)查订单的名称以及数量(对应order_details表中的number),所以涉及到联表查询。查询前10,所以只需limit 0,10
<!--统计销量前10-->
<select id="getSalesTop10" resultType="com.sky.dto.GoodsSalesDTO">
select od.name,sum(od.number) number from order_detail od,orders o where od.id=o.id and o.status=5
<if test="begin!=null">and order_time > #{begin}</if>
<if test="end!=null">and order_time < #{end}</if>
group by od.name order by number desc limit 0,10
</select>
Day12 数据统计-Excel报表
工作台
这里课程中的代码是直接导入的,我还是选择手敲一遍。
工作台展示的数据:
- 今日数据
- 订单管理
- 菜品总览
- 套餐总览
- 订单信息
为了简便展示,这里我直接给出一个类中的全部代码了,可以根据注释理解。
- WorkSpaceController
@RestController
@RequestMapping("/admin/workspace")
@Slf4j
@Api(tags = "工作台相关接口")
public class WorkSpaceController {
@Autowired
private WorkSpaceService workSpaceService;
//查询工作台今日数据
@GetMapping("/businessData")
@ApiOperation("查询工作台今日数据")
public Result<BusinessDataVO> businessData(){
log.info("查询工作台今日数据");
//获取开始时间
LocalDateTime beginTime=LocalDateTime.now().with(LocalTime.MIN);
//获取结束时间
LocalDateTime endTime=LocalDateTime.now().with(LocalTime.MAX);
BusinessDataVO businessDataVO=workSpaceService.getBusinessData(beginTime,endTime);
return Result.success(businessDataVO);
}
//查询订单管理数据
@GetMapping("/overviewOrders")
@ApiOperation("查询订单管理数据")
public Result<OrderOverViewVO> overViewOrders(){
log.info("查询订单管理数据");
return Result.success(workSpaceService.getOrderOverView());
}
//查询菜品总览
@GetMapping("/overviewDishes")
@ApiOperation("查询菜品总览")
public Result<DishOverViewVO> overViewDishes(){
return Result.success(workSpaceService.getDishOverView());
}
//查询套餐总览
@GetMapping("/overviewSetmeals")
@ApiOperation("查询套餐总览")
public Result<SetmealOverViewVO> overViewSetmeal(){
return Result.success(workSpaceService.getSetmealOvermeal());
}
}
- WorkSpaceService
public interface WorkSpaceService {
BusinessDataVO getBusinessData(LocalDateTime beginTime, LocalDateTime endTime);
OrderOverViewVO getOrderOverView();
DishOverViewVO getDishOverView();
SetmealOverViewVO getSetmealOvermeal();
}
- WorkSpaceServiceImpl(这里很多方法我们都在OrderMapper中已经写好了,直接调用即可)
@Service
public class WorkSpaceServiceImpl implements WorkSpaceService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private UserMapper userMapper;
@Autowired
private DishMapper dishMapper;
@Autowired
private SetmealMapper setmealMapper;
//查询工作台今日数据
public BusinessDataVO getBusinessData(LocalDateTime beginTime, LocalDateTime endTime) {
BusinessDataVO businessDataVO=new BusinessDataVO();
Map map=new HashMap();
map.put("begin",beginTime);
map.put("end",endTime);
//订单总数
Integer total=orderMapper.getByMap(map);
map.put("status",5);
//营业额
Double sum = orderMapper.getSumByMap(map);
sum = sum == null? 0.0 : sum;
//有效订单数
Integer real=orderMapper.getByMap(map);
//平均客单价
double average=0.0;
//订单完成率
double complete=0.0;
if(total!=0&&real!=0){
complete= (double) real /total;
average=sum/real;
}
//新增用户数
Integer newUser=userMapper.countByMap(map);
businessDataVO.setTurnover(sum);
businessDataVO.setNewUsers(newUser);
businessDataVO.setOrderCompletionRate(complete);
businessDataVO.setValidOrderCount(real);
businessDataVO.setUnitPrice(average);
return businessDataVO;
}
//查询订单管理数据
@Override
public OrderOverViewVO getOrderOverView() {
OrderOverViewVO orderOverViewVO=new OrderOverViewVO();
Map map = new HashMap();
map.put("begin", LocalDateTime.now().with(LocalTime.MIN));
//待接单
map.put("status", Orders.TO_BE_CONFIRMED);
Integer status1=orderMapper.getByMap(map);
//待派送
map.put("status",Orders.CONFIRMED);
Integer status2=orderMapper.getByMap(map);
//已完成
map.put("status",Orders.COMPLETED);
Integer status3=orderMapper.getByMap(map);
//已取消
map.put("status",Orders.CANCELLED);
Integer status4=orderMapper.getByMap(map);
//全部订单
map.put("status",null);
Integer status5=orderMapper.getByMap(map);
orderOverViewVO.setWaitingOrders(status1);
orderOverViewVO.setDeliveredOrders(status2);
orderOverViewVO.setCompletedOrders(status3);
orderOverViewVO.setCancelledOrders(status4);
orderOverViewVO.setAllOrders(status5);
return orderOverViewVO;
}
//查询菜品总览
@Override
public DishOverViewVO getDishOverView() {
DishOverViewVO dishOverViewVO=new DishOverViewVO();
Integer on=dishMapper.onStatus();
Integer off=dishMapper.offStatus();
dishOverViewVO.setSold(on);
dishOverViewVO.setDiscontinued(off);
return dishOverViewVO;
}
//查询套餐总览
@Override
public SetmealOverViewVO getSetmealOvermeal() {
SetmealOverViewVO setmealOverViewVO=new SetmealOverViewVO();
Integer on=setmealMapper.onStatus();
Integer off=setmealMapper.offStatus();
setmealOverViewVO.setSold(on);
setmealOverViewVO.setDiscontinued(off);
return setmealOverViewVO;
}
}
- DishMapper(这里是SQL语句少我使用这种方法,标准的应该是使用动态SQL)
@Select("select count(id) from dish where status=1")
Integer onStatus();
@Select("select count(id) from dish where status=0")
Integer offStatus();
- SetmealMapper
@Select("select count(id) from setmeal where status=1")
Integer onStatus();
@Select("select count(id) from setmeal where status=0")
Integer offStatus();
Apache POI
简介:Apache POI可以处理Office的各种文件格式。允许我们使用POI在Java程序中对Office文件进行读写操作。一般用于处理Excel文件。
实例:
写入Excel文件:
//在内存中创建一个excel文件
XSSFWorkbook excel=new XSSFWorkbook();
//在excel文件中创建一个sheet页同时指定其名称为info
XSSFSheet sheet=excel.createSheet("info");
//创建行对象,行和列都从0开始,这里我们指定1表示是第二行
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("E:\\Takeout\\info.xlsx"));
excel.write(out);
out.close();
excel.close();
读取Excel文件:
InputStream in=new FileInputStream(new File("E:\\Takeout\\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 stringCellValue1 = row.getCell(1).getStringCellValue();
String stringCellValue2 = row.getCell(2).getStringCellValue();
System.out.println(stringCellValue1+" "+stringCellValue2);
}
excel.close();
in.close();
导出Excel报表
业务规则:
- 导出Excel文件形式的报表文件
- 导出进30天的运营数据
实现步骤:
- 设计Excel模板文件
- 查询近30日的运营数据
- 将查询到的运营数据写入模板文件内
- 通过输出流将Excel文件下载到客户端浏览器
实现:
- ReportController
@GetMapping("/export")
@ApiOperation("导出Excel报表")
public Result export(HttpServletResponse response){
log.info("导出Excel报表");
reportService.export(response);
return Result.success();
}
- ReportServiceImpl
//导出运营数据报表
@Override
public void export(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 businessDataVO1=workSpaceService.getBusinessData(LocalDateTime.of(date,LocalTime.MIN),LocalDateTime.of(date,LocalTime.MAX));
//获取某一行并填充数据
row= sheet.getRow(7+i);
row.getCell(1).setCellValue(businessDataVO1.toString());
row.getCell(2).setCellValue(businessDataVO1.getTurnover());
row.getCell(3).setCellValue(businessDataVO1.getValidOrderCount());
row.getCell(4).setCellValue(businessDataVO1.getOrderCompletionRate());
row.getCell(5).setCellValue(businessDataVO1.getUnitPrice());
row.getCell(6).setCellValue(businessDataVO1.getNewUsers());
}
//3.通过输出流将Excel文件下载到客户端浏览器
ServletOutputStream out= response.getOutputStream();
excel.write(out);
//关闭资源
out.close();
excel.close();
}catch (Exception e){
e.printStackTrace();
}
这里我们需要注意的一个地方,这里老师没有加上这个后缀,不加的话我这里会报错:
苍穹外卖的学习就到这里啦,完结撒花!!!