苍穹外卖08——(涉及接收日期格式数据、ApachePOI导出报表、sql获取top10菜品数据)

营业额统计

service层

  • 在需要处理空值、与数据库交互或使用集合时,Integer 、Double是更好的选择。
java 复制代码
// 导入string工具类  
import org.apache.commons.lang.StringUtils;  


@Service // 标记该类为Spring的服务组件  
@Slf4j // 引入日志功能  
public class ReportServiceImpl implements ReportService {  
    
    @Override  
    public TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end) {  
        // 该方法用于获取从begin到end之间的时间区间  
        List<LocalDate> datelist = new ArrayList<>(); // 创建一个列表以存储日期  
        datelist.add(begin); // 将开始日期添加到日期列表中  

        // 循环直到开始日期等于结束日期  
        while (!begin.equals(end)) {  // 注意,最后一天end也存到集合里了 
            // 自动增加一天直到符合的日期  
            begin = begin.plusDays(1); // 将开始日期增加一天  
            datelist.add(begin); // 将新的开始日期添加到日期列表中  
        }  

        // 存储每个日期的营业额  
        List<Double> turnoverList = new ArrayList<>(); // 创建一个列表以存储营业额  
        for (LocalDate date : datelist) { // 遍历每个日期,将LocalDate 转为LocalDateTime 
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN); // 获取当天的最小时间(00:00)   
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX); // 获取当天的最大时间(23:59:59.999999999)  

            // 创建一个Map以存储查询条件  
            Map<String, Object> map = new HashMap<>();  
            map.put("begin", beginTime); // 将开始时间放入Map中  
            map.put("end", endTime); // 将结束时间放入Map中  
            map.put("status", Orders.COMPLETED); // 将订单状态放入Map中  

            // 调用数据访问层的方法,获取指定时间段内的营业额  
            Double turnover = orderMapper.sumByMap(map); // 获取营业额  
            turnover = turnover == null ? 0.0 : turnover; // 处理null
            turnoverList.add(turnover); // 将营业额添加到营业额列表中  
        }  

        // 构建并返回TurnoverReportVO对象  
        return TurnoverReportVO  
                .builder() // 创建构建器  
                .dateList(StringUtils.join(datelist, ",")) // 将日期列表转换为以逗号分隔的字符串  
                .turnoverList(StringUtils.join(turnoverList, ",")) // 将营业额列表转换为以逗号分隔的字符串  
                .build(); // 构建对象并返回  
    }  
}

mapper层

XML 复制代码
<select id="sumByMap" resultType="java.lang.Double">
        select sum(amount) from orders
        <where>
            <if test="begin != null">
                and order_time &gt; #{begin}
            </if>
            <if test="end != null">
                and order_time &lt; #{end}
            </if>
            <if test="status != null">
                and status = #{status}
            </if>
        </where>
    </select>

用户统计

controller层

java 复制代码
@GetMapping("/userStatistics")
    @ApiOperation("用户统计")
    public Result<UserReportVO> userStatistics(
            @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
            @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {
        log.info("用户数据统计:{},{}", begin, end);
        return Result.success(reportService.getUserStatistics(begin, end));
    }

service层

java 复制代码
@Override
public UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {
    // 收集begin和end之间的每天的日期
    // 创建一个列表来存储日期
    List<LocalDate> dateList = new ArrayList<>();
    // 将开始日期添加到列表中
    dateList.add(begin);

    // 使用循环将开始日期逐天增加,直到等于结束日期
    while (!begin.equals(end)) {
        // 将开始日期增加一天
        begin = begin.plusDays(1);
        // 将增加后的日期添加到列表中
        dateList.add(begin);
    }

    // 保存每天的新用户数
    // 创建一个列表来存储每天的新用户数
    List<Integer> newUserList = new ArrayList<>();

    // 保存每天的总用户数
    // 创建一个列表来存储每天的总用户数
    List<Integer> totalUserList = new ArrayList<>();

    // 遍历日期列表
    for (LocalDate date : dateList) {
        // 获取当前日期的开始时间(00:00:00)
        LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
        // 获取当前日期的结束时间(23:59:59)
        LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);

        // 创建一个Map来存储查询条件
        Map map = new HashMap();
        // 将结束时间作为查询条件添加到Map中
        map.put("end", endTime);

        // 查询当前日期的总用户数
        Integer totalUser = userMapper.countByMap(map);

        // 将开始时间也作为查询条件添加到Map中
        map.put("begin", beginTime);
        // 查询当前日期的新用户数
        Integer newUser = userMapper.countByMap(map);

        // 将查询到的总用户数添加到列表中
        totalUserList.add(totalUser);
        // 将查询到的新用户数添加到列表中
        newUserList.add(newUser);
    }

    // 返回结果  
    // 使用UserReportVO的构建器创建一个UserReportVO对象
    return UserReportVO.builder()
            // 将日期列表转换为逗号分隔的字符串,并设置到UserReportVO对象中
            .dateList(StringUtils.join(dateList,","))
            // 将总用户数列表转换为逗号分隔的字符串,并设置到UserReportVO对象中
            .totalUserList(StringUtils.join(totalUserList,","))
            // 将新用户数列表转换为逗号分隔的字符串,并设置到UserReportVO对象中
            .newUserList(StringUtils.join(newUserList,","))
            // 构建并返回UserReportVO对象
            .build();
}

