苍穹外卖学习 Day10 Day11 Day12

前言

用于记录苍穹外卖Day10、Day11、Day12的学习

Day10 订单状态定时处理 来电提醒 客户催单

订单状态定时处理

Spring Task

Spring Task是一个任务调度工具,可以按照约定的时间自动执行某个代码逻辑(定时自动执行某段Java代码)

cron表达式

cron表达式其实就是一个字符串,通过cron表达式可以定义任务触发的时间。

构成规则:分为6或7个域,用空格隔开,每个域代表一个含义。从左到右依次为秒、分钟、小时、日、月、周、年(可选)

在这里插入图片描述

cron在线生成器:https://cron.qqe2.com

Spring Task使用步骤

  • 导入坐标spring-context
  • 启动类添加注解@EnableScheduling开启任务调度
  • 自定义定时任务类

需求开发

存在的问题:

  • 下单后未支付,订单一直处于”待支付“状态
  • 用户收货后管理端未点击完成按钮,订单一直处于”派送中“状态

只需自定义个任务处理类来定时处理即可:

//定时任务类,定时处理订单状态
@Component
@Slf4j
public class OrderTask {
    @Autowired
    private OrderMapper orderMapper;

    //处理下单后未支付超时的情况
    @Scheduled(cron = "0 * * * * ? *")//每分钟触发一次
//    @Scheduled(cron="0/5 * * * * ?")
    public void processTimeOut(){
        log.info("定时处理下单未支付的订单");
        //当前时间减15分钟
        LocalDateTime localDateTime = LocalDateTime.now().plusMinutes(-15);
        List<Orders> list = orderMapper.getByStatusAndOrderTimeLT(Orders.PENDING_PAYMENT, localDateTime);

        if(list!=null&&list.size()>0){
            for (Orders orders : list) {
                orders.setStatus(Orders.CANCELLED);
                orders.setConsignee("订单超时,自动取消");
                orders.setCancelTime(LocalDateTime.now());

                orderMapper.update(orders);
            }
        }
    }

    //处理一直处于派送中,没有完成的订单
    @Scheduled(cron = "0 0 1 * * ?")//每天凌晨一点触发
//    @Scheduled(cron="0/10 * * * * ?")
    public void processDeliveryOrder(){
        log.info("定时处理一直在派送的订单");
        LocalDateTime localDateTime = LocalDateTime.now().plusMinutes(-60);
        List<Orders> list = orderMapper.getByStatusAndOrderTimeLT(Orders.DELIVERY_IN_PROGRESS, localDateTime);

        if(list!=null&&list.size()>0){
            for (Orders orders : list) {
                orders.setStatus(Orders.COMPLETED);

                orderMapper.update(orders);
            }
        }
    }
}

来电提醒

WebSocket

WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工通信–浏览器和服务器只需完成一次握手,两者之间即可建立持久性的连接,并进行双向数据传输

WebSocket和HTTP对比:

  • HTTP是短连接;WebSocket是长连接
  • HTTP是单向通信,基于请求响应模型;WebSocket支持双向通信。
  • WebSocket和HTTP都是基于TCP协议

应用场景:

  • 视频弹幕
  • 实况更新
  • 网页聊天

需求开发

实现思路:

  • 通过WebSocket实现管理端页面和服务端保持长连接
  • 当客户支付后,调用WebSocket的相关API从服务端向客户端推送消息
  • 客户端解析服务端发送的消息,判断是来电提醒还是客户催单,并进行相应的语音播报
  • 约定服务端向客户端发送的消息的数据格式为JSON,字段包括:type(消息类型,1为来单提醒、2为客户催单)、orderId、content(消息内容)

这里我们只需要在支付成功后提示管理端即可,在OrderServiceImpl的paySuccess方法中:

		//通过WebSocket向客户端浏览器推送数据
        Map map=new HashMap();
        map.put("type",1);
        map.put("orderId",ordersDB.getId());
        map.put("content","订单号:"+outTradeNo);

        String Json= JSON.toJSONString(map);
        webSocketServer.sendToAllClient(Json);

