【苍穹外卖】项目实战Day12

🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:苍穹外卖项目实战
🌠 首发时间:2024年5月22日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾

Apache ECharts

介绍

  • Apache ECharts 是一款基于 Javascript 的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。

  • 官网地址:https://echarts.apache.org/zh/index.html

    在这里插入图片描述

  • 效果展示:

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

入门案例

因为这部分是属于前端的内容,所以我们了解一下 Apache Echarts 官方提供的快速入门案例即可:https://echarts.apache.org/handbook/zh/get-started/

在这里插入图片描述

总结:使用 Echarts,重点在于研究当前图表所需的数据格式。通常是需要后端提供符合格式要求的动态数据,然后响应给前端来展示图表。

营业额统计

需求分析和设计

产品原型:
在这里插入图片描述
业务规则:

  • 营业额指订单状态为 “已完成” 的订单金额合计
  • 基于可视化报表的折线图展示营业额数据,X轴为日期,Y轴为营业额
  • 可以根据时间选择区间,展示每天的营业额数据

接口设计:
在这里插入图片描述

代码开发

根据接口定义设计对应的 VO
在这里插入图片描述

根据接口定义创建 ReportController,并创建 turnoverStatistics 方法:

import com.sky.result.Result;
import com.sky.service.ReportService;
import com.sky.vo.TurnoverReportVO;
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")
@Api(tags = "统计报表相关接口")
@Slf4j
public class ReportController {

    @Autowired
    private ReportService reportService;

    /**
     * 营业额数据统计
     *
     * @param begin
     * @param end
     * @return
     */
    @GetMapping("/turnoverStatistics")
    @ApiOperation("营业额数据统计")
    public Result<TurnoverReportVO> turnoverStatistics(
            @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
            @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {

        TurnoverReportVO turnoverReportVO = reportService.getTurnoverStatistics(begin, end);
        return Result.success(turnoverReportVO);
    }
}

创建 ReportService 接口,声明 getTurnoverStatistics 方法:

import com.sky.vo.TurnoverReportVO;

import java.time.LocalDate;

public interface ReportService {

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

创建 ReportServiceImpl 实现类,实现 getTurnoverStatistics 方法,同时创建私有方法 getDateList::

import com.sky.entity.Orders;
import com.sky.mapper.OrderMapper;
import com.sky.service.ReportService;
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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class ReportServiceImpl implements ReportService {

    @Autowired
    private OrderMapper orderMapper;

    /**
     * 根据时间区间统计营业额
     *
     * @param begin
     * @param end
     * @return
     */
    public TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end) {
        //获取时间列表
        List<LocalDate> dateList = getDateList(begin, end);

        //获取对应时间每一天的营业额
        List<Double> turnoverList = new ArrayList<>();
        dateList.forEach(date -> {
            LocalDateTime oneDayBeginTime = LocalDateTime.of(date, LocalTime.MIN);  //一天的开始时间, 即00:00
            LocalDateTime oneDayEndTime = LocalDateTime.of(date, LocalTime.MAX);    //一天的结束时间, 即23:59

            //查询营业额
            Map map = new HashMap();
            map.put("status", Orders.COMPLETED);    //订单状态需要为已完成
            map.put("begin", oneDayBeginTime);
            map.put("end", oneDayEndTime);

            Double turnover = orderMapper.sumByMap(map);
            turnover = turnover == null ? 0.0 : turnover;   //如果某一天没有订单, 需要将营业额设置为0, 不然会返回null
            turnoverList.add(turnover);
        });

        //数据封装
        return TurnoverReportVO.builder()
                .dateList(StringUtils.join(dateList, ","))
                .turnoverList(StringUtils.join(turnoverList, ","))
                .build();
    }

