package com.pig4cloud.pig.statistics.service.impl;


import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pig.common.core.exception.BusinessException;
import com.pig4cloud.pig.statistics.api.dto.user.*;
import com.pig4cloud.pig.statistics.api.entity.user.MktStatActiveUser;
import com.pig4cloud.pig.statistics.api.entity.user.MktStatNewUser;
import com.pig4cloud.pig.statistics.api.entity.user.MktStatUserAnalysis;
import com.pig4cloud.pig.statistics.api.vo.user.*;
import com.pig4cloud.pig.statistics.mapper.MktStatActiveUserMapper;
import com.pig4cloud.pig.statistics.mapper.MktStatNewUserMapper;
import com.pig4cloud.pig.statistics.mapper.MktStatUserAnalysisMapper;
import com.pig4cloud.pig.statistics.mapper.MktStatUserRetentionMapper;
import com.pig4cloud.pig.statistics.service.UserAnalyseService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
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.stream.Collectors;


/**
 * @author: lwh
 * @date: 2025-08-06
 * @description: 用户分析Service实现类
 */
@Slf4j
@Service
@AllArgsConstructor
public class UserAnalyseServiceImpl implements UserAnalyseService {

	private final MktStatUserAnalysisMapper userAnalysisMapper;

	private final MktStatActiveUserMapper activeUserMapper;

	private final MktStatNewUserMapper newUserMapper;

