苍穹外卖-工作台实现、Apache POI、导出Excel报表

目录

[1. 工作台](#1. 工作台)

[1.1 需求分析和设计](#1.1 需求分析和设计)

[1.1.1 产品原型](#1.1.1 产品原型)

[1.1.2 接口设计](#1.1.2 接口设计)

[1.2 代码实现](#1.2 代码实现)

[1.2.1 Controller层](#1.2.1 Controller层)

[1.2.2 Service层接口](#1.2.2 Service层接口)

[1.2.3 Service层实现类](#1.2.3 Service层实现类)

[1.2.4 Mapper层](#1.2.4 Mapper层)

[1.3 功能测试](#1.3 功能测试)

[2. Apache POI](#2. Apache POI)

[2.1 介绍](#2.1 介绍)

[2.2 入门案例](#2.2 入门案例)

[2.2.1 将数据写入Excel文件](#2.2.1 将数据写入Excel文件)

[2.2.2 通过POI基于模板写入数据到execl](#2.2.2 通过POI基于模板写入数据到execl)

[2.2.3 读取Excel文件中的数据](#2.2.3 读取Excel文件中的数据)

[3.2 代码开发](#3.2 代码开发)

[3.2.1 实现步骤](#3.2.1 实现步骤)

[3.2.2 Controller层](#3.2.2 Controller层)

[3.2.3 Service层接口](#3.2.3 Service层接口)

[3.2.4 Service层实现类](#3.2.4 Service层实现类)

[3.3 功能测试](#3.3 功能测试)

功能实现:工作台数据导出

工作台效果图:

数据导出效果图:

在数据统计页面点击数据导出:生成Excel报表

1. 工作台

1.1 需求分析和设计

1.1.1 产品原型

工作台是系统运营的数据看板,并提供快捷操作入口,可以有效提高商家的工作效率。

工作台展示的数据:

  • 今日数据

  • 订单管理

  • 菜品总览

  • 套餐总览

  • 订单信息

原型图:

名词解释:

  • 营业额:已完成订单的总金额

  • 有效订单:已完成订单的数量

  • 订单完成率:有效订单数 / 总订单数 * 100%

  • 平均客单价:营业额 / 有效订单数

  • 新增用户:新增用户的数量

1.1.2 接口设计

通过上述原型图分析,共包含6个接口。

接口设计:

  • 今日数据接口

  • 订单管理接口

  • 菜品总览接口

  • 套餐总览接口

  • 订单搜索(已完成)

  • 各个状态的订单数量统计(已完成)

1). 今日数据的接口设计

2). 订单管理的接口设计

3). 菜品总览的接口设计

4). 套餐总览的接口设计

1.2 代码实现

1.2.1 Controller层

添加WorkSpaceController.java

java 复制代码
package com.sky.controller.admin;

import com.sky.service.WorkspaceService;
import com.sky.result.Result;
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 {

    @Autowired
    private 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());
    }
}

1.2.2 Service层接口

添加WorkspaceService.java

java 复制代码
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();

}

1.2.3 Service层实现类

添加WorkspaceServiceImpl.java

java 复制代码
@Service
@Slf4j
public class WorkspaceServiceImpl implements WorkspaceService {

    @Autowired
    private OrdersMapper orderMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private SetmealMapper setmealMapper;

    /**
     * 根据时间段统计营业数据
     * @param begin
     * @param end
     * @return
     */
    public BusinessDataVO getBusinessData(LocalDateTime begin, LocalDateTime end) {
        /**
         * 营业额:当日已完成订单的总金额
         * 有效订单:当日已完成订单的数量
         * 订单完成率:有效订单数 / 总订单数
         * 平均客单价:营业额 / 有效订单数
         * 新增用户:当日新增用户的数量
         */

        Map map = new HashMap();
        map.put("beginTime",begin);
        map.put("endTime",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("beginTime", 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();
    }
}

一定要注意这里map里的属性名称要与xml文件中对应,否则sql语句的查询条件会出现错误。

1.2.4 Mapper层

在SetmealMapper中添加countByMap方法定义

java 复制代码
/**
     * 根据条件统计套餐数量
     * @param map
     * @return
     */
    Integer countByMap(Map map);

在SetmealMapper.xml中添加对应SQL实现

XML 复制代码
<!--    根据状态统计套餐数量-->
    <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中添加countByMap方法定义

java 复制代码
/**
     * 根据状态统计菜品数量
     * @param map
     * @return
     */
    Integer countByMap(Map map);

在DishMapper.xml中添加对应SQL实现

XML 复制代码
<!--    根据状态统计菜品数量-->
    <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>

1.3 功能测试

启动nginx ,访问 http://localhost,进入工作台

进入开发者模式,分别查看今日数据、订单管理、菜品总览、套餐总览

1). 今日数据查询

2). 订单管理数据查询

2. Apache POI

2.1 介绍

Apache POI 是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是,我们可以使用 POI 在 Java 程序中对Miscrosoft Office各种文件进行读写操作。 一般情况下,POI 都是用于操作 Excel 文件。

Apache POI 的应用场景:

  • 银行网银系统导出交易明细
  • 各种业务系统导出Excel报表
  • 批量导入业务数据

2.2 入门案例

Apache POI既可以将数据写入Excel文件,也可以读取Excel文件中的数据,接下来分别进行实现。

Apache POI的maven坐标:(项目中已导入)

XML 复制代码
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.16</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.16</version>
</dependency>

2.2.1 将数据写入Excel文件

1). 代码开发

java 复制代码
/**
 * POI读写excel测试
 */
public class TestPOI {
    /**
     * Excel的组成
     *      excel文件--->工作表对象
     *      excel里面可以有很多个Sheet表--->工作表对象
     *      Sheet表里面有很多行--->行对象
     *      行里面有很多单元格--->单元格对象
     *      单元格里面有数据--->数据对象
     */


    //需求:通过POI写入数据到执行的excel文件中
    @Test
    public void testWrite() throws Exception {
        //1.通过POI创建工作簿对象(文件)对象--excel对象
        XSSFWorkbook workbook = new XSSFWorkbook();

        //2.通过XSSFWorkbook创建工作表对象--Sheet对象
        XSSFSheet sheet = workbook.createSheet("itcast");

        //3.通过XSSFSheet创建行对象--Row对象,下标从0开始,创建第一行就写0
        XSSFRow row = sheet.createRow(0);

        //4.通过XSSFRow创建单元格对象--Cell对象(下标从0开始),并往格子中填充数据
        row.createCell(0).setCellValue("姓名");
        row.createCell(1).setCellValue("爱好");

        //第二行
        XSSFRow row1 = sheet.createRow(1);
        row1.createCell(0).setCellValue("张三");
        row1.createCell(1).setCellValue("足球");

        //5.将excel文件写出到指定的文件中
        FileOutputStream fos = new FileOutputStream("D:/itcast.xlsx");
        workbook.write(fos);
        System.out.println("写出完毕...");

        //6.释放资源
        fos.close();
        workbook.close();
    }

2). 实现效果

在D盘中生成itcast.xlsx文件,创建名称为itcast的Sheet页,同时将内容成功写入。

2.2.2 通过POI基于模板写入数据到execl

java 复制代码
//需求:通过POI写入数据到执行的excel文件中--基于模板写入
    @Test
    public void testWrite2() throws Exception {
        //1.基于excel模板文件通过POI创建工作簿对象(文件)对象--有字体模板的excel对象
        FileInputStream fis = new FileInputStream("D:\\demo.xlsx");
        XSSFWorkbook workbook = new XSSFWorkbook(fis);

        //2.通过XSSFWorkbook获取工作表对象--Sheet对象,注意不要createSheet,会覆盖的。
        XSSFSheet sheet = workbook.getSheet("itcast");

        //3.通过XSSFSheet获取行对象--Row对象,下标从0开始,创建第一行就写0
        XSSFRow row = sheet.getRow(0);

        //4.通过XSSFRow创建单元格对象--Cell对象(下标从0开始),并往格子中填充数据
        row.getCell(0).setCellValue("姓名");
        row.getCell(1).setCellValue("爱好");

        //第二行
        XSSFRow row1 = sheet.getRow(1);
        row1.getCell(0).setCellValue("张三");
        row1.getCell(1).setCellValue("足球");

        //第三行
        XSSFRow row2 = sheet.getRow(2);
        row2.getCell(0).setCellValue("李四");
        row2.getCell(1).setCellValue("书法");

        //5.将excel文件写出到指定的文件中
        FileOutputStream fos = new FileOutputStream("D:\\demo1.xlsx");
        workbook.write(fos);
        System.out.println("写出完毕...");

        //6.释放资源
        fos.close();
        workbook.close();
    }

一定注意这里面的create换成了get,如果继续用create会覆盖原本单元格里的格式

2.2.3 读取Excel文件中的数据

1). 代码开发

java 复制代码
 //需求:通过POI读磁盘中指定的excel文件到Java内存中,并输出到控制台
    @Test
    public void testRead() throws Exception {
        //1.基于excel模板文件通过POI创建工作簿对象(文件)对象--有字体模板的excel对象
        FileInputStream fis = new FileInputStream("D:\\itcast.xlsx");
        XSSFWorkbook workbook = new XSSFWorkbook(fis);

        //2.通过XSSFWorkbook获取工作表对象--Sheet对象,注意不要createSheet,会覆盖的。
        XSSFSheet sheet = workbook.getSheet("itcast");

        //3.通过XSSFSheet获取行对象--Row对象,下标从0开始,创建第一行就写0
//        XSSFRow row = sheet.getRow(0);

//        //4.通过XSSFRow创建单元格对象--Cell对象(下标从0开始),并往格子中填充数据
//        String name = row.getCell(0).getStringCellValue();
//        String hobby = row.getCell(1).getStringCellValue();
//        System.out.println(name + "|" + hobby);
//        //读第二行
//        XSSFRow row1 = sheet.getRow(1);
//        String name1 = row1.getCell(0).getStringCellValue();
//        String hobby1 = row1.getCell(1).getStringCellValue();
//        System.out.println(name1 + "|" + hobby1);

        //循环读取表中的每一行数据
        int firstRowNum = sheet.getFirstRowNum();
        int lastRowNum = sheet.getLastRowNum();
        System.out.println("firstRowNum = " + firstRowNum);
        System.out.println("lastRowNum = " + lastRowNum);
        for (int i = firstRowNum; i <= lastRowNum; i++) {
            XSSFRow row = sheet.getRow(i);
            String name = row.getCell(0).getStringCellValue();
            String hobby = row.getCell(1).getStringCellValue();
            System.out.println(name + "|" + hobby);
        }


        //6.释放资源
        fis.close();
        workbook.close();
    }

2). 实现效果

将itcast.xlsx文件中的数据进行读取

3.2 代码开发


3.2.1 实现步骤

1). 设计Excel模板文件

2). 查询近30天的运营数据

3). 将查询到的运营数据写入模板文件

4). 通过输出流将Excel文件下载到客户端浏览器

3.2.2 Controller层

根据接口定义,在ReportController中创建export方法:

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

3.2.3 Service层接口

在ReportService接口中声明导出运营数据报表的方法:

java 复制代码
	/**
     * 导出近30天的运营数据报表
     * @param response
     **/
    void exportBusinessData(HttpServletResponse response);

3.2.4 Service层实现类

在ReportServiceImpl实现类中实现导出运营数据报表的方法:

提前将资料中的运营数据报表模板.xlsx拷贝到项目的resources/template目录中

java 复制代码
    /**导出近30天的运营数据报表
     * @param response
     **/
    public void exportBusinessData(HttpServletResponse response) {
        LocalDate begin = LocalDate.now().minusDays(30);
        LocalDate end = LocalDate.now().minusDays(1);
        //查询概览运营数据,提供给Excel模板文件
        BusinessDataVO businessData = workspaceService.getBusinessData(LocalDateTime.of(begin,LocalTime.MIN), LocalDateTime.of(end, LocalTime.MAX));
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx");
        try {
            //基于提供好的模板文件创建一个新的Excel表格对象
            XSSFWorkbook excel = new XSSFWorkbook(inputStream);
            //获得Excel文件中的一个Sheet页
            XSSFSheet sheet = excel.getSheet("Sheet1");

            sheet.getRow(1).getCell(1).setCellValue(begin + "至" + end);
            //获得第4行
            XSSFRow row = sheet.getRow(3);
            //获取单元格
            row.getCell(2).setCellValue(businessData.getTurnover());
            row.getCell(4).setCellValue(businessData.getOrderCompletionRate());
            row.getCell(6).setCellValue(businessData.getNewUsers());
            row = sheet.getRow(4);
            row.getCell(2).setCellValue(businessData.getValidOrderCount());
            row.getCell(4).setCellValue(businessData.getUnitPrice());
            for (int i = 0; i < 30; i++) {
                LocalDate date = begin.plusDays(i);
               //准备明细数据
                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());
            }
            //通过输出流将文件下载到客户端浏览器中
            ServletOutputStream out = response.getOutputStream();
            excel.write(out);
            //关闭资源
            out.flush();
            out.close();
            excel.close();

        }catch (IOException e){
            e.printStackTrace();
        }
    }

3.3 功能测试

直接使用前后端联调测试。

进入数据统计

点击数据导出:Excel报表下载成功

相关推荐
zhangjin12222 小时前
kettle插件-excel插件,kettle读取excel动态表头,kettle根据列名读取excel
excel·kettle·kettle excel插件·kettle 动态excel
远洪17 小时前
excel 找出两列不同的数据
excel
pcplayer18 小时前
非常好用的 Excel 读写控件
excel·delphi·office
Navicat中国1 天前
使用 Navicat 导入向导导入 Excel 数据时,系统提示导入成功,表中也能看到数据,但行数统计显示为 0,这是什么原因?
数据库·excel·导入
穿着内裤的外星人1 天前
触控精灵远程读写Excel步骤配置
excel
是孑然呀1 天前
【小记】excel vlookup一对多(第二篇)
excel
开开心心就好1 天前
专为视障人士设计的免费辅助工具
windows·计算机视觉·计算机外设·excel·散列表·推荐算法·csdn开发云
transformer_WSZ1 天前
excel两列数据绘制折线图
excel·折线图
蒋胜山1 天前
Excel 练习题(5)
经验分享·excel
Data-Miner2 天前
数以轻舟聚焦Excel-Agent场景:当AI做表工具学会说人话
人工智能·excel