彩票猪手精推版

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.D10;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【d10(d10表)】的数据库操作Service
* @createDate 2025-08-21 15:06:10
*/
public interface D10Service extends IService<D10> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.D11;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【d11(d11表)】的数据库操作Service
* @createDate 2025-08-21 15:06:10
*/
public interface D11Service extends IService<D11> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.D12;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【d12(d12表)】的数据库操作Service
* @createDate 2025-08-21 15:06:10
*/
public interface D12Service extends IService<D12> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.D5;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【d5(d5表)】的数据库操作Service
* @createDate 2025-01-26 16:00:00
*/
public interface D5Service extends IService<D5> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.D6;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【d6(d6表)】的数据库操作Service
* @createDate 2025-08-21 14:10:22
*/
public interface D6Service extends IService<D6> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.D7;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【d7(d7表)】的数据库操作Service
* @createDate 2025-08-21 15:06:10
*/
public interface D7Service extends IService<D7> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.D8;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【d8(d8表)】的数据库操作Service
* @createDate 2025-08-21 15:06:10
*/
public interface D8Service extends IService<D8> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.D9;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【d9(d9表)】的数据库操作Service
* @createDate 2025-08-21 15:06:10
*/
public interface D9Service extends IService<D9> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.DltBackendHistory100;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【dlt_backend_history_100(大乐透后区最近100期数据表)】的数据库操作Service
* @createDate 2025-08-21 11:35:47
*/
public interface DltBackendHistory100Service extends IService<DltBackendHistory100> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.DltBackendHistoryAll;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【dlt_backend_history_all(大乐透后区全部历史数据表)】的数据库操作Service
* @createDate 2025-08-21 11:35:47
*/
public interface DltBackendHistoryAllService extends IService<DltBackendHistoryAll> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.DltBackendHistoryTop100;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【dlt_backend_history_top_100(大乐透后区百期数据排行表)】的数据库操作Service
* @createDate 2025-08-21 11:35:47
*/
public interface DltBackendHistoryTop100Service extends IService<DltBackendHistoryTop100> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.DltBackendHistoryTop;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【dlt_backend_history_top(大乐透后区历史数据排行表)】的数据库操作Service
* @createDate 2025-08-21 11:35:47
*/
public interface DltBackendHistoryTopService extends IService<DltBackendHistoryTop> {
}

View File

@@ -0,0 +1,41 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.vo.BallCombinationAnalysisVO;
/**
* 大乐透组合分析服务接口
*/
public interface DltCombinationAnalysisService {
/**
* 前区与前区的组合性分析
* @param masterBall 主球号码(前区)
* @param slaveBall 随球号码(前区)
* @return 组合分析结果
*/
BallCombinationAnalysisVO analyzeFrontFrontCombination(Integer masterBall, Integer slaveBall);
/**
* 前区与后区的组合性分析
* @param masterBall 主球号码(前区)
* @param slaveBall 随球号码(后区)
* @return 组合分析结果
*/
BallCombinationAnalysisVO analyzeFrontBackCombination(Integer masterBall, Integer slaveBall);
/**
* 后区与后区的组合性分析
* @param masterBall 主球号码(后区)
* @param slaveBall 随球号码(后区)
* @return 组合分析结果
*/
BallCombinationAnalysisVO analyzeBackBackCombination(Integer masterBall, Integer slaveBall);
/**
* 后区与前区的组合性分析
* @param masterBall 主球号码(后区)
* @param slaveBall 随球号码(前区)
* @return 组合分析结果
*/
BallCombinationAnalysisVO analyzeBackFrontCombination(Integer masterBall, Integer slaveBall);
}

View File

