Browse Source

Merge remote-tracking branch 'refs/remotes/origin/dev/lwh' into dev/wcl

wangcl 4 weeks ago
parent
commit
4a3590259b

+ 1 - 1
pig-common/pig-common-security/src/main/java/com/pig4cloud/pig/common/security/component/PigResourceServerConfiguration.java

@@ -79,7 +79,7 @@ public class PigResourceServerConfiguration {
 			.toArray(new PathPatternRequestMatcher[] {});
 			.toArray(new PathPatternRequestMatcher[] {});
 
 
 		http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(permitMatchers)
 		http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(permitMatchers)
-			.permitAll().requestMatchers("/uninstall/*").permitAll()
+			.permitAll()
 			.anyRequest()
 			.anyRequest()
 			.authenticated())
 			.authenticated())
 			.oauth2ResourceServer(
 			.oauth2ResourceServer(

+ 7 - 0
pig-statistics/pig-statistics-api/src/main/java/com/pig4cloud/pig/statistics/api/dto/user/PageActiveDetailDTO.java

@@ -24,6 +24,13 @@ public class PageActiveDetailDTO implements Serializable {
 	@Serial
 	@Serial
 	private static final long serialVersionUID = 1L;
 	private static final long serialVersionUID = 1L;
 
 
+	/**
+	 * 应用id
+	 */
+	@NotBlank(message = "应用id不能为空")
+	@Schema(description = "应用id", example = "FoigtBxJrmOi9nirwkjpsJ")
+	private String appId;
+
 	/**
 	/**
 	 * 渠道
 	 * 渠道
 	 */
 	 */

+ 10 - 3
pig-statistics/pig-statistics-api/src/main/java/com/pig4cloud/pig/statistics/api/dto/user/PageNewUserTrendDetailDTO.java → pig-statistics/pig-statistics-api/src/main/java/com/pig4cloud/pig/statistics/api/dto/user/PageUserAnalyseDTO.java

@@ -18,11 +18,18 @@ import java.util.List;
  */
  */
 @Data
 @Data
 @Schema(description = "获取新增趋势详情入参")
 @Schema(description = "获取新增趋势详情入参")
-public class PageNewUserTrendDetailDTO implements Serializable {
+public class PageUserAnalyseDTO implements Serializable {
 
 
 	@Serial
 	@Serial
 	private static final long serialVersionUID = 1L;
 	private static final long serialVersionUID = 1L;
 
 
+	/**
+	 * 应用ID
+	 */
+	@NotBlank(message = "应用ID不能为空")
+	@Schema(description = "应用ID", example = "Fqs2CL9CUn7U1AqilSFXgb")
+	private String appId;
+
 	/**
 	/**
 	 * 渠道
 	 * 渠道
 	 */
 	 */
@@ -59,12 +66,12 @@ public class PageNewUserTrendDetailDTO implements Serializable {
 	/**
 	/**
 	 * 每页显示条数,默认 10
 	 * 每页显示条数,默认 10
 	 */
 	 */
-	@Schema(description = "每页显示条数")
+	@Schema(description = "每页显示条数", example = "10")
 	private long size = 10;
 	private long size = 10;
 
 
 	/**
 	/**
 	 * 当前页
 	 * 当前页
 	 */
 	 */
-	@Schema(description = "当前页")
+	@Schema(description = "当前页",example = "1")
 	private long current = 1;
 	private long current = 1;
 }
 }

+ 1 - 1
pig-statistics/pig-statistics-api/src/main/java/com/pig4cloud/pig/statistics/api/dto/user/UserAnalyseQueryBaseDTO.java

@@ -26,7 +26,7 @@ public class UserAnalyseQueryBaseDTO implements Serializable {
 	 * 应用ID
 	 * 应用ID
 	 */
 	 */
 	@NotBlank(message = "应用ID不能为空")
 	@NotBlank(message = "应用ID不能为空")
-	@Schema(description = "应用ID", example = "FoigtBxJrmOi9nirwkjpsJ")
+	@Schema(description = "应用ID", example = "Fqs2CL9CUn7U1AqilSFXgb")
 	private String appId;
 	private String appId;
 
 
 	/**
 	/**

+ 1 - 1
pig-statistics/pig-statistics-api/src/main/java/com/pig4cloud/pig/statistics/api/vo/user/TrendBaseVO.java

@@ -20,7 +20,7 @@ public class TrendBaseVO implements Serializable {
 	 * 数量
 	 * 数量
 	 */
 	 */
 	@Schema(description = "数量")
 	@Schema(description = "数量")
-	private List<Integer> data;
+	private List<Long> data;
 
 
 	/**
 	/**
 	 * 名称
 	 * 名称

+ 9 - 9
pig-statistics/pig-statistics-biz/src/main/java/com/pig4cloud/pig/statistics/controller/UserAnalyseController.java

@@ -42,7 +42,7 @@ public class UserAnalyseController {
 
 
 	@PostMapping("/new/detail")
 	@PostMapping("/new/detail")
 	@Operation(summary = "分页查询新增趋势详情")
 	@Operation(summary = "分页查询新增趋势详情")
-	public R<Page<PageNewUserTrendDetailVO>> pageNewUserTrendDetail(@Valid @RequestBody PageNewUserTrendDetailDTO reqDto) {
+	public R<Page<PageNewUserTrendDetailVO>> pageNewUserTrendDetail(@Valid @RequestBody PageUserAnalyseDTO reqDto) {
 		Page<PageNewUserTrendDetailVO> result = userAnalyseService.pageNewUserTrendDetail(reqDto);
 		Page<PageNewUserTrendDetailVO> result = userAnalyseService.pageNewUserTrendDetail(reqDto);
 		return R.ok(result);
 		return R.ok(result);
 	}
 	}
@@ -55,13 +55,13 @@ public class UserAnalyseController {
 
 
 	@PostMapping("/new/retention")
 	@PostMapping("/new/retention")
 	@Operation(summary = "查询次日留存率")
 	@Operation(summary = "查询次日留存率")
-	public R<GetNewUserRetentionVO> getNewUserRetention(@Valid @RequestBody GetNewUserRetentionDTO reqDto) {
+	public R<GetNewUserRetentionVO> getNewUserRetention(@Valid @RequestBody UserAnalyseQueryBaseDTO reqDto) {
 		GetNewUserRetentionVO result = userAnalyseService.getNewUserRetention(reqDto);
 		GetNewUserRetentionVO result = userAnalyseService.getNewUserRetention(reqDto);
 		return R.ok(result);
 		return R.ok(result);
 	}
 	}
 	@PostMapping("/retention/detail")
 	@PostMapping("/retention/detail")
 	@Operation(summary = "分页查询次日留存率详情")
 	@Operation(summary = "分页查询次日留存率详情")
-	public R<Page<PageRetentionDetailVO>> pageNewUserRetentionDetail(@Valid @RequestBody PageRetentionDetailDTO reqDto) {
+	public R<Page<PageRetentionDetailVO>> pageNewUserRetentionDetail(@Valid @RequestBody PageUserAnalyseDTO reqDto) {
 		Page<PageRetentionDetailVO> result = userAnalyseService.pageNewUserRetentionDetail(reqDto);
 		Page<PageRetentionDetailVO> result = userAnalyseService.pageNewUserRetentionDetail(reqDto);
 		return R.ok(result);
 		return R.ok(result);
 	}
 	}
@@ -97,21 +97,21 @@ public class UserAnalyseController {
 
 
 	@PostMapping("/active/weekrate")
 	@PostMapping("/active/weekrate")
 	@Operation(summary = "查询周活跃率")
 	@Operation(summary = "查询周活跃率")
-	public R<GetActiveUserBaseVO> getActiveWeekrate(@Valid @RequestBody UserAnalyseQueryBaseDTO reqDto) {
-		GetActiveUserBaseVO result = userAnalyseService.getActiveWeekrate(reqDto);
+	public R<GetActiveUserBaseVO> getActiveWeekRate(@Valid @RequestBody UserAnalyseQueryBaseDTO reqDto) {
+		GetActiveUserBaseVO result = userAnalyseService.getActiveWeekRate(reqDto);
 		return R.ok(result);
 		return R.ok(result);
 	}
 	}
 
 
 	@PostMapping("/active/monthrate")
 	@PostMapping("/active/monthrate")
 	@Operation(summary = "查询月活跃率")
 	@Operation(summary = "查询月活跃率")
-	public R<GetActiveUserBaseVO> getActiveMonthrate(@Valid @RequestBody UserAnalyseQueryBaseDTO reqDto) {
-		GetActiveUserBaseVO result = userAnalyseService.getActiveMonthrate(reqDto);
+	public R<GetActiveUserBaseVO> getActiveMonthRate(@Valid @RequestBody UserAnalyseQueryBaseDTO reqDto) {
+		GetActiveUserBaseVO result = userAnalyseService.getActiveMonthRate(reqDto);
 		return R.ok(result);
 		return R.ok(result);
 	}
 	}
 
 
 	@PostMapping("/active/detail")
 	@PostMapping("/active/detail")
 	@Operation(summary = "分页查询活跃详情")
 	@Operation(summary = "分页查询活跃详情")
-	public R<Page<PageActiveDetailVO>> pageActiveDetail(@Valid @RequestBody PageActiveDetailDTO reqDto) {
+	public R<Page<PageActiveDetailVO>> pageActiveDetail(@Valid @RequestBody PageUserAnalyseDTO reqDto) {
 		Page<PageActiveDetailVO> result = userAnalyseService.pageActiveDetail(reqDto);
 		Page<PageActiveDetailVO> result = userAnalyseService.pageActiveDetail(reqDto);
 		return R.ok(result);
 		return R.ok(result);
 	}
 	}
@@ -132,7 +132,7 @@ public class UserAnalyseController {
 
 
 	@PostMapping("/launch/detail")
 	@PostMapping("/launch/detail")
 	@Operation(summary = "分页查询启动次数详情")
 	@Operation(summary = "分页查询启动次数详情")
-	public R<Page<PageLaunchDetailVO>> pageLaunchDetail(@Valid @RequestBody PageActiveDetailDTO reqDto) {
+	public R<Page<PageLaunchDetailVO>> pageLaunchDetail(@Valid @RequestBody PageUserAnalyseDTO reqDto) {
 		Page<PageLaunchDetailVO> result = userAnalyseService.pageLaunchDetail(reqDto);
 		Page<PageLaunchDetailVO> result = userAnalyseService.pageLaunchDetail(reqDto);
 		return R.ok(result);
 		return R.ok(result);
 	}
 	}

+ 7 - 7
pig-statistics/pig-statistics-biz/src/main/java/com/pig4cloud/pig/statistics/service/UserAnalyseService.java

@@ -28,21 +28,21 @@ public interface UserAnalyseService {
 	 * @param reqDto 请求参数
 	 * @param reqDto 请求参数
 	 * @return Page
 	 * @return Page
 	 */
 	 */
-	Page<PageNewUserTrendDetailVO> pageNewUserTrendDetail(@Valid PageNewUserTrendDetailDTO reqDto);
+	Page<PageNewUserTrendDetailVO> pageNewUserTrendDetail(@Valid PageUserAnalyseDTO reqDto);
 
 
 	/**
 	/**
 	 * 查询次日留存率
 	 * 查询次日留存率
 	 * @param reqDto 查询次日留存率入参
 	 * @param reqDto 查询次日留存率入参
 	 * @return GetNewUserRetentionVO
 	 * @return GetNewUserRetentionVO
 	 */
 	 */
-	GetNewUserRetentionVO getNewUserRetention(@Valid GetNewUserRetentionDTO reqDto);
+	GetNewUserRetentionVO getNewUserRetention(@Valid UserAnalyseQueryBaseDTO reqDto);
 
 
 	/**
 	/**
 	 * 分页查询次日留存率详情
 	 * 分页查询次日留存率详情
 	 * @param reqDto 分页查询次日留存率详情入参
 	 * @param reqDto 分页查询次日留存率详情入参
 	 * @return Page
 	 * @return Page
 	 */
 	 */
-	Page<PageRetentionDetailVO> pageNewUserRetentionDetail(@Valid PageRetentionDetailDTO reqDto);
+	Page<PageRetentionDetailVO> pageNewUserRetentionDetail(@Valid PageUserAnalyseDTO reqDto);
 
 
 
 
 	/************************************** 活跃用户 *****************************************
 	/************************************** 活跃用户 *****************************************
@@ -71,7 +71,7 @@ public interface UserAnalyseService {
 	 * @param reqDto 入参
 	 * @param reqDto 入参
 	 * @return GetActiveUserBaseVO
 	 * @return GetActiveUserBaseVO
 	 */
 	 */
-	GetActiveUserBaseVO getActiveWeekrate(@Valid UserAnalyseQueryBaseDTO reqDto);
+	GetActiveUserBaseVO getActiveWeekRate(@Valid UserAnalyseQueryBaseDTO reqDto);
 
 
 
 
 	/**
 	/**
@@ -79,14 +79,14 @@ public interface UserAnalyseService {
 	 * @param reqDto 入参
 	 * @param reqDto 入参
 	 * @return GetActiveUserBaseVO
 	 * @return GetActiveUserBaseVO
 	 */
 	 */
-	GetActiveUserBaseVO getActiveMonthrate(@Valid UserAnalyseQueryBaseDTO reqDto);
+	GetActiveUserBaseVO getActiveMonthRate(@Valid UserAnalyseQueryBaseDTO reqDto);
 
 
 	/**
 	/**
 	 * 分页查询活跃详情
 	 * 分页查询活跃详情
 	 * @param reqDto 入参
 	 * @param reqDto 入参
 	 * @return Page
 	 * @return Page
 	 */
 	 */
-	Page<PageActiveDetailVO> pageActiveDetail(@Valid PageActiveDetailDTO reqDto);
+	Page<PageActiveDetailVO> pageActiveDetail(@Valid PageUserAnalyseDTO reqDto);
 
 
 	/************************************** 启动次数 *****************************************
 	/************************************** 启动次数 *****************************************
 	 * 查询启动次数趋势
 	 * 查询启动次数趋势
@@ -100,7 +100,7 @@ public interface UserAnalyseService {
 	 * @param reqDto 入参
 	 * @param reqDto 入参
 	 * @return Page
 	 * @return Page
 	 */
 	 */
-	Page<PageLaunchDetailVO> pageLaunchDetail(@Valid PageActiveDetailDTO reqDto);
+	Page<PageLaunchDetailVO> pageLaunchDetail(@Valid PageUserAnalyseDTO reqDto);
 
 
 	/************************************** 版本分布 *****************************************
 	/************************************** 版本分布 *****************************************
 	 * 分页查询全部版本详情
 	 * 分页查询全部版本详情

+ 531 - 133
pig-statistics/pig-statistics-biz/src/main/java/com/pig4cloud/pig/statistics/service/impl/UserAnalyseServiceImpl.java

@@ -25,7 +25,6 @@ import java.math.RoundingMode;
 import java.time.DayOfWeek;
 import java.time.DayOfWeek;
 import java.time.LocalDate;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
-import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
 import java.time.format.DateTimeParseException;
 import java.time.temporal.ChronoUnit;
 import java.time.temporal.ChronoUnit;
@@ -65,6 +64,41 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
 		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
 		result.setDates(dates);
 		result.setDates(dates);
 
 
+		// 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());
+								return statNewUserCount(startTime, endTime, reqDto.getAppId(), version, reqDto.getChannel());
+							})
+							.collect(Collectors.toList());
+					trend.setData(counts);
+					return trend;
+				})
+				.toList();
+		result.setItems(voList);
+		return result;
+	}
+	public GetNewUserTrendVO getNewUserTrend2(UserAnalyseQueryBaseDTO reqDto) {
+		GetNewUserTrendVO result = new GetNewUserTrendVO();
+		// 1. 生成时间轴
+		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
+		result.setDates(dates);
+
 		// 2. 处理版本列表(为空时按"All"处理)
 		// 2. 处理版本列表(为空时按"All"处理)
 		List<String> versions = reqDto.getVersion();
 		List<String> versions = reqDto.getVersion();
 		List<String> processVersions = (versions == null || versions.isEmpty())
 		List<String> processVersions = (versions == null || versions.isEmpty())
@@ -84,7 +118,7 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 							.map(date -> calculateNewUserCount(date, version, reqDto.getChannel(), reqDto.getTimeUnit(), "newUser"))
 							.map(date -> calculateNewUserCount(date, version, reqDto.getChannel(), reqDto.getTimeUnit(), "newUser"))
 							.collect(Collectors.toList());
 							.collect(Collectors.toList());
 
 
-					trend.setData(counts);
+//					trend.setData(counts);
 					return trend;
 					return trend;
 				})
 				})
 				.collect(Collectors.toList());
 				.collect(Collectors.toList());
@@ -99,7 +133,58 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 	 * @return Page
 	 * @return Page
 	 */
 	 */
 	@Override
 	@Override
-	public Page<PageNewUserTrendDetailVO> pageNewUserTrendDetail(PageNewUserTrendDetailDTO reqDto) {
+	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; // 无数据直接返回
+		}
+
+		// 获取渠道和版本
+		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());
+			// 查询新增用户数
+			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;
+	}
+	public Page<PageNewUserTrendDetailVO> pageNewUserTrendDetail2(PageUserAnalyseDTO reqDto) {
 		// 1. 初始化分页对象并计算总条数
 		// 1. 初始化分页对象并计算总条数
 		Page<PageNewUserTrendDetailVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
 		Page<PageNewUserTrendDetailVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
 		page.setTotal(calculateTotalCount(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit()));
 		page.setTotal(calculateTotalCount(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit()));
@@ -172,7 +257,53 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 	 * @return GetNewUserRetentionVO
 	 * @return GetNewUserRetentionVO
 	 */
 	 */
 	@Override
 	@Override
-	public GetNewUserRetentionVO getNewUserRetention(GetNewUserRetentionDTO reqDto) {
+	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;
+	}
+
+	public GetNewUserRetentionVO getNewUserRetention2(GetNewUserRetentionDTO reqDto) {
 		String timeUnit = "day";
 		String timeUnit = "day";
 		GetNewUserRetentionVO result = new GetNewUserRetentionVO();
 		GetNewUserRetentionVO result = new GetNewUserRetentionVO();
 		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), timeUnit);
 		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), timeUnit);
@@ -215,7 +346,59 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 	 * @return Page
 	 * @return Page
 	 */
 	 */
 	@Override
 	@Override
-	public Page<PageRetentionDetailVO> pageNewUserRetentionDetail(PageRetentionDetailDTO reqDto) {
+	public Page<PageRetentionDetailVO> pageNewUserRetentionDetail(PageUserAnalyseDTO reqDto) {
+		// 初始化分页对象并计算总条数
+		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;
+	}
+	public Page<PageRetentionDetailVO> pageNewUserRetentionDetail2(PageUserAnalyseDTO reqDto) {
 		// 1. 初始化分页对象并设置总条数
 		// 1. 初始化分页对象并设置总条数
 		final String timeUnit = "day";
 		final String timeUnit = "day";
 		Page<PageRetentionDetailVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
 		Page<PageRetentionDetailVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
@@ -284,13 +467,47 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 	 * @return GetNewUserTrendVO
 	 * @return GetNewUserTrendVO
 	 */
 	 */
 	@Override
 	@Override
-
 	public GetNewUserTrendVO getActiveTrend(UserAnalyseQueryBaseDTO reqDto) {
 	public GetNewUserTrendVO getActiveTrend(UserAnalyseQueryBaseDTO reqDto) {
 		GetNewUserTrendVO result = new GetNewUserTrendVO();
 		GetNewUserTrendVO result = new GetNewUserTrendVO();
 		// 1. 生成时间轴
 		// 1. 生成时间轴
 		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
 		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
 		result.setDates(dates);
 		result.setDates(dates);
 
 
+		// 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());
+								return statActiveUserCount(startTime, endTime, reqDto.getAppId(), version, reqDto.getChannel());
+							})
+							.collect(Collectors.toList());
+					trend.setData(counts);
+					return trend;
+				})
+				.toList();
+		result.setItems(voList);
+		return result;
+	}
+	public GetNewUserTrendVO getActiveTrend2(UserAnalyseQueryBaseDTO reqDto) {
+		GetNewUserTrendVO result = new GetNewUserTrendVO();
+		// 1. 生成时间轴
+		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
+		result.setDates(dates);
+
 		// 2. 处理版本列表(为空时按"All"处理)
 		// 2. 处理版本列表(为空时按"All"处理)
 		List<String> versions = reqDto.getVersion();
 		List<String> versions = reqDto.getVersion();
 		List<String> processVersions = (versions == null || versions.isEmpty())
 		List<String> processVersions = (versions == null || versions.isEmpty())
@@ -310,7 +527,7 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 							.map(date -> calculateNewUserCount(date, version, reqDto.getChannel(), reqDto.getTimeUnit(), "activeUser"))
 							.map(date -> calculateNewUserCount(date, version, reqDto.getChannel(), reqDto.getTimeUnit(), "activeUser"))
 							.collect(Collectors.toList());
 							.collect(Collectors.toList());
 
 
-					trend.setData(counts);
+//					trend.setData(counts);
 					return trend;
 					return trend;
 				})
 				})
 				.collect(Collectors.toList());
 				.collect(Collectors.toList());
