《苍穹外卖》Day12部分知识点记录——数据统计-Excel报表

一、工作台

需求分析和设计

接口设计

  • 今日数据接口
  • 订单管理接口
  • 菜品总览接口
  • 套餐总览接口
  • 订单搜索(已完成)
  • 各个状态的订单数量统计(已完成)

代码实现

今日数据接口

1. WorkspaceController

注意不要导错包了

package com.sky.controller.admin;

import com.sky.result.Result;
import com.sky.service.WorkspaceService;
import com.sky.vo.BusinessDataVO;
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);
    }
}

2. WorkspaceService

    /**
     * 根据时间段统计营业数据
     * @param begin
     * @param end
     * @return
     */
    BusinessDataVO getBusinessData(LocalDateTime begin, LocalDateTime end);

3. WorkspaceServiceImpl

package com.sky.service.impl;

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 lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class WorkspaceServiceImpl implements WorkspaceService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private SetmealMapper setmealMapper;

    /**
     * 根据时间段统计营业数据
     * @param begin
     * @param end
     * @return
     */
    @Override
    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();
    }
}

4. 功能测试

订单管理接口

1. WorkspaceController

    /**
     * 查询订单管理数据
     * @return
     */
    @GetMapping("/overviewOrders")
    @ApiOperation("查询订单管理数据")
    public Result<OrderOverViewVO> orderOverView() {
        return Result.success(workspaceService.getOrderOverView());
    }

2. WorkspaceService

    /**
     * 查询订单管理数据
     * @return
     */
    OrderOverViewVO getOrderOverView();

3. WorkspaceServiceImpl

    /**
     * 查询订单管理数据
     * @return
     */
    @Override
    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();
    }

4. 测试

菜品总览接口 

1. WorkspaceController

    /**
     * 查询菜品总览
     * @return
     */
    @GetMapping("/overviewDishes")
    @ApiOperation("查询菜品总览")
    public Result<DishOverViewVO> dishOverView() {
        return Result.success(workspaceService.getDishOverView());
    }

2. WorkspaceService

    /**
     * 查询菜品总览
     * @return
     */
    DishOverViewVO getDishOverView();

3. WorkspaceServiceImpl

    /**
     * 查询菜品总览
     * @return
     */
    @Override
    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();
    }

4. DishMapper

    /**
     * 根据条件统计菜品数量
     * @param map
     * @return
     */
    Integer countByMap(Map map);

5. DishMapper.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>

6. 测试

套餐总览接口

1. WorkspaceController

    /**
     * 查询套餐总览
     * @return
     */
    @GetMapping("/overviewSetmeals")
    @ApiOperation("查询套餐总览")
    public Result<SetmealOverViewVO> setmealOverView() {
        return Result.success(workspaceService.getSetmealOverView());
    }

2. WorkspaceService

    /**
     * 查询套餐总览
     * @return
     */
    SetmealOverViewVO getSetmealOverView();

3. WorkspaceServiceImpl

    /**
     * 查询套餐总览
     * @return
     */
    @Override
    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();
    }

4. SetmealMapper

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

5. SetmealMapper.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>

6. 测试

二、Apache POI

介绍

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

Apache POI的应用场景

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

入门案例

1. 导入Apache POI的maven坐标

        <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. POITest

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.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

/**
 * 使用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页中创建行对象,rownum编号从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("E:\\info.xlsx"));
        excel.write(out);

        // 关闭资源
        out.close();
        excel.close();
    }

    /**
     * 通过POI读取EXCEL文件中的内容
     * @throws Exception
     */
    public static void read() throws Exception {
        // 读取磁盘上已经存在的excel文件
        FileInputStream in = new FileInputStream(new File("E:\\info.xlsx"));
        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 cellValue = row.getCell(1).getStringCellValue();
            String cellValue2 = row.getCell(2).getStringCellValue();
            System.out.println(cellValue + " " + cellValue2);
        }

        // 关闭资源
        in.close();
        excel.close();
    }

    public static void main(String[] args) throws Exception {
        write();
        read();
    }
}

3. 测试

三、导出运营数据Excel报表

需求分析和设计

产品原型

导出的Excel报表格式

业务规则

  • 导出Excel形式的报表文件
  • 导出最近30天的运营数据

接口设计

注意:当前接口没有返回数据,因为报表导出功能本质上是文件下载,服务端会通过输出流将Excel文件下载到客户端浏览器。

代码开发

实现步骤:

  • ①设计Excel模板文件
  • ②查询近30天的运营数据
  • ③将查询到的运营数据写入模板文件
  • ④通过输出流将Excel文件下载到客户端浏览器