	/**
	 * 获取新增趋势
	 * @param reqDto 请求参数
	 * @return GetNewUserTrendVO
	 */
	@Override
	public GetNewUserTrendVO getNewUserTrend(UserAnalyseQueryBaseDTO reqDto) {
		GetNewUserTrendVO result = new GetNewUserTrendVO();
		// 1. 生成时间轴
		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
		result.setDates(dates);
		LocalDateTime fromDate = getStartTime(reqDto.getFromDate().toString(), "day");
		LocalDateTime toDate = getEndTime(reqDto.getToDate().toString(), "day");

		// 2. 处理版本列表（为空时按null处理）
		List<String> versions = reqDto.getVersion();
		List<String> processVersions = (versions == null || versions.isEmpty())
				? Collections.singletonList(null)
				: versions.stream().filter(v -> v != null && !v.isEmpty()).toList();

		// 3. 处理每个版本的数据
		List<TrendBaseVO> voList = processVersions.stream()
				.map(version -> {
					TrendBaseVO trend = new TrendBaseVO();
					trend.setName("新增用户");
					trend.setVersion(version == null ? "All" : version);
					trend.setKey("newUser");

					// 计算每个时间点的活跃用户数
					List<Long> counts = dates.stream()
							.map(date -> {
								LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
								LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());
								// 如果startTime早于fromDate
								if (startTime.isBefore(fromDate)) {
									startTime = fromDate;
								}

								// 如果endTime晚于toDate
								if (endTime.isAfter(toDate)) {
									endTime = toDate;
								}
								return statNewUserCount(startTime, endTime, reqDto.getAppId(), version, reqDto.getChannel());
							})
							.collect(Collectors.toList());
					trend.setData(counts);
					return trend;
				})
				.toList();
		result.setItems(voList);
		return result;
	}

	/**
	 * 分页查询新增趋势详情
	 * @param reqDto 请求参数
	 * @return Page
	 */
	@Override
	public Page<PageNewUserTrendDetailVO> pageNewUserTrendDetail(PageUserAnalyseDTO reqDto) {
		// 初始化分页对象并计算总条数
		Page<PageNewUserTrendDetailVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
		page.setTotal(calculateTotalCount(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit()));
		// 生成当前页时间轴
		List<String> dates = generatePageTimeAxis(reqDto.getFromDate(), reqDto.getToDate(),
				reqDto.getTimeUnit(), reqDto.getCurrent(), reqDto.getSize());
		if (dates.isEmpty()) {
			return page; // 无数据直接返回
		}
		LocalDateTime fromDate = getStartTime(reqDto.getFromDate().toString(), "day");
		LocalDateTime toDate = getEndTime(reqDto.getToDate().toString(), "day");

		// 获取渠道和版本
		List<String> channel = reqDto.getChannel();
		String version = null;
		if (reqDto.getVersion() != null && !reqDto.getVersion().isEmpty()){
			version = reqDto.getVersion().get(0);
		}

		// 查询结果
		List<PageNewUserTrendDetailVO> items = new ArrayList<>(dates.size());
		for (String date : dates) {
			// 获取时间范围
			LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
			LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());
			// 如果startTime早于fromDate
			if (startTime.isBefore(fromDate)) {
				startTime = fromDate;
			}

			// 如果endTime晚于toDate
			if (endTime.isAfter(toDate)) {
				endTime = toDate;
			}
			// 查询新增用户数
			BigDecimal newUser = BigDecimal.valueOf(statNewUserCount(startTime, endTime, reqDto.getAppId(), version, channel));
			// 查询活跃用户数
			BigDecimal activeUser = BigDecimal.valueOf(statActiveUserCount(startTime, endTime, reqDto.getAppId(), version, channel));
			// 计算新用户占比
			BigDecimal newRate = newUser.compareTo(BigDecimal.ZERO) == 0 || activeUser.compareTo(BigDecimal.ZERO) == 0
					? BigDecimal.ZERO
					: newUser.divide(activeUser, 4, RoundingMode.HALF_UP);

			// 构建返回对象
			PageNewUserTrendDetailVO detail = new PageNewUserTrendDetailVO();
			// 定义日期格式
			DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
			// 处理日期显示
			boolean isRangeType = "week".equals(reqDto.getTimeUnit()) || "month".equals(reqDto.getTimeUnit());
			String displayDate = isRangeType && startTime != null && endTime != null
					? startTime.toLocalDate().format(dateFormatter) + "~" + endTime.minusDays(1).toLocalDate().format(dateFormatter)
					: date;
			detail.setDate(displayDate);
			detail.setNewUser(newUser.intValue());
			detail.setNewUserRate(newRate);
			items.add( detail);
		}
		// 返回结果
		page.setRecords(items);
		return page;
	}

	/**
	 * 查询次日留存率
	 * @param reqDto 获取次日留存率入参
	 * @return GetNewUserRetentionVO
	 */
	@Override
	public GetNewUserRetentionVO getNewUserRetention(UserAnalyseQueryBaseDTO reqDto) {
		// 校验时间粒度
		if (!"day".equals(reqDto.getTimeUnit())){
			throw new BusinessException("时间粒度只能为日");
		}
		// 生成时间轴
		GetNewUserRetentionVO result = new GetNewUserRetentionVO();
		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
		ArrayList<BigDecimal> retentions = new ArrayList<>();
		// 获取版本和渠道
		String version = null;
		if (reqDto.getVersion() != null && !reqDto.getVersion().isEmpty()){
			version = reqDto.getVersion().get(0);
		}
		List<String> channel = reqDto.getChannel();
		// 变量时间轴
		for (String date : dates) {
			// 获取时间范围
			LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
			LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());
			if (startTime == null || endTime == null){
				throw new BusinessException("获取时间范围错误");
			}
			// 留存率：今日新增的用户在明天活跃用户中有多少
			// 查询今日新增用户
			List<String> newUserIds = getNewUserIdList(startTime, endTime, reqDto.getAppId(), version, channel);
			// 查询明天活跃用户有多少
			List<String> activeUserIds = getActiveUserIdList(startTime.plusDays(1), endTime.plusDays(1), reqDto.getAppId(), version, channel);
			// 计算留存率
			Set<String> activeUserSet = new HashSet<>(activeUserIds);
			Set<String> newUserSet = new HashSet<>(newUserIds);
			//保留在活跃用户集合中的新增用户
			activeUserSet.retainAll(newUserSet);
			BigDecimal activeCount = BigDecimal.valueOf(activeUserSet.size());
			BigDecimal newUserCount = BigDecimal.valueOf(newUserIds.size());
			// 留存率
			BigDecimal retention = newUserCount.compareTo(BigDecimal.ZERO) == 0 || activeCount.compareTo(BigDecimal.ZERO) == 0
					? BigDecimal.ZERO
					: activeCount.divide(newUserCount, 4, RoundingMode.HALF_UP);
			retentions.add( retention);
		}
		result.setDates(dates);
		result.setRetentions(retentions);
		return result;
	}

	/**
	 * 分页查询次日留存率详情
	 * @param reqDto 分页查询次日留存率详情入参
	 * @return Page
	 */
	@Override
	public Page<PageRetentionDetailVO> pageNewUserRetentionDetail(PageUserAnalyseDTO reqDto) {
		if (!"day".equals(reqDto.getTimeUnit())) {
			throw new BusinessException("时间单位只能为day");
		}
		// 初始化分页对象并计算总条数
		Page<PageRetentionDetailVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
		page.setTotal(calculateTotalCount(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit()));
		// 生成当前页时间轴
		List<String> dates = generatePageTimeAxis(reqDto.getFromDate(), reqDto.getToDate(),
				reqDto.getTimeUnit(), reqDto.getCurrent(), reqDto.getSize());
		if (dates.isEmpty()) {
			return page; // 无数据直接返回
		}
		// 获取渠道和版本
		List<String> channel = reqDto.getChannel();
		String version = null;
		if (reqDto.getVersion() != null && !reqDto.getVersion().isEmpty()){
			version = reqDto.getVersion().get(0);
		}
		// 查询结果
		List<PageRetentionDetailVO> result = new ArrayList<>();
		// 遍历时间轴
		for (String date : dates) {
			// 获取时间范围
			LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
			LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());
			if (startTime == null || endTime == null){
				throw new BusinessException("获取时间范围错误");
			}
			// 留存率：今日新增的用户在明天活跃用户中有多少
			// 查询今日新增用户
			List<String> newUserIds = getNewUserIdList(startTime, endTime, reqDto.getAppId(), version, channel);
			// 查询明天活跃用户有多少
			List<String> activeUserIds = getActiveUserIdList(startTime.plusDays(1), endTime.plusDays(1), reqDto.getAppId(), version, channel);
			// 计算留存率
			Set<String> activeUserSet = new HashSet<>(activeUserIds);
			Set<String> newUserSet = new HashSet<>(newUserIds);
			//保留在活跃用户集合中的新增用户
			activeUserSet.retainAll(newUserSet);
			BigDecimal activeCount = BigDecimal.valueOf(activeUserSet.size());
			BigDecimal newUserCount = BigDecimal.valueOf(newUserIds.size());
			// 留存率
			BigDecimal retention = newUserCount.compareTo(BigDecimal.ZERO) == 0 || activeCount.compareTo(BigDecimal.ZERO) == 0
					? BigDecimal.ZERO
					: activeCount.divide(newUserCount, 4, RoundingMode.HALF_UP);
			// 组装结果
			PageRetentionDetailVO detail = new PageRetentionDetailVO();
			detail.setDate(date);
			detail.setRetention( retention);
			result.add( detail);
		}
		// 返回结果
		page.setRecords(result);
		return page;
	}

	/************************************** 活跃用户 *****************************************
	 * 查询活跃趋势
	 * @param reqDto 查询活跃趋势入参
	 * @return GetNewUserTrendVO
	 */
	@Override
	public GetNewUserTrendVO getActiveTrend(UserAnalyseQueryBaseDTO reqDto) {
		GetNewUserTrendVO result = new GetNewUserTrendVO();
		// 1. 生成时间轴
		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
		result.setDates(dates);

		LocalDateTime fromDate = getStartTime(reqDto.getFromDate().toString(), "day");
		LocalDateTime toDate = getEndTime(reqDto.getToDate().toString(), "day");


		// 2. 处理版本列表（为空时按null处理）
		List<String> versions = reqDto.getVersion();
		List<String> processVersions = (versions == null || versions.isEmpty())
				? Collections.singletonList(null)
				: versions.stream().filter(v -> v != null && !v.isEmpty()).toList();

		// 3. 处理每个版本的数据
		List<TrendBaseVO> voList = processVersions.stream()
				.map(version -> {
					TrendBaseVO trend = new TrendBaseVO();
					trend.setName("活跃用户");
					trend.setVersion(version == null ? "All" : version);
					trend.setKey("activeUser");

					// 计算每个时间点的活跃用户数
					List<Long> counts = dates.stream()
							.map(date -> {
								LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
								LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());
								// 如果startTime早于fromDate
								if (startTime.isBefore(fromDate)) {
									startTime = fromDate;
								}

								// 如果endTime晚于toDate
								if (endTime.isAfter(toDate)) {
									endTime = toDate;
								}
								return statActiveUserCount(startTime, endTime, reqDto.getAppId(), version, reqDto.getChannel());
							})
							.collect(Collectors.toList());
					trend.setData(counts);
					return trend;
				})
				.toList();
		result.setItems(voList);
		return result;
	}

	/**
	 * 查询活跃构成
	 * @param reqDto 查询活跃构成入参
	 * @return GetActiveComposeVO
	 */
	@Override
	public GetActiveUserBaseVO getActiveCompose(UserAnalyseQueryBaseDTO reqDto) {
		// 1. 获取时间坐标轴
		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
		LocalDateTime fromDate = getStartTime(reqDto.getFromDate().toString(), "day");
		LocalDateTime toDate = getEndTime(reqDto.getToDate().toString(), "day");

		// 获取版本信息
		String version = null;
		if (reqDto.getVersion() != null && !reqDto.getVersion().isEmpty()){
			version = reqDto.getVersion().get(0);
		}
		List<BigDecimal> newCounts = new ArrayList<>();
		List<BigDecimal> activeCounts = new ArrayList<>();
		List<BigDecimal> newRates = new ArrayList<>();
		// 2. 遍历时间坐标轴
		for (String date : dates) {
			LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
			LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());

			// 如果startTime早于fromDate
			if (startTime.isBefore(fromDate)) {
				startTime = fromDate;
			}

			// 如果endTime晚于toDate
			if (endTime.isAfter(toDate)) {
				endTime = toDate;
			}
			// 活跃用户数
			BigDecimal activeCount = BigDecimal.valueOf(statActiveUserCount(startTime, endTime, reqDto.getAppId(), version, reqDto.getChannel()));
			activeCounts.add( activeCount);
			// 活跃用户中的新增用户数
			BigDecimal newCount = BigDecimal.valueOf(getActiveNewUserCount(startTime, endTime, reqDto.getAppId(), version, reqDto.getChannel()));
			newCounts.add( newCount);
			// 新增用户占比
			BigDecimal newRate = activeCount.compareTo(BigDecimal.ZERO) == 0 || newCount.compareTo(BigDecimal.ZERO) == 0 ?
					BigDecimal.ZERO:
					newCount.divide(activeCount, 4, RoundingMode.HALF_UP);
			newRates.add(newRate);
		}

		// 组装结果
		List<ActiveUserComposeVO> items = new ArrayList<>();

		ActiveUserComposeVO activeUser = new ActiveUserComposeVO();
		activeUser.setName("活跃用户");
		activeUser.setData(activeCounts);
		activeUser.setKey("activeUser");
		activeUser.setVersion( version == null ? "All" : version);
		items.add(activeUser);

		ActiveUserComposeVO newUser = new ActiveUserComposeVO();
		newUser.setName("新增用户");
		newUser.setData(newCounts);
		newUser.setKey("newUser");
		newUser.setVersion( version == null ? "All" : version);
		items.add(newUser);

		ActiveUserComposeVO newUserRate = new ActiveUserComposeVO();
		newUserRate.setName("新增用户占比");
		newUserRate.setData(newRates);
		newUserRate.setKey("newUserRate");
		newUserRate.setVersion( version == null ? "All" : version);
		items.add(newUserRate);

		GetActiveUserBaseVO result = new GetActiveUserBaseVO();
		result.setDates(dates);
		result.setItems(items);
		return result;
	}

	/**
	 * 获取活跃粘度
	 * @param reqDto 获取活跃粘度入参
	 * @return GetActiveUserBaseVO
	 */
	@Override
	public GetActiveUserBaseVO getActiveViscosity(UserAnalyseQueryBaseDTO reqDto) {
		// 1. 时间单位校验
		if (!"day".equals(reqDto.getTimeUnit())){
			throw new BusinessException("时间单位只能为day");
		}
		// 2. 获取版本
		String version = null;
		if (reqDto.getVersion() != null && !reqDto.getVersion().isEmpty()) {
			version =reqDto.getVersion().get(0);
		}
		// 3. 定义相关变量
		List<ActiveUserComposeVO> items = new ArrayList<>();

		List<BigDecimal> dayCounts = new ArrayList<>();
		List<BigDecimal> sevenCounts = new ArrayList<>();
		List<BigDecimal> thirtyCounts = new ArrayList<>();
		List<BigDecimal> sevenRates = new ArrayList<>();
		List<BigDecimal> thirtyRates = new ArrayList<>();
		// 4. 获取时间坐标轴
		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
		// 5. 遍历时间坐标轴
		for (String date : dates) {
			// 5.1 定义时间范围
			LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
			LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());
			LocalDateTime sevenDaysAgo = startTime.minusDays(7);
			LocalDateTime thirtyDaysAgo = startTime.minusDays(30);
			// 5.2 今日活跃用户数
			BigDecimal dayCount = BigDecimal.valueOf(statActiveUserCount(startTime, endTime, reqDto.getAppId(), version, reqDto.getChannel()));
			dayCounts.add(dayCount);
			// 5.3 过去7日活跃用户
			BigDecimal sevenCount = BigDecimal.valueOf(statActiveUserCount(sevenDaysAgo, startTime, reqDto.getAppId(), version, reqDto.getChannel()));
			sevenCounts.add(sevenCount);
			// 5.4 过去30日活跃用户
			BigDecimal thirtyCount = BigDecimal.valueOf(statActiveUserCount(thirtyDaysAgo, startTime, reqDto.getAppId(), version, reqDto.getChannel()));
			thirtyCounts.add(thirtyCount);
			// 5.5 计算7日活跃用户占比
			sevenRates.add(dayCount.compareTo(BigDecimal.ZERO) == 0 || sevenCount.compareTo(BigDecimal.ZERO) == 0
					? BigDecimal.ZERO
					: dayCount.divide(sevenCount, 4, RoundingMode.HALF_UP));
			// 5.6 计算30日活跃用户占比
			thirtyRates.add(dayCount.compareTo(BigDecimal.ZERO) == 0 || thirtyCount.compareTo(BigDecimal.ZERO) == 0
					? BigDecimal.ZERO:
					dayCount.divide(thirtyCount, 4, RoundingMode.HALF_UP));
		}
		// 6. 组装返回数据
		ActiveUserComposeVO activeUser = new ActiveUserComposeVO();
		activeUser.setData(dayCounts);
		activeUser.setName("日活");
		activeUser.setKey("activeUser");
		activeUser.setVersion(version == null ? "All" : version);
		items.add(activeUser);

		ActiveUserComposeVO wau = new ActiveUserComposeVO();
		wau.setData(sevenCounts);
		wau.setName("过去7日活跃用户");
		wau.setKey("wau");
		wau.setVersion(version == null ? "All" : version);
		items.add(wau);

		ActiveUserComposeVO wauRate = new ActiveUserComposeVO();
		wauRate.setData(sevenRates);
		wauRate.setName("DAU/过去7日活跃用户");
		wauRate.setKey("wauRate");
		wauRate.setVersion(version == null ? "All" : version);
		items.add(wauRate);

		ActiveUserComposeVO mau = new ActiveUserComposeVO();
		mau.setData(thirtyCounts);
		mau.setName("过去30日活跃用户");
		mau.setKey("mau");
		mau.setVersion(version == null ? "All" : version);
		items.add(mau);

		ActiveUserComposeVO mauRate = new ActiveUserComposeVO();
		mauRate.setData(thirtyRates);
		mauRate.setName("DAU/过去30日活跃用户");
		mauRate.setKey("mauRate");
		mauRate.setVersion(version == null ? "All" : version);
		items.add(mauRate);

		// 7.返回结果
		GetActiveUserBaseVO result = new GetActiveUserBaseVO();
		result.setDates(dates);
		result.setItems(items);
		return result;
	}

	/**
	 * 获取周活跃率
	 * @param reqDto 获取周活跃率入参
	 * @return GetActiveUserBaseVO
	 */
	@Override
	public GetActiveUserBaseVO getActiveWeekRate(UserAnalyseQueryBaseDTO reqDto) {
		if (!"week".equals(reqDto.getTimeUnit())){
			throw new BusinessException("时间单位只能为week");
		}
		// 1. 生成时间轴
		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
		LocalDateTime fromDate = getStartTime(reqDto.getFromDate().toString(), "day");
		LocalDateTime toDate = getEndTime(reqDto.getToDate().toString(), "day");

		// 2. 定义存储列表
		List<BigDecimal> activeList = new ArrayList<>();
		List<BigDecimal> userList = new ArrayList<>();
		List<BigDecimal> weekRateList = new ArrayList<>();
		// 3. 遍历时间轴
		for (String date : dates) {
			LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
			LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());
			// 如果startTime早于fromDate
			if (startTime.isBefore(fromDate)) {
				startTime = fromDate;
			}

			// 如果endTime晚于toDate
			if (endTime.isAfter(toDate)) {
				endTime = toDate;
			}
			// 4. 查询、计算累计用户数
			BigDecimal totalUser = BigDecimal.valueOf(statNewUserCount(null, endTime, reqDto.getAppId(), null, null));
			userList.add(totalUser);
			// 5. 查询、计算本周活跃用户数
			BigDecimal activeUser = BigDecimal.valueOf(statActiveUserCount(startTime, endTime, reqDto.getAppId(), null, null));
			activeList.add(activeUser);
			// 6. 计算周活跃率
			BigDecimal weekRate = totalUser.compareTo(BigDecimal.ZERO) != 0 && activeUser.compareTo(BigDecimal.ZERO) != 0
					? activeUser.divide(totalUser, 4, RoundingMode.HALF_UP)
					: BigDecimal.ZERO;
			weekRateList.add(weekRate);
		}
		// 7. 组装结果数据
		ActiveUserComposeVO totalUser = new ActiveUserComposeVO();
		totalUser.setName("周累积用户");
		totalUser.setKey("weekTotalUser");
		totalUser.setVersion("All");
		totalUser.setData(userList);

		ActiveUserComposeVO activeUser = new ActiveUserComposeVO();
		activeUser.setName("周活跃用户");
		activeUser.setKey("weekActiveUser");
		activeUser.setVersion("All");
		activeUser.setData(activeList);

		ActiveUserComposeVO weekRate = new ActiveUserComposeVO();
		weekRate.setName("周活跃用户率");
		weekRate.setKey("weekActiveUserRate");
		weekRate.setVersion("All");
		weekRate.setData(weekRateList);
		// 8. 返回结果
		GetActiveUserBaseVO result = new GetActiveUserBaseVO();
		result.setDates(dates);
		result.setItems(Arrays.asList(totalUser, activeUser, weekRate));
		return result;
	}

	/**
	 * 获取月活跃率
	 * @param reqDto 获取月活跃率入参
	 * @return GetActiveUserBaseVO
	 */
	@Override
	public GetActiveUserBaseVO getActiveMonthRate(UserAnalyseQueryBaseDTO reqDto) {
		if (!"month".equals(reqDto.getTimeUnit())){
			throw new BusinessException("时间单位只能为month");
		}

		// 1. 生成时间轴
		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
		LocalDateTime fromDate = getStartTime(reqDto.getFromDate().toString(), "day");
		LocalDateTime toDate = getEndTime(reqDto.getToDate().toString(), "day");

		// 2. 定义存储列表
		List<BigDecimal> activeList = new ArrayList<>();
		List<BigDecimal> userList = new ArrayList<>();
		List<BigDecimal> monthRateList = new ArrayList<>();
		// 3. 遍历时间轴
		for (String date : dates) {
			LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
			LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());
			// 如果startTime早于fromDate
			if (startTime.isBefore(fromDate)) {
				startTime = fromDate;
			}

			// 如果endTime晚于toDate
			if (endTime.isAfter(toDate)) {
				endTime = toDate;
			}
			// 4. 查询、计算累计用户数
			BigDecimal totalUser = BigDecimal.valueOf(statNewUserCount(null, endTime, reqDto.getAppId(), null, null));
			userList.add(totalUser);
			// 5. 查询、计算本月活跃用户数
			BigDecimal activeUser = BigDecimal.valueOf(statActiveUserCount(startTime, endTime, reqDto.getAppId(), null, null));
			activeList.add(activeUser);
			// 6. 计算月活跃率
			BigDecimal weekRate = totalUser.compareTo(BigDecimal.ZERO) != 0 && activeUser.compareTo(BigDecimal.ZERO) != 0
					? activeUser.divide(totalUser, 4, RoundingMode.HALF_UP)
					: BigDecimal.ZERO;
			monthRateList.add(weekRate);
		}
		// 7. 组装结果数据
		ActiveUserComposeVO totalUser = new ActiveUserComposeVO();
		totalUser.setName("月累积用户");
		totalUser.setKey("monthTotalUser");
		totalUser.setVersion("All");
		totalUser.setData(userList);

		ActiveUserComposeVO activeUser = new ActiveUserComposeVO();
		activeUser.setName("月活跃用户");
		activeUser.setKey("monthActiveUser");
		activeUser.setVersion("All");
		activeUser.setData(activeList);

		ActiveUserComposeVO monthRate = new ActiveUserComposeVO();
		monthRate.setName("月活跃用户率");
		monthRate.setKey("monthActiveUserRate");
		monthRate.setVersion("All");
		monthRate.setData(monthRateList);
		// 8. 返回结果
		GetActiveUserBaseVO result = new GetActiveUserBaseVO();
		result.setDates(dates);
		result.setItems(Arrays.asList(totalUser, activeUser, monthRate));
		return result;
	}

	/**
	 * 分页查询活跃详情
	 * @param reqDto 分页查询活跃详情入参
	 * @return Page
	 */
	@Override
	public Page<PageActiveDetailVO> pageActiveDetail(PageUserAnalyseDTO reqDto) {
		// 1. 初始化分页对象并计算总条数
		Page<PageActiveDetailVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
		page.setTotal(calculateTotalCount(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit()));

		// 2. 生成当前页时间轴
		List<String> dates = generatePageTimeAxis(reqDto.getFromDate(), reqDto.getToDate(),
				reqDto.getTimeUnit(), reqDto.getCurrent(), reqDto.getSize());
		if (dates.isEmpty()) {
			return page; // 无数据直接返回
		}
		LocalDateTime fromDate = getStartTime(reqDto.getFromDate().toString(), "day");
		LocalDateTime toDate = getEndTime(reqDto.getToDate().toString(), "day");

		// 获取渠道和版本
		List<String> channel = reqDto.getChannel();
		String version = null;
		if (reqDto.getVersion() != null && !reqDto.getVersion().isEmpty()){
			version = reqDto.getVersion().get(0);
		}
		// 查询结果
		List<PageActiveDetailVO> result = new ArrayList<>();
		for (String date : dates) {
			// 获取时间范围
			LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
			LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());
			// 如果startTime早于fromDate
			if (startTime.isBefore(fromDate)) {
				startTime = fromDate;
			}

			// 如果endTime晚于toDate
			if (endTime.isAfter(toDate)) {
				endTime = toDate;
			}
			// 查询活跃用户数量
			BigDecimal activeCount = BigDecimal.valueOf(statActiveUserCount(startTime, endTime, reqDto.getAppId(), version, channel));
			// 查询活跃用户中的新增用户数
			BigDecimal newCount = BigDecimal.valueOf(getActiveNewUserCount(startTime, endTime, reqDto.getAppId(), version, channel));
			// 计算新增用户占比
			BigDecimal newUserRate = activeCount.compareTo(BigDecimal.ZERO) == 0 || newCount.compareTo(BigDecimal.ZERO) == 0 ?
					BigDecimal.ZERO :
					newCount.divide(activeCount, 4, RoundingMode.HALF_UP);
			// 构建返回对象
			PageActiveDetailVO detail = new PageActiveDetailVO();
			if("day".equals(reqDto.getTimeUnit())){
				LocalDateTime sevenDaysAgo = startTime.minusDays(7);
				LocalDateTime thirtyDaysAgo = startTime.minusDays(30);
				// 查询过去7天活跃用户数量
				BigDecimal sevenCount = BigDecimal.valueOf(statActiveUserCount(sevenDaysAgo, startTime, reqDto.getAppId(), version, channel));
				// 查询过去30天活跃用户数量
				BigDecimal thirtyCount = BigDecimal.valueOf(statActiveUserCount(thirtyDaysAgo, startTime, reqDto.getAppId(), version, channel));

				// 计算用户周活跃率
				BigDecimal wauRate = sevenCount.compareTo(BigDecimal.ZERO) != 0 && activeCount.compareTo(BigDecimal.ZERO) != 0
						? activeCount.divide(sevenCount, 4, RoundingMode.HALF_UP)
						: BigDecimal.ZERO;
				// 计算用户月活跃率
				BigDecimal mauRate = thirtyCount.compareTo(BigDecimal.ZERO) != 0 && activeCount.compareTo(BigDecimal.ZERO) != 0
						? activeCount.divide(thirtyCount, 4, RoundingMode.HALF_UP)
						: BigDecimal.ZERO;
				// 组装结果
				detail.setWauRate(wauRate);
				detail.setMauRate(mauRate);
			} else if("week".equals(reqDto.getTimeUnit()) || "month".equals(reqDto.getTimeUnit())){
				// 查询累计用户数
				BigDecimal totalUser = BigDecimal.valueOf(statNewUserCount(null, endTime, reqDto.getAppId(), null, null));
				// 查询活跃用户数
				BigDecimal activeUser = BigDecimal.valueOf(statActiveUserCount(startTime, endTime, reqDto.getAppId(), null, null));
				// 计算活跃率
				BigDecimal rate = totalUser.compareTo(BigDecimal.ZERO) != 0 && activeUser.compareTo(BigDecimal.ZERO) != 0
						? activeUser.divide(totalUser, 4, RoundingMode.HALF_UP)
						: BigDecimal.ZERO;
				if ("week".equals(reqDto.getTimeUnit())) {
					detail.setWeekActiveUserRate(rate);
				}else {
					detail.setMonthActiveUserRate(rate);
				}

			}
			// 定义日期格式
			DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
			// 处理日期显示
			boolean isRangeType = "week".equals(reqDto.getTimeUnit()) || "month".equals(reqDto.getTimeUnit());
			String displayDate = isRangeType && startTime != null && endTime != null
					? startTime.toLocalDate().format(dateFormatter) + "~" + endTime.minusDays(1).toLocalDate().format(dateFormatter)
					: date;
			detail.setDate(displayDate);
			detail.setActiveUser(activeCount.intValue());
			detail.setNewUserRate(newUserRate);
			result.add(detail);
		}
		// 返回结果
		page.setRecords( result);
		return page;
	}

	/************************************** 启动次数 *****************************************
	 * 查询启动次数趋势
	 * @param reqDto 查询启动次数趋势入参
	 * @return GetNewUserTrendVO
	 */
	@Override
	public GetNewUserTrendVO getLaunchTrend(UserAnalyseQueryBaseDTO reqDto) {
		GetNewUserTrendVO result = new GetNewUserTrendVO();
		// 1. 生成时间轴
		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
		result.setDates(dates);
		LocalDateTime fromDate = getStartTime(reqDto.getFromDate().toString(), "day");
		LocalDateTime toDate = getEndTime(reqDto.getToDate().toString(), "day");

		// 2. 处理版本列表（为空时按null处理）
		List<String> versions = reqDto.getVersion();
		List<String> processVersions = (versions == null || versions.isEmpty())
				? Collections.singletonList(null)
				: versions.stream().filter(v -> v != null && !v.isEmpty()).toList();

		// 3. 处理每个版本的数据
		List<TrendBaseVO> voList = processVersions.stream()
				.map(version -> {
					TrendBaseVO trend = new TrendBaseVO();
					trend.setName("启动次数");
					trend.setVersion(version == null ? "All" : version);
					trend.setKey("launch");

					// 计算每个时间点的启动次数
					List<Long> counts = dates.stream()
							.map(date -> {
								LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
								LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());
								// 如果startTime早于fromDate
								if (startTime.isBefore(fromDate)) {
									startTime = fromDate;
								}

								// 如果endTime晚于toDate
								if (endTime.isAfter(toDate)) {
									endTime = toDate;
								}
								return statLaunchCount(startTime, endTime, reqDto.getAppId(), version, reqDto.getChannel());
							})
							.collect(Collectors.toList());

					trend.setData(counts);
					return trend;
				})
				.collect(Collectors.toList());

		result.setItems(voList);
		return result;
	}

	/**
	 * 分页查询启动次数详情
	 * @param reqDto 分页查询启动次数详情入参
	 * @return Page
	 */
	@Override
	public Page<PageLaunchDetailVO> pageLaunchDetail(PageUserAnalyseDTO reqDto) {
		// 初始化分页对象并计算总条数
		Page<PageLaunchDetailVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
		page.setTotal(calculateTotalCount(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit()));
		// 生成当前页时间轴
		List<String> dates = generatePageTimeAxis(reqDto.getFromDate(), reqDto.getToDate(),
				reqDto.getTimeUnit(), reqDto.getCurrent(), reqDto.getSize());
		if (dates.isEmpty()) {
			return page; // 无数据直接返回
		}
		// 获取渠道和版本
		List<String> channel = reqDto.getChannel();
		String version = null;
		if (reqDto.getVersion() != null && !reqDto.getVersion().isEmpty()){
			version = reqDto.getVersion().get(0);
		}
		// 日期格式化
		String timeUnit = reqDto.getTimeUnit();
		boolean isRangeType = "week".equals(timeUnit) || "month".equals(timeUnit);
		DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
		// 查询时间范围内总启动次数
		LocalDateTime fromDate = getStartTime(reqDto.getFromDate().toString(), "day");
		LocalDateTime toDate = getEndTime(reqDto.getToDate().toString(), "day");
		BigDecimal totalLaunch = BigDecimal.valueOf(statLaunchCount(fromDate, toDate, reqDto.getAppId(), version, channel));
		// 查询结果
		List<PageLaunchDetailVO> result = new ArrayList<>(dates.size());
		for (String date : dates) {
			// 获取时间范围
			LocalDateTime startTime = getStartTime(date, timeUnit);
			LocalDateTime endTime = getEndTime(date, timeUnit);

			// 判断时间范围是否在查询范围内
			if(startTime == null || endTime == null){
				throw new BusinessException("时间范围错误");
			}

			// 如果startTime早于fromDate
			if (startTime.isBefore(fromDate)) {
				startTime = fromDate;
			}

			// 如果endTime晚于toDate
			if (endTime.isAfter(toDate)) {
				endTime = toDate;
			}
			// 查询启动次数
			BigDecimal launch = BigDecimal.valueOf(statLaunchCount(startTime, endTime, reqDto.getAppId(), version, channel));
			// 计算启动占比
			BigDecimal launchRate = launch.compareTo(BigDecimal.ZERO) == 0 || totalLaunch.compareTo(BigDecimal.ZERO) == 0
					? BigDecimal.ZERO
					: launch.divide(totalLaunch, 4, RoundingMode.HALF_UP);
			// 构建返回对象
			PageLaunchDetailVO detailVO = new PageLaunchDetailVO();
			// 处理日期显示
			String displayDate = isRangeType && startTime != null && endTime != null
					? startTime.toLocalDate().format(dateFormatter) + "~" + endTime.minusDays(1).toLocalDate().format(dateFormatter)
					: date;
			detailVO.setDate(displayDate);
			detailVO.setLaunchRate(launchRate);
			detailVO.setLaunch(launch.intValue());
			result.add(detailVO);
		}
		// 设置返回结果
		page.setRecords(result);
		return page;
	}

	/************************************** 版本分布 *****************************************
	 * 分页查询全部版本详情
	 * @param reqDto 分页查询全部版本详情入参
	 * @return Page
	 */
	@Override
	public Page<PageAllVersionDetailVO> pageAllVersionDetail(PageAllVersionDetailDTO reqDto) {

		// 初始化分页对象
		Page<PageAllVersionDetailVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());

		// 查询新增用户表中指定appId的所有版本列表
		List<String> allVersions = getAllVersionsByAppId(null, null, reqDto.getAppId());
		if (CollUtil.isEmpty(allVersions)) {
			page.setTotal(0);
			return page;
		}

		// 设置总记录数
		page.setTotal(allVersions.size());

		// 获取分页的版本列表
		List<String> pageVersions = getPageVersions(allVersions, reqDto.getCurrent(), reqDto.getSize());
		if (CollUtil.isEmpty(pageVersions)) {
			return page;
		}

		// 确定查询日期
		LocalDate queryDate = (reqDto.getQueryDate() != null && reqDto.getQueryDate() == 1)
				? LocalDate.now().minusDays(1)
				: LocalDate.now();

		// 构建版本详情列表
		final String timeUnit = "day";
		List<PageAllVersionDetailVO> result = new ArrayList<>();
		// 遍历版本列表，查询数据
		for (String version : pageVersions) {
			// 获取时间范围
			LocalDateTime startTime = getStartTime(queryDate.toString(), timeUnit);
			LocalDateTime endTime = getEndTime(queryDate.toString(), timeUnit);

			// 查询截至今日所有版本用户总数
			BigDecimal allVersionUser = BigDecimal.valueOf(statNewUserCount(null, endTime, reqDto.getAppId(), null, null));
			// 查询截至今日该版本的用户总数
			BigDecimal versionUser = BigDecimal.valueOf(statNewUserCount(null, endTime, reqDto.getAppId(), version, null));
			// 查询该版本今日新增的用户数量
			Long newUser = statNewUserCount(startTime, endTime, reqDto.getAppId(), version, null);
			// 查询该版本今日活跃的用户数量
			BigDecimal activeUser = BigDecimal.valueOf(statActiveUserCount(startTime, endTime, reqDto.getAppId(), version, null));
			// 查询该应用今日所有活跃用户数量
			BigDecimal totalActiveUser = BigDecimal.valueOf(statActiveUserCount(startTime, endTime, reqDto.getAppId(), null, null));
			// 查询该版本今日启动次数
			Long launch = statLaunchCount(startTime, endTime, reqDto.getAppId(), version, null);
			// 查询今日升级到该版本的用户数量
			Long upgradeUser = statUpgradeUserCount(startTime, endTime, reqDto.getAppId(), version, null, null);

			// 组装结果
			PageAllVersionDetailVO detail = new PageAllVersionDetailVO();
			detail.setVersion(version);
			detail.setNewUser(newUser.intValue());
			detail.setUpgradeUser(upgradeUser.intValue());
			detail.setLaunch(launch.intValue());
			detail.setActiveUser(activeUser.intValue());
			detail.setTotalUser(versionUser.intValue());
			// 累计用户占比
			detail.setTotalUserRate(allVersionUser.compareTo(BigDecimal.ZERO) == 0 || versionUser.compareTo(BigDecimal.ZERO) == 0
					? BigDecimal.ZERO
					: versionUser.divide(allVersionUser, 4, RoundingMode.HALF_UP));
			// 活跃用户占比
			detail.setActiveUserRate(totalActiveUser.compareTo(BigDecimal.ZERO) == 0 || activeUser.compareTo(BigDecimal.ZERO) == 0
					? BigDecimal.ZERO
					: activeUser.divide(totalActiveUser, 4, RoundingMode.HALF_UP));
			result.add( detail);
		}
		// 返回结果
		page.setRecords(result);
		return page;
	}

	/**
	 * 分页查询单版本详情
	 * @param reqDto 入参
	 * @return Page
	 */
	@Override
	public Page<PageSingleVersionDetailVO> pageSingleVersionDetail(PageUserAnalyseDTO reqDto) {
		// 校验版本数量
		if (reqDto.getVersion() ==  null || reqDto.getVersion().isEmpty()){
			throw new BusinessException("版本不能为空");
		}
		if (reqDto.getVersion().size() > 1){
			throw new BusinessException("只能查询单个版本");
		}
		String version = reqDto.getVersion().get(0);
		// 校验时间单位
		if (!"day".equals(reqDto.getTimeUnit())){
			throw new BusinessException("时间单位只能为day");
		}
		// 初始化分页对象并计算总条数
		Page<PageSingleVersionDetailVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
		page.setTotal(calculateTotalCount(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit()));

		// 生成当前页时间轴
		List<String> dates = generatePageTimeAxis(reqDto.getFromDate(), reqDto.getToDate(),
				reqDto.getTimeUnit(), reqDto.getCurrent(), reqDto.getSize());
		if (dates.isEmpty()) {
			return page; // 无数据直接返回
		}
		List<String> channel = reqDto.getChannel();
		// 查询结果
		List<PageSingleVersionDetailVO> result = new ArrayList<>();
		for (String date : dates) {
			LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
			LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());

			// 查询该版本新增用户数
			Long newUser = statNewUserCount(startTime, endTime, reqDto.getAppId(), version, channel);
			// 查询该版本活跃用户数
			Long activeUser = statActiveUserCount(startTime, endTime, reqDto.getAppId(), version, channel);
			// 查询该版本启动次数
			Long launch = statLaunchCount(startTime, endTime, reqDto.getAppId(), version, channel);
			// 查询该版本升级用户数
			Long upgradeUser = statUpgradeUserCount(startTime, endTime, reqDto.getAppId(), version, null, channel);

			// 组装结果
			PageSingleVersionDetailVO detail = new PageSingleVersionDetailVO();
			detail.setDate(date);
			detail.setNewUser(newUser.intValue());
			detail.setActiveUser(activeUser.intValue());
			detail.setLaunch(launch.intValue());
			detail.setUpgradeUser(upgradeUser.intValue());

			result.add( detail);
		}

		// 返回结果
		page.setRecords(result);
		return page;
	}

	/**
	 * 查询版本用户来源
	 * @param reqDto 入参
	 * @return GetVersionDistributionVO
	 */
	@Override
	public List<GetVersionDistributionVO> getVersionDistribution(GetVersionDistributionDTO reqDto) {
		List<GetVersionDistributionVO> result = new ArrayList<>();
		LocalDate date = LocalDate.now();

		if ("day".equals(reqDto.getTimeUnit())){
			date = date.minusDays(1);
		}else if ("week".equals(reqDto.getTimeUnit())){
			date = date.minusDays(7);
		} else if ("month".equals(reqDto.getTimeUnit())){
			date = date.minusDays(30);
		}
		LocalDateTime startTime = date.atStartOfDay();
		LocalDateTime endTime = LocalDate.now().atStartOfDay();
		if ("newUserChannel".equals(reqDto.getType())){
			List<String> allChannel = getAllChannelByAppId(startTime, endTime, reqDto.getAppId(), reqDto.getVersion(), false);
			// 新增用户渠道
			// 查询这个时间范围内新增用户渠道列表
			for (String channel : allChannel) {
				// 查询渠道新增用户数
				Long newUser = statNewUserCount(startTime, endTime, reqDto.getAppId(), reqDto.getVersion(), List.of(channel));
				if (newUser == 0){
					continue;
				}
				GetVersionDistributionVO detail = new GetVersionDistributionVO();
				detail.setName(channel);
				detail.setValue(newUser.intValue());
				result.add(detail);
			}

		}
		else if ("upgradeChannel".equals(reqDto.getType())){
			// 升级用户渠道
			List<String> allChannel = getAllChannelByAppId(startTime, endTime, reqDto.getAppId(), reqDto.getVersion(), true);
			// 查询这个时间范围内新增用户渠道列表
			for (String channel : allChannel) {
				// 查询渠道升级用户数
				Long upgradeUser = statUpgradeUserCount(startTime, endTime, reqDto.getAppId(), reqDto.getVersion(), null, List.of(channel));
				if (upgradeUser == 0){
					continue;
				}
				GetVersionDistributionVO detail = new GetVersionDistributionVO();
				detail.setName(channel);
				detail.setValue(upgradeUser.intValue());
				result.add(detail);
			}
		}
		else if ("upgradeVersion".equals(reqDto.getType())){
			List<String> allVersion = getAllOldVersions(startTime, endTime, reqDto.getAppId(), reqDto.getVersion());
			// 获取升级用户
			for (String version : allVersion) {
				// 统计这个旧版本的升级用户数
				Long count = statUpgradeUserCount(startTime, endTime, reqDto.getAppId(), reqDto.getVersion(), version, null);
				if (count == 0){
					continue;
				}
				GetVersionDistributionVO detail = new GetVersionDistributionVO();
				detail.setName(version);
				detail.setValue(count.intValue());
				result.add(detail);
			}
		}
		return result;
	}

	/**
	 * 分页查询版本用户来源详情
	 * @param reqDto 入参
	 * @return Page<PageVersionDistributionDetailVO>
	 */
	@Override
	public Page<PageVersionDistributionDetailVO> pageVersionDistributionDetail(PageVersionDistributionDetailDTO reqDto) {
		// 初始化分页对象
		Page<PageVersionDistributionDetailVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());

		List<PageVersionDistributionDetailVO> result = new ArrayList<>();

		LocalDate date = LocalDate.now();

		if ("day".equals(reqDto.getTimeUnit())){
			date = date.minusDays(1);
		}else if ("week".equals(reqDto.getTimeUnit())){
			date = date.minusDays(7);
		} else if ("month".equals(reqDto.getTimeUnit())){
			date = date.minusDays(30);
		}
		LocalDateTime startTime = date.atStartOfDay();
		LocalDateTime endTime = LocalDate.now().atStartOfDay();
		if ("newUserChannel".equals(reqDto.getType())){
			List<String> allChannel = getAllChannelByAppId(startTime, endTime, reqDto.getAppId(), reqDto.getVersion(), false);
			page.setTotal(allChannel.size());
			// 获取新增用户总数
			BigDecimal totalNewUser = BigDecimal.valueOf(statNewUserCount(startTime, endTime, reqDto.getAppId(), reqDto.getVersion(), allChannel));
			// 获取分页的版本列表
			List<String> pageList = getPageVersions(allChannel, reqDto.getCurrent(), reqDto.getSize());
			if (CollUtil.isEmpty(pageList)) {
				return page;
			}

			// 查询这个时间范围内新增用户渠道列表
			for (String channel : pageList) {
				// 查询渠道新增用户数
				BigDecimal newUser = BigDecimal.valueOf(statNewUserCount(startTime, endTime, reqDto.getAppId(), reqDto.getVersion(), List.of(channel)));
				if (newUser.compareTo(BigDecimal.ZERO) == 0){
					continue;
				}
				PageVersionDistributionDetailVO detail = new PageVersionDistributionDetailVO();
				detail.setName(channel);
				detail.setValue(newUser.intValue());
				detail.setRate(newUser.compareTo(BigDecimal.ZERO) == 0 || totalNewUser.compareTo(BigDecimal.ZERO) == 0
						? BigDecimal.ZERO
						: newUser.divide(totalNewUser, 4, RoundingMode.HALF_UP));
				result.add(detail);
			}

		}
		else if ("upgradeChannel".equals(reqDto.getType())){
			List<String> allChannel = getAllChannelByAppId(startTime, endTime, reqDto.getAppId(), reqDto.getVersion(), true);
			page.setTotal(allChannel.size());
			BigDecimal totalUpgrade = BigDecimal.valueOf(statUpgradeUserCount(startTime, endTime, reqDto.getAppId(), reqDto.getVersion(), null, allChannel));

			// 获取分页的版本列表
			List<String> pageList = getPageVersions(allChannel, reqDto.getCurrent(), reqDto.getSize());
			if (CollUtil.isEmpty(pageList)) {
				return page;
			}

			// 查询这个时间范围内新增用户渠道列表
			for (String channel : pageList) {
				// 查询渠道升级用户数
				BigDecimal upgradeUser = BigDecimal.valueOf(statUpgradeUserCount(startTime, endTime, reqDto.getAppId(), reqDto.getVersion(), null, List.of(channel)));
				if (upgradeUser.compareTo(BigDecimal.ZERO) == 0){
					continue;
				}
				PageVersionDistributionDetailVO detail = new PageVersionDistributionDetailVO();
				detail.setName(channel);
				detail.setValue(upgradeUser.intValue());
				detail.setRate(upgradeUser.compareTo(BigDecimal.ZERO) == 0 || totalUpgrade.compareTo(BigDecimal.ZERO) == 0
						? BigDecimal.ZERO
						: upgradeUser.divide(totalUpgrade, 4, RoundingMode.HALF_UP));
				result.add(detail);
			}
		}
		else if ("upgradeVersion".equals(reqDto.getType())){
			List<String> allVersion = getAllOldVersions(startTime, endTime, reqDto.getAppId(), reqDto.getVersion());
			page.setTotal(allVersion.size());
			BigDecimal totalOldVersion = BigDecimal.valueOf(statUpgradeUserCount(startTime, endTime, reqDto.getAppId(), reqDto.getVersion(), null, null));


			// 获取分页的版本列表
			List<String> pageList = getPageVersions(allVersion, reqDto.getCurrent(), reqDto.getSize());
			if (CollUtil.isEmpty(pageList)) {
				return page;
			}

			// 获取升级用户
			for (String version : pageList) {
				// 统计这个旧版本的升级用户数
				BigDecimal count = BigDecimal.valueOf(statUpgradeUserCount(startTime, endTime, reqDto.getAppId(), reqDto.getVersion(), version, null));
				if (count.compareTo(BigDecimal.ZERO) == 0){
					continue;
				}
				PageVersionDistributionDetailVO detail = new PageVersionDistributionDetailVO();
				detail.setName(version);
				detail.setValue(count.intValue());
				detail.setRate(count.compareTo(BigDecimal.ZERO) == 0 || totalOldVersion.compareTo(BigDecimal.ZERO) == 0
						? BigDecimal.ZERO
						: count.divide(totalOldVersion, 4, RoundingMode.HALF_UP));
				result.add(detail);
			}
		}
		// 返回结果
		page.setRecords(result);
		return page;
	}

	/**
	 * 获取应用版本列表
	 * @param appId 应用ID
	 * @return List
	 */
	@Override
	public List<String> getVersionList(String appId) {
		List<String> versions = getAllVersionsByAppId(null, null, appId);
		return versions;
	}

	/**
	 * 获取应用渠道列表
	 * @param appId 应用ID
	 * @return List
	 */
	@Override
	public List<String> getChannelList(String appId) {
		List<String> channels = getAllChannelByAppId(null, null, appId, null, false);
		return channels;
	}


	/************************************** 公用方法 **************************************
	 * 根据时间粒度和日期字符串获取开始时间
	 */
	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){
		List<String> newUsers = getNewUserIdList(startTime, endTime, appId, version, channels);
		return newUsers == null ? 0L : newUsers.size();
	}

	/**
	 * 统计指定时间范围内的活跃用户数
	 */
	private Long statActiveUserCount(LocalDateTime startTime, LocalDateTime endTime, String appId, String version, List<String> channels){
		List<String> activeUsers = getActiveUserIdList(startTime, endTime, appId, version, channels);
		return activeUsers == null ? 0L : activeUsers.size();
	}

	/**
	 * 查询指定时间范围内的活跃用户Id列表(去重)
	 */
	private List<String> getActiveUserIdList(LocalDateTime startTime, LocalDateTime endTime, String appId, String version, List<String> channels){
		// 1. 组装查询条件
		LambdaQueryWrapper<MktStatActiveUser> queryWrapper = Wrappers.<MktStatActiveUser>lambdaQuery()
				.eq(MktStatActiveUser::getAppId, appId)
				.ge(startTime != null, MktStatActiveUser::getStatDate, startTime)
				.lt(endTime != null, MktStatActiveUser::getStatDate, endTime)
				.select(MktStatActiveUser::getUserId)
				.groupBy(MktStatActiveUser::getUserId);

		// 2. 添加渠道条件
		if (channels != null && !channels.isEmpty()) {
			queryWrapper.in(MktStatActiveUser::getChannel, channels);
		}
		// 3. 添加版本条件
		if (version != null && !version.isEmpty()){
			queryWrapper.eq(MktStatActiveUser::getVersion, version);
		}
		// 4. 统计活跃用户数，根据userID去重
		List<MktStatActiveUser> activeUsers = activeUserMapper.selectList(queryWrapper);
		return activeUsers.stream().map(MktStatActiveUser::getUserId).toList();
	}

	/**
	 * 查询指定时间范围内的新增用户Id列表（去重）
	 */
	private List<String> getNewUserIdList(LocalDateTime startTime, LocalDateTime endTime, String appId, String version, List<String> channels){

		// 1. 组装查询条件
		LambdaQueryWrapper<MktStatNewUser> queryWrapper = Wrappers.<MktStatNewUser>lambdaQuery()
				.eq(MktStatNewUser::getAppId, appId)
				.ge(startTime != null ,MktStatNewUser::getStatDate, startTime)
				.lt(endTime != null ,MktStatNewUser::getStatDate, endTime)
				.select(MktStatNewUser::getUserId)
				.groupBy(MktStatNewUser::getUserId);

		// 2. 添加渠道条件
		if (channels != null && !channels.isEmpty()) {
			queryWrapper.in(MktStatNewUser::getChannel, channels);
		}
		// 3. 添加版本条件
		if (version != null && !version.isEmpty()){
			queryWrapper.eq(MktStatNewUser::getVersion, version);
		}
		// 4. 统计新增用户数
		List<MktStatNewUser> newUsers = newUserMapper.selectList(queryWrapper);
		return newUsers.stream().map(MktStatNewUser::getUserId).toList();
	}

	/**
	 * 获取活跃用户中新增用户的数量
	 */
	private Long getActiveNewUserCount(LocalDateTime startTime, LocalDateTime endTime, String appId, String version, List<String> channels) {
		// 1. 获取活跃用户ID列表
		List<String> activeUserIds = getActiveUserIdList(startTime, endTime, appId, version, channels);
		if (activeUserIds == null || activeUserIds.isEmpty()) {
			return 0L;
		}

		// 2. 获取新增用户ID列表
		List<String> newUserIds = getNewUserIdList(startTime, endTime, appId, version, channels);
		if (newUserIds == null || newUserIds.isEmpty()) {
			return 0L;
		}

		// 3. 计算交集：既是活跃用户又是新增用户的用户数量
		Set<String> activeUserSet = new HashSet<>(activeUserIds);
		Set<String> newUserSet = new HashSet<>(newUserIds);

		// 4. 保留在活跃用户集合中的新增用户
		activeUserSet.retainAll(newUserSet);
		return (long) activeUserSet.size();
	}

	/**
	 * 统计指定时间范围内的启动次数
	 */
	private Long statLaunchCount(LocalDateTime startTime, LocalDateTime endTime, String appId, String version, List<String> channels){
		// 1. 组装查询条件
		LambdaQueryWrapper<MktStatUserAnalysis> queryWrapper = Wrappers.<MktStatUserAnalysis>lambdaQuery()
				.eq(MktStatUserAnalysis::getAppId, appId)
				.ge(startTime != null ,MktStatUserAnalysis::getStatDate, startTime)
				.lt(endTime != null ,MktStatUserAnalysis::getStatDate, endTime)
				.select(MktStatUserAnalysis::getLaunch);

		// 2. 添加渠道条件
		if (channels != null && !channels.isEmpty()) {
			queryWrapper.in(MktStatUserAnalysis::getChannel, channels);
		}
		// 3. 添加版本条件
		if (version != null && !version.isEmpty()){
			queryWrapper.eq(MktStatUserAnalysis::getVersion, version);
		}
		// 4. 统计新增用户数
		List<MktStatUserAnalysis> userAnalyses = userAnalysisMapper.selectList(queryWrapper);
		Long launchCount = 0L;
		for (MktStatUserAnalysis user : userAnalyses) {
			launchCount+=user.getLaunch();
		}
		return launchCount;
	}

	/**
	 * 统计指定时间范围内的升级用户数
	 */
	private Long statUpgradeUserCount(LocalDateTime startTime, LocalDateTime endTime, String appId, String version, String oldVersion, List<String> channels){

		// 1. 组装查询条件
		LambdaQueryWrapper<MktStatNewUser> queryWrapper = Wrappers.<MktStatNewUser>lambdaQuery()
				.eq(MktStatNewUser::getAppId, appId)
				.eq(oldVersion != null && !oldVersion.isEmpty(), MktStatNewUser::getOldVersion, oldVersion)
				.isNotNull(MktStatNewUser::getOldVersion)
				.ne(MktStatNewUser::getOldVersion, "")
				.ge(startTime != null ,MktStatNewUser::getStatDate, startTime)
				.lt(endTime != null ,MktStatNewUser::getStatDate, endTime);

		// 2. 添加渠道条件
		if (channels != null && !channels.isEmpty()) {
			queryWrapper.in(MktStatNewUser::getChannel, channels);
		}
		// 3. 添加版本条件
		if (version != null && !version.isEmpty()){
			queryWrapper.eq(MktStatNewUser::getVersion, version);
		}
		// 4. 统计升级用户数
		List<MktStatNewUser> newUsers = newUserMapper.selectList(queryWrapper);
		return newUsers == null ? 0L : newUsers.size();
	}
	/**
	 * 查询新增表中指定appId，指定时间范围的所有version列表
	 */
	private List<String> getAllVersionsByAppId(LocalDateTime startTime, LocalDateTime endTime, String appId) {
		LambdaQueryWrapper<MktStatNewUser> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(MktStatNewUser::getAppId, appId)
				.ge(startTime != null ,MktStatNewUser::getStatDate, startTime)
				.lt(endTime != null ,MktStatNewUser::getStatDate, endTime)
				.select(MktStatNewUser::getVersion)
				.groupBy(MktStatNewUser::getVersion)
				.orderByAsc(MktStatNewUser::getVersion);

		List<MktStatNewUser> versionList = newUserMapper.selectList(queryWrapper);

		return versionList.stream()
				.map(MktStatNewUser::getVersion)
				.collect(Collectors.toList());
	}
	/**
	 * 查询新增表中指定appId，指定时间范围，指定版本的所有旧版本列表
	 */
	private List<String> getAllOldVersions(LocalDateTime startTime, LocalDateTime endTime, String appId, String version) {
		LambdaQueryWrapper<MktStatNewUser> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(MktStatNewUser::getAppId, appId)
				.ge(startTime != null ,MktStatNewUser::getStatDate, startTime)
				.lt(endTime != null ,MktStatNewUser::getStatDate, endTime)
				.isNotNull(MktStatNewUser::getOldVersion)
				.ne(MktStatNewUser::getOldVersion, "")
				.eq(version != null && !version.isEmpty() ,MktStatNewUser::getVersion, version)
				.select(MktStatNewUser::getOldVersion)
				.groupBy(MktStatNewUser::getOldVersion)
				.orderByAsc(MktStatNewUser::getOldVersion);

		List<MktStatNewUser> versionList = newUserMapper.selectList(queryWrapper);

		return versionList.stream()
				.map(MktStatNewUser::getOldVersion)
				.collect(Collectors.toList());
	}

	/**
	 * 查询新增表中指定appId，指定时间范围的所有渠道列表
	 */
	private List<String> getAllChannelByAppId(LocalDateTime startTime, LocalDateTime endTime, String appId, String version, Boolean isOld) {
		LambdaQueryWrapper<MktStatNewUser> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(MktStatNewUser::getAppId, appId)
				.ge(startTime != null ,MktStatNewUser::getStatDate, startTime)
				.eq(version != null && !version.isEmpty() ,MktStatNewUser::getVersion, version)
				.lt(endTime != null ,MktStatNewUser::getStatDate, endTime)
				.select(MktStatNewUser::getChannel)
				.groupBy(MktStatNewUser::getChannel)
				.orderByAsc(MktStatNewUser::getChannel);
		if (isOld){
			queryWrapper.isNotNull(MktStatNewUser::getOldVersion)
					.ne(MktStatNewUser::getOldVersion, "");
		}

		List<MktStatNewUser> channelList = newUserMapper.selectList(queryWrapper);

		return channelList.stream()
				.map(MktStatNewUser::getChannel)
				.collect(Collectors.toList());
	}

	/**
	 * 根据分页参数获取版本列表的分页数据
	 * @param allVersions 所有版本列表
	 * @param current 当前页码
	 * @param size 每页大小
	 * @return List<String> 分页后的版本列表
	 */
	private List<String> getPageVersions(List<String> allVersions, long current, long size) {
		if (CollUtil.isEmpty(allVersions)) {
			return new ArrayList<>();
		}

		// 计算分页起始和结束索引
		long startIndex = (current - 1) * size;
		long endIndex = Math.min(startIndex + size, allVersions.size());

		// 边界检查
		if (startIndex >= allVersions.size()) {
			return new ArrayList<>();
		}

		// 获取当前页的数据
		return allVersions.subList((int) startIndex, (int) endIndex);
	}

}
