苍穹外卖学习笔记——第十二天

数据统计(Excel报表)

工作台

需求分析和设计

工作台是系统运营的数据看板,并提供快捷操作入口,可以有效提高商家的工作效率。

产品原型

工作台展示的数据
  • 今日数据
  • 订单管理
  • 菜品总览
  • 套餐总览
  • 订单信息
名词解释
  • 营业额:已完成订单的总金额。
  • 有效订单:已完成订单的数量。
  • 订单完成率:有效订单数 / 总订单数 * 100%。
  • 平均客单价:营业额 / 有效订单数。
  • 新增用户:新增用户的数量。

接口设计

  • 今日数据接口
  • 订单管理接口
  • 菜品总览接口
  • 套餐总览接口
  • 订单搜索(已完成)

  • 各个状态的订单数量统计(已完成)

代码导入

  • 导入WorkSpaceController.java:
@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);
    }

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

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

    /**
     * 查询套餐总览
     *
     * @return
     */
    @GetMapping("/overviewSetmeals")
    @ApiOperation("查询套餐总览")
    public Result<SetmealOverViewVO> setmealOverView() {
        return Result.success(workspaceService.getSetmealOverView());
    }
}
  • 导入WorkSpaceService.java:
public interface WorkspaceService {

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

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

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

    /**
     * 查询套餐总览
     *
     * @return
     */
    SetmealOverViewVO getSetmealOverView();
}
  • 导入WorkSpaceServiceImpl.java:
@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
     */
    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();
    }

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

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

    /**
     * 查询套餐总览
     *
     * @return
     */
    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();
    }
}
  • 导入DishMapper.java中的countByMap方法声明:
Integer countByMap(Map map);
  • 导入DishMapper.xml中的countByMap方法的动态SQL语句:
<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>
  • 导入SetmealMapper.java中的countByMap方法声明:
Integer countByMap(Map map);
  • 导入SetmealMapper.xml中的countByMap方法的动态SQL语句:
<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>

功能测试

可以通过如下方式进行测试:通过接口文档测试,前后端联调测试。

Apache POI

介绍

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

Apache POI 的应用场景

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

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>

导出运营数据Excel报表

需求分析和设计

产品原型

导出的Excel报表格式

业务功能

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

接口设计

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

实现步骤

  1. 设计Excel模板文件。
  2. 查询近30天的运营数据。
  3. 将查询到的运营数据写入模板文件。
  4. 通过输出流将Excel文件下载到客户端浏览器。

代码开发

  • 根据接口定义,在ReportController中创建export方法:
@GetMapping("/export")
@ApiOperation("导出运营数据报表")
public void export(HttpServletResponse response) {
    log.info("导出运营数据报表,员工id为:{}", BaseContext.getCurrentId());
    reportService.exportBusinessData(response);
}
  • 在ReportService接口中声明导出运营数据报表的方法:
void exportBusinessData(HttpServletResponse response);
  • 在ReportServiceImpl实现类中实现导出运营数据报表的方法:
@Override
public void exportBusinessData(HttpServletResponse response) {
    //1.查询数据库,获取营业资源
    LocalDate dateBegin = LocalDate.now().minusDays(30);
    LocalDate dateEnd = LocalDate.now().minusDays(1);

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

    //2.通过POI将数据写入到Excel文件中
    try (
            //基于模板文件创建一个新的excel文件
            InputStream in = this.getClass().getClassLoader().getResourceAsStream("template/OperationalDataReportFormTemplate.xlsx");
            XSSFWorkbook excel = new XSSFWorkbook(in);
            ServletOutputStream out = response.getOutputStream()
    ) {
        //填充时间和概览数据
        XSSFSheet sheet = excel.getSheetAt(0);
        sheet.getRow(1).getCell(1).setCellValue("时间:" + dateBegin + "至" + dateEnd);
        XSSFRow row = sheet.getRow(3); //获取第四行数据
        row.getCell(2).setCellValue(businessDataVO.getTurnover());
        row.getCell(4).setCellValue(businessDataVO.getOrderCompletionRate());
        row.getCell(6).setCellValue(businessDataVO.getNewUsers());
        row = sheet.getRow(4); //获取第5行数据
        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(i + 7); //获取第i+1行数据,即第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文件上传到客户端浏览器
        excel.write(out);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

功能测试

可以通过前后端联调进行测试。

posted @   zgg1h  阅读(212)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示