@@ -326,105 +543,59 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 	 */
 	 */
 	@Override
 	@Override
 	public GetActiveUserBaseVO getActiveCompose(UserAnalyseQueryBaseDTO reqDto) {
 	public GetActiveUserBaseVO getActiveCompose(UserAnalyseQueryBaseDTO reqDto) {
-		// 创建返回对象
-		GetActiveUserBaseVO result = new GetActiveUserBaseVO();
-		Random random = new Random();
-
-		// 1. 生成日期列表
-		List<String> dates = new ArrayList<>();
-		LocalDate startDate = reqDto.getFromDate();
-		LocalDate endDate = reqDto.getToDate();
-		LocalDateTime currentDateTime = LocalDateTime.of(startDate, LocalTime.MIN);
-		LocalDateTime endDateTime = LocalDateTime.of(endDate, LocalTime.MAX);
-
-		// 根据时间单位生成对应日期
-		while (!currentDateTime.isAfter(endDateTime)) {
-			String dateStr;
-			switch (reqDto.getTimeUnit()) {
-				case "hour":
-					dateStr = currentDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
-					currentDateTime = currentDateTime.plusHours(1);
-					break;
-				case "day":
-					dateStr = currentDateTime.toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE);
-					currentDateTime = currentDateTime.plusDays(1);
-					break;
-				case "week":
-					LocalDate weekStart = currentDateTime.toLocalDate();
-					LocalDate weekEnd = weekStart.plusDays(6);
-					if (weekEnd.isAfter(endDate)) {
-						weekEnd = endDate;
-					}
-					dateStr = weekStart.format(DateTimeFormatter.ISO_LOCAL_DATE) + "~" +
-							weekEnd.format(DateTimeFormatter.ISO_LOCAL_DATE);
-					currentDateTime = currentDateTime.plusWeeks(1);
-					break;
-				case "month":
-					LocalDate monthStart = currentDateTime.toLocalDate();
-					LocalDate monthEnd = monthStart.plusMonths(1).minusDays(1);
-					if (monthEnd.isAfter(endDate)) {
-						monthEnd = endDate;
-					}
-					dateStr = monthStart.withDayOfMonth(1).format(DateTimeFormatter.ISO_LOCAL_DATE) + "~" +
-							monthEnd.format(DateTimeFormatter.ISO_LOCAL_DATE);
-					currentDateTime = currentDateTime.plusMonths(1);
-					break;
-				default:
-					dateStr = currentDateTime.toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE);
-					currentDateTime = currentDateTime.plusDays(1);
-			}
-			dates.add(dateStr);
+		// 1. 获取时间坐标轴
+		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
+		// 获取版本信息
+		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());
+			// 活跃用户数
+			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);
 		}
 		}
