苍穹外卖 数据可视化

news2024/11/14 5:36:38

        将营业额、用户数据、订单数据、商品销量top10数据全部使用Apache Echarts可视化,展现在前端,后端只需要按照需要的格式,为前端提供数据即可。

        ReportController

package com.sky.controller.admin;

import com.sky.result.Result;
import com.sky.service.ReportService;
import com.sky.vo.OrderReportVO;
import com.sky.vo.SalesTop10ReportVO;
import com.sky.vo.TurnoverReportVO;
import com.sky.vo.UserReportVO;
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.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;

@RestController
@RequestMapping("/admin/report")
@Slf4j
@Api(tags = "统计报表相关接口")
public class ReportController {
    // Apache Echarts
    // Apache Echarts是一个基于JavaScript的数据可视化图标库,提供直观、生动、可交互的数据可视化图表
    // 无论是什么形式的图形,其本质上是数据,ApacheEcharts就是对数据的可视化展示

    // 但是Apache Echarts是前端需要使用的东西,后端只需要按照和前端的约定,为其提供数据即可

    @Autowired
    private ReportService reportService;

    /**
     * 营业额数据统计
     *
     * @param begin
     * @param end
     * @return
     */
    // 查询一段时间内的营业额,前端给后端传递开始时间和结束时间,后端需要查询这段时间内的营业额,并封装到VO中、
    // 约定好VO中封装两个字符串,时间和对应的营业额,用","分隔
    @GetMapping("/turnoverStatistics")
    @ApiOperation("营业额数据统计")
    // 使用@DateTimeFormat限定前端传递的时间的格式
    public Result<TurnoverReportVO> turnoverStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd")
                                                       LocalDate begin,
                                                       @DateTimeFormat(pattern = "yyyy-MM-dd")
                                                       LocalDate end) {
        return Result.success(reportService.getTurnoverStatistics(begin, end));
    }

    /**
     * 用户数据统计
     *
     * @param begin
     * @param end
     * @return
     */
    // 用户数据统计分为两个部分:用户总量和新增用户;用户总量很好理解————
    // 而新增用户可以理解为:假如是今天是11.11日,
    // 那么在11.11日最小时间(00:00:00)————11.11日最大时间(23:59:59)创建的用户都是这一天的新用户
    @GetMapping("/userStatistics")
    @ApiOperation("用户数据统计")
    public Result<UserReportVO> userStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd")
                                               LocalDate begin,
                                               @DateTimeFormat(pattern = "yyyy-MM-dd")
                                               LocalDate end) {
        return Result.success(reportService.getUserStatistics(begin, end));
    }

    /**
     * 订单数据统计
     *
     * @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) {
        return Result.success(reportService.getOrdersStatistics(begin, end));
    }

    /**
     * top10畅销商品统计
     *
     * @param begin
     * @param end
     * @return
     */
    @GetMapping("top10")
    @ApiOperation("top10畅销商品统计")
    public Result<SalesTop10ReportVO> top10Statistics(@DateTimeFormat(pattern = "yyyy-MM-dd")
                                                      LocalDate begin,
                                                      @DateTimeFormat(pattern = "yyyy-MM-dd")
                                                      LocalDate end) {
        return Result.success(reportService.getSalesTop10Statistics(begin, end));
    }
}

        ReportService

package com.sky.service;

import com.sky.vo.OrderReportVO;
import com.sky.vo.SalesTop10ReportVO;
import com.sky.vo.TurnoverReportVO;
import com.sky.vo.UserReportVO;
import org.springframework.stereotype.Service;

import java.time.LocalDate;

@Service
public interface ReportService {

