Day11-苍穹外卖(数据统计篇)

news2025/5/20 3:58:20

前言:

今天写day11的内容,主要讲了四个统计接口的制作。看起来内容较多,其实代码逻辑都是相似的,这里我们过一遍。

今日所学:

  • Apache ECharts
  • 营业额统计
  • 用户统计
  • 订单统计
  • 销量排行统计

1. Apache ECharts

1.1 介绍

Apache ECharts 是一个功能强大、易于使用的数据可视化工具,适用于多种场景下的数据展示需求。通过简单的配置和灵活的定制,用户可以快速生成美观且交互性强的图表,提升数据分析和展示的效果。

总结:

Apache ECharts 是一个前端数据可视化库,适用于Web端的数据展示和分析

我们后端需要做的,就是提供符合格式要求的动态数据,然后响应给前端来展示图表。

1.2 使用流程:

1.下载echarts.js

这边黑马的资料中给我们准备好了

2.在前端的代码中引用

打开echartsDemo.html文件代码,可以看到已成功引入了echarts.js

运行这个html文件

2.营业额统计

需求分析

业务规则:

1.营业额指的是订单状态为完成的订单金额合计

2.X轴表示日期,Y轴表示营业额

3. 根据时间选择区间,展示每天的营业额数据

请求参数是开始时间和结束时间,这里注意传给前端的dataList和turnoverList都要是String类型的数据

代码展示

Controller层:

这里注意begin,end是LocalData变量,传入的时候要指定时间格式

service层:

这里具体的代码逻辑是:

1.先是定义两个arrayList容器用于后续储存每日的日期还有相应的营业额

2.while循环储存从开始(begin)到结束(end)的每一天日期数据

3.将每一天的日期数据(LocalData变量)转换成(LocalDataTime变量),分别取极小值(00:00:00)和极大值(23:59:5999)

4.调用Ordermapper,执行如下SQL语句:

select sum(amount) from Orders where order_time > beginTime(一天的极小值) and order_time < endTime(一天的极大值) and status = 5(已完成)

注意下这里传给mapper的数据是由map储存的

/**
 * 指定时间内的营业额
 * @param begin
 * @param end
 * @return
 */
@Override
public TurnoverReportVO turnoverStatistics(LocalDate begin, LocalDate end) {

    // 用于储存begin,end范围内每天的日期
    List<LocalDate> dateList = new ArrayList<>();

    List<Double> turnoverList = new ArrayList<>();

    dateList.add(begin);

    while(!begin.equals(end)) {
        // 指定日期的后一天
        begin = begin.plusDays(1);
        dateList.add(begin);
    }
    for (LocalDate date : dateList) {
        LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
        Map map = new HashMap();
        map.put("beginTime", beginTime);
        map.put("endTime", endTime);
        map.put("status", Orders.COMPLETED);
        Double amount = orderMapper.getSumBymap(map);
        amount = amount == null ? 0.0 : amount;
        turnoverList.add(amount);
    }


    return TurnoverReportVO
            .builder()
            .turnoverList(StringUtils.join(turnoverList, ","))
            .dateList(StringUtils.join(dateList, ","))
            .build();
}

mapper层

<select id="getSumBymap" resultType="java.lang.Double">
    select sum(amount) from orders
    <where>
        <if test="begin != null">
            and order_time &gt; #{beginTime}
        </if>
        <if test="end != null">
            and order_time &lt; #{endTime}
        </if>
        <if test="status != null">
            and status = #{status}
        </if>

    </where>
</select>

运行展示:

问题

这里我遇到的问题是几个:

1.路径写错了(写成另一个接口的了),导致前端图表一直展示不出来,改了挺久的

2.StringUtils包导错了

这里要注意导入的是我标注的那个包,而不是我最上面注释掉的那个

3.这里要注意要写的是Double,而不是double.Double是包装类,是一种类,存在null值,而double是一种基本数据类型,是不存在null值的

3.用户统计

需求分析

业务规则:

  • 基于可视化报表的折线图展示用户数据,X轴为日期,Y轴为用户数
  • 根据时间选择区间,展示每天的用户总量和新增用户量数据

这里的传入参数和返回数据跟营业额统计格式上基本是一致的:传入开始日期和结束日期,

返回数据也都是要String类型

代码展示:

Controller层