-		result.setDates(dates);
 
 
-		// 2. 生成各项数据(调整为Double类型列表)
+		// 组装结果
 		List<ActiveUserComposeVO> items = new ArrayList<>();
 		List<ActiveUserComposeVO> items = new ArrayList<>();
-		int dataSize = dates.size();
 
 
-		// 活跃用户数据(整数转为Double)
 		ActiveUserComposeVO activeUser = new ActiveUserComposeVO();
 		ActiveUserComposeVO activeUser = new ActiveUserComposeVO();
 		activeUser.setName("活跃用户");
 		activeUser.setName("活跃用户");
-		List<Double> activeData = new ArrayList<>();
-		for (int i = 0; i < dataSize; i++) {
-			int value = 30000 + random.nextInt(5000) + (i * 50);
-			activeData.add((double) value); // 整数转Double
-		}
-//		activeUser.setData(activeData);
+		activeUser.setData(activeCounts);
+		activeUser.setKey("activeUser");
+		activeUser.setVersion( version == null ? "All" : version);
 		items.add(activeUser);
 		items.add(activeUser);
 
 
-		// 新增用户数据(整数转为Double)
 		ActiveUserComposeVO newUser = new ActiveUserComposeVO();
 		ActiveUserComposeVO newUser = new ActiveUserComposeVO();
 		newUser.setName("新增用户");
 		newUser.setName("新增用户");