1. ReportController

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

2. ReportService

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

3. ReportServiceImpl

    @Autowired
    private WorkspaceService workspaceService;

    /**
     * 导出运营数据报表
     * @param response
     */
    @Override
    public void exportBusinessData(HttpServletResponse response) {
        // 1. 查询数据库,获取营业数据--查询最近30天的运营数据
        LocalDate dateBegin = LocalDate.now().minusDays(30);
        LocalDate dateEnd = LocalDate.now().minusDays(1);
        LocalDateTime beginTime = LocalDateTime.of(dateBegin, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(dateEnd, LocalTime.MAX);

        // 查询概览数据
        BusinessDataVO businessDataVO = workspaceService.getBusinessData(beginTime, endTime);

        // 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);

            // 获取第4行
            XSSFRow row = sheet.getRow(3);
            // 营业额
            row.getCell(2).setCellValue(businessDataVO.getTurnover());
            // 订单完成率
            row.getCell(4).setCellValue(businessDataVO.getOrderCompletionRate());
            // 新增用户数
            row.getCell(6).setCellValue(businessDataVO.getNewUsers());

            // 获得第5行
            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);

            // 4. 关闭资源
            out.close();
            excel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

功能测试

完结撒花~~~

后端完整源码:https://pan.baidu.com/s/1hHnA-H_xOFiVEeIVi92A3Q?pwd=0k80 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/592899.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux IO基础】缓冲区

概念 缓冲区的主要作用是提高效率 --- 提高使用者的效率&#xff0c;因为有缓冲区的存在&#xff0c;我们可以积累一部分再统一发送&#xff0c;提高发送的效率。 刷新方式 缓冲区因为能够暂存数据&#xff0c;必定要有一定的刷新方式&#xff1a; 一般策略&#xff1a; 无…

【JavaEE】线程的概念

文章目录 1、什么是线程2、进程和线程的区别3、多线程的概述4、在Java中实现多线程的方法1.继承Thread类2.实现Runnable接口3.使用匿名内部类来继承Thread类&#xff0c;实现run方法4.使用匿名内部类来实现Runnable接口&#xff0c;实现run方法5.使用 lambda表达式 1、什么是线…

ubuntu20中ros与anaconda的python版本冲突问题

系统环境 原本系统是ubuntu20 noetic&#xff0c;python都在/usr/bin中&#xff0c;一共是两个版本的python&#xff0c;一个是python3.8&#xff0c;另一个是python2.7。 问题发现 当安装anaconda后&#xff0c;并且将anaconda的bin目录加入到系统环境中时候&#xff0c;…

【微服务】——Docker 基础知识

这里写自定义目录标题 1.1 了解Docker1.1.1应用部署的环境问题1.1.2.Docker解决依赖兼容问题1.1.3.Docker解决操作系统环境差异1.1.4.小结 1.2.Docker和虚拟机的区别1.3.Docker架构1.3.1.镜像和容器1.3.2.DockerHub1.3.3.Docker架构1.3.4.小结 1.4.安装Docker——未实践 2.Dock…

【莫比乌斯变换-03】python实现圆对圆的变换

文章目录 一、说明二、python实现复平面的莫比乌斯变换三、线的变换四、画笑脸 一、说明 我们在前面的文章中&#xff0c;叙述了莫比乌斯变换的复数分析&#xff0c;以及种种几何属性&#xff0c;本篇中叙述如何程序地实现&#xff1a;复平面上的圆在莫比乌斯变换下的图像是另…

【ONE·基础算法 || 回溯和剪枝(暴搜/深搜)】

总言 主要内容&#xff1a;编程题举例&#xff0c;熟悉理解回溯剪枝类题型&#xff08;如何画决策树&#xff0c;如何使用深搜进行递归&#xff0c;如何运用剪枝&#xff0c;如何在一维数组/二维数组中使用&#xff09;。 文章目录 总言1、回溯和剪枝2、全排列&#xff08;med…

【机器学习】BK- SDM与LCM的融合策略在文本到图像生成中的应用

突破边缘设备限制&#xff1a;BK-SDM与LCM的融合策略在文本到图像生成中的应用 一、引言二、稳定扩散算法的挑战与现状三、BK-SDM与LCM的融合策略利用高质量图像-文本对进行训练为LCM量身定制高级蒸馏过程 四、结论与展望 一、引言 随着人工智能技术的飞速发展&#xff0c;文本…

数据结构(十)----图

目录 一.图的概念 1.图的定义 2.图的类别 3.图的性质 4.几种特殊形态的图 二.图的存储结构 1.邻接矩阵&#xff08;顺序存储&#xff09; 2.邻接表&#xff08;顺序链式存储&#xff09; 3.十字链表 4.邻接多重表 四.图的遍历 1.广度优先遍历&#xff08;BFS&#…

Mysql复习笔记: 基础概念(待补充)

一. 基础概念 通用概念: 网络连接必须得分配给一个线程去进行处理&#xff0c;由一个线程来监听请求以及读取请求数据&#xff0c;比如从网络连接中读取和解析出来一条我们的系统发送过去的SQL语句 在数据库中&#xff0c;哪怕执行一条SQL语句&#xff0c;其实也可以是一个独立…

Python | Leetcode Python题解之第59题螺旋矩阵II

题目&#xff1a; 题解&#xff1a; class Solution:def generateMatrix(self, n: int) -> List[List[int]]:matrix [[0] * n for _ in range(n)]num 1left, right, top, bottom 0, n - 1, 0, n - 1while left < right and top < bottom:for col in range(left, r…

pandas学习笔记11

DataFrame结构 DataFrame 一个表格型的数据结构&#xff0c;既有行标签&#xff08;index&#xff09;&#xff0c;又有列标签&#xff08;columns&#xff09;&#xff0c;它也被称异构数据表&#xff0c;所谓异构&#xff0c;指的是表格中每列的数据类型可以不同&#xff0c;…

python中type,object,class 三者关系

type,object,class 三者关系 在python中&#xff0c;所有类的创建关系遵循&#xff1a; type -> int -> 1 type -> class -> obj例如&#xff1a; a 1 b "abc" print(type(1)) # <class int> 返回对象的类型 print(type(int)) …

力扣打卡第二天

206. 反转链表 class Solution { public:ListNode* reverseList(ListNode* head) {// //迭代法// ListNode *pre nullptr;// ListNode *curr head;// while(curr){// ListNode *next curr -> next;// curr -> next pre;// pre curr;// curr next;/…

Unity UGUI Image 点击事件忽略空白像素区域

我们会遇到图片不是方形的不规则图片。这个时候我们希望只有点击到图像内容本身才算点击&#xff0c;点击空白区域则不算点击。而UGUI对图片的处理是整个图片都会算作点击区域&#xff0c;这样不能满足于我们的使用需求了。 首先我们需要把图片本身的Read/Write 选项打开 然后…

质因数分解(cpp实现)--一种快速求得一个数有多少个因子的黑魔法

前言 最近机试没少吃不会质因数分解的亏&#xff0c;用传统的求得因子个数只能过一点点…(ex, 20%) 质因数分解后&#xff0c;可以将因子问题转化为 集合的组合问题&#xff0c;因此会很快&#xff0c;目测是 l o g n log n logn (n是该整数的值)。 传统解法 假设输入整数的…

基于OpenCv的图像特征点检测

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

从0开始linux(1)——文件操作

欢迎来到博主的专栏——从0开始linux 博主ID&#xff1a;代码小豪 博主使用的linux发行版是&#xff1a;CentOS 7.6 不同版本下的操作可能存在差异 文章目录 命令文件操作命令文件树和文件路径文件树绝对路径相对路径 文件属性tree指令删除文件复制文件 大家还记得在小学第一次…

C语言-链表实现贪吃蛇控制台游戏

使用C语言和链表实现贪吃蛇游戏 一、引言 贪吃蛇游戏是一个经典的游戏&#xff0c;它的玩法简单而富有挑战性。在这个博客中&#xff0c;我将分享如何使用C语言和链表数据结构来自主实现贪吃蛇游戏。我会详细介绍游戏的设计思路、编码过程、遇到的问题及解决方案&#xff0c;…

将要上市的自动驾驶新书《自动驾驶系统开发》中摘录各章片段 1

以下摘录一些章节片段&#xff1a; 1. 概论 自动驾驶系统的认知中有一些模糊的地方&#xff0c;比如自动驾驶系统如何定义的问题&#xff0c;自动驾驶的研发为什么会有那么多的子模块&#xff0c;怎么才算自动驾驶落地等等。本章想先给读者一个概括介绍&#xff0c;了解自动驾…

Rust 生命周期浅谈

1. 简述 Rust 中的每一个引用都有其 生命周期&#xff08;lifetime&#xff09;&#xff0c;也就是引用保持有效的作用域。大部分时候生命周期是隐含并可以推断的&#xff0c;正如大部分时候类型也是可以推断的一样。类似于当因为有多种可能类型的时候必须注明类型&#xff0c;…