|
@@ -6,9 +6,13 @@ 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.MktStatUserAnalysis;
|
|
|
-import com.pig4cloud.pig.statistics.api.entity.MktStatUserRetention;
|
|
|
+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.entity.user.MktStatUserRetention;
|
|
|
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;
|
|
@@ -43,8 +47,12 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
|
|
|
|
|
|
private final MktStatUserAnalysisMapper userAnalysisMapper;
|
|
|
|
|
|
-
|
|
|
private final MktStatUserRetentionMapper userRetentionMapper;
|
|
|
+
|
|
|
+ private final MktStatActiveUserMapper activeUserMapper;
|
|
|
+
|
|
|
+ private final MktStatNewUserMapper newUserMapper;
|
|
|
+
|
|
|
/**
|
|
|
* 获取新增趋势
|
|
|
* @param reqDto 请求参数
|
|
@@ -428,49 +436,83 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
|
|
|
*/
|
|
|
@Override
|
|
|
public GetActiveUserBaseVO getActiveViscosity(UserAnalyseQueryBaseDTO reqDto) {
|
|
|
- GetActiveUserBaseVO result = new GetActiveUserBaseVO();
|
|
|
+ if (!"day".equals(reqDto.getTimeUnit())){
|
|
|
+ throw new BusinessException("时间单位只能为day");
|
|
|
+ }
|
|
|
+ String version = null;
|
|
|
+ if (reqDto.getVersion() != null && !reqDto.getVersion().isEmpty()) {
|
|
|
+ version =reqDto.getVersion().get(0);
|
|
|
+ }
|
|
|
+ List<ActiveUserComposeVO> items = new ArrayList<>();
|
|
|
|
|
|
- // 构建日期列表(可根据 reqDto 的 fromDate 和 toDate 动态生成,此处为示例固定日期)
|
|
|
- List<String> dates = Arrays.asList(
|
|
|
- "2025-07-29", "2025-07-30", "2025-07-31",
|
|
|
- "2025-08-01", "2025-08-02", "2025-08-03",
|
|
|
- "2025-08-04", "2025-08-05"
|
|
|
- );
|
|
|
- result.setDates(dates);
|
|
|
+ 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<>();
|
|
|
|
|
|
- // 构建活跃用户组成数据
|
|
|
- List<ActiveUserComposeVO> items = new ArrayList<>();
|
|
|
+ List<String> dates = generateTimeAxis(reqDto.getFromDate(), reqDto.getToDate(), reqDto.getTimeUnit());
|
|
|
+ for (String date : dates) {
|
|
|
+ LocalDateTime startTime = getStartTime(date, reqDto.getTimeUnit());
|
|
|
+ LocalDateTime endTime = getEndTime(date, reqDto.getTimeUnit());
|
|
|
+ LocalDateTime sevenDaysAgo = startTime.minusDays(7);
|
|
|
+ LocalDateTime thirtyDaysAgo = startTime.minusDays(30);
|
|
|
+ // 今日活跃用户数
|
|
|
+ BigDecimal dayCount = BigDecimal.valueOf(statActiveUserCount(startTime, endTime, reqDto.getAppId(), version, reqDto.getChannel()));
|
|
|
+ dayCounts.add(dayCount);
|
|
|
+ // 过去7日活跃用户
|
|
|
+ BigDecimal sevenCount = BigDecimal.valueOf(statActiveUserCount(sevenDaysAgo, startTime, reqDto.getAppId(), version, reqDto.getChannel()));
|
|
|
+ sevenCounts.add(sevenCount);
|
|
|
+ // 过去7日活跃用户
|
|
|
+ BigDecimal thirtyCount = BigDecimal.valueOf(statActiveUserCount(thirtyDaysAgo, startTime, reqDto.getAppId(), version, reqDto.getChannel()));
|
|
|
+ thirtyCounts.add(thirtyCount);
|
|
|
+ // 计算7日活跃用户占比
|
|
|
+ sevenRates.add(dayCount.compareTo(BigDecimal.ZERO) == 0 || sevenCount.compareTo(BigDecimal.ZERO) == 0
|
|
|
+ ? BigDecimal.ZERO
|
|
|
+ : dayCount.divide(sevenCount, 4, RoundingMode.HALF_UP));
|
|
|
+ // 计算30日活跃用户占比
|
|
|
+ thirtyRates.add(dayCount.compareTo(BigDecimal.ZERO) == 0 || thirtyCount.compareTo(BigDecimal.ZERO) == 0
|
|
|
+ ? BigDecimal.ZERO:
|
|
|
+ dayCount.divide(thirtyCount, 4, RoundingMode.HALF_UP));
|
|
|
+ }
|
|
|
+ // 组装返回数据
|
|
|
+ ActiveUserComposeVO activeUser = new ActiveUserComposeVO();
|
|
|
+ activeUser.setData(dayCounts);
|
|
|
+ activeUser.setName("日活");
|
|
|
+ activeUser.setKey("activeUser");
|
|
|
+ activeUser.setVersion(version == null ? "All" : version);
|
|
|
+ items.add(activeUser);
|
|
|
|
|
|
- // 日活数据
|
|
|
- ActiveUserComposeVO activeUserItem = new ActiveUserComposeVO();
|
|
|
- activeUserItem.setName("日活");
|
|
|
-// activeUserItem.setData(Arrays.asList(1523.0, 1489.0, 1602.0, 1576.0, 987.0, 923.0, 1756.0, 1632.0));
|
|
|
- items.add(activeUserItem);
|
|
|
-
|
|
|
- // 过去7日活跃用户数据
|
|
|
- ActiveUserComposeVO wauItem = new ActiveUserComposeVO();
|
|
|
- wauItem.setName("过去7日活跃用户");
|
|
|
-// wauItem.setData(Arrays.asList(8234.0, 8156.0, 8321.0, 8289.0, 8356.0, 8412.0, 8531.0, 8498.0));
|
|
|
- items.add(wauItem);
|
|
|
-
|
|
|
- // DAU/过去7日活跃用户比率
|
|
|
- ActiveUserComposeVO wauRateItem = new ActiveUserComposeVO();
|
|
|
- wauRateItem.setName("DAU/过去7日活跃用户");
|
|
|
-// wauRateItem.setData(Arrays.asList(18.5, 18.25, 19.25, 19.01, 11.81, 10.97, 20.58, 19.21));
|
|
|
- items.add(wauRateItem);
|
|
|
-
|
|
|
- // 过去30日活跃用户数据
|
|
|
- ActiveUserComposeVO mauItem = new ActiveUserComposeVO();
|
|
|
- mauItem.setName("过去30日活跃用户");
|
|
|
-// mauItem.setData(Arrays.asList(35621.0, 35489.0, 35723.0, 35698.0, 34987.0, 34215.0, 35123.0, 36012.0));
|
|
|
- items.add(mauItem);
|
|
|
-
|
|
|
- // DAU/过去30日活跃用户比率
|
|
|
- ActiveUserComposeVO mauRateItem = new ActiveUserComposeVO();
|
|
|
- mauRateItem.setName("DAU/过去30日活跃用户");
|
|
|
-// mauRateItem.setData(Arrays.asList(4.28, 4.19, 4.48, 4.42, 2.82, 2.70, 5.00, 4.53));
|
|
|
- items.add(mauRateItem);
|
|
|
+ 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);
|
|
|
|
|
|
+ GetActiveUserBaseVO result = new GetActiveUserBaseVO();
|
|
|
+ result.setDates(dates);
|
|
|
result.setItems(items);
|
|
|
return result;
|
|
|
}
|
|
@@ -1461,4 +1503,60 @@ public class UserAnalyseServiceImpl implements UserAnalyseService {
|
|
|
throw new IllegalArgumentException("不支持的时间单位: " + timeUnit);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 统计指定时间范围内的新增用户数
|
|
|
+ */
|
|
|
+ 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);
|
|
|
+ return newUsers == null ? 0L : newUsers.size();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 统计指定时间范围内的活跃用户数(去重)
|
|
|
+ */
|
|
|
+ private Long statActiveUserCount(LocalDateTime startTime, LocalDateTime endTime, String appId, String version, List<String> channels){
|
|
|
+ // 1. 空值判断
|
|
|
+ if (startTime == null || endTime == null) {
|
|
|
+ return 0L;
|
|
|
+ }
|
|
|
+ // 2. 组装查询条件
|
|
|
+ LambdaQueryWrapper<MktStatActiveUser> queryWrapper = Wrappers.<MktStatActiveUser>lambdaQuery()
|
|
|
+ .eq(MktStatActiveUser::getAppId, appId)
|
|
|
+ .ge(MktStatActiveUser::getStatDate, startTime)
|
|
|
+ .lt(MktStatActiveUser::getStatDate, endTime)
|
|
|
+ .select(MktStatActiveUser::getUserId)
|
|
|
+ .groupBy(MktStatActiveUser::getUserId);
|
|
|
+
|
|
|
+ // 添加渠道条件
|
|
|
+ if (channels != null && !channels.isEmpty()) {
|
|
|
+ queryWrapper.in(MktStatActiveUser::getChannel, channels);
|
|
|
+ }
|
|
|
+ // 添加版本条件
|
|
|
+ if (version != null && !version.isEmpty()){
|
|
|
+ queryWrapper.eq(MktStatActiveUser::getVersion, version);
|
|
|
+ }
|
|
|
+ // 3. 统计活跃用户数,根据userID去重
|
|
|
+ List<MktStatActiveUser> activeUsers = activeUserMapper.selectList(queryWrapper);
|
|
|
+ return activeUsers == null ? 0L : activeUsers.size();
|
|
|
+ }
|
|
|
}
|