-		List<Double> newUserData = new ArrayList<>();
-		for (int i = 0; i < dataSize; i++) {
-			int value = activeData.get(i).intValue() - 100 - random.nextInt(300);
-			newUserData.add((double) value); // 整数转Double
-		}
-//		newUser.setData(newUserData);
+		newUser.setData(newCounts);
+		newUser.setKey("newUser");
+		newUser.setVersion( version == null ? "All" : version);
 		items.add(newUser);
 		items.add(newUser);
 
 
-		// 新增用户占比数据(保持Double类型)
 		ActiveUserComposeVO newUserRate = new ActiveUserComposeVO();
 		ActiveUserComposeVO newUserRate = new ActiveUserComposeVO();
 		newUserRate.setName("新增用户占比");
 		newUserRate.setName("新增用户占比");
-		List<Double> rateData = new ArrayList<>();
-		for (int i = 0; i < dataSize; i++) {
-			double rate = (newUserData.get(i) / activeData.get(i)) * 100;
-			rateData.add(Math.round(rate * 100) / 100.0); // 保留两位小数
-		}
-//		newUserRate.setData(rateData);
+		newUserRate.setData(newRates);
+		newUserRate.setKey("newUserRate");
+		newUserRate.setVersion( version == null ? "All" : version);
 		items.add(newUserRate);
 		items.add(newUserRate);
 
 