注意:启动项目的时候看看你是否连接上WebSocket,如果没连接上可能是因为自己修改过端口号的问题,将端口号改回80或者改下前端代码即可。

客户催单

实现思路和来电提醒差不多。当用户在客户端点击催单按钮时,发起请求

  • OrderController
	@GetMapping("/reminder/{id}")
    @ApiOperation("客户催单")
    public Result reminder(@PathVariable Long id){
        orderService.reminder(id);
        return Result.success();
    }
  • OrderServiceImpl
	public void reminder(Long id) {
        // 根据id查询订单
        Orders orders1= orderMapper.getById(id);

        // 校验订单是否存在
        if (orders1 == null) {
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }
        //通过WebSocket向客户端浏览器推送数据
        Map map=new HashMap();
        map.put("type",2);
        map.put("orderId",id);
        map.put("content","订单号:"+orders1.getNumber());

        String Json= JSON.toJSONString(map);
        webSocketServer.sendToAllClient(Json);
    }

Day11 数据统计-图形报表

效果如下所示:

在这里插入图片描述

在这里插入图片描述

Apache ECharts

Apache ECharts是一款基于JavaScript的数据可视化图表库,提供直观、生动、可交互、可个性化定制的数据可视化图表。简单来说,它就是一款数据可视化工具。我们只需大致知道它是干啥的,它是在前端使用的,后端开发中我们使用不到。

营业额统计

业务规则:

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

ReportController:注意时间的数据格式

@RestController
@Slf4j
@RequestMapping("/admin/report")
@Api(tags = "数据统计相关接口")
public class ReportController {
    @Autowired
    private ReportService reportService;

    @GetMapping("/turnoverStatistics")
    @ApiOperation("营业额统计")
    public Result<TurnoverReportVO> turnoverStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){
        log.info("营业额数据统计:{},{}",begin,end);
        TurnoverReportVO turnoverReportVO=reportService.getTurnoverStatistics(begin,end);
        return Result.success(turnoverReportVO);
    }
}

ReportServiceImpl:

这里我的实现方法与课程中略有不同,可以参考一下

public TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end) {
        TurnoverReportVO turnoverReportVO=new TurnoverReportVO();

        //1.封装日期数据
        //先去得到一个日期集合,包含begin到end中的所有日期
        List<LocalDate> dateList=new ArrayList<>();

        dateList.add(begin);
        while(!begin.equals(end)){
            begin=begin.plusDays(1);
            dateList.add(begin);
        }
        //将集合转成字符串的同时在每个元素间加一个逗号
        String dateList1=StringUtils.join(dateList,",");
        turnoverReportVO.setDateList(dateList1);

        //2.封装营业额数据
        //查询对应日期的订单的总营业额
        List<Double> moneyList=new ArrayList<>();
        for (LocalDate localDate : dateList) {
            //根据日期查询状态为已完成的订单的营业额

            //00:00:00
            LocalDateTime beginTime=LocalDateTime.of(localDate, LocalTime.MIN);
            //23:59:59
            LocalDateTime endTime=LocalDateTime.of(localDate, LocalTime.MAX);

            Map map=new HashMap();
            map.put("begin",beginTime);
            map.put("end",endTime);
            map.put("status", Orders.COMPLETED);
            Double money=orderMapper.getSumByMap(map);
            if(money==null){
                money=0.0;
            }
            moneyList.add(money);
        }
        String moneyList1=StringUtils.join(moneyList,",");
        turnoverReportVO.setTurnoverList(moneyList1);

        return turnoverReportVO;
    }

用户统计

ReportController:

@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);
        UserReportVO userReportVO=reportService.getUserStatistics(begin,end);
        return Result.success(userReportVO);
    }

ReportServiceImpl:

//用户数据统计
    public UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {
        UserReportVO userReportVO=new UserReportVO();
        //1.封装日期数据
        //先去得到一个日期集合,包含begin到end中的所有日期
        List<LocalDate> dateList=new ArrayList<>();

        dateList.add(begin);
        while(!begin.equals(end)){
            begin=begin.plusDays(1);
            dateList.add(begin);
        }
        //将集合转成字符串的同时在每个元素间加一个逗号
        String dateList1=StringUtils.join(dateList,",");
        userReportVO.setDateList(dateList1);

        //2.封装用户总量数据
        List<Integer> totalList=new ArrayList<>();
        //3.封装新增用户数量数据
        List<Integer> newList=new ArrayList<>();

        for (LocalDate localDate : dateList) {
            //查询用户表createTime这一天的用户的总量
            //00:00:00
            LocalDateTime beginTime=LocalDateTime.of(localDate, LocalTime.MIN);
            //23:59:59
            LocalDateTime endTime=LocalDateTime.of(localDate, LocalTime.MAX);

            Map map=new HashMap();
            map.put("end",endTime);
            Integer total=userMapper.countByMap(map);
            if(total==null){
                total=0;
            }
            totalList.add(total);
            map.put("begin",beginTime);
            Integer today= userMapper.countByMap(map);
            if(today==null){
                today=0;
            }
            newList.add(today);

        }
        String userList1=StringUtils.join(totalList,",");
        userReportVO.setTotalUserList(userList1);

        String list1=StringUtils.join(newList,",");
        userReportVO.setNewUserList(list1);

        return userReportVO;
    }

订单统计

业务规则:

  • 两条折线,一条代表总订单数,另一条代表有效订单数(状态为已完成的订单)
  • 展示订单总数、有效订单数、订单完成率数据

ReportController:

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

ReportServiceImpl:

@Override
    public OrderReportVO getOrderReportStatistics(LocalDate begin, LocalDate end) {
        OrderReportVO orderReportVO=new OrderReportVO();

        //1.封装日期数据
        List<LocalDate> dateList=new ArrayList<>();
        dateList.add(begin);
        while(!begin.equals(end)){
            begin=begin.plusDays(1);
            dateList.add(begin);
        }
        //将集合转成字符串的同时在每个元素间加一个逗号
        String dateList1=StringUtils.join(dateList,",");
        orderReportVO.setDateList(dateList1);

        //2.订单总数
        List<Integer> totalOrder=new ArrayList<>();
        //3.有效订单数
        List<Integer> realOrder=new ArrayList<>();
        //每天的订单总数以及有效订单数
        for (LocalDate localDate : dateList) {
            //00:00:00
            LocalDateTime beginTime=LocalDateTime.of(localDate, LocalTime.MIN);
            //23:59:59
            LocalDateTime endTime=LocalDateTime.of(localDate, LocalTime.MAX);
            Map map=new HashMap();
            map.put("begin",beginTime);
            map.put("end",endTime);
            Integer total=orderMapper.getByMap(map);
            if(total==null){
                total=0;
            }
            totalOrder.add(total);
            map.put("status",Orders.COMPLETED);
            Integer real=orderMapper.getByMap(map);
            if(real==null){
                real=0;
            }
            realOrder.add(real);
        }
        String totalOrder1=StringUtils.join(totalOrder,",");
        String realOrder1=StringUtils.join(realOrder,",");
        //计算时间区间内的订单总数量
        Integer sum=0;
        for (Integer integer : totalOrder) {
            sum+=integer;
        }

        //计算时间区间内的有效订单数量
        Integer real=0;
        for (Integer integer : realOrder) {
            real+=integer;
        }

        //计算订单完成率
        double orderCompletionRate=0.0;
        if (sum!=0) {
            orderCompletionRate= (double) real /sum;
        }
        orderReportVO.setOrderCompletionRate(orderCompletionRate);
        orderReportVO.setOrderCountList(totalOrder1);
        orderReportVO.setValidOrderCountList(realOrder1);
        orderReportVO.setTotalOrderCount(sum);
        orderReportVO.setValidOrderCount(real);

        System.out.println(orderReportVO);
        return orderReportVO;
    }

