统计分析--线索统计--新增线索数量折线图
需求:
统计出一段时间内的每一天 新增的线索数量,通过每天新增的线索数量和线索总数量,分析线上线下活动的执行情况
难度级别:B级
接口名 /report/salesStatistics
请求方式 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,
0,
0,
0,
0
]
},
{
"name":"线索总数量",
"data":[
0,
0,
0,
0,
0
]
}
]
}
步骤:
1.阅读产品文档(接口名,请求方式,参数列表)
2.根据产品的返回值和接收参数构建VO类
3.编写mapper层操作数据库
4.编写service层操作数据
5.编写controller层接收参数和返回数据
思路步骤:
1.首先阅读接口文档
{
"xAxis":[
"2021-03-11",
"2021-03-12",
"2021-03-13",
"2021-03-14",
"2021-03-15"
],
"series":[
{
"name":"新增线索数量",
"data":[
0,
0,
0,
0,
0
]
},
{
"name":"线索总数量",
"data":[
0,
0,
0,
0,
0
]
}
]
}
2.根据产品的返回值和接收参数构建VO类 (第一个难点)
首先我们来看返回的json
首先看到最外层是一个大括号{}包裹了整个数据,证明这是一个大对象,如果最外层是一个[]包裹则证明是一个数组
好的现在来看这个对象里的属性
第一个属性的属性名是xAxis,对应的类型怎么判断呢?看到对应的值上有""表示这是一个字符串
所以我们可以判断出第一个属性是
List<String> xAxis = new ArrayList<String>();
然后继续来看里面的属性series:[xxx] 那么证明series也是一个数组
我们来看series里每一项的内容
是用{}来包裹的,证明什么?
里面的每一个属性都是一个对象
{
"name":"新增线索数量",
"data":[
0,
0,
0,
0,
0
]
}
我们来看这个属性
name是一个字符串
data也是一个数组因为是[]包裹的,而data里的属性是一个数组,里面的属性是数字,因为没有被""包裹
那么我们应该建立一个对象用来包裹整内部的对象
建立一个新的对象用来封装series里的属性
package com.huike.report.domain.vo;
import java.util.ArrayList;
import java.util.List;
public class LineSeriesVo {
private String name;
private List<Integer> data =new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Integer> getData() {
return data;
}
public void setData(List<Integer> data) {
this.data = data;
}
}
而最外层的对象也需要构建一个对象
package com.huike.report.domain.vo;
import java.util.ArrayList;
import java.util.List;
/**
* 折线图
*/
public class LineChartVo {
private List<String> xAxis = new ArrayList<>();
private List<LineSeriesVo> series = new ArrayList<>();
public List<String> getxAxis() {
return xAxis;
}
public void setxAxis(List<String> xAxis) {
this.xAxis = xAxis;
}
public List<LineSeriesVo> getSeries() {
return series;
}
public void setSeries(List<LineSeriesVo> series) {
this.series = series;
}
}
上述主要考验的是大家对于数据封装的理解,基于json来封装对象的能力
4.编写mapper层操作数据库
SQL的编写
基于需求来编写业务
我们先看页面
可以看到需要按照传入的时间范围查询出每天的线索增长和线索总数
作为两部分数据,第一部分是新增线索数,第二部分是线索总数量
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') >= date_format(#{beginCreateTime},'%y-%m-%d')
</if>
<if test="endCreateTime != null and endCreateTime != ''"><!-- -->
and date_format(create_time,'%y-%m-%d') <= date_format(#{endCreateTime},'%y-%m-%d')
</if>
</where>
GROUP BY dd;
</select>
完成了第一部分的sql查询,剩下的就是线索总数了,但是其实所有的线索已经都查询出来了,封装线索的总数放在service中完成,并且假如某天没有数据,我们的sql通过group by来进行分组,但是由于没有数据这一天的数据应该补0,这部分的操作应该在service层中完成
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;
}
这个方法能获取到时间范围内的每一天并返回一个数组,对应的包装类里的xAxis里的属性就拿到了
对应的剩下series里的属性还需要进行封装
而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.编写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);
}