13-2- 统计分析--线索统计--新增线索数量折线图(实现)

YangeIT大约 7 分钟

13-2- 统计分析--线索统计--新增线索数量折线图(实现)

统计分析--线索统计--新增线索数量折线图

需求: 统计出一段时间内的 新增的线索数量,通过每天新增的线索数量和线索总数量,分析线上线下活动的执行情况

⚠️ 注意 :如果这一天没有数据,需要

  • 接口名 /report/cluesStatistics
  • 请求方式 get请求
  • 参数列表
    • 传入参数:/report/cluesStatistics/2022-03-06/2022-03-13
      • beginCreateTime 开始时间
      • endCreateTime 结束时间
  • 返回值:
{
    "xAxis":[
        "2021-03-11",
        "2021-03-12",
        "2021-03-13",
        "2021-03-14",
        "2021-03-15"
    ],
    "series":[
        {
            "name":"新增线索数量",
            "data":[
                0, //对应2021-03-11的线索量
                0, //对应2021-03-12的线索量
                0, //对应2021-03-13的线索量
                0, //对应2021-03-14的线索量
                0  //对应2021-03-14的线索量
            ]
        },
        {
            "name":"线索总数量",
            "data":[
                0, //对应2021-03-11以来累加的总线索量
                0, //对应2021-03-12以来累加的总线索量
                0, //对应2021-03-13以来累加的总线索量
                0, //对应2021-03-14以来累加的总线索量
                0  //对应2021-03-15以来累加的总线索量
            ]
        }
    ]
}


 
 
 
 
 





 
 
 
 
 





 
 
 
 
 




思路步骤:

思路步骤:

  1. 阅读产品文档(接口名,请求方式,参数列表)
  2. 根据产品的返回值和接收参数构建VO类
  3. 编写mapper层操作数据库
  4. 编写service层操作数据
  5. 编写controller层接收参数和返回数据

1️⃣ 1.首先阅读接口文档

同上,确定接口的返回参数

2️⃣ 2.根据产品的返回值和接收参数构建VO类 (第一个难点)

1.首先我们来看返回的json

首先看到最外层是一个大括号{}包裹了整个数据,证明这是一个大对象,如果最外层是一个[]包裹则证明是一个数组

好的现在来看这个对象里的属性

第一个属性的属性名是xAxis,对应的类型怎么判断呢?看到对应的值上有""表示这是一个字符串

所以我们可以判断出第一个属性是

List<String> xAxis = new ArrayList<String>();

2.继续来看里面的属性series:[xxx] 那么证明series也是一个数组

我们来看series里每一项的内容

是用{}来包裹的,证明什么?--->里面的每一个属性都是一个对象

		{
            "name":"新增线索数量",
            "data":[
                0,
                0,
                0,
                0,
                0
            ]
        }

name是一个字符串 data也是一个数组因为是[]包裹的,而data里的属性是一个数组,里面的属性是数字,因为没有被""包裹

那么我们应该建立一个对象用来包裹整内部的对象

3.建立一个新的对象用来封装series里的属性

package com.huike.report.domain.vo;

@Data
public class LineSeriesVo {
    private String name;
    private List<Integer> data =new ArrayList<>();

}

4.最外层的对象也需要构建一个对象

package com.huike.report.domain.vo;


/**
 * 折线图
 */
@Data
public class LineChartVo {
    private List<String> xAxis = new ArrayList<>();
    private List<LineSeriesVo> series = new ArrayList<>();
}

⚠️上述主要考验的是大家对于数据封装的理解,基于json来封装对象的能力 🎉

3️⃣ 3.SQL的编写

基于需求来编写业务

我们先看页面

可以看到需要按照查询出每天

共计两部分数据,第一部分是新增线索数,第二部分是线索总数量

1)先查询新增线索数

查询线索应该要查询线索表

select * from tb_clue

然后需要确定哪些线索是具体哪天添加的需要使用group by 进行分组

SELECT * from tb_clue group by create_time

这样的sql看起来似乎没有问题,但是由于数据中存储的create_time是带有时分秒的所以这样分组还是不可取

由于需求需要按天来进行统计,所以还需要按天来进行统计

SELECT * FROM tb_clue GROUP BY DATE_FORMAT(create_time,'%y-%m-%d')

但是这样依然有问题,我们在查询的结果集里应该统计的是数量,并且最好是直接把日期也返回了

我们修改一下sql

SELECT count(1) as num,DATE_FORMAT(create_time,'%Y-%m-%d') dd from tb_clue  GROUP BY dd

这样基本上就能满足需求了统计出了每天的创建的线索的数量,剩下的就是把时间范围的判断加上

SELECT count(1) AS num, DATE_FORMAT(create_time, '%Y-%m-%d') AS dd
FROM tb_clue
WHERE 
	date_format(create_time, '%y-%m-%d') >= date_format('2019-05-05', '%y-%m-%d')
	AND 
	date_format(create_time, '%y-%m-%d') <= date_format('2022-05-05', '%y-%m-%d')
GROUP BY dd

sql语句确定了以后编写对应的xml 🎉 🎉

4️⃣ 4.编写mapper层操作数据库

TbClueMapper.xml