Mapper层(动态sql实现,可以使得service层调用同一个方法)

XML 复制代码
<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>

销量排名统计top10

service层

java 复制代码
@Override
    public SalesTop10ReportVO getSalesTop10Statistics(LocalDate begin, LocalDate end) {
        // 将LocalDate转换为LocalDateTime,分别设置为当天的开始和结束时间
        LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);

        // 调用orderMapper.getSalesTop10方法获取销量排名前10的商品信息
        List<GoodsSalesDTO> salesTop10 = orderMapper.getSalesTop10(beginTime, endTime);

        // 使用流操作获取所有商品的名称,并收集到列表中
        List<String> names = salesTop10.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList());
        // 将商品名称列表转换为逗号分隔的字符串
        String nameList = StringUtils.join(names, ","); // 注意:separator参数应为",",而不是separator:","

        // 使用流操作获取所有商品的销量,并收集到列表中
        List<Integer> numbers = salesTop10.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList());
        // 将销量列表转换为逗号分隔的字符串
        String numberList = StringUtils.join(numbers, ","); // 同上,应为","

        // 使用构建器模式封装返回结果
        return SalesTop10ReportVO.builder()
                .nameList(nameList) // 设置商品名称列表
                .numberList(numberList) // 设置销量列表
                .build(); // 构建并返回SalesTop10ReportVO对象
    }

mapper层(关于获取top10排名的sql)

XML 复制代码
<select id="getSalesTop10" resultType="com.sky.dto.GoodsSalesDTO">
        select od.name, sum(od.number) number
        from order_detail od, orders o
        where od.order_id = o.id and o.status = 5
        <if test="begin != null">
            and o.order_time &gt; #{begin}
        </if>
        <if test="end != null">
            and o.order_time &lt; #{end}
        </if>
        group by od.name
        order by number desc
        limit 0, 10
    </select>

ApachePOI

介绍

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

一般情况下,POI 都是用于操作 Excel 文件。

坐标导入

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>

案例引入:读取文件和写入文件

实现步骤:

  1. 设计Excel模板文件(不要手动编程去实现)
  2. 查询近30天的运营数据
  3. 将查询到的运营数据写入模板文件
  4. 通过输出来将Excel文件下载到客户端浏览器

代码开发:

java 复制代码
    @GetMapping("/export")
    @ApiOperation("导出运营数据结果")
 // 传入HttpServletResponse ,是负责设置响应携带的文件
    public void export(HttpServletResponse response){
        reportService.exportBusinessData(response);
    }
java 复制代码
/**  
 * 导出运营数据报表  
 * @param response HTTP响应对象,用于输出Excel文件  
 */  
public void exportBusinessData(HttpServletResponse response) {  
    // 1. 查询数据库,获取营业数据---查询最近30天的运营数据  
    LocalDate dateBegin = LocalDate.now().minusDays(30); // 获取30天前的日期  
    LocalDate dateEnd = LocalDate.now().minusDays(1); // 获取昨天的日期  

    // 查询概览数据  
    BusinessDataVO businessDataVO = workspaceService.getBusinessData(  
        LocalDateTime.of(dateBegin, LocalTime.MIN),   
        LocalDateTime.of(dateEnd, LocalTime.MAX)  
    );  

    // 2. 通过POI将数据写入到Excel文件中  
    InputStream in = this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx"); // 加载Excel模板  
 // 加载资源文件,并以输入流的形式返回
    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++) { // 遍历最近30天  
            LocalDate date = dateBegin.plusDays(i); // 获取当前遍历的日期  
            // 查询某一天的营业数据  
            BusinessDataVO businessData = workspaceService.getBusinessData(  
                LocalDateTime.of(date, LocalTime.MIN),   
                LocalDateTime.of(date, LocalTime.MAX)  
            );  

            // 获得某一行  
            row = sheet.getRow(7 + i); // 从第8行开始填充数据  
            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); // 将Excel内容写入输出流  

        // 关闭资源  
        out.close(); // 关闭输出流  
        excel.close(); // 关闭Excel文件  
    } catch (IOException e) {  
        e.printStackTrace(); // 捕获并打印异常  
    }  
}

效果展示

相关推荐
devlei1 小时前
从源码泄露看AI Agent未来:深度对比Claude Code原生实现与OpenClaw开源方案
android·前端·后端
pshdhx_albert2 小时前
AI agent实现打字机效果
java·http·ai编程
沉鱼.442 小时前
第十二届题目
java·前端·算法
努力的小郑3 小时前
Canal 不难,难的是用好:从接入到治理
后端·mysql·性能优化
赫瑞3 小时前
数据结构中的排列组合 —— Java实现
java·开发语言·数据结构
Victor3564 小时前
MongoDB(87)如何使用GridFS?
后端
Victor3564 小时前
MongoDB(88)如何进行数据迁移?
后端
小红的布丁4 小时前
单线程 Redis 的高性能之道
redis·后端
GetcharZp4 小时前
Go 语言只能写后端?这款 2D 游戏引擎刷新你的认知!
后端
周末也要写八哥4 小时前
多进程和多线程的特点和区别
java·开发语言·jvm