-		// 老用户数据(计算后转为Double)
-		ActiveUserComposeVO oldUser = new ActiveUserComposeVO();
-		oldUser.setName("老用户");
-		List<Double> oldUserData = new ArrayList<>();
-		for (int i = 0; i < dataSize; i++) {
-			double value = activeData.get(i) - newUserData.get(i);
-			oldUserData.add(value); // 直接为Double类型
-		}
-//		oldUser.setData(oldUserData);
-		items.add(oldUser);
-
+		GetActiveUserBaseVO result = new GetActiveUserBaseVO();
+		result.setDates(dates);
 		result.setItems(items);
 		result.setItems(items);
 		return result;
 		return result;
 	}
 	}
@@ -436,13 +607,16 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 	 */
 	 */
 	@Override
 	@Override
 	public GetActiveUserBaseVO getActiveViscosity(UserAnalyseQueryBaseDTO reqDto) {
 	public GetActiveUserBaseVO getActiveViscosity(UserAnalyseQueryBaseDTO reqDto) {
+		// 1. 时间单位校验
 		if (!"day".equals(reqDto.getTimeUnit())){
 		if (!"day".equals(reqDto.getTimeUnit())){
 			throw new BusinessException("时间单位只能为day");
 			throw new BusinessException("时间单位只能为day");
 		}
 		}
+		// 2. 获取版本
 		String version = null;
 		String version = null;
 		if (reqDto.getVersion() != null && !reqDto.getVersion().isEmpty()) {
 		if (reqDto.getVersion() != null && !reqDto.getVersion().isEmpty()) {
 			version =reqDto.getVersion().get(0);
 			version =reqDto.getVersion().get(0);
 		}
 		}
+		// 3. 定义相关变量
 		List<ActiveUserComposeVO> items = new ArrayList<>();
 		List<ActiveUserComposeVO> items = new ArrayList<>();
 
 
 		List<BigDecimal> dayCounts = new ArrayList<>();
 		List<BigDecimal> dayCounts = new ArrayList<>();
@@ -450,32 +624,34 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 		List<BigDecimal> thirtyCounts = new ArrayList<>();
 		List<BigDecimal> thirtyCounts = new ArrayList<>();
 		List<BigDecimal> sevenRates = new ArrayList<>();
 		List<BigDecimal> sevenRates = new ArrayList<>();
 		List<BigDecimal> thirtyRates = new ArrayList<>();
 		List<BigDecimal> thirtyRates = new ArrayList<>();
-
+		// 4. 获取时间坐标轴
 		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
 		List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
+		// 5. 遍历时间坐标轴
 		for (String date : dates) {
 		for (String date : dates) {
+			// 5.1 定义时间范围
 			LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
 			LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
 			LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());
 			LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());
 			LocalDateTime sevenDaysAgo = startTime.minusDays(7);
 			LocalDateTime sevenDaysAgo = startTime.minusDays(7);
 			LocalDateTime thirtyDaysAgo = startTime.minusDays(30);
 			LocalDateTime thirtyDaysAgo = startTime.minusDays(30);
-			// 今日活跃用户数
+			// 5.2 今日活跃用户数
 			BigDecimal dayCount = BigDecimal.valueOf(statActiveUserCount(startTime, endTime, reqDto.getAppId(), version, reqDto.getChannel()));
 			BigDecimal dayCount = BigDecimal.valueOf(statActiveUserCount(startTime, endTime, reqDto.getAppId(), version, reqDto.getChannel()));
 			dayCounts.add(dayCount);
 			dayCounts.add(dayCount);
-			// 过去7日活跃用户
+			// 5.3 过去7日活跃用户
 			BigDecimal sevenCount = BigDecimal.valueOf(statActiveUserCount(sevenDaysAgo, startTime, reqDto.getAppId(), version, reqDto.getChannel()));
 			BigDecimal sevenCount = BigDecimal.valueOf(statActiveUserCount(sevenDaysAgo, startTime, reqDto.getAppId(), version, reqDto.getChannel()));
 			sevenCounts.add(sevenCount);
 			sevenCounts.add(sevenCount);
-			// 过去7日活跃用户
+			// 5.4 过去30日活跃用户
 			BigDecimal thirtyCount = BigDecimal.valueOf(statActiveUserCount(thirtyDaysAgo, startTime, reqDto.getAppId(), version, reqDto.getChannel()));
 			BigDecimal thirtyCount = BigDecimal.valueOf(statActiveUserCount(thirtyDaysAgo, startTime, reqDto.getAppId(), version, reqDto.getChannel()));
 			thirtyCounts.add(thirtyCount);
 			thirtyCounts.add(thirtyCount);
-			// 计算7日活跃用户占比
+			// 5.5 计算7日活跃用户占比
 			sevenRates.add(dayCount.compareTo(BigDecimal.ZERO) == 0 || sevenCount.compareTo(BigDecimal.ZERO) == 0
 			sevenRates.add(dayCount.compareTo(BigDecimal.ZERO) == 0 || sevenCount.compareTo(BigDecimal.ZERO) == 0
 					? BigDecimal.ZERO
 					? BigDecimal.ZERO
 					: dayCount.divide(sevenCount, 4, RoundingMode.HALF_UP));
 					: dayCount.divide(sevenCount, 4, RoundingMode.HALF_UP));
-			// 计算30日活跃用户占比
+			// 5.6 计算30日活跃用户占比
 			thirtyRates.add(dayCount.compareTo(BigDecimal.ZERO) == 0 || thirtyCount.compareTo(BigDecimal.ZERO) == 0
 			thirtyRates.add(dayCount.compareTo(BigDecimal.ZERO) == 0 || thirtyCount.compareTo(BigDecimal.ZERO) == 0
 					? BigDecimal.ZERO:
 					? BigDecimal.ZERO:
 					dayCount.divide(thirtyCount, 4, RoundingMode.HALF_UP));
 					dayCount.divide(thirtyCount, 4, RoundingMode.HALF_UP));
 		}
 		}
