苍穹外卖学习 Day10 Day11 Day12

news2024/11/17 9:37:46

前言

用于记录苍穹外卖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();
        }

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

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

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

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

相关文章

[GXYCTF2019]BabyUpload1 -- 题目分析与详解

目录 一、题目分析 1、判断题目类型&#xff1a; 2、上传不同类型的文件进行测试&#xff1a; 二、题目详解 1、写出.htaccess文件&#xff1a; 2、.htaccess 文件配合 .jpg 上传&#xff1a; 3、利用 中国蚁剑/中国菜刀 获取flag&#xff1a; 一、题目分析 1、判断题目…

亚马逊云科技实时 AI 编程助手 Amazon CodeWhisperer,开发快人一步

​ 亚马逊云科技&#xff1a;https://mic.anruicloud.com/url/1024 ​ Amazon CodeWhisperer 是一款 AI 编码配套应用程序&#xff0c;可在 IDE 中生成 整行代码和完整的函数代码建议&#xff0c;以帮助您更快地完成更多工作。在本系列 文章中&#xff0c;我们将为您详细介绍 A…

Cloud+Consul

Cloud整合Zookeeper代替Eureka-CSDN博客 Consul简介 Consul是一套开源的分布式服务发现和配置管理系统 What is Consul? | Consul | HashiCorp DeveloperConsul is a service networking solution that delivers service discovery, service mesh, and network security ca…

#WEB前端(CCS选择器)

1.实验&#xff1a;CCS选择器 2.IDE&#xff1a;VSCODE 3.记录&#xff1a; 子代选择器、后代选择器、相邻兄弟选择器、类选择器、伪元素选择器&#xff08;鼠标悬停&#xff09;、ID选择器、调用选择器&#xff08;全选&#xff09; 4.代码&#xff1a; <!DOCTYPE html…

2024龙年特别篇 -- 魔法指针 之 指针变量

目录 ​编辑 字符指针变量 字符指针 字符数组 关于字符数组的试题 数组指针变量 数组指针 利用指针数组打印数组 打印二维数组 数组作为形参 当形参为指针时&#xff08;指针数组&#xff09; 函数指针变量 利用函数实现加法输出的多种方式 字符指针变量 字符指针 char …

超详细红黑树的模拟实现

前言 有人说设计出AVL树的的人是个大牛&#xff0c;那写红黑树&#xff08;RBTree&#xff09;的人就是天才&#xff01; 上一篇文章&#xff0c;我们已经学习了AVL树&#xff0c;牛牛个人认为AVL树已经够优秀了&#xff0c;那让我们一起探究一下&#xff0c;为什么红黑树比AV…

【C语言】InfiniBand 驱动mlx4_ib_init和mlx4_ib_cleanup

一、中文讲解 这两个函数是Linux内核模块中对于Mellanox InfiniBand 驱动程序初始化和清理的函数。 mlx4_ib_init()函数是模块初始化函数&#xff0c;使用__init宏标注&#xff0c;表示该函数只在模块加载时运行一次。 函数执行的步骤如下&#xff1a; 1. 通过alloc_ordered_w…

sklearn.preprocessing.RobustScaler(解释和原理,分位数,四分位差)

提示&#xff1a;sklearn.preprocessing.RobustScaler&#xff08;解释和原理&#xff0c;分位数&#xff0c;四分位差&#xff09; 文章目录 [TOC](文章目录) 一、RobustScaler 是什么&#xff1f;二、代码1.代码2.输出结果 总结 提示&#xff1a;以下是本篇文章正文内容&…

计算机网络|Socket

文章目录 Socket并发socket Socket Socket是一种工作在TCP/IP协议栈上的API。 端口用于区分不同应用&#xff0c;IP地址用于区分不同主机。 以下是某一个服务器的socket代码。 其中with是python中的一个语法糖&#xff0c;代表当代码块离开with时&#xff0c;自动对s进行销毁…

[VulnHub靶机渗透] CONNECT THE DOTS

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

miniconda3彻底删除虚拟环境

退出虚拟环境&#xff1a;确保您不在要删除的虚拟环境中。如果在&#xff0c;使用命令 conda deactivate 来退出当前激活的虚拟环境。查看虚拟环境列表&#xff1a;运行命令 conda env list 或 conda info -e 来查看所有存在的虚拟环境及其路径。删除虚拟环境&#xff1a;使用命…

HTTP Cookie 你了解多少?

Cookie是什么&#xff1f; 先给大家举个例子&#xff0c;F12 打开浏览器的页面之后&#xff0c;我们能在 Response Headers 的字段里面看到一个header 叫做 Set-Cookie&#xff0c;如下所示 图中包含的 Set-Cookie 为 Set-Cookie:uuid_tt_dd10_20293537580-1709432565344-232…

Maven(黑马学习笔记)

初识Maven 什么是Maven Maven是Apache旗下的一个开源项目&#xff0c;是一款用于管理和构建java项目的工具。 官网&#xff1a;https://maven.apache.org/ Apache 软件基金会&#xff0c;成立于1999年7月&#xff0c;是目前世界上最大的最受欢迎的开源软件基金会&#xff0…

Sqli-labs靶场第15关详解[Sqli-labs-less-15]自动化注入-SQLmap工具注入

Sqli-labs-Less-15 #自动化注入-SQLmap工具注入 SQLmap用户手册&#xff1a;文档介绍 - sqlmap 用户手册 由于这题是post请求&#xff0c;所以先使用burp进行抓包&#xff0c;然后将数据包存入txt文件中打包 用-r 选择目标txt文件 python sqlmap.py -r data.txt -current-db…

JavaScript之数据类型

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 数据类型   Java…

2023天津公租房网上登记流程图,注册到信息填写

2023年天津市公共租赁住房网上登记流程图 小编为大家整理了天津市公共租赁住房网上登记流程&#xff0c;从登记到填写信息。 想要体验的朋友请看一下。 申请天津公共租赁住房时拒绝申报家庭情况会怎样&#xff1f; 天津市住房保障家庭在享受住房保障期间&#xff0c;如在应申…

力扣 第 125 场双周赛 解题报告 | 珂学家 | 树形DP + 组合数学

前言 整体评价 T4感觉有简单的方法&#xff0c;无奈树形DP一条路上走到黑了&#xff0c;这场还是有难度的。 T1. 超过阈值的最少操作数 I 思路: 模拟 class Solution {public int minOperations(int[] nums, int k) {return (int)Arrays.stream(nums).filter(x -> x <…

Windows上构建一个和Linux类似的Terminal

preview 目的是在Windows上构建一个和Linux类似的Terminal&#xff0c;让Windows炼丹和Linux一样舒适&#xff0c;同是让Terminal取代Xshell完成远程链接。 预览如下图 在Linux下我们使用zsh和oh-my-zsh结合&#xff0c;Windows下我们使用powershell7和oh-my-posh结合。 前提…

力扣● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零

● 1049. 最后一块石头的重量 II 题目要把石头分成两堆&#xff0c;这两堆的重量差值最小。相撞之后剩下的石头重量就最小。其实就是要尽量把石头分为差不多重量的两堆&#xff0c;和昨天的● 416. 分割等和子集相似&#xff0c;这样就转换成了01背包问题。 和416题一样&…

【字符串相加】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 字符串相加 方法一&#xff1a; 方法二&#xff1a; 总结 前言 世上有两种耀眼的光芒&#xff0c;一种是正在升起的太阳&#xff0c;一种是正在努力学习编程的…