销量排名统计

ReportController:

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

ReportServiceImpl:

public SalesTop10ReportVO getTop10(LocalDate begin, LocalDate end) {
        SalesTop10ReportVO salesTop10ReportVO=new SalesTop10ReportVO();
        //00:00:00
        LocalDateTime beginTime=LocalDateTime.of(begin, LocalTime.MIN);
        //23:59:59
        LocalDateTime endTime=LocalDateTime.of(end, LocalTime.MAX);

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

        //遍历取出DTO中的numa和number放到对应的集合中去
        List<String> nameList=new ArrayList<>();
        List<String> numberList=new ArrayList<>();
        for (GoodsSalesDTO goodsSalesDTO : goodsSalesDTOList) {
            nameList.add(goodsSalesDTO.getName());
            numberList.add(String.valueOf(goodsSalesDTO.getNumber()));
        }
        String nameLists=StringUtils.join(nameList,",");
        String numberLists=StringUtils.join(numberList,",");
        salesTop10ReportVO.setNameList(nameLists);
        salesTop10ReportVO.setNumberList(numberLists);

        return salesTop10ReportVO;
    }

OrderMapper.xml:

分析一下这里的SQL语句,因为我们要根据订单状态(对应orders表中的status)查订单的名称以及数量(对应order_details表中的number),所以涉及到联表查询。查询前10,所以只需limit 0,10

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

Day12 数据统计-Excel报表

工作台

这里课程中的代码是直接导入的,我还是选择手敲一遍。

工作台展示的数据:

  • 今日数据
  • 订单管理
  • 菜品总览
  • 套餐总览
  • 订单信息

为了简便展示,这里我直接给出一个类中的全部代码了,可以根据注释理解。

  • WorkSpaceController
@RestController
@RequestMapping("/admin/workspace")
@Slf4j
@Api(tags = "工作台相关接口")
public class WorkSpaceController {
    @Autowired
    private WorkSpaceService workSpaceService;

    //查询工作台今日数据
    @GetMapping("/businessData")
    @ApiOperation("查询工作台今日数据")
    public Result<BusinessDataVO> businessData(){
        log.info("查询工作台今日数据");
        //获取开始时间
        LocalDateTime beginTime=LocalDateTime.now().with(LocalTime.MIN);
        //获取结束时间
        LocalDateTime endTime=LocalDateTime.now().with(LocalTime.MAX);

        BusinessDataVO businessDataVO=workSpaceService.getBusinessData(beginTime,endTime);
        return Result.success(businessDataVO);
    }
    //查询订单管理数据
    @GetMapping("/overviewOrders")
    @ApiOperation("查询订单管理数据")
    public Result<OrderOverViewVO> overViewOrders(){
        log.info("查询订单管理数据");
        return Result.success(workSpaceService.getOrderOverView());
    }
    //查询菜品总览
    @GetMapping("/overviewDishes")
    @ApiOperation("查询菜品总览")
    public Result<DishOverViewVO> overViewDishes(){
        return Result.success(workSpaceService.getDishOverView());
    }
    //查询套餐总览
    @GetMapping("/overviewSetmeals")
    @ApiOperation("查询套餐总览")
    public Result<SetmealOverViewVO> overViewSetmeal(){
        return Result.success(workSpaceService.getSetmealOvermeal());
    }
}
  • WorkSpaceService
public interface WorkSpaceService {
    BusinessDataVO getBusinessData(LocalDateTime beginTime, LocalDateTime endTime);

    OrderOverViewVO getOrderOverView();

    DishOverViewVO getDishOverView();

    SetmealOverViewVO getSetmealOvermeal();
}
  • WorkSpaceServiceImpl(这里很多方法我们都在OrderMapper中已经写好了,直接调用即可)
