彩票猪手精推版

This commit is contained in:
lihanqi
2025-11-04 17:18:21 +08:00
parent 7c2b23f1a2
commit dc59f393fa
172 changed files with 23112 additions and 122 deletions

View File

@@ -1,26 +1,28 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.common.ErrorCode;
import com.xy.xyaicpzs.constant.UserConstant;
import com.xy.xyaicpzs.domain.dto.user.UserPhoneLoginRequest;
import com.xy.xyaicpzs.domain.dto.user.UserPhoneRegisterRequest;
import com.xy.xyaicpzs.domain.entity.User;
import com.xy.xyaicpzs.domain.vo.*;
import com.xy.xyaicpzs.exception.BusinessException;
import com.xy.xyaicpzs.mapper.UserMapper;
import com.xy.xyaicpzs.service.SmsService;
import com.xy.xyaicpzs.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author XY003
@@ -329,4 +331,239 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
}
return DigestUtils.md5DigestAsHex((SALT + password).getBytes());
}
// region 统计相关方法实现
@Override
public UserStatisticsVO getNewUsersStatistics(String startDate, String endDate) {
try {
// 构建查询条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.between("createTime", startDate + " 00:00:00", endDate + " 23:59:59");
// 获取新增用户总数
long totalNewUsers = this.count(queryWrapper);
// 获取新增用户列表最近20个
queryWrapper.orderByDesc("createTime");
queryWrapper.last("LIMIT 20");
List<User> recentUsers = this.list(queryWrapper);
List<UserVO> recentUserVOs = recentUsers.stream().map(user -> {
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user, userVO);
return userVO;
}).collect(Collectors.toList());
return UserStatisticsVO.builder()
.totalNewUsers(totalNewUsers)
.startDate(startDate)
.endDate(endDate)
.recentUsers(recentUserVOs)
.build();
} catch (Exception e) {
log.error("获取新增用户统计失败:{}", e.getMessage());
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取统计数据失败");
}
}
@Override
public VipStatisticsVO getNewVipsStatistics(String startDate, String endDate) {
try {
// 构建查询条件 - 新增会员isVip != 0 且在时间范围内)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.ne("isVip", 0);
queryWrapper.between("createTime", startDate + " 00:00:00", endDate + " 23:59:59");
// 获取新增会员总数
long totalNewVips = this.count(queryWrapper);
// 获取新增会员列表最近20个
queryWrapper.orderByDesc("createTime");
queryWrapper.last("LIMIT 20");
List<User> recentVips = this.list(queryWrapper);
List<UserVO> recentVipVOs = recentVips.stream().map(user -> {
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user, userVO);
return userVO;
}).collect(Collectors.toList());
// 计算会员转化率(该时间段内新增会员 / 新增用户总数)
QueryWrapper<User> allUsersWrapper = new QueryWrapper<>();
allUsersWrapper.between("createTime", startDate + " 00:00:00", endDate + " 23:59:59");
long totalNewUsers = this.count(allUsersWrapper);
double conversionRate = totalNewUsers > 0 ? (double) totalNewVips / totalNewUsers * 100 : 0.0;
return VipStatisticsVO.builder()
.totalNewVips(totalNewVips)
.totalNewUsers(totalNewUsers)
.conversionRate(Math.round(conversionRate * 100.0) / 100.0)
.startDate(startDate)
.endDate(endDate)
.recentVips(recentVipVOs)
.build();
} catch (Exception e) {
log.error("获取新增会员统计失败:{}", e.getMessage());
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取统计数据失败");
}
}
@Override
public RegistrationTrendVO getRegistrationTrend(String startDate, String endDate, String granularity) {
try {
// 这里简化处理实际应该使用SQL的GROUP BY和DATE_FORMAT函数
// 由于MyBatis-Plus的限制我们先获取所有数据然后在Java中分组
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.between("createTime", startDate + " 00:00:00", endDate + " 23:59:59");
queryWrapper.orderByAsc("createTime");
List<User> users = this.list(queryWrapper);
Map<String, Long> trendData = new HashMap<>();
Map<String, Long> vipTrendData = new HashMap<>();
// 在Java中按指定粒度分组统计
for (User user : users) {
String key = formatDateByGranularity(user.getCreateTime(), granularity);
trendData.put(key, trendData.getOrDefault(key, 0L) + 1);
// 同时统计会员趋势
if (user.getIsVip() != null && user.getIsVip() != 0) {
vipTrendData.put(key, vipTrendData.getOrDefault(key, 0L) + 1);
}
}
return RegistrationTrendVO.builder()
.startDate(startDate)
.endDate(endDate)
.granularity(granularity)
.userTrend(trendData)
.vipTrend(vipTrendData)
.totalUsers(users.size())
.totalVips(users.stream().mapToLong(u -> u.getIsVip() != null && u.getIsVip() != 0 ? 1 : 0).sum())
.build();
} catch (Exception e) {
log.error("获取用户注册趋势失败:{}", e.getMessage());
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取趋势数据失败");
}
}
@Override
public Page<UserVO> getExpiringVips(Integer days, Long current, Long pageSize) {
try {
// 计算目标日期范围
Date now = new Date();
Date futureDate = new Date(now.getTime() + days * 24 * 60 * 60 * 1000L);
// 构建查询条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.ne("isVip", 0); // 是会员
queryWrapper.isNotNull("vipExpire"); // 有到期时间
queryWrapper.between("vipExpire", now, futureDate); // 在即将到期的时间范围内
queryWrapper.orderByAsc("vipExpire"); // 按到期时间升序
Page<User> userPage = this.page(new Page<>(current, pageSize), queryWrapper);
// 转换为UserVO
Page<UserVO> userVOPage = new Page<>(userPage.getCurrent(), userPage.getSize(), userPage.getTotal());
List<UserVO> userVOList = userPage.getRecords().stream().map(user -> {
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user, userVO);
return userVO;
}).collect(Collectors.toList());
userVOPage.setRecords(userVOList);
return userVOPage;
} catch (Exception e) {
log.error("获取即将到期会员失败:{}", e.getMessage());
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取数据失败");
}
}
@Override
public VipDistributionVO getVipDistribution() {
try {
Date now = new Date();
// 总用户数
long totalUsers = this.count();
// 普通用户数isVip = 0 或 null
QueryWrapper<User> normalWrapper = new QueryWrapper<>();
normalWrapper.and(wrapper -> wrapper.eq("isVip", 0).or().isNull("isVip"));
long normalUsers = this.count(normalWrapper);
// 有效会员数isVip != 0 且 vipExpire > now
QueryWrapper<User> activeVipWrapper = new QueryWrapper<>();
activeVipWrapper.ne("isVip", 0)
.and(wrapper -> wrapper.isNull("vipExpire").or().gt("vipExpire", now));
long activeVips = this.count(activeVipWrapper);
// 过期会员数isVip != 0 且 vipExpire <= now
QueryWrapper<User> expiredVipWrapper = new QueryWrapper<>();
expiredVipWrapper.ne("isVip", 0)
.isNotNull("vipExpire")
.le("vipExpire", now);
long expiredVips = this.count(expiredVipWrapper);
// 即将到期会员数7天内到期
Date sevenDaysLater = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000L);
QueryWrapper<User> soonExpireWrapper = new QueryWrapper<>();
soonExpireWrapper.ne("isVip", 0)
.between("vipExpire", now, sevenDaysLater);
long soonExpireVips = this.count(soonExpireWrapper);
// 计算百分比
double normalPercentage = totalUsers > 0 ? (double) normalUsers / totalUsers * 100 : 0;
double activeVipPercentage = totalUsers > 0 ? (double) activeVips / totalUsers * 100 : 0;
double expiredVipPercentage = totalUsers > 0 ? (double) expiredVips / totalUsers * 100 : 0;
return VipDistributionVO.builder()
.totalUsers(totalUsers)
.normalUsers(normalUsers)
.normalPercentage(Math.round(normalPercentage * 100.0) / 100.0)
.activeVips(activeVips)
.activeVipPercentage(Math.round(activeVipPercentage * 100.0) / 100.0)
.expiredVips(expiredVips)
.expiredVipPercentage(Math.round(expiredVipPercentage * 100.0) / 100.0)
.soonExpireVips(soonExpireVips)
.statisticsTime(now)
.build();
} catch (Exception e) {
log.error("获取会员分布统计失败:{}", e.getMessage());
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取统计数据失败");
}
}
/**
* 根据时间粒度格式化日期
*/
private String formatDateByGranularity(Date date, String granularity) {
if (date == null) return "";
java.time.LocalDateTime localDateTime = date.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDateTime();
java.time.format.DateTimeFormatter formatter;
switch (granularity) {
case "week":
// 获取年份和周数
int year = localDateTime.getYear();
int week = localDateTime.get(java.time.temporal.WeekFields.of(java.util.Locale.getDefault()).weekOfYear());
return year + "-W" + String.format("%02d", week);
case "month":
formatter = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM");
break;
default:
formatter = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd");
break;
}
return localDateTime.format(formatter);
}
// endregion
}