-		// 组装返回数据
+		// 6. 组装返回数据
 		ActiveUserComposeVO activeUser = new ActiveUserComposeVO();
 		ActiveUserComposeVO activeUser = new ActiveUserComposeVO();
 		activeUser.setData(dayCounts);
 		activeUser.setData(dayCounts);
 		activeUser.setName("日活");
 		activeUser.setName("日活");
@@ -511,6 +687,7 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 		mauRate.setVersion(version == null ? "All" : version);
 		mauRate.setVersion(version == null ? "All" : version);
 		items.add(mauRate);
 		items.add(mauRate);
 
 
+		// 7.返回结果
 		GetActiveUserBaseVO result = new GetActiveUserBaseVO();
 		GetActiveUserBaseVO result = new GetActiveUserBaseVO();
 		result.setDates(dates);
 		result.setDates(dates);
 		result.setItems(items);
 		result.setItems(items);
@@ -523,7 +700,57 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 	 * @return GetActiveUserBaseVO
 	 * @return GetActiveUserBaseVO
 	 */
 	 */
 	@Override
 	@Override
-	public GetActiveUserBaseVO getActiveWeekrate(UserAnalyseQueryBaseDTO reqDto) {
+	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());
+		// 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());
+			// 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;
+	}
+	public GetActiveUserBaseVO getActiveWeekrate2(UserAnalyseQueryBaseDTO reqDto) {
 		if (!"week".equals(reqDto.getTimeUnit())){
 		if (!"week".equals(reqDto.getTimeUnit())){
 			throw new BusinessException("时间单位错误");
 			throw new BusinessException("时间单位错误");
 		}
 		}
@@ -585,7 +812,58 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 	 * @return GetActiveUserBaseVO
 	 * @return GetActiveUserBaseVO
 	 */
 	 */
 	@Override
 	@Override
-	public GetActiveUserBaseVO getActiveMonthrate(UserAnalyseQueryBaseDTO reqDto) {
+	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());
+		// 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());
+			// 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;
+	}
+	public GetActiveUserBaseVO getActiveMonthRate2(UserAnalyseQueryBaseDTO reqDto) {
 		if (!"month".equals(reqDto.getTimeUnit())){
 		if (!"month".equals(reqDto.getTimeUnit())){
 			throw new BusinessException("时间单位错误");
 			throw new BusinessException("时间单位错误");
 		}
 		}
@@ -648,7 +926,91 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 	 * @return Page
 	 * @return Page
 	 */
 	 */
 	@Override
 	@Override
-	public Page<PageActiveDetailVO> pageActiveDetail(PageActiveDetailDTO reqDto) {
+	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; // 无数据直接返回
+		}
+		// 获取渠道和版本
+		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());
+			// 查询活跃用户数量
+			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;
+	}
+	public Page<PageActiveDetailVO> pageActiveDetail2(PageUserAnalyseDTO reqDto) {
 		// 活跃构成:活跃用户中新增用户的占比比例
 		// 活跃构成:活跃用户中新增用户的占比比例
 		// 活跃粘度:DAU/过去7日活跃用户,DAU/过去30日活跃用户
 		// 活跃粘度:DAU/过去7日活跃用户,DAU/过去30日活跃用户
 		// 过去7日活跃用户:过去7日(不含今日)的活跃用户数(去重)
 		// 过去7日活跃用户:过去7日(不含今日)的活跃用户数(去重)
@@ -802,6 +1164,7 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 		return page;
 		return page;
 	}
 	}
 
 