@Service
public class WorkSpaceServiceImpl implements WorkSpaceService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private SetmealMapper setmealMapper;

    //查询工作台今日数据
    public BusinessDataVO getBusinessData(LocalDateTime beginTime, LocalDateTime endTime) {
        BusinessDataVO businessDataVO=new BusinessDataVO();
        Map map=new HashMap();
        map.put("begin",beginTime);
        map.put("end",endTime);

        //订单总数
        Integer total=orderMapper.getByMap(map);

        map.put("status",5);
        //营业额
        Double sum = orderMapper.getSumByMap(map);
        sum = sum == null? 0.0 : sum;

        //有效订单数
        Integer real=orderMapper.getByMap(map);
        //平均客单价
        double average=0.0;
        //订单完成率
        double complete=0.0;
        if(total!=0&&real!=0){
            complete= (double) real /total;
            average=sum/real;
        }

        //新增用户数
        Integer newUser=userMapper.countByMap(map);

        businessDataVO.setTurnover(sum);
        businessDataVO.setNewUsers(newUser);
        businessDataVO.setOrderCompletionRate(complete);
        businessDataVO.setValidOrderCount(real);
        businessDataVO.setUnitPrice(average);

        return businessDataVO;
    }

    //查询订单管理数据
    @Override
    public OrderOverViewVO getOrderOverView() {
        OrderOverViewVO orderOverViewVO=new OrderOverViewVO();
        Map map = new HashMap();
        map.put("begin", LocalDateTime.now().with(LocalTime.MIN));

        //待接单
        map.put("status", Orders.TO_BE_CONFIRMED);
        Integer status1=orderMapper.getByMap(map);
        //待派送
        map.put("status",Orders.CONFIRMED);
        Integer status2=orderMapper.getByMap(map);
        //已完成
        map.put("status",Orders.COMPLETED);
        Integer status3=orderMapper.getByMap(map);
        //已取消
        map.put("status",Orders.CANCELLED);
        Integer status4=orderMapper.getByMap(map);
        //全部订单
        map.put("status",null);
        Integer status5=orderMapper.getByMap(map);

        orderOverViewVO.setWaitingOrders(status1);
        orderOverViewVO.setDeliveredOrders(status2);
        orderOverViewVO.setCompletedOrders(status3);
        orderOverViewVO.setCancelledOrders(status4);
        orderOverViewVO.setAllOrders(status5);
        return orderOverViewVO;
    }

    //查询菜品总览
    @Override
    public DishOverViewVO getDishOverView() {
        DishOverViewVO dishOverViewVO=new DishOverViewVO();

        Integer on=dishMapper.onStatus();
        Integer off=dishMapper.offStatus();

        dishOverViewVO.setSold(on);
        dishOverViewVO.setDiscontinued(off);

        return dishOverViewVO;
    }

    //查询套餐总览
    @Override
    public SetmealOverViewVO getSetmealOvermeal() {
        SetmealOverViewVO setmealOverViewVO=new SetmealOverViewVO();

        Integer on=setmealMapper.onStatus();
        Integer off=setmealMapper.offStatus();

        setmealOverViewVO.setSold(on);
        setmealOverViewVO.setDiscontinued(off);

        return setmealOverViewVO;
    }
}
  • DishMapper(这里是SQL语句少我使用这种方法,标准的应该是使用动态SQL)
	@Select("select count(id) from dish where status=1")
    Integer onStatus();

    @Select("select count(id) from dish where status=0")
    Integer offStatus();
  • SetmealMapper
	@Select("select count(id) from setmeal where status=1")
    Integer onStatus();

    @Select("select count(id) from setmeal where status=0")
    Integer offStatus();

Apache POI

简介:Apache POI可以处理Office的各种文件格式。允许我们使用POI在Java程序中对Office文件进行读写操作。一般用于处理Excel文件。

实例:

写入Excel文件:

		//在内存中创建一个excel文件
        XSSFWorkbook excel=new XSSFWorkbook();
        //在excel文件中创建一个sheet页同时指定其名称为info
        XSSFSheet sheet=excel.createSheet("info");
        //创建行对象,行和列都从0开始,这里我们指定1表示是第二行
        XSSFRow row= sheet.createRow(1);
        //创建单元格并写入内容
        row.createCell(1).setCellValue("姓名");
        row.createCell(2).setCellValue("爱好");

        row= sheet.createRow(2);
        //创建单元格并写入内容
        row.createCell(1).setCellValue("张三");
        row.createCell(2).setCellValue("篮球");

        row= sheet.createRow(3);
        //创建单元格并写入内容
        row.createCell(1).setCellValue("李四");
        row.createCell(2).setCellValue("游泳");

        //通过输出流将内存中的Excel文件写入到磁盘中
        FileOutputStream out=new FileOutputStream(new File("E:\\Takeout\\info.xlsx"));
        excel.write(out);

        out.close();
        excel.close();

读取Excel文件:

		InputStream in=new FileInputStream(new File("E:\\Takeout\\info.xlsx"));

        //读取磁盘上已经存在的Excel文件
        XSSFWorkbook excel=new XSSFWorkbook(in);
        //读取Excel文件中第一个Sheet页
        XSSFSheet sheet= excel.getSheetAt(0);

        //获取Sheet页中最后一行的的行号(有内容的最后一行)
        int lastRowNum=sheet.getLastRowNum();
        for (int i = 1; i <= lastRowNum; i++) {
            //获取某一行
            XSSFRow row= sheet.getRow(i);
            //获取单元格对象
            String stringCellValue1 = row.getCell(1).getStringCellValue();
            String stringCellValue2 = row.getCell(2).getStringCellValue();
            System.out.println(stringCellValue1+" "+stringCellValue2);
        }
        excel.close();
        in.close();

导出Excel报表

业务规则:

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

实现步骤:

  • 设计Excel模板文件
  • 查询近30日的运营数据
  • 将查询到的运营数据写入模板文件内
  • 通过输出流将Excel文件下载到客户端浏览器

实现:

  • ReportController
	@GetMapping("/export")
    @ApiOperation("导出Excel报表")
    public Result export(HttpServletResponse response){
        log.info("导出Excel报表");
        reportService.export(response);
        return Result.success();
    }
  • ReportServiceImpl
	//导出运营数据报表
    @Override
    public void export(HttpServletResponse response) {
        //1.查询数据库,获取近30日的营业数据
        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文件中
        InputStream in=this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx");
        try{
            //基于模板文件创建一个新的Excel文件
            XSSFWorkbook excel=new XSSFWorkbook(in);

            //获取报表文件的Sheet页
            XSSFSheet sheet= excel.getSheet("Sheet1");

            //填充概览数据-时间
            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);
            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 businessDataVO1=workSpaceService.getBusinessData(LocalDateTime.of(date,LocalTime.MIN),LocalDateTime.of(date,LocalTime.MAX));

                //获取某一行并填充数据
                row= sheet.getRow(7+i);
                row.getCell(1).setCellValue(businessDataVO1.toString());
                row.getCell(2).setCellValue(businessDataVO1.getTurnover());
                row.getCell(3).setCellValue(businessDataVO1.getValidOrderCount());
                row.getCell(4).setCellValue(businessDataVO1.getOrderCompletionRate());
                row.getCell(5).setCellValue(businessDataVO1.getUnitPrice());
                row.getCell(6).setCellValue(businessDataVO1.getNewUsers());
            }

            //3.通过输出流将Excel文件下载到客户端浏览器
            ServletOutputStream out= response.getOutputStream();
            excel.write(out);
            //关闭资源
            out.close();
            excel.close();
        }catch (Exception e){
            e.printStackTrace();
        }