@ApiOperation("用户统计")
@GetMapping("/userStatistics")
public Result<UserReportVO> userStatistics(
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){

    UserReportVO userReportVO = reportService.userStatistics(begin, end);
    return Result.success(userReportVO);
}

这里同样注意传入的LocalData类型的数据要用@DataTimeFormat规定时间格式

Service层

具体逻辑:

1.定义三个ArrayList格式的数据,分别用于储存每一天的时间,每一天的用户总量,每一天的用户增量

2.while循环得到开始日期到结束日期之间每天的日期

3..将每一天的日期数据(LocalData变量)转换成(LocalDataTime变量),分别取极小值(00:00:00)和极大值(23:59:5999)

4.调用mapper,执行相应的语句,得到每一天的用户总量和用户新增量

这里注意用户总量只要查询endTime就行了,用户新增量再查询beginTime和endTime之间创建的用户,具体SQL执行逻辑:

select count(id) from user where create_time < endTime and create_time > beginTime(求用户新增量的时间再传入相应参数)

mapper参数还是由map传入

/**
 * 用户统计
 * @param begin
 * @param end
 * @return
 */
@Override
public UserReportVO userStatistics(LocalDate begin, LocalDate end) {
    List<LocalDate> dateList = new ArrayList<>();

    dateList.add(begin);
    while(!begin.equals(end)) {
        begin = begin.plusDays(1);
        dateList.add(begin);
    }

    List<Integer> totalUserList = new ArrayList<>();

    List<Integer> newUserList = new ArrayList<>();

    for(LocalDate date : dateList) {
        LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
        Map map = new HashMap();
        map.put("endTime", endTime);
        Integer userNum = userMapper.countByMap(map);
        map.put("beginTime", beginTime);
        Integer newUserNum = userMapper.countByMap(map);
        userNum = userNum == null ? 0 : userNum;
        totalUserList.add(userNum);
        newUserList.add(newUserNum);
    }


    return UserReportVO.builder()
            .dateList(StringUtils.join(dateList,","))
            .totalUserList(StringUtils.join(totalUserList, ","))
            .newUserList(StringUtils.join(newUserList, ","))
            .build();
}

mapper层

<select id="countByMap" resultType="java.lang.Integer">
    select count(id) from user
    <where>
        <if test="beginTime != null">
            and create_time &gt; #{beginTime}
        </if>
        <if test="beginTime != null">
            and create_time &lt; #{endTime}
        </if>
    </where>
</select>

运行展示:

4.订单统计

需求分析

业务规则:

  • 有效订单指状态为 “已完成” 的订单
  • 基于可视化报表的折线图展示订单数据,X轴为日期,Y轴为订单数量
  • 根据时间选择区间,展示每天的订单总数和有效订单数
  • 展示所选时间区间内的有效订单数、总订单数、订单完成率,订单完成率 = 有效订单数 / 总订单数 * 100%

跟上两个接口传入参数返回数据格式上是一致的,请求参数是开始时间和结束时间,传给前端的orderCountList和validOrderCountList都要是String类型的数据

代码展示

Controller层

/**
 * 订单统计接口
 * @param begin
 * @param end
 * @return
 */
@ApiOperation("订单统计接口")
@GetMapping("/ordersStatistics")
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.orderStatistics(begin, end);
    return Result.success(orderReportVO);
}

这里注意begin,end是LocalData变量,传入的时候要指定时间格式

service层

具体执行逻辑:

1.先是定义三个arrayList容器,分别用于储存每日的日期,每日的订单数,以及有效订单数

2.再定义两个total(Integer),用来统计订单总数和有效订单总数

3.while循环储存从开始(begin)到结束(end)的每一天日期数据

4.将每一天的日期数据(LocalData变量)转换成(LocalDataTime变量),分别取极小值(00:00:00)和极大值(23:59:5999)

4.调用Ordermapper,执行如下SQL语句:

select count(id) from Orders where order_time > beginTime(一天的极小值) and order_time < endTime(一天的极大值) and status = 5(已完成->当统计有效订单再传入相应参数)

每统计一天的,加入相应的容器和总数中

注意下这里传给mapper的数据是由map储存的

/**
 * 订单统计
 * @param begin
 * @param end
 * @return
 */