@@ -0,0 +1,60 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.vo.PrizeEstimateVO;
import com.xy.xyaicpzs.domain.vo.RedBallHitRateVO;
import com.xy.xyaicpzs.domain.vo.UserPredictStatVO;
/**
* 大乐透数据分析服务接口
*/
public interface DltDataAnalysisService {
/**
* 处理大乐透待开奖记录,匹配开奖结果
* @return 处理的记录数量
*/
int processPendingDltPredictions();
/**
* 获取用户大乐透预测统计数据
* @param userId 用户ID
* @return 用户大乐透预测统计数据
*/
UserPredictStatVO getUserDltPredictStat(Long userId);
/**
* 大乐透奖金统计
* @param userId 用户ID
* @param predictId 预测记录ID如果为null则统计所有记录
* @return 奖金统计信息
*/
PrizeEstimateVO getDltPrizeStatistics(Long userId, Long predictId);
/**
* 大乐透前区首球命中率分析
* @param userId 用户ID
* @return 前区首球命中率统计信息
*/
RedBallHitRateVO getFrontFirstBallHitRate(Long userId);
/**
* 大乐透前区球号命中率分析
* @param userId 用户ID
* @return 前区球号命中率统计信息
*/
RedBallHitRateVO getFrontBallHitRate(Long userId);
/**
* 大乐透后区首球命中率分析
* @param userId 用户ID
* @return 后区首球命中率统计信息
*/
RedBallHitRateVO getBackFirstBallHitRate(Long userId);
/**
* 大乐透后区球号命中率分析
* @param userId 用户ID
* @return 后区球号命中率统计信息
*/
RedBallHitRateVO getBackBallHitRate(Long userId);
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.DltDrawRecord;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【dlt_draw_record(大乐透开奖信息表)】的数据库操作Service
* @createDate 2025-08-20 15:55:06
*/
public interface DltDrawRecordService extends IService<DltDrawRecord> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.DltFrontendHistory100;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【dlt_frontend_history_100(大乐透前区最近100期数据表)】的数据库操作Service
* @createDate 2025-08-20 16:24:40
*/
public interface DltFrontendHistory100Service extends IService<DltFrontendHistory100> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.DltFrontendHistoryAll;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【dlt_frontend_history_all(大乐透前区全部历史数据表)】的数据库操作Service
* @createDate 2025-08-20 16:24:40
*/
public interface DltFrontendHistoryAllService extends IService<DltFrontendHistoryAll> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.DltFrontendHistoryTop100;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【dlt_frontend_history_top_100(大乐透前区百期数据排行表)】的数据库操作Service
* @createDate 2025-08-20 16:24:40
*/
public interface DltFrontendHistoryTop100Service extends IService<DltFrontendHistoryTop100> {
}

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.DltFrontendHistoryTop;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author XY003
* @description 针对表【dlt_frontend_history_top(大乐透前区历史数据排行表)】的数据库操作Service
* @createDate 2025-08-20 16:24:40
*/
public interface DltFrontendHistoryTopService extends IService<DltFrontendHistoryTop> {
}

View File

@@ -0,0 +1,41 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.vo.BallPersistenceAnalysisVO;
/**
* 大乐透持续性分析服务接口
*/
public interface DltPersistenceAnalysisService {
/**
* 前区与前区的持续性分析
* @param masterBall 主球号码(前区)
* @param slaveBall 随球号码(前区)
* @return 持续性分析结果
*/
BallPersistenceAnalysisVO analyzeFrontFrontPersistence(Integer masterBall, Integer slaveBall);
/**
* 后区与后区的持续性分析
* @param masterBall 主球号码(后区)
* @param slaveBall 随球号码(后区)
* @return 持续性分析结果
*/
BallPersistenceAnalysisVO analyzeBackBackPersistence(Integer masterBall, Integer slaveBall);
/**
* 前区与后区的持续性分析
* @param masterBall 主球号码(前区)
* @param slaveBall 随球号码(后区)
* @return 持续性分析结果
*/
BallPersistenceAnalysisVO analyzeFrontBackPersistence(Integer masterBall, Integer slaveBall);
/**
* 后区与前区的持续性分析
* @param masterBall 主球号码(后区)
* @param slaveBall 随球号码(前区)
* @return 持续性分析结果
*/
BallPersistenceAnalysisVO analyzeBackFrontPersistence(Integer masterBall, Integer slaveBall);
}

View File

@@ -0,0 +1,43 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.DltPredictRecord;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.Date;
import java.util.List;
/**
* @author XY003
* @description 针对表【dlt_predict_record(大乐透推测记录表)】的数据库操作Service
* @createDate 2025-09-08 14:01:12
*/
public interface DltPredictRecordService extends IService<DltPredictRecord> {
/**
* 创建大乐透预测记录
* @param userId 用户ID
* @param drawId 开奖期号
* @param drawDate 开奖日期
* @param frontBalls 5个前区球号码
* @param backBalls 2个后区球号码
* @return 创建的预测记录
*/
DltPredictRecord createDltPredictRecord(Long userId, Long drawId, Date drawDate, List<Integer> frontBalls, List<Integer> backBalls);
/**
* 根据用户ID分页获取大乐透预测记录
* @param userId 用户ID
* @param page 页码从1开始
* @param size 每页大小
* @return 用户的大乐透预测记录列表,按预测时间倒序排列
*/
List<DltPredictRecord> getDltPredictRecordsByUserIdWithPaging(Long userId, Integer page, Integer size);
/**
* 根据用户ID获取大乐透预测记录总数
* @param userId 用户ID
* @return 用户的大乐透预测记录总数
*/
Long getDltPredictRecordsCountByUserId(Long userId);
}

View File

@@ -1,9 +1,11 @@
package com.xy.xyaicpzs.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
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 jakarta.servlet.http.HttpServletRequest;
@@ -96,4 +98,53 @@ public interface UserService extends IService<User> {
* @return 加密后的密码
*/
String encryptPassword(String password);
// region 统计相关方法
/**
* 根据时间段获取新增用户统计
*
* @param startDate 开始日期 (格式: yyyy-MM-dd)
* @param endDate 结束日期 (格式: yyyy-MM-dd)
* @return 新增用户统计数据
*/
UserStatisticsVO getNewUsersStatistics(String startDate, String endDate);
/**
* 根据时间段获取新增会员统计
*
* @param startDate 开始日期 (格式: yyyy-MM-dd)
* @param endDate 结束日期 (格式: yyyy-MM-dd)
* @return 新增会员统计数据
*/
VipStatisticsVO getNewVipsStatistics(String startDate, String endDate);
/**
* 根据时间段获取用户注册趋势
*
* @param startDate 开始日期 (格式: yyyy-MM-dd)
* @param endDate 结束日期 (格式: yyyy-MM-dd)
* @param granularity 时间粒度 (day/week/month)
* @return 用户注册趋势数据
*/
RegistrationTrendVO getRegistrationTrend(String startDate, String endDate, String granularity);
/**
* 获取即将到期的会员列表
*
* @param days 提前多少天提醒 (默认7天)
* @param current 当前页
* @param pageSize 页大小
* @return 即将到期的会员列表
*/
Page<UserVO> getExpiringVips(Integer days, Long current, Long pageSize);
/**
* 获取会员状态分布统计
*
* @return 会员状态分布数据
*/
VipDistributionVO getVipDistribution();
// endregion
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.D10;
import com.xy.xyaicpzs.service.D10Service;
import com.xy.xyaicpzs.mapper.D10Mapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【d10(d10表)】的数据库操作Service实现
* @createDate 2025-08-21 15:06:10
*/
@Service
public class D10ServiceImpl extends ServiceImpl<D10Mapper, D10>
implements D10Service{
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.D11;
import com.xy.xyaicpzs.service.D11Service;
import com.xy.xyaicpzs.mapper.D11Mapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【d11(d11表)】的数据库操作Service实现
* @createDate 2025-08-21 15:06:10
*/
@Service
public class D11ServiceImpl extends ServiceImpl<D11Mapper, D11>
implements D11Service{
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.D12;
import com.xy.xyaicpzs.service.D12Service;
import com.xy.xyaicpzs.mapper.D12Mapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【d12(d12表)】的数据库操作Service实现
* @createDate 2025-08-21 15:06:10
*/
@Service
public class D12ServiceImpl extends ServiceImpl<D12Mapper, D12>
implements D12Service{
}

View File

@@ -0,0 +1,17 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.D5;
import com.xy.xyaicpzs.service.D5Service;
import com.xy.xyaicpzs.mapper.D5Mapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【d5(d5表)】的数据库操作Service实现
* @createDate 2025-01-26 16:00:00
*/
@Service
public class D5ServiceImpl extends ServiceImpl<D5Mapper, D5> implements D5Service {
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.D6;
import com.xy.xyaicpzs.service.D6Service;
import com.xy.xyaicpzs.mapper.D6Mapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【d6(d6表)】的数据库操作Service实现
* @createDate 2025-08-21 14:10:22
*/
@Service
public class D6ServiceImpl extends ServiceImpl<D6Mapper, D6>
implements D6Service{
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.D7;
import com.xy.xyaicpzs.service.D7Service;
import com.xy.xyaicpzs.mapper.D7Mapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【d7(d7表)】的数据库操作Service实现
* @createDate 2025-08-21 15:06:10
*/
@Service
public class D7ServiceImpl extends ServiceImpl<D7Mapper, D7>
implements D7Service{
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.D8;
import com.xy.xyaicpzs.service.D8Service;
import com.xy.xyaicpzs.mapper.D8Mapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【d8(d8表)】的数据库操作Service实现
* @createDate 2025-08-21 15:06:10
*/
@Service
public class D8ServiceImpl extends ServiceImpl<D8Mapper, D8>
implements D8Service{
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.D9;
import com.xy.xyaicpzs.service.D9Service;
import com.xy.xyaicpzs.mapper.D9Mapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【d9(d9表)】的数据库操作Service实现
* @createDate 2025-08-21 15:06:10
*/
@Service
public class D9ServiceImpl extends ServiceImpl<D9Mapper, D9>
implements D9Service{
}

View File

@@ -34,7 +34,8 @@ public class DataAnalysisServiceImpl implements DataAnalysisService {
// 查询用户的所有预测记录
QueryWrapper<PredictRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userId", userId);
queryWrapper.eq("userId", userId)
.eq("type", "ssq");
List<PredictRecord> predictRecords = predictRecordMapper.selectList(queryWrapper);
if (predictRecords == null || predictRecords.isEmpty()) {
@@ -83,7 +84,8 @@ public class DataAnalysisServiceImpl implements DataAnalysisService {
public int processPendingPredictions() {
// 查询所有待开奖的预测记录
QueryWrapper<PredictRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("predictStatus", "待开奖");
queryWrapper.eq("predictStatus", "待开奖")
.eq("type", "ssq");
List<PredictRecord> pendingRecords = predictRecordMapper.selectList(queryWrapper);
int processedCount = 0;
@@ -129,6 +131,9 @@ public class DataAnalysisServiceImpl implements DataAnalysisService {
queryWrapper.eq("predictStatus", request.getPredictStatus());
}
// 添加类型筛选条件,只查询双色球类型
queryWrapper.eq("type", "ssq");
// 按预测时间降序排序
queryWrapper.orderByDesc("predictTime");
@@ -147,8 +152,10 @@ public class DataAnalysisServiceImpl implements DataAnalysisService {
@Override
public long getTotalPredictCount() {
// 获取全部预测记录数量
return predictRecordMapper.selectCount(null);
// 获取全部预测记录数量(只统计双色球类型)
QueryWrapper<PredictRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("type", "ssq");
return predictRecordMapper.selectCount(queryWrapper);
}
/**

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.DltBackendHistory100;
import com.xy.xyaicpzs.service.DltBackendHistory100Service;
import com.xy.xyaicpzs.mapper.DltBackendHistory100Mapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【dlt_backend_history_100(大乐透后区最近100期数据表)】的数据库操作Service实现
* @createDate 2025-08-21 11:35:47
*/
@Service
public class DltBackendHistory100ServiceImpl extends ServiceImpl<DltBackendHistory100Mapper, DltBackendHistory100>
implements DltBackendHistory100Service{
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.DltBackendHistoryAll;
import com.xy.xyaicpzs.service.DltBackendHistoryAllService;
import com.xy.xyaicpzs.mapper.DltBackendHistoryAllMapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【dlt_backend_history_all(大乐透后区全部历史数据表)】的数据库操作Service实现
* @createDate 2025-08-21 11:35:47
*/
@Service
public class DltBackendHistoryAllServiceImpl extends ServiceImpl<DltBackendHistoryAllMapper, DltBackendHistoryAll>
implements DltBackendHistoryAllService{
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.DltBackendHistoryTop100;
import com.xy.xyaicpzs.service.DltBackendHistoryTop100Service;
import com.xy.xyaicpzs.mapper.DltBackendHistoryTop100Mapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【dlt_backend_history_top_100(大乐透后区百期数据排行表)】的数据库操作Service实现
* @createDate 2025-08-21 11:35:47
*/
@Service
public class DltBackendHistoryTop100ServiceImpl extends ServiceImpl<DltBackendHistoryTop100Mapper, DltBackendHistoryTop100>
implements DltBackendHistoryTop100Service{
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.DltBackendHistoryTop;
import com.xy.xyaicpzs.service.DltBackendHistoryTopService;
import com.xy.xyaicpzs.mapper.DltBackendHistoryTopMapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【dlt_backend_history_top(大乐透后区历史数据排行表)】的数据库操作Service实现
* @createDate 2025-08-21 11:35:47
*/
@Service
public class DltBackendHistoryTopServiceImpl extends ServiceImpl<DltBackendHistoryTopMapper, DltBackendHistoryTop>
implements DltBackendHistoryTopService{
}

View File

@@ -0,0 +1,251 @@
package com.xy.xyaicpzs.service.impl;
import com.xy.xyaicpzs.domain.entity.DltDrawRecord;
import com.xy.xyaicpzs.domain.entity.D5;
import com.xy.xyaicpzs.domain.entity.D6;
import com.xy.xyaicpzs.domain.entity.D7;
import com.xy.xyaicpzs.domain.entity.D8;
import com.xy.xyaicpzs.domain.vo.BallCombinationAnalysisVO;
import com.xy.xyaicpzs.service.DltCombinationAnalysisService;
import com.xy.xyaicpzs.service.DltDrawRecordService;
import com.xy.xyaicpzs.service.D5Service;
import com.xy.xyaicpzs.service.D6Service;
import com.xy.xyaicpzs.service.D7Service;
import com.xy.xyaicpzs.service.D8Service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
/**
* 大乐透组合分析服务实现类
*/
@Slf4j
@Service
public class DltCombinationAnalysisServiceImpl implements DltCombinationAnalysisService {
@Autowired
private D5Service d5Service;
@Autowired
private D6Service d6Service;
@Autowired
private D7Service d7Service;
@Autowired
private D8Service d8Service;
@Autowired
private DltDrawRecordService dltDrawRecordService;
@Override
public BallCombinationAnalysisVO analyzeFrontFrontCombination(Integer masterBall, Integer slaveBall) {
// 参数校验
validateBallRange(masterBall, 1, 35, "主球(前区)");
validateBallRange(slaveBall, 1, 35, "随球(前区)");
// 查询指定组合记录
D5 targetRecord = d5Service.lambdaQuery()
.eq(D5::getMasterBallNumber, masterBall)
.eq(D5::getSlaveBallNumber, slaveBall)
.one();
if (targetRecord == null) {
throw new RuntimeException(String.format("未找到主球(前区)%d和随球前区%d的组合记录", masterBall, slaveBall));
}
// 查询主球与所有其他球的组合记录
List<D5> allCombinations = d5Service.lambdaQuery()
.eq(D5::getMasterBallNumber, masterBall)
.ne(D5::getSlaveBallNumber, masterBall) // 排除自身
.list();
if (allCombinations.isEmpty()) {
throw new RuntimeException(String.format("未找到主球(前区)%d的组合记录", masterBall));
}
return buildCombinationAnalysisVO(
targetRecord.getCoefficient(),
allCombinations,
D5::getCoefficient,
D5::getSlaveBallNumber
);
}
@Override
public BallCombinationAnalysisVO analyzeFrontBackCombination(Integer masterBall, Integer slaveBall) {
// 参数校验
validateBallRange(masterBall, 1, 35, "主球(前区)");
validateBallRange(slaveBall, 1, 12, "随球(后区)");
// 查询指定组合记录
D6 targetRecord = d6Service.lambdaQuery()
.eq(D6::getMasterBallNumber, masterBall)
.eq(D6::getSlaveBallNumber, slaveBall)
.one();
if (targetRecord == null) {
throw new RuntimeException(String.format("未找到主球(前区)%d和随球后区%d的组合记录", masterBall, slaveBall));
}
// 查询主球与所有其他球的组合记录
List<D6> allCombinations = d6Service.lambdaQuery()
.eq(D6::getMasterBallNumber, masterBall)
.list();
if (allCombinations.isEmpty()) {
throw new RuntimeException(String.format("未找到主球(前区)%d的组合记录", masterBall));
}
return buildCombinationAnalysisVO(
targetRecord.getCoefficient(),
allCombinations,
D6::getCoefficient,
D6::getSlaveBallNumber
);
}
@Override
public BallCombinationAnalysisVO analyzeBackBackCombination(Integer masterBall, Integer slaveBall) {
// 参数校验
validateBallRange(masterBall, 1, 12, "主球(后区)");
validateBallRange(slaveBall, 1, 12, "随球(后区)");
// 查询指定组合记录
D7 targetRecord = d7Service.lambdaQuery()
.eq(D7::getMasterBallNumber, masterBall)
.eq(D7::getSlaveBallNumber, slaveBall)
.one();
if (targetRecord == null) {
throw new RuntimeException(String.format("未找到主球(后区)%d和随球后区%d的组合记录", masterBall, slaveBall));
}
// 查询主球与所有其他球的组合记录
List<D7> allCombinations = d7Service.lambdaQuery()
.eq(D7::getMasterBallNumber, masterBall)
.ne(D7::getSlaveBallNumber, masterBall) // 排除自身
.list();
if (allCombinations.isEmpty()) {
throw new RuntimeException(String.format("未找到主球(后区)%d的组合记录", masterBall));
}
return buildCombinationAnalysisVO(
targetRecord.getCoefficient(),
allCombinations,
D7::getCoefficient,
D7::getSlaveBallNumber
);
}
@Override
public BallCombinationAnalysisVO analyzeBackFrontCombination(Integer masterBall, Integer slaveBall) {
// 参数校验
validateBallRange(masterBall, 1, 12, "主球(后区)");
validateBallRange(slaveBall, 1, 35, "随球(前区)");
// 查询指定组合记录
D8 targetRecord = d8Service.lambdaQuery()
.eq(D8::getMasterBallNumber, masterBall)
.eq(D8::getSlaveBallNumber, slaveBall)
.one();
if (targetRecord == null) {
throw new RuntimeException(String.format("未找到主球(后区)%d和随球前区%d的组合记录", masterBall, slaveBall));
}
// 查询主球与所有其他球的组合记录
List<D8> allCombinations = d8Service.lambdaQuery()
.eq(D8::getMasterBallNumber, masterBall)
.list();
if (allCombinations.isEmpty()) {
throw new RuntimeException(String.format("未找到主球(后区)%d的组合记录", masterBall));
}
return buildCombinationAnalysisVO(
targetRecord.getCoefficient(),
allCombinations,
D8::getCoefficient,
D8::getSlaveBallNumber
);
}
/**
* 验证球号范围
*/
private void validateBallRange(Integer ballNumber, int minValue, int maxValue, String ballType) {
if (ballNumber == null) {
throw new IllegalArgumentException(ballType + "号码不能为空");
}
if (ballNumber < minValue || ballNumber > maxValue) {
throw new IllegalArgumentException(String.format("%s号码必须在%d-%d范围内错误值%d",
ballType, minValue, maxValue, ballNumber));
}
}
/**
* 构建组合分析结果VO
*/
private <T> BallCombinationAnalysisVO buildCombinationAnalysisVO(
Double targetCoefficient,
List<T> allCombinations,
Function<T, Double> coefficientGetter,
Function<T, Integer> ballNumberGetter) {
// 找出系数最高的球号
T highest = allCombinations.stream()
.max(Comparator.comparing(coefficientGetter))
.orElse(null);
// 找出系数最低的球号
T lowest = allCombinations.stream()
.min(Comparator.comparing(coefficientGetter))
.orElse(null);
// 计算平均系数
double avgCoefficient = allCombinations.stream()
.mapToDouble(coefficientGetter::apply)
.average()
.orElse(0.0);
// 获取最新开奖期号
Long latestDrawId = getLatestDrawId();
return BallCombinationAnalysisVO.builder()
.faceCoefficient(targetCoefficient)
.highestBall(highest != null ? ballNumberGetter.apply(highest) : null)
.highestCoefficient(highest != null ? coefficientGetter.apply(highest) : null)
.lowestBall(lowest != null ? ballNumberGetter.apply(lowest) : null)
.lowestCoefficient(lowest != null ? coefficientGetter.apply(lowest) : null)
.averageCoefficient(avgCoefficient)
.latestDrawId(latestDrawId)
.build();
}
/**
* 获取最新开奖期号
*/
private Long getLatestDrawId() {
String latestDrawId = dltDrawRecordService.lambdaQuery()
.orderByDesc(DltDrawRecord::getDrawId)
.last("LIMIT 1")
.oneOpt()
.map(DltDrawRecord::getDrawId)
.orElse(null);
if (latestDrawId != null) {
try {
return Long.parseLong(latestDrawId);
} catch (NumberFormatException e) {
log.warn("开奖期号格式转换失败:{}", latestDrawId);
}
}
return null;
}
}

View File

@@ -0,0 +1,485 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xy.xyaicpzs.domain.entity.DltDrawRecord;
import com.xy.xyaicpzs.domain.entity.DltPredictRecord;
import com.xy.xyaicpzs.mapper.DltDrawRecordMapper;
import com.xy.xyaicpzs.mapper.DltPredictRecordMapper;
import com.xy.xyaicpzs.domain.vo.PrizeEstimateVO;
import com.xy.xyaicpzs.domain.vo.RedBallHitRateVO;
import com.xy.xyaicpzs.domain.vo.UserPredictStatVO;
import com.xy.xyaicpzs.service.DltDataAnalysisService;
import com.xy.xyaicpzs.service.DltDrawRecordService;
import com.xy.xyaicpzs.util.DltPrizeCalculator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 大乐透数据分析服务实现类
*/
@Slf4j
@Service
public class DltDataAnalysisServiceImpl implements DltDataAnalysisService {
@Autowired
private DltPredictRecordMapper dltPredictRecordMapper;
@Autowired
private DltDrawRecordMapper dltDrawRecordMapper;
@Autowired
private DltDrawRecordService dltDrawRecordService;
@Override
public int processPendingDltPredictions() {
log.info("开始处理大乐透待开奖预测记录");
// 查询所有待开奖的大乐透预测记录
QueryWrapper<DltPredictRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("predictStatus", "待开奖");
List<DltPredictRecord> pendingRecords = dltPredictRecordMapper.selectList(queryWrapper);
log.info("找到{}条待开奖的大乐透预测记录", pendingRecords.size());
int processedCount = 0;
for (DltPredictRecord record : pendingRecords) {
try {
// 查询对应期号的开奖结果
// 注意DltDrawRecord的drawId是String类型DltPredictRecord的drawId是Long类型
QueryWrapper<DltDrawRecord> drawQueryWrapper = new QueryWrapper<>();
drawQueryWrapper.eq("drawId", String.valueOf(record.getDrawId()));
DltDrawRecord drawRecord = dltDrawRecordMapper.selectOne(drawQueryWrapper);
if (drawRecord != null) {
log.debug("处理预测记录ID{},期号:{}", record.getId(), record.getDrawId());
// 构建预测号码数组 [前区5个号码, 后区2个号码]
Integer[] predictNumbers = {
record.getFrontendBall1(),
record.getFrontendBall2(),
record.getFrontendBall3(),
record.getFrontendBall4(),
record.getFrontendBall5(),
record.getBackendBall1(),
record.getBackendBall2()
};
// 构建开奖号码数组 [前区5个号码, 后区2个号码]
Integer[] drawNumbers = {
drawRecord.getFrontBall1(),
drawRecord.getFrontBall2(),
drawRecord.getFrontBall3(),
drawRecord.getFrontBall4(),
drawRecord.getFrontBall5(),
drawRecord.getBackBall1(),
drawRecord.getBackBall2()
};
// 使用DltPrizeCalculator计算中奖结果
DltPrizeCalculator.PrizeResult prizeResult = DltPrizeCalculator.calculatePrize(predictNumbers, drawNumbers);
// 更新预测记录
// 根据中奖结果设置状态
if(prizeResult.getPrizeLevel().equals("未中奖")){
record.setPredictStatus("未中奖");
}else{
record.setPredictStatus("已中奖");
}
record.setPredictResult(prizeResult.getPrizeLevel());
record.setBonus(prizeResult.getBonus());
dltPredictRecordMapper.updateById(record);
log.debug("预测记录ID{} 处理完成,中奖等级:{},奖金:{}",
record.getId(), prizeResult.getPrizeLevel(), prizeResult.getBonus());
processedCount++;
} else {
log.debug("未找到期号{}的开奖记录", record.getDrawId());
}
} catch (Exception e) {
log.error("处理预测记录ID{} 时发生错误:{}", record.getId(), e.getMessage(), e);
}
}
log.info("大乐透待开奖预测记录处理完成,共处理{}条记录", processedCount);
return processedCount;
}
@Override
public UserPredictStatVO getUserDltPredictStat(Long userId) {
log.info("开始获取用户{}的大乐透预测统计数据", userId);
UserPredictStatVO statVO = new UserPredictStatVO();
// 查询用户的所有大乐透预测记录
QueryWrapper<DltPredictRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userId", userId);
List<DltPredictRecord> predictRecords = dltPredictRecordMapper.selectList(queryWrapper);
if (predictRecords == null || predictRecords.isEmpty()) {
log.info("用户{}没有大乐透预测记录", userId);
statVO.setUserId(userId);
statVO.setPredictCount(0L);
statVO.setHitCount(0L);
statVO.setPendingCount(0L);
statVO.setDrawnCount(0L);
statVO.setHitRate(BigDecimal.ZERO);
return statVO;
}
// 统计数据
long totalPredicts = predictRecords.size();
long hitCount = 0L;
long pendingCount = 0L;
long drawnCount = 0L;
for (DltPredictRecord record : predictRecords) {
if ("待开奖".equals(record.getPredictStatus())) {
pendingCount++;
} else if ("已开奖".equals(record.getPredictStatus())) {
drawnCount++;
// 检查是否中奖(除了"未中奖"都算中奖)
if (!"未中奖".equals(record.getPredictResult()) &&
record.getPredictResult() != null &&
!record.getPredictResult().trim().isEmpty()) {
hitCount++;
}
}
}
// 计算命中率
BigDecimal hitRate = drawnCount > 0 ?
BigDecimal.valueOf(hitCount).divide(BigDecimal.valueOf(drawnCount), 4, RoundingMode.HALF_UP) :
BigDecimal.ZERO;
statVO.setUserId(userId);
statVO.setPredictCount(totalPredicts);
statVO.setHitCount(hitCount);
statVO.setPendingCount(pendingCount);
statVO.setDrawnCount(drawnCount);
statVO.setHitRate(hitRate);
log.info("用户{}大乐透预测统计数据:预测次数={},待开奖次数={},已开奖次数={},命中次数={},命中率={}",
userId, totalPredicts, pendingCount, drawnCount, hitCount, hitRate);
return statVO;
}
@Override
public PrizeEstimateVO getDltPrizeStatistics(Long userId, Long predictId) {
log.info("开始为用户{}进行大乐透奖金统计predictId={}", userId, predictId);
// 查询条件
QueryWrapper<DltPredictRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userId", userId);
// 排除待开奖状态
queryWrapper.eq("predictStatus", "已开奖");
// 如果指定了预测记录ID则只查询该记录
if (predictId != null) {
queryWrapper.eq("id", predictId);
}
// 查询用户的大乐透预测记录
List<DltPredictRecord> records = dltPredictRecordMapper.selectList(queryWrapper);
// 按中奖等级分组统计
Map<String, Integer> countByPrizeLevel = new HashMap<>();
Map<String, BigDecimal> totalBonusByPrizeLevel = new HashMap<>();
// 初始化大乐透的所有等级
String[] prizeLevels = {"一等奖", "二等奖", "三等奖", "四等奖", "五等奖", "六等奖", "七等奖", "八等奖", "九等奖", "未中奖"};
for (String level : prizeLevels) {
countByPrizeLevel.put(level, 0);
totalBonusByPrizeLevel.put(level, BigDecimal.ZERO);
}
// 统计各等级数量和奖金
for (DltPredictRecord record : records) {
String prizeLevel = record.getPredictResult();
if (prizeLevel == null || prizeLevel.trim().isEmpty()) {
prizeLevel = "未中奖";
}
// 更新计数
countByPrizeLevel.put(prizeLevel, countByPrizeLevel.getOrDefault(prizeLevel, 0) + 1);
// 累计奖金
if (record.getBonus() != null) {
BigDecimal bonus = new BigDecimal(record.getBonus().toString());
totalBonusByPrizeLevel.put(prizeLevel,
totalBonusByPrizeLevel.getOrDefault(prizeLevel, BigDecimal.ZERO).add(bonus));
}
}
// 构建返回结果
List<PrizeEstimateVO.PrizeDetailItem> prizeDetails = new ArrayList<>();
// 按顺序添加各等级的统计结果(从高到低)
for (String level : prizeLevels) {
// 跳过没有记录的等级
if (countByPrizeLevel.get(level) <= 0) {
continue;
}
int count = countByPrizeLevel.get(level);
BigDecimal totalBonus = totalBonusByPrizeLevel.get(level);
BigDecimal singlePrize = BigDecimal.ZERO;
if (count > 0 && totalBonus.compareTo(BigDecimal.ZERO) > 0) {
singlePrize = totalBonus.divide(new BigDecimal(count), 2, RoundingMode.HALF_UP);
}
prizeDetails.add(PrizeEstimateVO.PrizeDetailItem.builder()
.prizeLevel(level)
.winningCount(count)
.singlePrize(singlePrize)
.subtotal(totalBonus)
.build());
}
// 计算总奖金
BigDecimal totalPrize = prizeDetails.stream()
.map(PrizeEstimateVO.PrizeDetailItem::getSubtotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
PrizeEstimateVO result = PrizeEstimateVO.builder()
.totalPrize(totalPrize)
.prizeDetails(prizeDetails)
.build();
log.info("用户{}的大乐透奖金统计结果:总奖金{},各等级明细数量:{}",
userId, totalPrize, prizeDetails.size());
return result;
}
@Override
public RedBallHitRateVO getFrontFirstBallHitRate(Long userId) {
log.info("开始统计用户{}的大乐透前区首球命中率", userId);
// 查询用户的所有预测记录(除了"待开奖"状态的)
QueryWrapper<DltPredictRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userId", userId)
.eq("predictStatus", "已开奖");
List<DltPredictRecord> predictRecords = dltPredictRecordMapper.selectList(queryWrapper);
// 总预测次数
int totalCount = predictRecords.size();
// 统计前区首球命中次数
int hitCount = 0;
for (DltPredictRecord record : predictRecords) {
// 获取该预测记录对应的开奖信息
DltDrawRecord draw = dltDrawRecordService.lambdaQuery()
.eq(DltDrawRecord::getDrawId, String.valueOf(record.getDrawId()))
.one();
if (draw != null && record.getFrontendBall1() != null) {
Integer predictedFirstBall = record.getFrontendBall1();
List<Integer> drawnFrontBalls = Arrays.asList(
draw.getFrontBall1(),
draw.getFrontBall2(),
draw.getFrontBall3(),
draw.getFrontBall4(),
draw.getFrontBall5()
);
// 比较预测的第一个前区球是否在开奖的五个前区球中
if (drawnFrontBalls.contains(predictedFirstBall)) {
hitCount++;
}
}
}
// 计算命中率
double hitRate = totalCount > 0 ? (double) hitCount / totalCount * 100 : 0;
RedBallHitRateVO result = RedBallHitRateVO.builder()
.totalHitCount(hitCount)
.totalPredictedCount(totalCount)
.hitRate(hitRate)
.build();
log.info("用户{}的大乐透前区首球命中率统计结果:命中{}次,总计{}次,命中率{}%",
userId, hitCount, totalCount, String.format("%.2f", hitRate));
return result;
}
@Override
public RedBallHitRateVO getFrontBallHitRate(Long userId) {
log.info("开始统计用户{}的大乐透前区球号命中率", userId);
// 查询用户的所有预测记录(除了"待开奖"状态的)
QueryWrapper<DltPredictRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userId", userId)
.eq("predictStatus", "已开奖");
List<DltPredictRecord> predictRecords = dltPredictRecordMapper.selectList(queryWrapper);
int totalHitCount = 0;
int totalRecords = predictRecords.size();
for (DltPredictRecord record : predictRecords) {
// 获取该预测记录对应的开奖信息
DltDrawRecord draw = dltDrawRecordService.lambdaQuery()
.eq(DltDrawRecord::getDrawId, String.valueOf(record.getDrawId()))
.one();
if (draw != null) {
List<Integer> predictedFrontBalls = Arrays.asList(
record.getFrontendBall1(),
record.getFrontendBall2(),
record.getFrontendBall3(),
record.getFrontendBall4(),
record.getFrontendBall5()
);
List<Integer> drawnFrontBalls = Arrays.asList(
draw.getFrontBall1(),
draw.getFrontBall2(),
draw.getFrontBall3(),
draw.getFrontBall4(),
draw.getFrontBall5()
);
// 计算当前记录命中的前区球数
for (Integer predictedBall : predictedFrontBalls) {
if (predictedBall != null && drawnFrontBalls.contains(predictedBall)) {
totalHitCount++;
}
}
}
}
int totalPredictedCount = totalRecords * 5; // 每条记录预测5个前区球
double hitRate = (totalPredictedCount > 0) ? ((double) totalHitCount / totalPredictedCount) * 100 : 0;
RedBallHitRateVO result = RedBallHitRateVO.builder()
.totalHitCount(totalHitCount)
.totalPredictedCount(totalPredictedCount)
.hitRate(hitRate)
.build();
log.info("用户{}的大乐透前区球号命中率统计结果:命中总数{},预测总数{},命中率{}%",
userId, totalHitCount, totalPredictedCount, String.format("%.2f", hitRate));
return result;
}
@Override
public RedBallHitRateVO getBackFirstBallHitRate(Long userId) {
log.info("开始统计用户{}的大乐透后区首球命中率", userId);
// 查询用户的所有预测记录(除了"待开奖"状态的)
QueryWrapper<DltPredictRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userId", userId)
.eq("predictStatus", "已开奖");
List<DltPredictRecord> predictRecords = dltPredictRecordMapper.selectList(queryWrapper);
// 总预测次数
int totalCount = predictRecords.size();
// 统计后区首球命中次数
int hitCount = 0;
for (DltPredictRecord record : predictRecords) {
// 获取该预测记录对应的开奖信息
DltDrawRecord draw = dltDrawRecordService.lambdaQuery()
.eq(DltDrawRecord::getDrawId, String.valueOf(record.getDrawId()))
.one();
if (draw != null && record.getBackendBall1() != null) {
Integer predictedFirstBackBall = record.getBackendBall1();
List<Integer> drawnBackBalls = Arrays.asList(
draw.getBackBall1(),
draw.getBackBall2()
);
// 比较预测的第一个后区球是否在开奖的两个后区球中
if (drawnBackBalls.contains(predictedFirstBackBall)) {
hitCount++;
}
}
}
// 计算命中率
double hitRate = totalCount > 0 ? (double) hitCount / totalCount * 100 : 0;
RedBallHitRateVO result = RedBallHitRateVO.builder()
.totalHitCount(hitCount)
.totalPredictedCount(totalCount)
.hitRate(hitRate)
.build();
log.info("用户{}的大乐透后区首球命中率统计结果:命中{}次,总计{}次,命中率{}%",
userId, hitCount, totalCount, String.format("%.2f", hitRate));
return result;
}
@Override
public RedBallHitRateVO getBackBallHitRate(Long userId) {
log.info("开始统计用户{}的大乐透后区球号命中率", userId);
// 查询用户的所有预测记录(除了"待开奖"状态的)
QueryWrapper<DltPredictRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userId", userId)
.eq("predictStatus", "已开奖");
List<DltPredictRecord> predictRecords = dltPredictRecordMapper.selectList(queryWrapper);
int totalHitCount = 0;
int totalRecords = predictRecords.size();
for (DltPredictRecord record : predictRecords) {
// 获取该预测记录对应的开奖信息
DltDrawRecord draw = dltDrawRecordService.lambdaQuery()
.eq(DltDrawRecord::getDrawId, String.valueOf(record.getDrawId()))
.one();
if (draw != null) {
List<Integer> predictedBackBalls = Arrays.asList(
record.getBackendBall1(),
record.getBackendBall2()
);
List<Integer> drawnBackBalls = Arrays.asList(
draw.getBackBall1(),
draw.getBackBall2()
);
// 计算当前记录命中的后区球数
for (Integer predictedBall : predictedBackBalls) {
if (predictedBall != null && drawnBackBalls.contains(predictedBall)) {
totalHitCount++;
}
}
}
}
int totalPredictedCount = totalRecords * 2; // 每条记录预测2个后区球
double hitRate = (totalPredictedCount > 0) ? ((double) totalHitCount / totalPredictedCount) * 100 : 0;
RedBallHitRateVO result = RedBallHitRateVO.builder()
.totalHitCount(totalHitCount)
.totalPredictedCount(totalPredictedCount)
.hitRate(hitRate)
.build();
log.info("用户{}的大乐透后区球号命中率统计结果:命中总数{},预测总数{},命中率{}%",
userId, totalHitCount, totalPredictedCount, String.format("%.2f", hitRate));
return result;
}
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.DltDrawRecord;
import com.xy.xyaicpzs.service.DltDrawRecordService;
import com.xy.xyaicpzs.mapper.DltDrawRecordMapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【dlt_draw_record(大乐透开奖信息表)】的数据库操作Service实现
* @createDate 2025-08-20 15:55:06
*/
@Service
public class DltDrawRecordServiceImpl extends ServiceImpl<DltDrawRecordMapper, DltDrawRecord>
implements DltDrawRecordService{
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.DltFrontendHistory100;
import com.xy.xyaicpzs.service.DltFrontendHistory100Service;
import com.xy.xyaicpzs.mapper.DltFrontendHistory100Mapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【dlt_frontend_history_100(大乐透前区最近100期数据表)】的数据库操作Service实现
* @createDate 2025-08-20 16:24:40
*/
@Service
public class DltFrontendHistory100ServiceImpl extends ServiceImpl<DltFrontendHistory100Mapper, DltFrontendHistory100>
implements DltFrontendHistory100Service{
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.DltFrontendHistoryAll;
import com.xy.xyaicpzs.service.DltFrontendHistoryAllService;
import com.xy.xyaicpzs.mapper.DltFrontendHistoryAllMapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【dlt_frontend_history_all(大乐透前区全部历史数据表)】的数据库操作Service实现
* @createDate 2025-08-20 16:24:40
*/
@Service
public class DltFrontendHistoryAllServiceImpl extends ServiceImpl<DltFrontendHistoryAllMapper, DltFrontendHistoryAll>
implements DltFrontendHistoryAllService{
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.DltFrontendHistoryTop100;
import com.xy.xyaicpzs.service.DltFrontendHistoryTop100Service;
import com.xy.xyaicpzs.mapper.DltFrontendHistoryTop100Mapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【dlt_frontend_history_top_100(大乐透前区百期数据排行表)】的数据库操作Service实现
* @createDate 2025-08-20 16:24:40
*/
@Service
public class DltFrontendHistoryTop100ServiceImpl extends ServiceImpl<DltFrontendHistoryTop100Mapper, DltFrontendHistoryTop100>
implements DltFrontendHistoryTop100Service{
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.DltFrontendHistoryTop;
import com.xy.xyaicpzs.service.DltFrontendHistoryTopService;
import com.xy.xyaicpzs.mapper.DltFrontendHistoryTopMapper;
import org.springframework.stereotype.Service;
/**
* @author XY003
* @description 针对表【dlt_frontend_history_top(大乐透前区历史数据排行表)】的数据库操作Service实现
* @createDate 2025-08-20 16:24:40
*/
@Service
public class DltFrontendHistoryTopServiceImpl extends ServiceImpl<DltFrontendHistoryTopMapper, DltFrontendHistoryTop>
implements DltFrontendHistoryTopService{
}

View File

@@ -0,0 +1,249 @@
package com.xy.xyaicpzs.service.impl;
import com.xy.xyaicpzs.domain.entity.DltDrawRecord;
import com.xy.xyaicpzs.domain.entity.D9;
import com.xy.xyaicpzs.domain.entity.D10;
import com.xy.xyaicpzs.domain.entity.D11;
import com.xy.xyaicpzs.domain.entity.D12;
import com.xy.xyaicpzs.domain.vo.BallPersistenceAnalysisVO;
import com.xy.xyaicpzs.service.DltPersistenceAnalysisService;
import com.xy.xyaicpzs.service.DltDrawRecordService;
import com.xy.xyaicpzs.service.D9Service;
import com.xy.xyaicpzs.service.D10Service;
import com.xy.xyaicpzs.service.D11Service;
import com.xy.xyaicpzs.service.D12Service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
/**
* 大乐透持续性分析服务实现类
*/
@Slf4j
@Service
public class DltPersistenceAnalysisServiceImpl implements DltPersistenceAnalysisService {
@Autowired
private D9Service d9Service;
@Autowired
private D10Service d10Service;
@Autowired
private D11Service d11Service;
@Autowired
private D12Service d12Service;
@Autowired
private DltDrawRecordService dltDrawRecordService;
@Override
public BallPersistenceAnalysisVO analyzeFrontFrontPersistence(Integer masterBall, Integer slaveBall) {
// 参数校验
validateBallRange(masterBall, 1, 35, "主球(前区)");
validateBallRange(slaveBall, 1, 35, "随球(前区)");
// 查询指定组合记录
D9 targetRecord = d9Service.lambdaQuery()
.eq(D9::getMasterBallNumber, masterBall)
.eq(D9::getSlaveBallNumber, slaveBall)
.one();
if (targetRecord == null) {
throw new RuntimeException(String.format("未找到主球(前区)%d和随球前区%d的持续性记录", masterBall, slaveBall));
}
// 查询主球与所有其他球的组合记录
List<D9> allCombinations = d9Service.lambdaQuery()
.eq(D9::getMasterBallNumber, masterBall)
.list();
if (allCombinations.isEmpty()) {
throw new RuntimeException(String.format("未找到主球(前区)%d的持续性记录", masterBall));
}
return buildPersistenceAnalysisVO(
targetRecord.getCoefficient(),
allCombinations,
D9::getCoefficient,
D9::getSlaveBallNumber
);
}
@Override
public BallPersistenceAnalysisVO analyzeBackBackPersistence(Integer masterBall, Integer slaveBall) {
// 参数校验
validateBallRange(masterBall, 1, 12, "主球(后区)");
validateBallRange(slaveBall, 1, 12, "随球(后区)");
// 查询指定组合记录
D11 targetRecord = d11Service.lambdaQuery()
.eq(D11::getMasterBallNumber, masterBall)
.eq(D11::getSlaveBallNumber, slaveBall)
.one();
if (targetRecord == null) {
throw new RuntimeException(String.format("未找到主球(后区)%d和随球后区%d的持续性记录", masterBall, slaveBall));
}
// 查询主球与所有其他球的组合记录
List<D11> allCombinations = d11Service.lambdaQuery()
.eq(D11::getMasterBallNumber, masterBall)
.list();
if (allCombinations.isEmpty()) {
throw new RuntimeException(String.format("未找到主球(后区)%d的持续性记录", masterBall));
}
return buildPersistenceAnalysisVO(
targetRecord.getCoefficient(),
allCombinations,
D11::getCoefficient,
D11::getSlaveBallNumber
);
}
@Override
public BallPersistenceAnalysisVO analyzeFrontBackPersistence(Integer masterBall, Integer slaveBall) {
// 参数校验
validateBallRange(masterBall, 1, 35, "主球(前区)");
validateBallRange(slaveBall, 1, 12, "随球(后区)");
// 查询指定组合记录
D10 targetRecord = d10Service.lambdaQuery()
.eq(D10::getMasterBallNumber, masterBall)
.eq(D10::getSlaveBallNumber, slaveBall)
.one();
if (targetRecord == null) {
throw new RuntimeException(String.format("未找到主球(前区)%d和随球后区%d的持续性记录", masterBall, slaveBall));
}
// 查询主球与所有其他球的组合记录
List<D10> allCombinations = d10Service.lambdaQuery()
.eq(D10::getMasterBallNumber, masterBall)
.list();
if (allCombinations.isEmpty()) {
throw new RuntimeException(String.format("未找到主球(前区)%d的持续性记录", masterBall));
}
return buildPersistenceAnalysisVO(
targetRecord.getCoefficient(),
allCombinations,
D10::getCoefficient,
D10::getSlaveBallNumber
);
}
@Override
public BallPersistenceAnalysisVO analyzeBackFrontPersistence(Integer masterBall, Integer slaveBall) {
// 参数校验
validateBallRange(masterBall, 1, 12, "主球(后区)");
validateBallRange(slaveBall, 1, 35, "随球(前区)");
// 查询指定组合记录
D12 targetRecord = d12Service.lambdaQuery()
.eq(D12::getMasterBallNumber, masterBall)
.eq(D12::getSlaveBallNumber, slaveBall)
.one();
if (targetRecord == null) {
throw new RuntimeException(String.format("未找到主球(后区)%d和随球前区%d的持续性记录", masterBall, slaveBall));
}
// 查询主球与所有其他球的组合记录
List<D12> allCombinations = d12Service.lambdaQuery()
.eq(D12::getMasterBallNumber, masterBall)
.list();
if (allCombinations.isEmpty()) {
throw new RuntimeException(String.format("未找到主球(后区)%d的持续性记录", masterBall));
}
return buildPersistenceAnalysisVO(
targetRecord.getCoefficient(),
allCombinations,
D12::getCoefficient,
D12::getSlaveBallNumber
);
}
/**
* 验证球号范围
*/
private void validateBallRange(Integer ballNumber, int minValue, int maxValue, String ballType) {
if (ballNumber == null) {
throw new IllegalArgumentException(ballType + "号码不能为空");
}
if (ballNumber < minValue || ballNumber > maxValue) {
throw new IllegalArgumentException(String.format("%s号码必须在%d-%d范围内错误值%d",
ballType, minValue, maxValue, ballNumber));
}
}
/**
* 构建持续性分析结果VO
*/
private <T> BallPersistenceAnalysisVO buildPersistenceAnalysisVO(
Double targetCoefficient,
List<T> allCombinations,
Function<T, Double> coefficientGetter,
Function<T, Integer> ballNumberGetter) {
// 找出系数最高的球号
T highest = allCombinations.stream()
.max(Comparator.comparing(coefficientGetter))
.orElse(null);
// 找出系数最低的球号
T lowest = allCombinations.stream()
.min(Comparator.comparing(coefficientGetter))
.orElse(null);
// 计算平均系数
double avgCoefficient = allCombinations.stream()
.mapToDouble(coefficientGetter::apply)
.average()
.orElse(0.0);
// 获取最新开奖期号
Long latestDrawId = getLatestDrawId();
return BallPersistenceAnalysisVO.builder()
.lineCoefficient(targetCoefficient)
.highestBall(highest != null ? ballNumberGetter.apply(highest) : null)
.highestCoefficient(highest != null ? coefficientGetter.apply(highest) : null)
.lowestBall(lowest != null ? ballNumberGetter.apply(lowest) : null)
.lowestCoefficient(lowest != null ? coefficientGetter.apply(lowest) : null)
.averageCoefficient(avgCoefficient)
.latestDrawId(latestDrawId)
.build();
}
/**
* 获取最新开奖期号
*/
private Long getLatestDrawId() {
String latestDrawId = dltDrawRecordService.lambdaQuery()
.orderByDesc(DltDrawRecord::getDrawId)
.last("LIMIT 1")
.oneOpt()
.map(DltDrawRecord::getDrawId)
.orElse(null);
if (latestDrawId != null) {
try {
return Long.parseLong(latestDrawId);
} catch (NumberFormatException e) {
log.warn("开奖期号格式转换失败:{}", latestDrawId);
}
}
return null;
}
}

View File

@@ -0,0 +1,75 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.DltPredictRecord;
import com.xy.xyaicpzs.service.DltPredictRecordService;
import com.xy.xyaicpzs.mapper.DltPredictRecordMapper;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
/**
* @author XY003
* @description 针对表【dlt_predict_record(大乐透推测记录表)】的数据库操作Service实现
* @createDate 2025-09-08 14:01:12
*/
@Service
public class DltPredictRecordServiceImpl extends ServiceImpl<DltPredictRecordMapper, DltPredictRecord>
implements DltPredictRecordService{
@Override
public DltPredictRecord createDltPredictRecord(Long userId, Long drawId, Date drawDate, List<Integer> frontBalls, List<Integer> backBalls) {
DltPredictRecord predictRecord = new DltPredictRecord();
predictRecord.setUserId(userId);
predictRecord.setDrawId(drawId);
predictRecord.setDrawDate(drawDate);
predictRecord.setPredictTime(new Date());
// 设置前区球号码5个
if (frontBalls != null && frontBalls.size() >= 5) {
predictRecord.setFrontendBall1(frontBalls.get(0));
predictRecord.setFrontendBall2(frontBalls.get(1));
predictRecord.setFrontendBall3(frontBalls.get(2));
predictRecord.setFrontendBall4(frontBalls.get(3));
predictRecord.setFrontendBall5(frontBalls.get(4));
}
// 设置后区球号码2个
if (backBalls != null && backBalls.size() >= 2) {
predictRecord.setBackendBall1(backBalls.get(0));
predictRecord.setBackendBall2(backBalls.get(1));
}
// 默认状态为待开奖
predictRecord.setPredictStatus("待开奖");
save(predictRecord);
return predictRecord;
}
@Override
public List<DltPredictRecord> getDltPredictRecordsByUserIdWithPaging(Long userId, Integer page, Integer size) {
QueryWrapper<DltPredictRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userId", userId).orderByDesc("predictTime");
// 计算偏移量
int offset = (page - 1) * size;
queryWrapper.last("LIMIT " + offset + ", " + size);
return list(queryWrapper);
}
@Override
public Long getDltPredictRecordsCountByUserId(Long userId) {
QueryWrapper<DltPredictRecord> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userId", userId);
return count(queryWrapper);
}
}

View File

@@ -44,6 +44,7 @@ public class PredictRecordServiceImpl extends ServiceImpl<PredictRecordMapper, P
// 默认状态为待开奖
predictRecord.setPredictStatus("待开奖");
predictRecord.setType("ssq");
save(predictRecord);
return predictRecord;

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
}