    /**
     * 根据时间区间统计营业额数据
     *
     * @param begin
     * @param end
     * @return
     */
    TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end);

    /**
     * 根据时间区间统计用户数量
     *
     * @param begin
     * @param end
     * @return
     */
    UserReportVO getUserStatistics(LocalDate begin, LocalDate end);

    /**
     * 根据时间区间统计订单数据
     *
     * @param begin
     * @param end
     * @return
     */
    OrderReportVO getOrdersStatistics(LocalDate begin, LocalDate end);

    /**
     * 根据时间区间统计畅销top10商品
     *
     * @param begin
     * @param end
     * @return
     */
    SalesTop10ReportVO getSalesTop10Statistics(LocalDate begin, LocalDate end);
}

        实现类

package com.sky.service.impl;

import com.sky.dto.GoodsSalesDTO;
import com.sky.entity.Orders;
import com.sky.mapper.OrderMapper;
import com.sky.mapper.UserMapper;
import com.sky.service.ReportService;
import com.sky.vo.OrderReportVO;
import com.sky.vo.SalesTop10ReportVO;
import com.sky.vo.TurnoverReportVO;
import com.sky.vo.UserReportVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ReportServiceImpl implements ReportService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private UserMapper userMapper;

    /**
     * 抽取方法:根据begin————end时间区间创建日期集合
     *
     * @param begin
     * @param end
     * @return
     */
    private List<LocalDate> createTimeList (LocalDate begin, LocalDate end) {
        // 创建一个集合,用于存储从begin-end时间内的所有日期,用于查找对应的营业额
        List<LocalDate> dateList = new ArrayList<>();
        // 先加入起始日期
        dateList.add(begin);
        // 若还没有加入到最后一个日期,就一直加入
        while (!begin.equals(end)) {
            // 每次都将begin日期后延1天,直到begin = end
            begin = begin.plusDays(1);
            // 加入集合
            dateList.add(begin);
        }
        return dateList;
    }

    /**
     * 根据时间区间查询营业额数据
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end) {
        // 通过抽取的方法创建时间区间的日期集合
        List<LocalDate> dateList = createTimeList(begin, end);
        // 创建营业额集合,用于存储每天的营业额
        List<Double> turnoverList = new ArrayList<>();
        // 遍历日期集合,按照每一天进行逻辑处理
        for (LocalDate date : dateList) {
            // 因为表中的订单的时间是LocalDateTime类型的,所以说要将日期集合中的LocalDate封装为LocalDateTime
            // 当天的营业额是大于当天的最小时间(00:00:00),小于当天的最大时间的(23:59:59),所以说可以将日期集合中的元素对应的
            // 那天的beginTime设置为当天最小时间;endTime设置为当天的最大时间
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
            // 用Map来封装查询的条件
            Map<Object, Object> map = new HashMap<>();
            // 要统计当天的营业额,只统计已经完成了的订单
            map.put("status", Orders.COMPLETED);
            // 封装查询的时间(时间为当天)
            map.put("begin", beginTime);
            map.put("end", endTime);
            Double turnover = orderMapper.sumAmount(map);
            // 判断当天是否有营业额,若没有营业额则turnover为空,但是这不符合前端展示的逻辑,需要对其检查,若没有营业额,那么营业额是0.0
            turnover = turnover == null ? 0.0 : turnover;
            // 将当前date对应的营业额加入turnover集合
            turnoverList.add(turnover);
        }
        // 处理数据返回
        // 使用StringUtils进行数据封装,封装为前端需要的格式返回。StringUtils是Apache的
        return TurnoverReportVO.builder()
                .dateList(StringUtils.join(dateList, ","))
                .turnoverList(StringUtils.join(turnoverList, ","))
                .build();
    }

    /**
     * 根据时间区间查询用户数据
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {
        // 通过抽取的方法创建时间区间的日期集合
        List<LocalDate> dateList = createTimeList(begin, end);
        // 新增用户集合
        List<Integer> newUserList = new ArrayList<>();
        // 总用户集合
        List<Integer> totalUserList = new ArrayList<>();
        for (LocalDate date : dateList) {
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
            // 总用户,只要在目标时间之前创建的用户都算是当前时间的总用户
            // 建议先查询总用户数,因为查询条件更加简单
            Integer totalUser = getUserCount(null, endTime);
            // 新增用户可以理解为:假如是今天是11.11日,
            // 那么在11.11日最小时间(00:00:00)————11.11日最大时间(23:59:59)创建的用户都是这一天的新用户
            Integer newUser = getUserCount(beginTime, endTime);
            // 进行前端逻辑处理,将null变为0
            totalUser = totalUser == null ? 0 : totalUser;
            newUser = newUser == null ? 0 : newUser;
            // 加入对应集合
            totalUserList.add(totalUser);
            newUserList.add(newUser);
        }
        // 封装数据返回
        return UserReportVO.builder()
                .dateList(StringUtils.join(dateList, ","))
                .newUserList(StringUtils.join(newUserList, ","))
                .totalUserList(StringUtils.join(totalUserList, ","))
                .build();
    }

    /**
     * 根据时间区间统计用户数量
     *
     * @param beginTime
     * @param endTime
     * @return
     */
    private Integer getUserCount(LocalDateTime beginTime, LocalDateTime endTime) {
        // 将开始时间和结束时间封装为map再在数据库中进行查询
        Map<Object, Object> map = new HashMap<>();
        map.put("begin", beginTime);
        map.put("end", endTime);
        return userMapper.countUsersByTime(map);
    }

    /**
     * 根据时间区间查询订单数据
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public OrderReportVO getOrdersStatistics(LocalDate begin, LocalDate end) {
        // 通过抽取的方法创建时间区间的日期集合
        List<LocalDate> dateList = createTimeList(begin, end);

        // 总订单集合
        List<Integer> totalOrdersList = new ArrayList<>();
        // 有效订单集合 valid   adj.有效的
        List<Integer> validOrdersList = new ArrayList<>();

        for (LocalDate date : dateList) {
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);

            // 查询总订单
            Integer totalOrders = getOrdersCount(beginTime, endTime, null);
            // 查询有效订单
            Integer validOrders = getOrdersCount(beginTime, endTime, Orders.COMPLETED);
            // 进行前端逻辑处理,将null变为0
            totalOrders = totalOrders == null ? 0 : totalOrders;
            // TODO 细心!细心!细心!不要再犯这种傻逼错误
            validOrders = validOrders == null ? 0 : validOrders;
            // 将其加入对应的集合
            totalOrdersList.add(totalOrders);
            validOrdersList.add(validOrders);
        }

        // 计算总订单数量
        Integer totalOrdersCount = 0;
        for (Integer order : totalOrdersList) {
            totalOrdersCount += order;
        }
        // 计算总有效订单数量
        Integer validOrdersCount = 0;
        for (Integer order : validOrdersList) {
            validOrdersCount += order;
        }
        // 计算完单率
        // 如果没有订单,完单率就是0
        Double orderCompletionRate = 0.0;
        if (totalOrdersCount != 0) {
            // 只有存在订单,才计算完单率
         orderCompletionRate = validOrdersCount.doubleValue() / totalOrdersCount;
        }

        return OrderReportVO.builder()
                .dateList(StringUtils.join(dateList, ","))
                .orderCountList(StringUtils.join(totalOrdersList, ","))
                .validOrderCountList(StringUtils.join(validOrdersList, ","))
                .totalOrderCount(totalOrdersCount)
                .validOrderCount(validOrdersCount)
                .orderCompletionRate(orderCompletionRate)
                .build();
    }

    /**
     * 根据时间区间和订单状态查询订单数据
     *
     * @param beginTime
     * @param endTime
     * @param status
     * @return
     */
    private Integer getOrdersCount(LocalDateTime beginTime, LocalDateTime endTime, Integer status) {
        // 将时间和状态封装为map进行查询
        // 在SQL中先根据status查询,若status不对,那么就可以不用比对后面的属性,提高效率
        Map<Object, Object> map = new HashMap<>();
        map.put("status", status);
        map.put("begin", beginTime);
        map.put("end", endTime);
        return orderMapper.statisticsOrders(map);
    }

    /**
     * 根据时间区间统计畅销top10商品
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public SalesTop10ReportVO getSalesTop10Statistics(LocalDate begin, LocalDate end) {
        // 因为不需要统计每一天的销量,只需要统计在这段时间之内的销量,所以说不需要遍历每一天的销量,可以直接使用begin和end
        LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);

        // 查询销量前10的商品,并封装在GoodsSalesDTO中(商品名和销量)
        List<GoodsSalesDTO> goodsSalesDTOList = orderMapper.getSalesTop10(beginTime, endTime);

        // 处理goodsSalesDTOList中数据
        String nameList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getName)
                .collect(Collectors.toList()), ",");

        String numberList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getNumber)
                .collect(Collectors.toList()), ",");

        // 封装成对应的VO返回
        return SalesTop10ReportVO.builder()
                .nameList(nameList)
                .numberList(numberList)
                .build();
    }


}

        Mapper相对简单,这里不过多赘述。 

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ubuntu18.04 配置安卓编译环境

目前有个项目&#xff0c;验收时有个要求是在linux中进行编译打包生成apk文件。我平时都是在windows环境android studio中进行打包的&#xff0c;花了半天时间研究了一下&#xff0c;记录如下&#xff1a; 安装安卓sdk cd /opt wget https://dl.google.com/android/reposito…

英伟达HOVER——用于人形机器人的多功能全身控制器:整合不同的控制模式且实现彼此之间的无缝切换

前言 前几天&#xff0c;一在长沙的朋友李总发我一个英伟达HOVER的视频(自从我今年年初以来持续不断的解读各大顶级实验室的最前沿paper、以及分享我司七月在具身领域的探索与落地后&#xff0c;影响力便越来越大了&#xff0c;不断加油 )&#xff0c;该视频说的有点玄乎&…

C语言中“type”的含义

在C语言中&#xff0c;“type”是指数据类型&#xff0c;它定义了变量可以存储的数据种类以及可以对这些数据执行的操作。C语言提供了一系列基本的数据类型&#xff0c;它们包括但不限于&#xff1a; 整型&#xff08;Integer Types&#xff09;&#xff1a; int&#xff1a;用…

ffmpeg 视频滤镜:屏蔽边框杂色- fillborders

滤镜描述 fillborders 官网链接 > FFmpeg Filters Documentation fillborders滤镜有几种方式帮你屏蔽边框的杂色、不好的图案。 滤镜使用 参数 left <int> ..FV.....T. set the left fill border (from 0 to INT_MAX) (default 0)right …

云计算答案

情境一习题练习 一、选择题 1、在虚拟机VMware软件中实现联网过程&#xff0c;图中箭头所指的网络连接方式与下列哪个相关&#xff08; C &#xff09;。 A.仅主机模式 B.桥接 C.NAT D.嫁接 2、请问下图这个虚拟化架构属于什么类型&#xff08; A …

[ Linux 命令基础 2 ] Linux 命令详解-系统管理命令

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

Golang | Leetcode Golang题解之第554题砖墙

题目&#xff1a; 题解&#xff1a; func leastBricks(wall [][]int) int {cnt : map[int]int{}for _, widths : range wall {sum : 0for _, width : range widths[:len(widths)-1] {sum widthcnt[sum]}}maxCnt : 0for _, c : range cnt {if c > maxCnt {maxCnt c}}retur…

QT中使用图表之QChart绘制动态折线图

使用QChart绘制一个随着时间的变化而动态显示的折线图 每一秒增加1个点&#xff0c;总共显示10s内的数据 显然x轴我们使用日期时间轴 同时使用1个定时器&#xff0c;每隔1秒往折线系列中添加1个数据进去 步骤如下&#xff1a; 1、创建图表视图 //1、创建图表视图 QChartV…

自然语言处理在客户服务中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 自然语言处理在客户服务中的应用 自然语言处理在客户服务中的应用 自然语言处理在客户服务中的应用 引言 自然语言处理概述 定义…

vs2022搭建opencv开发环境

1 下载OpenCV库 https://opencv.org/ 下载对应版本然后进行安装 将bin目录添加到系统环境变量opencv\build\x64\vc16\bin 复制该路径 打开高级设置添加环境变量 vs2022新建一个空项目 修改属性添加头文件路径和库路径 修改链接器&#xff0c;将OpenCV中lib库里的o…

【含文档】基于ssm+jsp的校园疫情管理系统(含源码+数据库+lw)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: apache tomcat 主要技术: Java,Spring,SpringMvc,mybatis,mysql,vue 2.视频演示地址 3.功能 系统定义了两个…

在Django中安装、配置、使用CKEditor5,并将CKEditor5录入的文章展现出来,实现一个简单博客网站的功能

在Django中可以使用CKEditor4和CKEditor5两个版本&#xff0c;分别对应软件包django-ckeditor和django-ckeditor-5。原来使用的是CKEditor4&#xff0c;python manager.py makemigrations时总是提示CKEditor4有安全风险&#xff0c;建议升级到CKEditor5。故卸载了CKEditor4&…

网络管理之---3种网络模式配置

目标&#xff1a; 了解几个概念&#xff1a; 1.什么是IP&#xff1f;什么是IP地址&#xff1f; 2.什么是桥接、NAT、仅主机模式 3.端口&#xff1f; 4.什么是网络接口命名规则 5.网络管理器 IP&#xff1a;指网络之间互联的协议&#xff0c;是TCP/IP 体系中的网络协议 I…

统信UOS开发环境支持Electron

全面支持Electron开发环境,同时还提供了丰富的开发工具和开发资源,进一步提升工作效率。 文章目录 一、环境部署1. Electron应用开发介绍2. Electron开发环境安装安装Node.js和npm安装electron环境配置二、代码示例Electron开发案例三、常见问题一、环境部署 1. Electron应用…

三级等保安全解决方案,实施方案,整改方案(Word,PPT等相关资料学习)

信息系统进行三级等保的主要原因在于保障信息安全&#xff0c;维护国家安全和公共利益。三级等保是我国根据相关法律法规制定的信息安全等级保护制度中的一部分&#xff0c;旨在确保信息系统的完整性、可用性和保密性。通过三级等保&#xff0c;信息系统可以得到一系列的安全保…

优选算法合集————双指针(专题一)

题目一&#xff1a;移动零 题目描述&#xff1a; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输…

python基于深度学习的音乐推荐方法研究系统

需求设计 一款好的音乐推荐系统其目的是为用户进行合理的音乐推荐&#xff0c;普通的用户在登录到系统之后&#xff0c;能够通过搜索的方式获取与输入内容相关的音乐推荐&#xff0c;而以管理员登录到系统之后&#xff0c;则可以进行徐昂管的数据管理等内容操作。此次的需求主…

Docker 镜像和容器的导入导出及常用命令

Docker 镜像和容器的导入导出 1.1 镜像的导入导出 1.1.1 镜像的保存 通过镜像ID保存 方式一&#xff1a; docker save image_id > image-save.tar例如&#xff1a; rootUbuntu:/usr/local/docker/nginx# docker imagesREPOSITORY TAG IMAGE ID …

Java集合 List——针对实习面试

目录 Java集合 ListJava List的三种主要实现是什么&#xff1f;它们各自的特点是什么&#xff1f;Java List和Array&#xff08;数组&#xff09;的区别&#xff1f;Java List和Set有什么区别&#xff1f;ArrayList和Vector有什么区别&#xff1f;什么是LinkedList&#xff1f;…

超级干货O2OA数据中心-查询配置开发

O2OA提供的数据管理中心&#xff0c;可以让用户通过配置的形式完成对数据的汇总&#xff0c;统计和数据分组展现&#xff0c;查询和搜索数据形成列表数据展现。也支持用户配置独立的数据表来适应特殊的业务的数据存储需求。本文主要介绍如何在O2OA中开发和配置自定义数据查询语…