+
 	/************************************** 启动次数 *****************************************
 	/************************************** 启动次数 *****************************************
 	 * 查询启动次数趋势
 	 * 查询启动次数趋势
 	 * @param reqDto 查询启动次数趋势入参
 	 * @param reqDto 查询启动次数趋势入参
@@ -829,8 +1192,9 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 					trend.setKey("launch");
 					trend.setKey("launch");
 
 
 					// 计算每个时间点的启动次数
 					// 计算每个时间点的启动次数
-					List<Integer> counts = dates.stream()
+					List<Long> counts = dates.stream()
 							.map(date -> calculateNewUserCount(date, version, reqDto.getChannel(), reqDto.getTimeUnit(), "launch"))
 							.map(date -> calculateNewUserCount(date, version, reqDto.getChannel(), reqDto.getTimeUnit(), "launch"))
+							.map(Integer::longValue)
 							.collect(Collectors.toList());
 							.collect(Collectors.toList());
 
 
 					trend.setData(counts);
 					trend.setData(counts);
@@ -847,7 +1211,7 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 	 * @return Page
 	 * @return Page
 	 */
 	 */
 	@Override
 	@Override
-	public Page<PageLaunchDetailVO> pageLaunchDetail(PageActiveDetailDTO reqDto) {
+	public Page<PageLaunchDetailVO> pageLaunchDetail(PageUserAnalyseDTO reqDto) {
 		// 1. 初始化分页对象并计算总条数
 		// 1. 初始化分页对象并计算总条数
 		Page<PageLaunchDetailVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
 		Page<PageLaunchDetailVO> page = new Page<>(reqDto.getCurrent(), reqDto.getSize());
 		page.setTotal(calculateTotalCount(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit()));
 		page.setTotal(calculateTotalCount(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit()));
@@ -1508,26 +1872,7 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 	 * 统计指定时间范围内的新增用户数
 	 * 统计指定时间范围内的新增用户数
 	 */
 	 */
 	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){
-		// 1. 空值判断
-		if (startTime == null || endTime == null) {
-			return 0L;
-		}
-		// 2. 组装查询条件
-		LambdaQueryWrapper<MktStatNewUser> queryWrapper = Wrappers.<MktStatNewUser>lambdaQuery()
-				.eq(MktStatNewUser::getAppId, appId)
-				.ge(MktStatNewUser::getStatDate, startTime)
-				.lt(MktStatNewUser::getStatDate, endTime);
-
-		// 添加渠道条件
-		if (channels != null && !channels.isEmpty()) {
-			queryWrapper.in(MktStatNewUser::getChannel, channels);
-		}
-		// 添加版本条件
-		if (version != null && !version.isEmpty()){
-			queryWrapper.eq(MktStatNewUser::getVersion, version);
-		}
-		// 3. 统计新增用户数,不用去重
-		List<MktStatNewUser> newUsers = newUserMapper.selectList(queryWrapper);
+		List<String> newUsers = getNewUserIdList(startTime, endTime, appId, version, channels);
 		return newUsers == null ? 0L : newUsers.size();
 		return newUsers == null ? 0L : newUsers.size();
 	}
 	}
 
 
