1. 需求分析
所谓用户统计,实际上统计的是用户的数量。通过折线图来展示,上面这根蓝色线代表的是用户总量,下边这根绿色线代表的是新增用户数量,是具体到每一天。所以说用户统计主要统计两个数据,一个是总的用户数量,另外一个是新增用户数量。
-
基于可视化报表的折线图展示用户数据,X轴为日期,Y轴为用户数
-
根据时间选择区间,展示每天的用户总量和新增用户量数据
2. 开发思路
Apache ECharts 是一款基于 Javascript 的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。
官网地址:Apache ECharts
前端开发,可以基于Apache ECharts,展示各种各样的图表。
不管是哪种形式的图形,最本质的东西实际上是数据,它其实是对数据的一种可视化展示。
所以后端需要提供符合格式要求的动态数据,然后响应给前端来展示图表。
3. 代码开发
3.1 VO设计
/**
* 用户数据报告视图对象 (UserReportVO)。
* 用于封装在指定时间范围内用户数据统计的输出结果,
* 包含日期列表、用户总量列表、新增用户列表等信息。
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserReportVO implements Serializable {
/**
* 日期列表,以逗号分隔的字符串格式表示。
* 每个日期对应一天的用户统计数据,如"2024-10-01,2024-10-10,2024-10-29"。
* 在前端展示时可以使用此字段来标识统计数据的时间范围。
*/
private String dateList;
/**
* 用户总量列表,以逗号分隔的字符串格式表示。
* 对应日期列表中每个日期的累计用户总数,如"200,210,220"。
* 可以用于分析用户数量的增长趋势。
*/
private String totalUserList;
/**
* 新增用户数量列表,以逗号分隔的字符串格式表示。
* 对应日期列表中每个日期的新增用户数量,如"20,21,10"。
* 可以用于分析每日的用户增长情况。
*/
private String newUserList;
}
3.2 Controller层
/**
* 用户数据统计接口,提供给前端用于获取在指定时间范围内的用户统计数据。
*
* @param begin 开始日期,查询统计数据的起始时间,格式为"yyyy-MM-dd"。
* 使用 @DateTimeFormat 注解确保请求参数格式正确。
* @param end 结束日期,查询统计数据的结束时间,格式为"yyyy-MM-dd"。
* 使用 @DateTimeFormat 注解确保请求参数格式正确。
* @return 返回封装在 Result 对象中的 UserReportVO 数据,
* 包含统计信息,如用户增长数量、活跃用户数等。
*/
@GetMapping("/userStatistics")
@ApiOperation("用户数据统计") // 描述接口功能,便于生成 API 文档
public Result<UserReportVO> userStatistics(
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin, // 确保接收参数的格式正确
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) { // 确保接收参数的格式正确
// 调用 reportService 的 getUserStatistics 方法,
// 使用传入的 begin 和 end 时间段获取用户统计数据
UserReportVO reportData = reportService.getUserStatistics(begin, end);
// 将统计数据封装在 Result 对象中并返回,表示调用成功和所请求的统计数据
return Result.success(reportData);
}
3.3 Service层
reportService层方法实现思路如下:
@Service
public class ReportServiceImpl implements ReportService {
/**
* 根据时间区间统计用户数量
* @param begin 开始日期
* @param end 结束日期
* @return 用户统计结果的封装对象 UserReportVO
*/
@Override
public UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {
// 创建一个日期列表,用于存放从开始日期到结束日期的每一天
List<LocalDate> dateList = new ArrayList<>();
dateList.add(begin); // 将起始日期加入日期列表
// 循环遍历,将每一天依次加入日期列表,直到达到结束日期
while (!begin.equals(end)) {
begin = begin.plusDays(1); // 将日期加一天
dateList.add(begin); // 加入列表
}
// 初始化新增用户数量列表和总用户数量列表
List<Integer> newUserList = new ArrayList<>(); // 存放每一天的新增用户数量
List<Integer> totalUserList = new ArrayList<>(); // 存放每一天的累计用户总数
// 遍历日期列表,逐日统计用户数据
for (LocalDate date : dateList) {
// 创建当日的开始时间(beginTime)和结束时间(endTime)
LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN); // 当日0点
LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX); // 当日23:59:59
// 查询新增用户数量: 从数据库中统计在beginTime和endTime之间创建的用户数量
Integer newUser = getUserCount(beginTime, endTime);
// 查询总用户数量: 从数据库中统计在endTime之前创建的所有用户数量
Integer totalUser = getUserCount(null, endTime);
// 将统计结果加入相应的列表
newUserList.add(newUser);
totalUserList.add(totalUser);
}
// 构建并返回 UserReportVO 对象,使用逗号分隔的字符串来格式化各个列表
return UserReportVO.builder()
.dateList(StringUtils.join(dateList, ",")) // 日期列表
.newUserList(StringUtils.join(newUserList, ",")) // 新增用户数量列表
.totalUserList(StringUtils.join(totalUserList, ",")) // 用户总量列表
.build();
}
/**
* 根据给定的时间区间统计用户数量
* @param beginTime 起始时间,用于限定查询的开始范围;传入null表示不限定开始时间
* @param endTime 结束时间,用于限定查询的结束范围;传入null表示不限定结束时间
* @return Integer 用户数量,表示在指定时间区间内创建的用户数量
*/
private Integer getUserCount(LocalDateTime beginTime, LocalDateTime endTime) {
// 创建一个参数映射,用于传递到查询方法中
Map<String, LocalDateTime> map = new HashMap<>();
map.put("begin", beginTime); // 将起始时间放入映射,键为"begin"
map.put("end", endTime); // 将结束时间放入映射,键为"end"
// 调用 userMapper 的方法,传入参数映射以执行数据库查询
// 返回查询到的用户数量
return userMapper.countByMap(map);
}
}
3.4 Mapper层
在UserMapper接口中声明countByMap方法:
/**
* 根据动态条件统计用户数量
* @param map
* @return
*/
Integer countByMap(Map map);
在UserMapper.xml文件中编写动态SQL:
<!--
根据指定的时间区间统计用户数量的查询语句。
该查询语句通过 `begin` 和 `end` 参数的值确定统计范围:
- 若 `begin` 有值,则统计范围从 `begin` 开始;
- 若 `end` 有值,则统计范围到 `end` 结束。
该方法返回在此时间区间内创建的用户数量。
-->
<select id="countByMap" resultType="java.lang.Integer">
<!-- 执行统计用户数量的查询 -->
select count(id) from user
<where>
<!-- 若参数 `begin` 非空,则增加时间范围条件 -->
<if test="begin != null">
and create_time >= #{begin} <!-- 查询用户创建时间在 `begin` 之后或等于 `begin` 的记录 -->
</if>
<!-- 若参数 `end` 非空,则增加结束时间条件 -->
<if test="end != null">
and create_time <= #{end} <!-- 查询用户创建时间在 `end` 之前或等于 `end` 的记录 -->
</if>
</where>
</select>
4. 总结
对于图表统计的场景,前端开发,可以基于Apache ECharts,展示各种各样的图表。
不管是哪种形式的图形,最本质的东西实际上是数据,它其实是对数据的一种可视化展示。
所以后端只需要提供符合格式要求的动态数据,然后响应给前端来展示图表。
-
日期生成
- 在
getUserStatistics
方法中,使用开始和结束日期生成一个完整的日期列表。通过逐日增加的方式,从begin
到end
遍历每一天,以便逐日统计数据。
- 在
-
数据统计
- 通过循环遍历日期列表,生成每一天的起始时间(
beginTime
)和结束时间(endTime
)。 - 对于每一天,调用
getUserCount
方法分别获取:- 新增用户数:在
beginTime
和endTime
之间创建的用户数。 - 累计用户数:在
endTime
之前创建的所有用户数。
- 新增用户数:在
- 通过循环遍历日期列表,生成每一天的起始时间(
-
查询逻辑
getUserCount
方法中,构造一个Map
将起始时间和结束时间作为参数传递给countByMap
查询方法。- SQL 查询
countByMap
根据提供的begin
和end
参数动态生成 SQL 语句,通过条件判断确保查询范围正确。
-
结果构建
- 收集各天的日期、新增用户数和累计用户数列表,构建
UserReportVO
返回结果,将这些列表以逗号分隔的字符串格式化返回,便于前端展示。
- 收集各天的日期、新增用户数和累计用户数列表,构建