@Override
public OrderReportVO orderStatistics(LocalDate begin, LocalDate end) {
    List<LocalDate> dateList = new ArrayList<>();
    dateList.add(begin);
    while(!begin.equals(end)) {
        begin = begin.plusDays(1);
        dateList.add(begin);
    }

    List<Integer> orderCountList = new ArrayList<>();
    Integer totalOrderCount = 0;
    List<Integer> vaildOrderCountList = new ArrayList<>();
    Integer VaildOrderCount = 0;

    for(LocalDate date : dateList) {
        LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
        Map map = new HashMap();
        map.put("beginTime", beginTime);
        map.put("endTime", endTime);
        Integer orderCount = orderMapper.CountByMap(map);
        orderCountList.add(orderCount);
        totalOrderCount += orderCount;
        map.put("status",Orders.COMPLETED);
        Integer vaildCount = orderMapper.CountByMap(map);
        vaildOrderCountList.add(vaildCount);
        VaildOrderCount += vaildCount;
    }

    return OrderReportVO.builder()
            .orderCountList(StringUtils.join(orderCountList, ","))
            .totalOrderCount(totalOrderCount)
            .validOrderCount(VaildOrderCount)
            .validOrderCountList(StringUtils.join(vaildOrderCountList, ","))
            .dateList(StringUtils.join(dateList,","))
            .build();
}

mapper层

<select id="CountByMap" resultType="java.lang.Integer">
    select count(id) from orders
    <where>
        <if test="beginTime != null">
            and order_time &gt; #{beginTime}
        </if>
        <if test="endTime != null">
            and order_time &lt; #{endTime}
        </if>
        <if test="status != null">
            and status = #{status}
        </if>
    </where>
</select>

运行展示:

5. 销量排行统计

需求分析:

业务规则:

  • 根据时间选择区间,展示销量前10的商品(包括菜品和套餐)
  • 基于可视化报表的柱状图降序展示商品销量
  • 此处的销量为商品销售的份数

请求参数是开始时间和结束时间,这里注意传给前端的nameList和numberList都要是String类型的数据

代码展示:

controller层

/**
 * 销量排行展示
 * @param begin
 * @param end
 * @return
 */
@ApiOperation("销量排名展示")
@GetMapping("/top10")
public Result<SalesTop10ReportVO> salesTop10Statistics(
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end
){
    log.info("销量排名展示{},{}", begin,end);
    SalesTop10ReportVO salesTop10ReportVO = reportService.top10(begin, end);
    return Result.success(salesTop10ReportVO);
}

这里注意begin,end是LocalData变量,传入的时候要指定时间格式

service层:

具体实现逻辑:

1.这里我们项目有一个专门封装了菜品名称和菜品销量的dto(GoodsSalesDTO),所以我们不用专门再创建容器去记录数据

2.因为销量统计是统计我们这一整段时间(从begin到end)的菜品销量,所以不用在遍历每一天的订单数据

3.将LocalDate变量数据转换成LocalDataTime,求得这一段时间的极小值和极大值

4.调用ordermapper,因为涉及到订单(orders)的下单时间和订单详情(order_detail)的菜品数据,所以这里我采用join进行连接查询,具体SQL语句为:

select od.name as name , coount(od.name) as number from orders o join order_detail od

on od.order_id = o.id

where status = 5(查看已下单的数据) and order_time > beginTime and order_time < endTime

group by name  order by name desc limit 0, 10(查询销量前十的菜品)

5.得到相应的数据后使用stream流进行遍历,最后用StringUtils.join连接起来

/**
 * 销量统计
 * @param begin
 * @param end
 * @return
 */
@Override
public SalesTop10ReportVO top10(LocalDate begin, LocalDate end) {
    LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
    LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);

    List<GoodsSalesDTO> goodsSalesDTOList = orderMapper.getByNameNum(beginTime, endTime);

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

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


    return SalesTop10ReportVO.builder()
            .nameList(nameList)
            .numberList(numList)
            .build();
}

mapper层

<select id="getByNameNum" resultType="com.sky.dto.GoodsSalesDTO">
    select od.name as name, sum(od.name) as number from orders o join order_detail od
    on  o.id = od.order_id
    and o.status = 5
    <where>
        <if test="beginTime != null">
            and order_time &gt; #{beginTime}
        </if>
        <if test="endTime != null">
            and order_time &lt; #{endTime}
        </if>
    </where>
    group by name
    order by number desc
    limit 0, 10
</select>

运行展示:

最后:

今天的分享就到这里。如果我的内容对你有帮助,请点赞评论收藏。创作不易,大家的支持就是我坚持下去的动力!(๑`・ᴗ・´๑)

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

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

相关文章

Tomcat简述介绍

文章目录 Web服务器Tomcat的作用Tomcat分析目录结构 Web服务器 Web服务器的作用是接收客户端的请求&#xff0c;给客户端作出响应。 知名Java Web服务器 Tomcat&#xff08;Apache&#xff09;&#xff1a;用来开发学习使用&#xff1b;免费&#xff0c;开源JBoss&#xff0…

《从零开始:Spring Cloud Eureka 配置与服务注册全流程》​

关于Eureka的学习&#xff0c;主要学习如何搭建Eureka&#xff0c;将order-service和product-service都注册到Eureka。 1.为什么使用Eureka? 我在实现一个查询订单功能时&#xff0c;希望可以根据订单中productId去获取对应商品的详细信息&#xff0c;但是产品服务和订单服…

FPGA学习知识(汇总)

1. wire与reg理解&#xff0c;阻塞与非阻塞 2. 时序取值&#xff0c;时钟触发沿向左看 3. ip核/setup debug 添加 ila 一、ila使用小技巧 二、同步复位、异步复位和异步复位同步释放 设计复位设计&#xff0c;尽量使用 异步复位同步释放&#xff1b;尽管该方法仍然对毛刺敏感…

Redisson分布式锁-锁的可重入、可重试、WatchDog超时续约、multLock联锁(一文全讲透,超详细!!!)

本文涉及到使用Redis实现基础分布式锁以及Lua脚本的内容&#xff0c;如有需要可以先参考博主的上一篇文章&#xff1a;Redis实现-优惠卷秒杀(基础版本) 一、功能介绍 (1)前面分布式锁存在的问题 在JDK当中就存在一种可重入锁ReentrantLock&#xff0c;可重入指的是在同一线…

语言模型:AM-Thinking-v1 能和大参数语言模型媲美的 32B 单卡推理模型

介绍 a-m-team 是北科 &#xff08;Ke.com&#xff09; 的一个内部团队&#xff0c;致力于探索 AGI 技术。这是一个专注于增强推理能力的 32B 密集语言模型。 a-m-team / AM-Thinking-v1 是其旗下的一个语言模型&#xff0c;采用低成本的方式能实现和大参数模型媲美。 DeepSe…

ChatGPT:OpenAI Codex—一款基于云的软件工程 AI 代理,赋能 ChatGPT,革新软件开发模式

ChatGPT&#xff1a;OpenAI Codex—一款基于云的软件工程 AI 代理&#xff0c;赋能 ChatGPT&#xff0c;革新软件开发模式 导读&#xff1a;2025年5月16日&#xff0c;OpenAI 发布了 Codex&#xff0c;一个基于云的软件工程 AI 代理&#xff0c;它集成在 ChatGPT 中&#xff0c…

智能视觉检测技术:制造业质量管控的“隐形守护者”

在工业4.0浪潮的推动下&#xff0c;制造业正经历一场以智能化为核心的变革。传统人工质检模式因效率低、误差率高、成本高昂等问题&#xff0c;逐渐难以满足现代生产对高精度、高速度的需求。智能视觉检测技术作为人工智能与机器视觉融合的产物&#xff0c;正成为制造业质量管控…

利用html制作简历网页和求职信息网页

前言 大家好&#xff0c;我是maybe。今天下午初步学习了html的基础知识。做了两个小网页&#xff0c;一个网页是简历网页&#xff0c;一个网页是求职信息填写网页。跟大家分享一波~ 说明:我不打算上传图片。所以如果有朋友按照我的代码运行网页&#xff0c;会出现一个没有图片…

卷积神经网络进阶:转置卷积与棋盘效应详解

【内容摘要】 本文深入解析卷积神经网络中的转置卷积&#xff08;反卷积&#xff09;技术&#xff0c;重点阐述标准卷积与转置卷积的计算过程、转置卷积的上采样作用&#xff0c;以及其常见问题——棋盘效应的产生原因与解决方法&#xff0c;为图像分割、超分辨率等任务提供理论…

2025年5月13日第一轮

1.百词斩 2.安全状态和死锁 3.银行家算法和状态图 4.Vue运行 5.英语听力 6.词汇 7.英语 长篇:数学竞赛 8.数学 间断点类型和数量 The rapid development of artificial intelligence has led to widerspareasd concreasns about job displacemant.As AI technology conti…

小结:Android系统架构

https://developer.android.com/topic/architecture?hlzh-cn Android系统的架构&#xff0c;分为四个主要层次&#xff1a;应用程序层、应用框架层、库和运行时层以及Linux内核层。&#xff1a; 1. 应用程序层&#xff08;Applications&#xff09; 功能&#xff1a;这一层包…

基于C#的MQTT通信实战:从EMQX搭建到发布订阅全解析

MQTT(Message Queueing Telemetry Transport) 消息队列遥测传输&#xff0c;在物联网领域应用的很广泛&#xff0c;它是基于Publish/Subscribe模式&#xff0c;具有简单易用&#xff0c;支持QoS&#xff0c;传输效率高的特点。 它被设计用于低带宽&#xff0c;不稳定或高延迟的…

ISP中拖影问题的处理

有时候会出现如下的阴影问题该如何处理呢&#xff1f;本文将提供几个思路。 1、降低曝光时间 如果曝光时间过大&#xff0c;会统计整个曝光时间内的图像信息&#xff0c;就会导致拖影的产生&#xff0c;这个时候可以考虑降低一下曝光时间。 2、时域降噪过大 只要明白时域降噪…

SQLMesh 模型管理指南:从创建到验证的全流程解析

本文全面介绍SQLMesh这一现代化数据转换工具的核心功能&#xff0c;重点讲解模型创建、编辑、验证和删除的全生命周期管理方法。通过具体示例和最佳实践&#xff0c;帮助数据工程师掌握SQLMesh的高效工作流程&#xff0c;包括增量模型配置、变更影响评估、安全回滚机制等关键操…

HarmonyOS AVPlayer 音频播放器

鸿蒙文档中心&#xff1a;使用AVPlayer播放视频(ArkTS)文档中心https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/video-playback 这张图描述的是 HarmonyOS AVPlayer 音频播放器的状态流转过程&#xff0c;展示了 AVPlayer 在不同状态之间的切换条件和关键操作…

⭐️白嫖的阿里云认证⭐️ 第二弹【课时1:提示词(Prompt)技巧】for 「大模型Clouder认证:利用大模型提升内容生产能力」

「大模型Clouder认证:利用大模型提升内容生产能力」这个认证目前在阿里云认证中心还是免费的,简单几步就可以申请考试,有两次的免费考试机会。而且,这个课程中的内容对于所有普通用户来说都非常实用,课程整体长度也就3节课,非常快速就能学完。心动不如行动,赶紧开始吧!…

Filament引擎(一) ——渲染框架设计

filament是谷歌开源的一个基于物理渲染(PBR)的轻量级、高性能的实时渲染框架&#xff0c;其框架架构设计并不复杂&#xff0c;后端RHI的设计也比较简单。重点其实在于项目中材质、光照模型背后的方程式和理论&#xff0c;以及对它们的实现。相关的信息&#xff0c;可以参考官方…

c++从入门到精通(六)--特殊工具与技术-完结篇

文章目录 特殊工具与技术-完结篇控制内存分配运行时类型识别成员指针嵌套类局部类固有的不可抑制特性位域volatile限定符链接指示 extern "C" 特殊工具与技术-完结篇 控制内存分配 重载new和delete&#xff1a; ​ 如果应用程序希望控制内存分配的过程&#xff0c;…

MCP实战:在扣子空间用扣子工作流MCP,一句话生成儿童故事rap视频

扣子最近迎来重要更新&#xff0c;支持将扣子工作流一键发布成MCP&#xff0c;在扣子空间里使用。 这个功能非常有用&#xff0c;因为我有很多业务工作流是在扣子平台上做的&#xff0c;两者打通之后&#xff0c;就可以在扣子空间里直接通过对话方式调用扣子工作流了&#xff0…

SpringBoot基础项目搭建

资料链接&#xff1a;https://download.csdn.net/download/ly1h1/90855288?spm1001.2014.3001.5501 1.准备工作 1.1 安装IntelliJ IDEA 2023.3.4 (Ultimate Edition) 1.2 采用apache-maven-3.6.3 1.2.1 maven配置文件设置 1.2.2 IDEA配置maven 1.3 JDK采用17版本 2.手动创建…