这里我们需要注意的一个地方,这里老师没有加上这个后缀,不加的话我这里会报错:

在这里插入图片描述
苍穹外卖的学习就到这里啦,完结撒花!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

小程序动态调试-解密加密数据与签名校验

前言&#xff1a; 微信小程序的加密与验签早前大多数情况&#xff0c;要么就是逆向获取源码而后拿到加密秘钥&#xff0c;要么就是逆向拿到源码后使用腾讯自带的小程序开发者功能进行动态调试模拟&#xff0c;今天介绍一款志远大佬的开源工具—WeChatOpenDevTool 工具下载地址…

01 MySQL之连接

1. 连接 1.0 基础认知 多表(主表)和一表(从表的区别): 多表一般是主表&#xff0c;一般存储主要数据&#xff0c;每个字段都可能存在重复值&#xff0c;没有主键&#xff0c;无法根据某个字段定位到准确的记录&#xff1b; 一表一般是从表&#xff0c;一般存储辅助数据&…

点云数据处理常用外部库(C++/Windows)的项目配置

一、点云数据处理常用外部库&#xff08;C版本&#xff09;的下载安装与项目配置 &#xff08;一&#xff09;PCL 基于VS2019编程平台的PCL外部库下载安装及项目配置已有大量博客&#xff0c;本文不再赘述。具体下载安装及项目配置流程可参考外部库编译配置参考资料/*1*/ 。需…

redis-RedisTemplate.opsForGeo 的geo地理位置及实现附近的人的功能

redis内部使用的是 zset 数据结构存储&#xff0c;如下 import cn.huawei.VideoApplication; import cn.huawei.domain.Jingqu; import cn.huawei.service.JingquService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired…

Redis 协议与异步方式

redis pipeline 模式 redis pipeline 是一个客户端提供的机制&#xff0c;与 redis 无关。pipeline 不具备事务性。目的&#xff1a;节约网络传输时间。通过一次发送多条请求命令&#xff0c;从而减少网络传输时间。 时间窗口限流 系统限定某个用户的某个行为在指定的时间范围…

SpringBoot项目中如何结合Mybatis进行数据库查询

在Spring Boot项目中使用Mybatis进行数据库操作是一种常见的实现方式。下面我将展示如何在Spring Boot项目中整合Mybatis。这个示例将包括几个主要部分&#xff1a;项目依赖配置、配置文件、实体类、Mapper接口及其XML配置文件、服务类、以及一个简单的控制器。 1. 项目依赖配…

一键安装|卸载 mysql 8.2.0 shell脚本

场景&#xff1a;为了在无网、外网 mysql 安装方便&#xff0c;这里分享一个自己编写得 shell脚本 这里以当前最新版 mysql 8.2.0&#xff1b;centos-7 二进制包下载&#xff1a; 下载地址 mysql_install.sh #!/bin/bash # 解压安装包 tar -xf mysql-8.2.0-linux-glibc2.17-x8…

opencv中两个LSD直线检测算法的区别与应用

opencv中两个LSD直线检测算法的区别与应用 同样是Line Segment Detector(lsd)算法&#xff0c;opencv中提供了两种实现&#xff0c;并且位于不同的模块。下面分别介绍它们的使用方法&#xff1a; 1. LineSegmentDetector 由于源码许可证问题 OpenCV 3.4.6-3.4.15、4.1.0-4.5.…

IDEA类和方法注释模板设置

一、概述 IDEA自带的注释模板不是太好用&#xff0c;我本人到网上搜集了很多资料系统的整理了一下制作了一份比较完整的模板来分享给大家&#xff0c;我不是专业玩博客的&#xff0c;写这篇文章只是为了让大家省事。 这里设置的注释模板采用Eclipse的格式&#xff0c;下面先贴…

【深度优先搜索】【图论】【推荐】332. 重新安排行程