@@ -1535,28 +1880,81 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
 	 * 统计指定时间范围内的活跃用户数(去重)
 	 * 统计指定时间范围内的活跃用户数(去重)
 	 */
 	 */
 	private Long statActiveUserCount(LocalDateTime startTime, LocalDateTime endTime, String appId, String version, List<String> channels){
 	private Long statActiveUserCount(LocalDateTime startTime, LocalDateTime endTime, String appId, String version, List<String> channels){
-		// 1. 空值判断
-		if (startTime == null || endTime == null) {
-			return 0L;
-		}
-		// 2. 组装查询条件
+		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()
 		LambdaQueryWrapper<MktStatActiveUser> queryWrapper = Wrappers.<MktStatActiveUser>lambdaQuery()
 				.eq(MktStatActiveUser::getAppId, appId)
 				.eq(MktStatActiveUser::getAppId, appId)
-				.ge(MktStatActiveUser::getStatDate, startTime)
-				.lt(MktStatActiveUser::getStatDate, endTime)
+				.ge(startTime != null, MktStatActiveUser::getStatDate, startTime)
+				.lt(endTime != null, MktStatActiveUser::getStatDate, endTime)
 				.select(MktStatActiveUser::getUserId)
 				.select(MktStatActiveUser::getUserId)
 				.groupBy(MktStatActiveUser::getUserId);
 				.groupBy(MktStatActiveUser::getUserId);
 
 
-		// 添加渠道条件
+		// 2. 添加渠道条件
 		if (channels != null && !channels.isEmpty()) {
 		if (channels != null && !channels.isEmpty()) {
 			queryWrapper.in(MktStatActiveUser::getChannel, channels);
 			queryWrapper.in(MktStatActiveUser::getChannel, channels);
 		}
 		}
-		// 添加版本条件
+		// 3. 添加版本条件
 		if (version != null && !version.isEmpty()){
 		if (version != null && !version.isEmpty()){
 			queryWrapper.eq(MktStatActiveUser::getVersion, version);
 			queryWrapper.eq(MktStatActiveUser::getVersion, version);
 		}
 		}
-		// 3. 统计活跃用户数,根据userID去重
+		// 4. 统计活跃用户数,根据userID去重
 		List<MktStatActiveUser> activeUsers = activeUserMapper.selectList(queryWrapper);
 		List<MktStatActiveUser> activeUsers = activeUserMapper.selectList(queryWrapper);
-		return activeUsers == null ? 0L : activeUsers.size();
+		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);
+
+		// 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();
 	}
 	}
 }
 }