<select id="cluesStatistics" resultType="java.util.Map">
    SELECT count(1) as num,DATE_FORMAT(create_time,'%Y-%m-%d') dd from tb_clue
    <where>
    <if test="beginCreateTime != null and beginCreateTime != ''"><!-- 开始创建时间 -->
        and date_format(create_time,'%y-%m-%d') &gt;= date_format(#{beginCreateTime},'%y-%m-%d')
    </if>
    <if test="endCreateTime != null and endCreateTime != ''"><!--  -->
        and date_format(create_time,'%y-%m-%d') &lt;= date_format(#{endCreateTime},'%y-%m-%d')
    </if>
    </where>
    GROUP BY dd;
</select>

完成了第一部分的sql查询,剩下的就是了,但是其实所有的线索已经都查询出来了,封装线索的总数放在service中完成,并且假如某天没有数据,我们的sql通过group by来进行分组,但是由于没有数据这一天的数据应该补0,这部分的操作应该在service层中完成

5️⃣ 5.编写service层操作数据

1)首先需要获取时间范围内的每一天

提供一个方法

public static List<String> findDates(String beginTime, String endTime) throws ParseException {
    List<String> allDate = new ArrayList();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    Date dBegin = sdf.parse(beginTime);
    Date dEnd = sdf.parse(endTime);
    allDate.add(sdf.format(dBegin));
    Calendar calBegin = Calendar.getInstance();
    // 使用给定的 Date 设置此 Calendar 的时间
    calBegin.setTime(dBegin);
    Calendar calEnd = Calendar.getInstance();
    // 使用给定的 Date 设置此 Calendar 的时间
    calEnd.setTime(dEnd);
    // 测试此日期是否在指定日期之后
    while (dEnd.after(calBegin.getTime())) {
        // 根据日历的规则,为给定的日历字段添加或减去指定的时间量
        calBegin.add(Calendar.DAY_OF_MONTH, 1);
        allDate.add(sdf.format(calBegin.getTime()));
    }
    System.out.println("时间==" + allDate);
    return allDate;
}

这个方法能获取到时间范围内的每一天并返回一个数组,对应的包装类里的里的属性就拿到了

对应的剩下里的属性还需要进行封装,而series里的属性需要和每一天的数据进行比对,如果有则表示为新增线索数,如果没有则补0,将每之前每一天的结果求和作为总线索总数

这里用到的是stream流的方式进行操作

如果获取的的数据里有值

ReportServiceImpl

 	@Override
    public LineChartVo cluesStatistics(String beginCreateTime, String endCreateTime) {
        LineChartVo lineChartVo =new LineChartVo();
        try {
            List<String> timeList= findDates(beginCreateTime,endCreateTime);
            lineChartVo.setxAxis(timeList);
            List<LineSeriesVo> series = new ArrayList<>();
            List<Map<String,Object>>  statistics = clueMapper.cluesStatistics(beginCreateTime,endCreateTime);
            LineSeriesVo lineSeriesVo1=new LineSeriesVo();
            lineSeriesVo1.setName("新增线索数量");
            LineSeriesVo lineSeriesVo2=new LineSeriesVo();
            lineSeriesVo2.setName("线索总数量");
            int sum = 0;
            for (String s : timeList) {
                Optional optional=  statistics.stream().filter(d->d.get("dd").equals(s)).findFirst();
                if(optional.isPresent()){
                    Map<String,Object> cuurentData=  (Map<String,Object>)optional.get();
                    lineSeriesVo1.getData().add(cuurentData.get("num"));
                    sum += Integer.parseInt(cuurentData.get("num").toString());
                }else{
                    lineSeriesVo1.getData().add(0);
                }
                lineSeriesVo2.getData().add(sum);
            }
            series.add(lineSeriesVo1);
            series.add(lineSeriesVo2);
            lineChartVo.setSeries(series);
        } catch (ParseException e) {
            // e.printStackTrace();
        }
        return  lineChartVo;
    }

public static List<String> findDates(String beginTime, String endTime)
            throws ParseException {
        List<String> allDate = new ArrayList();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        Date dBegin = sdf.parse(beginTime);
        Date dEnd = sdf.parse(endTime);
        allDate.add(sdf.format(dBegin));
        Calendar calBegin = Calendar.getInstance();
        // 使用给定的 Date 设置此 Calendar 的时间
        calBegin.setTime(dBegin);
        Calendar calEnd = Calendar.getInstance();
        // 使用给定的 Date 设置此 Calendar 的时间
        calEnd.setTime(dEnd);
        // 测试此日期是否在指定日期之后
        while (dEnd.after(calBegin.getTime())) {
            // 根据日历的规则,为给定的日历字段添加或减去指定的时间量
            calBegin.add(Calendar.DAY_OF_MONTH, 1);
            allDate.add(sdf.format(calBegin.getTime()));
        }
        System.out.println("时间==" + allDate);
        return allDate;
    }

IReportService

/**
    * 线索统计
    * @param beginCreateTime
    * @param endCreateTime
    * @return
    */
public LineChartVo cluesStatistics(String beginCreateTime, String endCreateTime);

6️⃣ 6.编写controller层接收参数和返回数据

ReportController

/**
    * 线索统计
    * @param beginCreateTime
    * @param endCreateTime
    * @return
    */
@GetMapping("/cluesStatistics/{beginCreateTime}/{endCreateTime}")
public LineChartVo cluesStatistics(@PathVariable String beginCreateTime, @PathVariable String endCreateTime){
    return reportService.cluesStatistics(beginCreateTime,endCreateTime);
}