作者推荐 动态规划的时间复杂度优化 本文涉及知识点 深度优先搜索 图论 LeetCode332. 重新安排行程 给你一份航线列表 tickets &#xff0c;其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票都属于一个从 JFK&a…

C++:常量表达式

C11开始constexpr作为一种声明&#xff0c;为编译器提供了在编译期间确认结果的优化建议&#xff0c;满足部分编译期特性的需求 constexpr和const区别 int b10; const int ab; //运行成功 constexpr int cb; //编译器报错&#xff0c;b的值在编译期间不能确定 const int size1…

研发日记,MatlabSimulink开箱报告(九)——Simulink Test模块

文章目录 前言 Simulink Test模块 静态测试 动态测试 逻辑测试 前言 见《开箱报告&#xff0c;Simulink Toolbox库模块使用指南&#xff08;四&#xff09;——S-Fuction模块》 见《开箱报告&#xff0c;Simulink Toolbox库模块使用指南&#xff08;五&#xff09;——S-F…

vue a-table 实现指定字段相同数据合并行

vue a-table 实现相同数据合并行 实现效果代码实现cloums数据格式数据源格式合并代码 实现效果 代码实现 cloums数据格式 const getColumns function () {return [{title: "分类",dataIndex: "checked",width: "150px",customRender: (text, …

有哪些视频媒体?邀请视频媒体报道活动的好处

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 视频媒体在当今的媒体生态中占据了重要的地位。以下是一些主要的视频媒体类型&#xff1a; 电视台&#xff1a;如中央电视台、各省级卫视台、地方电视台等&#xff0c;他们拥有专业的视…

mac命令行下计算文件SHA-256散列值

源起 从国内的第三方网站下载了Android sutiod的zip包下载地址&#xff0c;为了安全起见还是得跟Android官网上的对应的zip包的SHA值做下对比。以前是经常使用md5命令的&#xff0c;所以理论在命令行下应该是有对应的命令行工具可以计算SHA值的。后来搜索到可以用 shasum命令来…

yolov8训练目标检测模型

1.环境安装 conda安装&#xff08;miniconda&#xff09;&#xff0c;配置环境变量 创建环境 conda create -n yolo python3.8安装ultralytics conda activate yolopip install ultralytics2.数据集标注 使用labelimg标注工具对图片进行标注&#xff1a;将标注产生的xml转为t…

表格图片太大怎么批量压缩?快速处理图片大小的方法

在工作或者学习中制作表格的时候&#xff0c;经常需要插入一些图片来修饰内容&#xff0c;当遇到图片太大无法导入的情况就比较麻烦&#xff0c;尤其是多张图片处理的时候&#xff0c;那么表格图片太大怎么批量压缩呢&#xff1f;接下来小编就分享给大家一个快速图片压缩的方法…

HTTP详解(HTTP的特点,状态码,工作原理,GET和POST的区别,如何解决无状态通信)!!!

文章目录 一、HTTP协议简介二、HTTP的主要特点三、HTTP之URL四、Request和Respons五、HTTP的状态码六、HTTP工作原理七、GET和POST请求的区别八、解决HTTP无状态通信——Cookie和Session 一、HTTP协议简介 HTTP协议是Hyper Text Transfer Protocol&#xff08;超文本传输协议&…

92. 递归实现指数型枚举 刷题笔记

思路 dfs 考虑选或者不选每个位置 用0表示未考虑 1表示选 2表示不选 用u表示搜索状态 u>n时 已经搜到底层了 需要输出当前方案 遍历 如果选了则输出 #include<iostream> using namespace std; int n; const int N16; int st[N]; void dfs(int u){ //u来记…

JVM运行时数据区——程序计数器

1、程序计数器介绍 JVM中的程序计数器英文全称是Program Counter Register&#xff0c;其中Register的命名源于CPU的寄存器&#xff0c;寄存器用于存储指令相关的现场信息&#xff0c;CPU只有把数据装载到寄存器才能够运行。 程序计数器中的寄存器并非是广义上所指的物理寄存…