|
@@ -21,16 +21,14 @@ import org.springframework.stereotype.Service;
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
import java.math.BigDecimal;
|
|
import java.math.RoundingMode;
|
|
import java.math.RoundingMode;
|
|
-import java.time.DayOfWeek;
|
|
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDateTime;
|
|
import java.time.LocalDateTime;
|
|
import java.time.format.DateTimeFormatter;
|
|
import java.time.format.DateTimeFormatter;
|
|
-import java.time.format.DateTimeParseException;
|
|
|
|
-import java.time.temporal.ChronoUnit;
|
|
|
|
-import java.time.temporal.TemporalAdjusters;
|
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
+import static com.pig4cloud.pig.common.core.util.stat.TimeUtil.*;
|
|
|
|
+
|
|
|
|
|
|
/**
|
|
/**
|
|
* @author: lwh
|
|
* @author: lwh
|
|
@@ -1164,243 +1162,6 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
|
|
|
|
|
|
|
|
|
|
/************************************** 公用方法 **************************************
|
|
/************************************** 公用方法 **************************************
|
|
- * 根据时间粒度和日期字符串获取开始时间
|
|
|
|
- */
|
|
|
|
- private LocalDateTime getStartTime(String dateStr, String timeUnit) {
|
|
|
|
- try {
|
|
|
|
- return switch (timeUnit) {
|
|
|
|
- case "hour" -> {
|
|
|
|
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00");
|
|
|
|
- yield LocalDateTime.parse(dateStr, formatter);
|
|
|
|
- }
|
|
|
|
- case "day", "week", "month" -> {
|
|
|
|
- DateTimeFormatter day = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
|
- yield LocalDate.parse(dateStr, day).atStartOfDay();
|
|
|
|
- }
|
|
|
|
- default -> {
|
|
|
|
- log.warn("不支持的时间粒度: {}", timeUnit);
|
|
|
|
- yield null;
|
|
|
|
- }
|
|
|
|
- };
|
|
|
|
- } catch (DateTimeParseException e) {
|
|
|
|
- log.error("日期解析失败, date: {}, timeUnit: {}", dateStr, timeUnit, e);
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 根据时间粒度和日期字符串获取结束时间
|
|
|
|
- */
|
|
|
|
- private LocalDateTime getEndTime(String dateStr, String timeUnit) {
|
|
|
|
- try {
|
|
|
|
- switch (timeUnit) {
|
|
|
|
- case "hour":
|
|
|
|
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00");
|
|
|
|
- return LocalDateTime.parse(dateStr, formatter).plusHours(1);
|
|
|
|
- case "day":
|
|
|
|
- DateTimeFormatter day = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
|
- return LocalDate.parse(dateStr, day).atStartOfDay().plusDays(1);
|
|
|
|
- case "week":
|
|
|
|
- DateTimeFormatter week = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
|
- return LocalDate.parse(dateStr, week).atStartOfDay().plusWeeks(1);
|
|
|
|
- case "month":
|
|
|
|
- DateTimeFormatter month = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
|
- LocalDate startDate = LocalDate.parse(dateStr, month);
|
|
|
|
- return startDate.plusMonths(1).atStartOfDay();
|
|
|
|
- default:
|
|
|
|
- log.warn("不支持的时间粒度: {}", timeUnit);
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
- } catch (DateTimeParseException e) {
|
|
|
|
- log.error("日期解析失败, date: {}, timeUnit: {}", dateStr, timeUnit, e);
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 生成时间轴列表
|
|
|
|
- */
|
|
|
|
- private List<String> generateTimeAxis(LocalDate fromDate, LocalDate toDate, String timeUnit) {
|
|
|
|
- List<String> timeAxis = new ArrayList<>();
|
|
|
|
- DateTimeFormatter formatter;
|
|
|
|
-
|
|
|
|
- switch (timeUnit) {
|
|
|
|
- case "hour":
|
|
|
|
- formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00");
|
|
|
|
- LocalDateTime startHour = fromDate.atStartOfDay();
|
|
|
|
- LocalDateTime endHour = toDate.atTime(23, 0);
|
|
|
|
-
|
|
|
|
- while (!startHour.isAfter(endHour)) {
|
|
|
|
- timeAxis.add(startHour.format(formatter));
|
|
|
|
- startHour = startHour.plusHours(1);
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case "day":
|
|
|
|
- formatter = DateTimeFormatter.ISO_LOCAL_DATE;
|
|
|
|
- LocalDate currentDay = fromDate;
|
|
|
|
-
|
|
|
|
- while (!currentDay.isAfter(toDate)) {
|
|
|
|
- timeAxis.add(currentDay.format(formatter));
|
|
|
|
- currentDay = currentDay.plusDays(1);
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case "week":
|
|
|
|
- formatter = DateTimeFormatter.ISO_LOCAL_DATE;
|
|
|
|
- LocalDate firstSunday = fromDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));
|
|
|
|
- LocalDate lastSunday = toDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));
|
|
|
|
- LocalDate currentSunday = firstSunday;
|
|
|
|
- while (!currentSunday.isAfter(lastSunday)) {
|
|
|
|
- timeAxis.add(currentSunday.format(formatter));
|
|
|
|
- currentSunday = currentSunday.plusWeeks(1);
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case "month":
|
|
|
|
- formatter = DateTimeFormatter.ISO_LOCAL_DATE;
|
|
|
|
- LocalDate firstDayOfMonth = fromDate.with(TemporalAdjusters.firstDayOfMonth());
|
|
|
|
- LocalDate lastDayOfMonth = toDate.with(TemporalAdjusters.firstDayOfMonth());
|
|
|
|
-
|
|
|
|
- LocalDate currentMonth = firstDayOfMonth;
|
|
|
|
- while (!currentMonth.isAfter(lastDayOfMonth)) {
|
|
|
|
- timeAxis.add(currentMonth.format(formatter));
|
|
|
|
- currentMonth = currentMonth.plusMonths(1).with(TemporalAdjusters.firstDayOfMonth());
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default:
|
|
|
|
- throw new IllegalArgumentException("不支持的时间单位: " + timeUnit);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return timeAxis;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 生成指定分页的时间坐标轴
|
|
|
|
- */
|
|
|
|
- private List<String> generatePageTimeAxis(LocalDate fromDate, LocalDate toDate, String timeUnit, long current, long size) {
|
|
|
|
- List<String> timeAxis = new ArrayList<>();
|
|
|
|
- DateTimeFormatter formatter;
|
|
|
|
-
|
|
|
|
- // 计算起始位置
|
|
|
|
- long startPosition = (current - 1) * size;
|
|
|
|
- long currentPosition = 0;
|
|
|
|
-
|
|
|
|
- switch (timeUnit) {
|
|
|
|
- case "hour":
|
|
|
|
- formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00");
|
|
|
|
- LocalDateTime startHour = fromDate.atStartOfDay();
|
|
|
|
- LocalDateTime endHour = toDate.atTime(23, 0);
|
|
|
|
-
|
|
|
|
- // 跳过前面页的数据
|
|
|
|
- while (!startHour.isAfter(endHour) && currentPosition < startPosition) {
|
|
|
|
- startHour = startHour.plusHours(1);
|
|
|
|
- currentPosition++;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 收集当前页的数据
|
|
|
|
- while (!startHour.isAfter(endHour) && timeAxis.size() < size) {
|
|
|
|
- timeAxis.add(startHour.format(formatter));
|
|
|
|
- startHour = startHour.plusHours(1);
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case "day":
|
|
|
|
- formatter = DateTimeFormatter.ISO_LOCAL_DATE;
|
|
|
|
- LocalDate currentDay = fromDate;
|
|
|
|
-
|
|
|
|
- // 跳过前面页的数据
|
|
|
|
- while (!currentDay.isAfter(toDate) && currentPosition < startPosition) {
|
|
|
|
- currentDay = currentDay.plusDays(1);
|
|
|
|
- currentPosition++;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 收集当前页的数据
|
|
|
|
- while (!currentDay.isAfter(toDate) && timeAxis.size() < size) {
|
|
|
|
- timeAxis.add(currentDay.format(formatter));
|
|
|
|
- currentDay = currentDay.plusDays(1);
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case "week":
|
|
|
|
- formatter = DateTimeFormatter.ISO_LOCAL_DATE;
|
|
|
|
- LocalDate firstSunday = fromDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));
|
|
|
|
- LocalDate lastSunday = toDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));
|
|
|
|
- LocalDate currentSunday = firstSunday;
|
|
|
|
-
|
|
|
|
- // 跳过前面页的数据
|
|
|
|
- while (!currentSunday.isAfter(lastSunday) && currentPosition < startPosition) {
|
|
|
|
- currentSunday = currentSunday.plusWeeks(1);
|
|
|
|
- currentPosition++;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 收集当前页的数据
|
|
|
|
- while (!currentSunday.isAfter(lastSunday) && timeAxis.size() < size) {
|
|
|
|
- timeAxis.add(currentSunday.format(formatter));
|
|
|
|
- currentSunday = currentSunday.plusWeeks(1);
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case "month":
|
|
|
|
- formatter = DateTimeFormatter.ISO_LOCAL_DATE;
|
|
|
|
- LocalDate firstDayOfMonth = fromDate.with(TemporalAdjusters.firstDayOfMonth());
|
|
|
|
- LocalDate lastDayOfMonth = toDate.with(TemporalAdjusters.firstDayOfMonth());
|
|
|
|
-
|
|
|
|
- LocalDate currentMonth = firstDayOfMonth;
|
|
|
|
-
|
|
|
|
- // 跳过前面页的数据
|
|
|
|
- while (!currentMonth.isAfter(lastDayOfMonth) && currentPosition < startPosition) {
|
|
|
|
- currentMonth = currentMonth.plusMonths(1).with(TemporalAdjusters.firstDayOfMonth());
|
|
|
|
- currentPosition++;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 收集当前页的数据
|
|
|
|
- while (!currentMonth.isAfter(lastDayOfMonth) && timeAxis.size() < size) {
|
|
|
|
- timeAxis.add(currentMonth.format(formatter));
|
|
|
|
- currentMonth = currentMonth.plusMonths(1).with(TemporalAdjusters.firstDayOfMonth());
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default:
|
|
|
|
- throw new IllegalArgumentException("不支持的时间单位: " + timeUnit);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return timeAxis;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * 计算总记录数(用于分页)
|
|
|
|
- */
|
|
|
|
- private long calculateTotalCount(LocalDate fromDate, LocalDate toDate, String timeUnit) {
|
|
|
|
- switch (timeUnit) {
|
|
|
|
- case "hour":
|
|
|
|
- // 计算小时数: 天数差 * 24 + 结束日小时数 - 开始日小时数 + 1
|
|
|
|
- long days = ChronoUnit.DAYS.between(fromDate, toDate);
|
|
|
|
- return days * 24 + 24; // 每天24小时
|
|
|
|
-
|
|
|
|
- case "day":
|
|
|
|
- // 计算天数
|
|
|
|
- return ChronoUnit.DAYS.between(fromDate, toDate) + 1;
|
|
|
|
-
|
|
|
|
- case "week":
|
|
|
|
- // 计算周数
|
|
|
|
- LocalDate firstSunday = fromDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));
|
|
|
|
- LocalDate lastSunday = toDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));
|
|
|
|
- return ChronoUnit.WEEKS.between(firstSunday, lastSunday) + 1;
|
|
|
|
-
|
|
|
|
- case "month":
|
|
|
|
- // 计算月数:基于月份第一天计算,与generatePageTimeAxis逻辑保持一致
|
|
|
|
- LocalDate firstMonDay = fromDate.with(TemporalAdjusters.firstDayOfMonth());
|
|
|
|
- LocalDate lastMonDay = toDate.with(TemporalAdjusters.firstDayOfMonth());
|
|
|
|
- return ChronoUnit.MONTHS.between(firstMonDay, lastMonDay) + 1;
|
|
|
|
-
|
|
|
|
- default:
|
|
|
|
- throw new IllegalArgumentException("不支持的时间单位: " + timeUnit);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
* 统计指定时间范围内的新增用户数
|
|
* 统计指定时间范围内的新增用户数
|
|
*/
|
|
*/
|
|
private Long statNewUserCount(LocalDateTime startTime, LocalDateTime endTime, String appId, String version, List<String> channels){
|
|
private Long statNewUserCount(LocalDateTime startTime, LocalDateTime endTime, String appId, String version, List<String> channels){
|