    /**
     * 根据起止时间计算出对应的时间列表
     *
     * @param begin
     * @param end
     * @return
     */
    private static List<LocalDate> getDateList(LocalDate begin, LocalDate end) {
        List<LocalDate> dateList = new ArrayList<>();
        dateList.add(begin);

        while (!begin.equals(end)) {
            begin = begin.plusDays(1);  //日期计算, 获取指定日期后1天的日期
            dateList.add(begin);
        }

        return dateList;
    }
}

OrderMapper 接口声明 sumByMap 方法:

/**
 * 根据动态条件统计营业额
 *
 * @param map
 * @return
 */
Double sumByMap(Map map);

OrderMapper.xml 文件中编写动态 SQL

<!--    根据动态条件统计营业额-->
<select id="sumByMap" resultType="java.lang.Double">
    select sum(amount)
    from orders
    <where>
        <if test="status != null">
            and status = #{status}
        </if>
        <if test="begin != null">
            and order_time &gt;= #{begin}
        </if>
        <if test="end != null">
            and order_time &lt;= #{end}
        </if>
    </where>
</select>

功能测试

在这里插入图片描述

用户统计

需求分析和设计

产品原型:
在这里插入图片描述

业务规则:

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

接口设计:
在这里插入图片描述

代码开发

根据用户统计接口的返回结果设计 VO

在这里插入图片描述
根据接口定义,在 ReportController 中创建 userStatistics 方法:

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

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

ReportService 接口中声明 getUserStatistics 方法:

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

ReportServiceImpl 实现类中实现 getUserStatistics 方法,同时创建私有方法 getUserCount

/**
 * 根据时间区间统计用户数量
 *
 * @param begin
 * @param end
 * @return
 */
public UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {
    //获取时间列表
    List<LocalDate> dateList = getDateList(begin, end);

    //统计对应时间每一天的新增用户数和总用户数
    List<Integer> newUserList = new ArrayList<>();      //新增用户数
    List<Integer> totalUserList = new ArrayList<>();    //总用户数

    dateList.forEach(date -> {
        LocalDateTime oneDayBeginTime = LocalDateTime.of(date, LocalTime.MIN);  //一天的开始时间, 即00:00
        LocalDateTime oneDayEndTime = LocalDateTime.of(date, LocalTime.MAX);    //一天的结束时间, 即23:59

        Integer newUser = getUserCount(oneDayBeginTime, oneDayEndTime); //当天新增用户数
        Integer totalUser = getUserCount(null, oneDayEndTime);          //截止当天总用户数

        newUserList.add(newUser);
        totalUserList.add(totalUser);
    });

    //数据封装返回
    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 = new HashMap();
    map.put("begin", beginTime);
    map.put("end", endTime);
    return userMapper.countByMap(map);
}

UserMapper 接口中声明 countByMap 方法:

/**
 * 根据动态条件统计用户数量
 *
 * @param map
 * @return
 */
Integer countByMap(Map map);

UserMapper.xml 文件中编写动态 SQL

<!--    根据动态条件统计用户数量-->
<select id="countByMap" resultType="java.lang.Integer">
    select count(id)
    from user
    <where>
        <if test="begin != null">
            and create_time &gt;= #{begin}
        </if>
        <if test="end != null">
            and create_time &lt;= #{end}
        </if>
    </where>
</select>

功能测试

在这里插入图片描述

订单统计

需求分析和设计

产品原型:
在这里插入图片描述
业务规则:

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

接口设计:
在这里插入图片描述

代码开发

根据订单统计接口的返回结果设计 VO
在这里插入图片描述
ReportController 中根据订单统计接口创建 orderStatistics 方法:

/**
 * 订单数据统计
 *
 * @param begin
 * @param end
 * @return
 */
@GetMapping("/ordersStatistics")
@ApiOperation("订单数据统计")
public Result<OrderReportVO> orderStatistics(
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {

    OrderReportVO orderReportVO = reportService.getOrderStatistics(begin, end);
    return Result.success(orderReportVO);
}

ReportService 接口中声明 getOrderStatistics 方法:

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

ReportServiceImpl 实现类中实现 getOrderStatistics 方法,同时创建私有方法 getOrderCount

/**
 * 根据时间区间统计订单数量
 *
 * @param begin
 * @param end
 * @return
 */
public OrderReportVO getOrderStatistics(LocalDate begin, LocalDate end) {
    //获取时间列表
    List<LocalDate> dateList = getDateList(begin, end);

    //每天订单总数集合
    List<Integer> orderCountList = new ArrayList<>();

    //每天有效订单数集合
    List<Integer> validOrderCountList = new ArrayList<>();

    dateList.forEach(date -> {
        LocalDateTime oneDayBeginTime = LocalDateTime.of(date, LocalTime.MIN);  //一天的开始时间, 即00:00
        LocalDateTime oneDayEndTime = LocalDateTime.of(date, LocalTime.MAX);    //一天的结束时间, 即23:59

        //查询每天的总订单数
        Integer orderCount = getOrderCount(oneDayBeginTime, oneDayEndTime, null);

        //查询每天的有效订单数
        Integer validOrderCount = getOrderCount(oneDayBeginTime, oneDayEndTime, Orders.COMPLETED);

        orderCountList.add(orderCount);
        validOrderCountList.add(validOrderCount);
    });

    //时间区间内的总订单数
    Integer totalOrderCount = orderCountList.stream().reduce(Integer::sum).get();

    //时间区间内的总有效订单数
    Integer totalValidOrderCount = validOrderCountList.stream().reduce(Integer::sum).get();

    //订单完成率
    Double orderCompletionRate = 0.0;
    if (totalOrderCount != 0) {
        orderCompletionRate = totalValidOrderCount.doubleValue() / totalOrderCount;
    }

    //数据封装返回
    return OrderReportVO.builder()
            .dateList(StringUtils.join(dateList, ","))
            .orderCountList(StringUtils.join(orderCountList, ","))
            .validOrderCountList(StringUtils.join(validOrderCountList, ","))
            .totalOrderCount(totalOrderCount)
            .validOrderCount(totalValidOrderCount)
            .orderCompletionRate(orderCompletionRate)
            .build();
}

/**
 * 根据时间区间统计指定状态的订单数量
 *
 * @param beginTime
 * @param endTime
 * @param status
 * @return
 */
private Integer getOrderCount(LocalDateTime beginTime, LocalDateTime endTime, Integer status) {
    Map map = new HashMap();
    map.put("status", status);
    map.put("begin", beginTime);
    map.put("end", endTime);
    return orderMapper.countByMap(map);
}

OrderMapper 接口中声明 countByMap 方法:

/**
 * 根据动态条件统计订单数量
 *
 * @param map
 * @return
 */
Integer countByMap(Map map);

OrderMapper.xml 文件中编写动态 SQL

<!--    根据动态条件统计订单数量-->
<select id="countByMap" resultType="java.lang.Integer">
    select count(id)
    from orders
    <where>
        <if test="status != null">
            and status = #{status}
        </if>
        <if test="begin != null">
            and order_time &gt;= #{begin}
        </if>
        <if test="end != null">
            and order_time &lt;= #{end}
        </if>
    </where>
</select>

功能测试

在这里插入图片描述

销量排名Top10

需求分析和设计

产品原型:
在这里插入图片描述
业务规则:

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

接口设计:
在这里插入图片描述

代码开发

根据销量排名接口的返回结果设计 VO

在这里插入图片描述
ReportController 中根据销量排名接口创建 top10Statistics 方法:

/**
 * 销量排名统计
 *
 * @param begin
 * @param end
 * @return
 */
@GetMapping("/top10")
@ApiOperation("销量排名统计")
public Result<SalesTop10ReportVO> top10Statistics(
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {

    SalesTop10ReportVO salesTop10ReportVO = reportService.getSalesTop10Statistics(begin, end);
    return Result.success(salesTop10ReportVO);
}

ReportService 接口中声明 getSalesTop10Statistics 方法:

/**
 * 查询指定时间区间内的销量排名top10
 *
 * @param begin
 * @param end
 * @return
 */
SalesTop10ReportVO getSalesTop10Statistics(LocalDate begin, LocalDate end);

ReportServiceImpl 实现类中实现 getSalesTop10Statistics 方法:

/**
 * 查询指定时间区间内的销量排名top10
 *
 * @param begin
 * @param end
 * @return
 */
public SalesTop10ReportVO getSalesTop10Statistics(LocalDate begin, LocalDate end) {
    LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
    LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);

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

    //top10菜品套餐名列表
    String nameList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList()), ",");

    //top10销量列表
    String numberList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList()), ",");

    //数据封装返回
    return SalesTop10ReportVO.builder()
            .nameList(nameList)
            .numberList(numberList)
            .build();
}

OrderMapper 接口中声明 getSalesTop10 方法:

/**
 * 查询商品销量排名
 *
 * @param beginTime
 * @param endTime
 * @return
 */
List<GoodsSalesDTO> getSalesTop10(LocalDateTime beginTime, LocalDateTime endTime);

OrderMapper.xml 文件中编写动态 SQL

<!--    查询商品销量排名-->
<select id="getSalesTop10" resultType="com.sky.dto.GoodsSalesDTO">
    select od.name name, sum(od.number) number
    from order_detail od,
    orders o
    where od.order_id = o.id
    and o.status = 5
    <if test = "beginTime != null">
        and o.order_time &gt;= #{beginTime}
    </if>
    <if test = "endTime != null">
        and o.order_time &lt;= #{endTime}
    </if>
    group by name
    order by number desc
    limit 0,10
</select>

功能测试

在这里插入图片描述

  • 34
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序喵正在路上

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值