彩票猪手精推版
This commit is contained in:
178
sql/ddl.sql
178
sql/ddl.sql
@@ -183,6 +183,7 @@ create table if not exists user
|
||||
userPassword varchar(512) not null comment '密码',
|
||||
isVip int default 0 not null comment '是否会员:0-非会员,1-会员',
|
||||
vipExpire datetime null comment '会员到期时间',
|
||||
# vipNum int not null comment '会员编号',
|
||||
`status` tinyint DEFAULT '0' COMMENT '状态0正常1不正常',
|
||||
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
|
||||
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
|
||||
@@ -209,7 +210,7 @@ CREATE TABLE IF NOT EXISTS `predict_record` (
|
||||
`predictTime` datetime default CURRENT_TIMESTAMP not null comment '预测时间',
|
||||
`bonus` BIGINT default 0 NOT NULL COMMENT '奖金'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = '彩票开奖信息表';
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = '双色球推测记录表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `vip_code` (
|
||||
`id` BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
@@ -225,8 +226,6 @@ CREATE TABLE IF NOT EXISTS `vip_code` (
|
||||
`updateTime` datetime NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员码表';
|
||||
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `vip_exchange_record` (
|
||||
`id` BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
`userId` bigint NOT NULL COMMENT '用户ID',
|
||||
@@ -264,5 +263,178 @@ CREATE TABLE IF NOT EXISTS `chat_message`
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT ='聊天消息表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dlt_draw_record` (
|
||||
`id` BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
`drawId` VARCHAR(50) NOT NULL COMMENT '开奖期号',
|
||||
`drawDate` DATE NOT NULL COMMENT '开奖日期',
|
||||
`frontBall1` INT NOT NULL COMMENT '前区1',
|
||||
`frontBall2` INT NOT NULL COMMENT '前区2',
|
||||
`frontBall3` INT NOT NULL COMMENT '前区3',
|
||||
`frontBall4` INT NOT NULL COMMENT '前区4',
|
||||
`frontBall5` INT NOT NULL COMMENT '前区5',
|
||||
`backBall1` INT NOT NULL COMMENT '后区1',
|
||||
`backBall2` INT NOT NULL COMMENT '后区2'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = '大乐透开奖信息表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dlt_frontend_history_all` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
ballNumber INT NOT NULL COMMENT '球号',
|
||||
frequencyCount INT NULL COMMENT '出现频次',
|
||||
frequencyPercentage FLOAT NULL COMMENT '出现频率%',
|
||||
averageHiddenAppear INT NULL COMMENT '平均隐现期(次)',
|
||||
maxHiddenInterval INT NULL COMMENT '最长隐现期(次)',
|
||||
maxConsecutive INT NULL COMMENT '最多连出期(次)',
|
||||
activeCoefficient FLOAT NULL COMMENT '活跃系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = '大乐透前区全部历史数据表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dlt_frontend_history_100` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
ballNumber INT NOT NULL COMMENT '球号',
|
||||
frequencyCount INT NULL COMMENT '出现频次',
|
||||
averageHiddenAppear FLOAT NULL COMMENT '平均隐现期(次)',
|
||||
currentHiddenInterval INT NULL COMMENT '当前隐现期',
|
||||
maxConsecutive INT NULL COMMENT '最多连出期(次)',
|
||||
activeCoefficient FLOAT NULL COMMENT '活跃系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = '大乐透前区最近100期数据表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dlt_frontend_history_top` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
ranking INT NULL COMMENT '排位',
|
||||
ballNumber INT NULL COMMENT '球号',
|
||||
activeCoefficient FLOAT NULL COMMENT '活跃系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = '大乐透前区历史数据排行表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dlt_frontend_history_top_100` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
ranking INT NULL COMMENT '排位',
|
||||
ballNumber INT NULL COMMENT '球号',
|
||||
activeCoefficient FLOAT NULL COMMENT '活跃系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = '大乐透前区百期数据排行表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dlt_backend_history_all` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
ballNumber INT NOT NULL COMMENT '球号',
|
||||
frequencyCount INT NULL COMMENT '出现频次',
|
||||
frequencyPercentage FLOAT NULL COMMENT '出现频率%',
|
||||
averageHiddenAppear INT NULL COMMENT '平均隐现期(次)',
|
||||
maxHiddenInterval INT NULL COMMENT '最长隐现期(次)',
|
||||
maxConsecutive INT NULL COMMENT '最多连出期(次)',
|
||||
activeCoefficient FLOAT NULL COMMENT '活跃系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = '大乐透后区全部历史数据表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dlt_backend_history_100` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
ballNumber INT NOT NULL COMMENT '球号',
|
||||
frequencyCount INT NULL COMMENT '出现频次',
|
||||
averageHiddenAppear FLOAT NULL COMMENT '平均隐现期(次)',
|
||||
currentHiddenInterval INT NULL COMMENT '当前隐现期',
|
||||
maxConsecutive INT NULL COMMENT '最多连出期(次)',
|
||||
activeCoefficient FLOAT NULL COMMENT '活跃系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = '大乐透后区最近100期数据表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dlt_backend_history_top` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
ranking INT NULL COMMENT '排位',
|
||||
ballNumber INT NULL COMMENT '球号',
|
||||
activeCoefficient FLOAT NULL COMMENT '活跃系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = '大乐透后区历史数据排行表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dlt_backend_history_top_100` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
ranking INT NULL COMMENT '排位',
|
||||
ballNumber INT NULL COMMENT '球号',
|
||||
activeCoefficient FLOAT NULL COMMENT '活跃系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = '大乐透后区百期数据排行表';
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `d5` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
masterBallNumber INT NULL COMMENT '主球',
|
||||
slaveBallNumber INT NULL COMMENT '从球',
|
||||
coefficient FLOAT NULL COMMENT '系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = 'd5表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `d6` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
masterBallNumber INT NULL COMMENT '主球',
|
||||
slaveBallNumber INT NULL COMMENT '从球',
|
||||
coefficient FLOAT NULL COMMENT '系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = 'd6表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `d7` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
masterBallNumber INT NULL COMMENT '主球',
|
||||
slaveBallNumber INT NULL COMMENT '从球',
|
||||
coefficient FLOAT NULL COMMENT '系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = 'd7表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `d8` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
masterBallNumber INT NULL COMMENT '主球',
|
||||
slaveBallNumber INT NULL COMMENT '从球',
|
||||
coefficient FLOAT NULL COMMENT '系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = 'd8表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `d9` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
masterBallNumber INT NULL COMMENT '主球',
|
||||
slaveBallNumber INT NULL COMMENT '从球',
|
||||
coefficient FLOAT NULL COMMENT '系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = 'd9表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `d10` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
masterBallNumber INT NULL COMMENT '主球',
|
||||
slaveBallNumber INT NULL COMMENT '从球',
|
||||
coefficient FLOAT NULL COMMENT '系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = 'd10表';
|
||||
CREATE TABLE IF NOT EXISTS `d11` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
masterBallNumber INT NULL COMMENT '主球',
|
||||
slaveBallNumber INT NULL COMMENT '从球',
|
||||
coefficient FLOAT NULL COMMENT '系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = 'd11表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `d12` (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
masterBallNumber INT NULL COMMENT '主球',
|
||||
slaveBallNumber INT NULL COMMENT '从球',
|
||||
coefficient FLOAT NULL COMMENT '系数'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = 'd12表';
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dlt_predict_record` (
|
||||
`id` BIGINT AUTO_INCREMENT COMMENT '唯一标识符' PRIMARY KEY,
|
||||
`userId` BIGINT NOT NULL COMMENT '用户ID',
|
||||
`drawId` BIGINT NOT NULL COMMENT '开奖期号' ,
|
||||
`drawDate` DATE NOT NULL COMMENT '开奖日期',
|
||||
`frontendBall1` INT NOT NULL COMMENT '前区1',
|
||||
`frontendBall2` INT NOT NULL COMMENT '前区2',
|
||||
`frontendBall3` INT NOT NULL COMMENT '前区3',
|
||||
`frontendBall4` INT NOT NULL COMMENT '前区4',
|
||||
`frontendBall5` INT NOT NULL COMMENT '前区5',
|
||||
`backendBall1` INT NOT NULL COMMENT '后区1',
|
||||
`backendBall2` INT NOT NULL COMMENT '后区2',
|
||||
`predictStatus` VARCHAR(100) default '待开奖' NOT NULL COMMENT '预测状态(待开奖/已开奖)',
|
||||
`predictResult` VARCHAR(100) default '待开奖' NOT NULL COMMENT '预测结果(未中奖/三等奖/二等奖/一等奖)',
|
||||
`predictTime` datetime default CURRENT_TIMESTAMP not null comment '预测时间',
|
||||
`bonus` BIGINT default 0 NOT NULL COMMENT '奖金'
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT = '大乐透推测记录表';
|
||||
|
||||
@@ -25,7 +25,7 @@ public class ResultUtils {
|
||||
* @return
|
||||
*/
|
||||
public static <T> ApiResponse<T> error(ErrorCode errorCode) {
|
||||
return ApiResponse.error(errorCode.getMessage());
|
||||
return ApiResponse.error(errorCode.getCode(), errorCode.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,7 +36,7 @@ public class ResultUtils {
|
||||
* @return
|
||||
*/
|
||||
public static <T> ApiResponse<T> error(int code, String message) {
|
||||
return ApiResponse.error(message);
|
||||
return ApiResponse.error(code, message);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,6 +46,6 @@ public class ResultUtils {
|
||||
* @return
|
||||
*/
|
||||
public static <T> ApiResponse<T> error(ErrorCode errorCode, String message) {
|
||||
return ApiResponse.error(message);
|
||||
return ApiResponse.error(errorCode.getCode(), message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.common.requset;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class BackBallPredictionRequest {
|
||||
private String level;
|
||||
private List<Integer> nextFrontBalls;
|
||||
private List<Integer> previousFrontBalls;
|
||||
private List<Integer> previousBackBalls;
|
||||
private List<Integer> nextBackBalls;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.xy.xyaicpzs.common.requset;
|
||||
|
||||
import com.xy.xyaicpzs.common.PageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 大乐透预测记录查询请求
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DltPredictRecordQueryRequest extends PageRequest {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 预测状态(待开奖/已开奖)
|
||||
*/
|
||||
private String predictStatus;
|
||||
|
||||
/**
|
||||
* 预测结果(未中奖/三等奖/二等奖/一等奖)
|
||||
*/
|
||||
private String predictResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.xy.xyaicpzs.common.requset;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class FirstBallPredictionRequest {
|
||||
private String level;
|
||||
private List<Integer> redBalls;
|
||||
private List<Integer> blueBalls;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.common.requset;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class FollowBackBallPredictionRequest {
|
||||
private String level;
|
||||
private Integer backFirstBall;
|
||||
private List<Integer> nextFrontBalls;
|
||||
private List<Integer> previousFrontBalls;
|
||||
private List<Integer> previousBackBalls;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.xy.xyaicpzs.common.requset;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class FollowerBallPredictionRequest {
|
||||
private String level;
|
||||
private List<Integer> wellRegardedBalls;
|
||||
private List<Integer> previousFrontBalls;
|
||||
private List<Integer> previousBackBalls;
|
||||
}
|
||||
@@ -7,12 +7,14 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
public class ApiResponse<T> {
|
||||
private Integer code;
|
||||
private boolean success;
|
||||
private String message;
|
||||
private T data;
|
||||
|
||||
public static <T> ApiResponse<T> success(T data) {
|
||||
ApiResponse<T> response = new ApiResponse<>();
|
||||
response.code = 0;
|
||||
response.success = true;
|
||||
response.message = "操作成功";
|
||||
response.data = data;
|
||||
@@ -21,6 +23,15 @@ public class ApiResponse<T> {
|
||||
|
||||
public static <T> ApiResponse<T> error(String message) {
|
||||
ApiResponse<T> response = new ApiResponse<>();
|
||||
response.code = 1;
|
||||
response.success = false;
|
||||
response.message = message;
|
||||
return response;
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> error(Integer code, String message) {
|
||||
ApiResponse<T> response = new ApiResponse<>();
|
||||
response.code = code;
|
||||
response.success = false;
|
||||
response.message = message;
|
||||
return response;
|
||||
|
||||
@@ -22,7 +22,6 @@ import com.xy.xyaicpzs.domain.entity.T3;
|
||||
import com.xy.xyaicpzs.domain.entity.T4;
|
||||
import com.xy.xyaicpzs.domain.entity.T5;
|
||||
import com.xy.xyaicpzs.domain.entity.T6;
|
||||
import com.xy.xyaicpzs.exception.BusinessException;
|
||||
import com.xy.xyaicpzs.service.BallAnalysisService;
|
||||
import com.xy.xyaicpzs.service.LotteryDrawsService;
|
||||
import com.xy.xyaicpzs.service.PredictRecordService;
|
||||
@@ -47,6 +46,9 @@ import com.xy.xyaicpzs.domain.vo.PrizeEstimateVO;
|
||||
import com.xy.xyaicpzs.domain.vo.BallCombinationAnalysisVO;
|
||||
import com.xy.xyaicpzs.domain.vo.BallPersistenceAnalysisVO;
|
||||
import com.xy.xyaicpzs.domain.vo.RedBallHitRateVO;
|
||||
import com.xy.xyaicpzs.domain.vo.BallAnalysisResultVO;
|
||||
import com.xy.xyaicpzs.domain.vo.FollowBallAnalysisResultVO;
|
||||
import com.xy.xyaicpzs.domain.vo.BlueBallAnalysisResultVO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -407,8 +409,8 @@ public class BallAnalysisController {
|
||||
* @return 分析结果:出现频率最高的前11位数字
|
||||
*/
|
||||
@PostMapping("/analyze")
|
||||
@Operation(summary = "首球算法", description = "根据输入的级别、红球和蓝球,分析出现频率最高的前11位数字")
|
||||
public ApiResponse<List<Integer>> analyzeBalls(
|
||||
@Operation(summary = "首球算法", description = "根据输入的级别、红球和蓝球,分析出现频率最高的前11位数字及筛选过程说明")
|
||||
public ApiResponse<BallAnalysisResultVO> analyzeBalls(
|
||||
@Parameter(description = "用户ID,例如:1001", required = true)
|
||||
@RequestParam Long userId,
|
||||
|
||||
@@ -438,10 +440,10 @@ public class BallAnalysisController {
|
||||
// 解析红球号码
|
||||
List<Integer> redBallList = parseRedBalls(redBalls, 6, "红球");
|
||||
|
||||
// 调用分析服务
|
||||
List<Integer> result = ballAnalysisService.analyzeBalls(level, redBallList, blueBall);
|
||||
// 调用分析服务(包含筛选过程说明)
|
||||
BallAnalysisResultVO result = ballAnalysisService.analyzeBallsWithProcess(level, redBallList, blueBall);
|
||||
|
||||
log.info("球号分析完成,结果:{}", result);
|
||||
log.info("球号分析完成,结果:{},过程说明:{}", result.getResult(), result.getFilteringProcess());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -457,11 +459,11 @@ public class BallAnalysisController {
|
||||
* @param firstThreeRedBalls 前3个红球号码,用逗号分隔
|
||||
* @param lastSixRedBalls 后6个红球号码,用逗号分隔
|
||||
* @param blueBall 蓝球号码
|
||||
* @return 分析结果:出现频率最高的前8位数字
|
||||
* @return 分析结果:出现频率最高的前8位数字及筛选过程说明
|
||||
*/
|
||||
@PostMapping("/fallow")
|
||||
@Operation(summary = "跟随球号分析算法", description = "根据输入的级别、前3个红球、后6个红球和蓝球,分析出现频率最高的前8位数字")
|
||||
public ApiResponse<List<Integer>> fallowBallAnalysis(
|
||||
@Operation(summary = "跟随球号分析算法", description = "根据输入的级别、前3个红球、后6个红球和蓝球,分析出现频率最高的前8位数字及筛选过程说明")
|
||||
public ApiResponse<FollowBallAnalysisResultVO> fallowBallAnalysis(
|
||||
@Parameter(description = "用户ID,例如:1001", required = true)
|
||||
@RequestParam Long userId,
|
||||
|
||||
@@ -496,10 +498,10 @@ public class BallAnalysisController {
|
||||
List<Integer> firstThreeRedBallList = parseRedBalls(firstThreeRedBalls, 3, "前3个红球");
|
||||
List<Integer> lastSixRedBallList = parseRedBalls(lastSixRedBalls, 6, "后6个红球");
|
||||
|
||||
// 调用分析服务
|
||||
List<Integer> result = ballAnalysisService.fallowBallAnalysis(level, firstThreeRedBallList, lastSixRedBallList, blueBall);
|
||||
// 调用分析服务(包含筛选过程说明)
|
||||
FollowBallAnalysisResultVO result = ballAnalysisService.fallowBallAnalysisWithProcess(level, firstThreeRedBallList, lastSixRedBallList, blueBall);
|
||||
|
||||
log.info("跟随球号分析完成,结果:{}", result);
|
||||
log.info("跟随球号分析完成,结果:{},过程说明:{}", result.getResult(), result.getFilteringProcess());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -549,11 +551,11 @@ public class BallAnalysisController {
|
||||
* @param predictedBlueBalls 2个预测蓝球号码,用逗号分隔
|
||||
* @param lastRedBalls 6个上期红球号码,用逗号分隔
|
||||
* @param lastBlueBall 上期蓝球号码
|
||||
* @return 分析结果:频率最高的前4个蓝球号码
|
||||
* @return 分析结果:频率最高的前4个蓝球号码及其筛选过程说明
|
||||
*/
|
||||
@PostMapping("/blue-ball")
|
||||
@Operation(summary = "蓝球分析算法", description = "根据输入的级别、预测红球、预测蓝球、上期红球和上期蓝球,分析出频率最高的前4个蓝球号码")
|
||||
public ApiResponse<List<Integer>> blueBallAnalysis(
|
||||
@Operation(summary = "蓝球分析算法", description = "根据输入的级别、预测红球、预测蓝球、上期红球和上期蓝球,分析出频率最高的前4个蓝球号码,并返回详细的筛选过程说明")
|
||||
public ApiResponse<BlueBallAnalysisResultVO> blueBallAnalysis(
|
||||
@Parameter(description = "用户ID,例如:1001", required = true)
|
||||
@RequestParam Long userId,
|
||||
|
||||
@@ -592,11 +594,11 @@ public class BallAnalysisController {
|
||||
List<Integer> predictedBlueBallList = parseBlueBalls(predictedBlueBalls, 2, "预测蓝球");
|
||||
List<Integer> lastRedBallList = parseRedBalls(lastRedBalls, 6, "上期红球");
|
||||
|
||||
// 调用分析服务
|
||||
List<Integer> result = ballAnalysisService.blueBallAnalysis(
|
||||
// 调用分析服务(带过程说明)
|
||||
BlueBallAnalysisResultVO result = ballAnalysisService.blueBallAnalysisWithProcess(
|
||||
level, predictedRedBallList, predictedBlueBallList, lastRedBallList, lastBlueBall);
|
||||
|
||||
log.info("蓝球分析完成,结果:{}", result);
|
||||
log.info("蓝球分析完成,结果:{},过程说明:{}", result.getResult(), result.getFilteringProcess());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
package com.xy.xyaicpzs.controller;
|
||||
|
||||
import com.xy.xyaicpzs.common.ErrorCode;
|
||||
import com.xy.xyaicpzs.common.ResultUtils;
|
||||
import com.xy.xyaicpzs.common.response.ApiResponse;
|
||||
import com.xy.xyaicpzs.domain.entity.DltFrontendHistoryAll;
|
||||
import com.xy.xyaicpzs.domain.entity.DltFrontendHistory100;
|
||||
import com.xy.xyaicpzs.domain.entity.DltFrontendHistoryTop;
|
||||
import com.xy.xyaicpzs.domain.entity.DltFrontendHistoryTop100;
|
||||
import com.xy.xyaicpzs.domain.entity.DltBackendHistoryAll;
|
||||
import com.xy.xyaicpzs.domain.entity.DltBackendHistory100;
|
||||
import com.xy.xyaicpzs.domain.entity.DltBackendHistoryTop;
|
||||
import com.xy.xyaicpzs.domain.entity.DltBackendHistoryTop100;
|
||||
import com.xy.xyaicpzs.service.DltFrontendHistoryAllService;
|
||||
import com.xy.xyaicpzs.service.DltFrontendHistory100Service;
|
||||
import com.xy.xyaicpzs.service.DltFrontendHistoryTopService;
|
||||
import com.xy.xyaicpzs.service.DltFrontendHistoryTop100Service;
|
||||
import com.xy.xyaicpzs.service.DltBackendHistoryAllService;
|
||||
import com.xy.xyaicpzs.service.DltBackendHistory100Service;
|
||||
import com.xy.xyaicpzs.service.DltBackendHistoryTopService;
|
||||
import com.xy.xyaicpzs.service.DltBackendHistoryTop100Service;
|
||||
import com.xy.xyaicpzs.util.UserAuthValidator;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 大乐透球号活跃分析控制器
|
||||
* 提供大乐透前区和后区历史数据查询API接口
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/dlt/ball-active-analysis")
|
||||
@Tag(name = "大乐透球号活跃分析", description = "大乐透前区和后区历史数据查询API")
|
||||
public class DltBallActiveAnalysisController {
|
||||
|
||||
@Autowired
|
||||
private DltFrontendHistoryAllService dltFrontendHistoryAllService;
|
||||
|
||||
@Autowired
|
||||
private DltFrontendHistory100Service dltFrontendHistory100Service;
|
||||
|
||||
@Autowired
|
||||
private DltFrontendHistoryTopService dltFrontendHistoryTopService;
|
||||
|
||||
@Autowired
|
||||
private DltFrontendHistoryTop100Service dltFrontendHistoryTop100Service;
|
||||
|
||||
@Autowired
|
||||
private DltBackendHistoryAllService dltBackendHistoryAllService;
|
||||
|
||||
@Autowired
|
||||
private DltBackendHistory100Service dltBackendHistory100Service;
|
||||
|
||||
@Autowired
|
||||
private DltBackendHistoryTopService dltBackendHistoryTopService;
|
||||
|
||||
@Autowired
|
||||
private DltBackendHistoryTop100Service dltBackendHistoryTop100Service;
|
||||
|
||||
@Autowired
|
||||
private UserAuthValidator userAuthValidator;
|
||||
|
||||
/**
|
||||
* 获取前区历史数据全部记录
|
||||
* @return 前区历史数据全部记录列表
|
||||
*/
|
||||
@GetMapping("/frontend-history-all")
|
||||
@Operation(summary = "获取前区历史数据全部记录", description = "获取dlt_frontend_history_all表中的所有前区历史数据记录")
|
||||
public ApiResponse<List<DltFrontendHistoryAll>> getFrontendHistoryAll(HttpServletRequest request) {
|
||||
// 权限验证
|
||||
ApiResponse<List<DltFrontendHistoryAll>> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取前区历史数据全部记录请求");
|
||||
|
||||
// 调用服务获取前区全部历史数据
|
||||
List<DltFrontendHistoryAll> result = dltFrontendHistoryAllService.list();
|
||||
|
||||
log.info("获取前区历史数据全部记录完成,返回{}条记录", result.size());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取前区历史数据全部记录失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取前区历史数据全部记录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取前区最近100期数据记录
|
||||
* @return 前区最近100期数据记录列表
|
||||
*/
|
||||
@GetMapping("/frontend-history-100")
|
||||
@Operation(summary = "获取前区最近100期数据记录", description = "获取dlt_frontend_history_100表中的所有前区最近100期数据记录")
|
||||
public ApiResponse<List<DltFrontendHistory100>> getFrontendHistory100(HttpServletRequest request) {
|
||||
// 权限验证
|
||||
ApiResponse<List<DltFrontendHistory100>> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取前区最近100期数据记录请求");
|
||||
|
||||
// 调用服务获取前区最近100期数据
|
||||
List<DltFrontendHistory100> result = dltFrontendHistory100Service.list();
|
||||
|
||||
log.info("获取前区最近100期数据记录完成,返回{}条记录", result.size());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取前区最近100期数据记录失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取前区最近100期数据记录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取前区历史数据排行记录
|
||||
* @return 前区历史数据排行记录列表
|
||||
*/
|
||||
@GetMapping("/frontend-history-top")
|
||||
@Operation(summary = "获取前区历史数据排行记录", description = "获取dlt_frontend_history_top表中的所有前区历史数据排行记录")
|
||||
public ApiResponse<List<DltFrontendHistoryTop>> getFrontendHistoryTop(HttpServletRequest request) {
|
||||
// 权限验证
|
||||
ApiResponse<List<DltFrontendHistoryTop>> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取前区历史数据排行记录请求");
|
||||
|
||||
// 调用服务获取前区历史数据排行
|
||||
List<DltFrontendHistoryTop> result = dltFrontendHistoryTopService.list();
|
||||
|
||||
log.info("获取前区历史数据排行记录完成,返回{}条记录", result.size());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取前区历史数据排行记录失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取前区历史数据排行记录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取前区100期数据排行记录
|
||||
* @return 前区100期数据排行记录列表
|
||||
*/
|
||||
@GetMapping("/frontend-history-top-100")
|
||||
@Operation(summary = "获取前区100期数据排行记录", description = "获取dlt_frontend_history_top_100表中的所有前区100期数据排行记录")
|
||||
public ApiResponse<List<DltFrontendHistoryTop100>> getFrontendHistoryTop100(HttpServletRequest request) {
|
||||
// 权限验证
|
||||
ApiResponse<List<DltFrontendHistoryTop100>> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取前区100期数据排行记录请求");
|
||||
|
||||
// 调用服务获取前区100期数据排行
|
||||
List<DltFrontendHistoryTop100> result = dltFrontendHistoryTop100Service.list();
|
||||
|
||||
log.info("获取前区100期数据排行记录完成,返回{}条记录", result.size());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取前区100期数据排行记录失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取前区100期数据排行记录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后区历史数据全部记录
|
||||
* @return 后区历史数据全部记录列表
|
||||
*/
|
||||
@GetMapping("/backend-history-all")
|
||||
@Operation(summary = "获取后区历史数据全部记录", description = "获取dlt_backend_history_all表中的所有后区历史数据记录")
|
||||
public ApiResponse<List<DltBackendHistoryAll>> getBackendHistoryAll(HttpServletRequest request) {
|
||||
// 权限验证
|
||||
ApiResponse<List<DltBackendHistoryAll>> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取后区历史数据全部记录请求");
|
||||
|
||||
// 调用服务获取后区全部历史数据
|
||||
List<DltBackendHistoryAll> result = dltBackendHistoryAllService.list();
|
||||
|
||||
log.info("获取后区历史数据全部记录完成,返回{}条记录", result.size());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取后区历史数据全部记录失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取后区历史数据全部记录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后区最近100期数据记录
|
||||
* @return 后区最近100期数据记录列表
|
||||
*/
|
||||
@GetMapping("/backend-history-100")
|
||||
@Operation(summary = "获取后区最近100期数据记录", description = "获取dlt_backend_history_100表中的所有后区最近100期数据记录")
|
||||
public ApiResponse<List<DltBackendHistory100>> getBackendHistory100(HttpServletRequest request) {
|
||||
// 权限验证
|
||||
ApiResponse<List<DltBackendHistory100>> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取后区最近100期数据记录请求");
|
||||
|
||||
// 调用服务获取后区最近100期数据
|
||||
List<DltBackendHistory100> result = dltBackendHistory100Service.list();
|
||||
|
||||
log.info("获取后区最近100期数据记录完成,返回{}条记录", result.size());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取后区最近100期数据记录失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取后区最近100期数据记录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后区历史数据排行记录
|
||||
* @return 后区历史数据排行记录列表
|
||||
*/
|
||||
@GetMapping("/backend-history-top")
|
||||
@Operation(summary = "获取后区历史数据排行记录", description = "获取dlt_backend_history_top表中的所有后区历史数据排行记录")
|
||||
public ApiResponse<List<DltBackendHistoryTop>> getBackendHistoryTop(HttpServletRequest request) {
|
||||
// 权限验证
|
||||
ApiResponse<List<DltBackendHistoryTop>> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取后区历史数据排行记录请求");
|
||||
|
||||
// 调用服务获取后区历史数据排行
|
||||
List<DltBackendHistoryTop> result = dltBackendHistoryTopService.list();
|
||||
|
||||
log.info("获取后区历史数据排行记录完成,返回{}条记录", result.size());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取后区历史数据排行记录失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取后区历史数据排行记录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后区100期数据排行记录
|
||||
* @return 后区100期数据排行记录列表
|
||||
*/
|
||||
@GetMapping("/backend-history-top-100")
|
||||
@Operation(summary = "获取后区100期数据排行记录", description = "获取dlt_backend_history_top_100表中的所有后区100期数据排行记录")
|
||||
public ApiResponse<List<DltBackendHistoryTop100>> getBackendHistoryTop100(HttpServletRequest request) {
|
||||
// 权限验证
|
||||
ApiResponse<List<DltBackendHistoryTop100>> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取后区100期数据排行记录请求");
|
||||
|
||||
// 调用服务获取后区100期数据排行
|
||||
List<DltBackendHistoryTop100> result = dltBackendHistoryTop100Service.list();
|
||||
|
||||
log.info("获取后区100期数据排行记录完成,返回{}条记录", result.size());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取后区100期数据排行记录失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取后区100期数据排行记录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,493 @@
|
||||
package com.xy.xyaicpzs.controller;
|
||||
|
||||
import com.xy.xyaicpzs.common.ErrorCode;
|
||||
import com.xy.xyaicpzs.common.ResultUtils;
|
||||
import com.xy.xyaicpzs.common.requset.BackBallPredictionRequest;
|
||||
import com.xy.xyaicpzs.common.requset.FirstBallPredictionRequest;
|
||||
import com.xy.xyaicpzs.common.requset.FollowBackBallPredictionRequest;
|
||||
import com.xy.xyaicpzs.common.requset.FollowerBallPredictionRequest;
|
||||
import com.xy.xyaicpzs.common.response.ApiResponse;
|
||||
import com.xy.xyaicpzs.domain.entity.DltPredictRecord;
|
||||
import com.xy.xyaicpzs.domain.vo.BallCombinationAnalysisVO;
|
||||
import com.xy.xyaicpzs.domain.vo.BallPersistenceAnalysisVO;
|
||||
import com.xy.xyaicpzs.domain.vo.FirstBallPredictionResultVO;
|
||||
import com.xy.xyaicpzs.domain.vo.FollowerBallPredictionResultVO;
|
||||
import com.xy.xyaicpzs.domain.vo.BackBallPredictionResultVO;
|
||||
import com.xy.xyaicpzs.domain.vo.FollowBackBallPredictionResultVO;
|
||||
import com.xy.xyaicpzs.dlt.BackBallPredictor;
|
||||
import com.xy.xyaicpzs.dlt.FirstBallPredictor;
|
||||
import com.xy.xyaicpzs.dlt.FirstBallPredictor.FirstBallPredictionResult;
|
||||
import com.xy.xyaicpzs.dlt.FollowBackBallPredictor;
|
||||
import com.xy.xyaicpzs.dlt.FollowerBallPredictor;
|
||||
import com.xy.xyaicpzs.service.DltCombinationAnalysisService;
|
||||
import com.xy.xyaicpzs.service.DltPersistenceAnalysisService;
|
||||
import com.xy.xyaicpzs.service.DltPredictRecordService;
|
||||
import com.xy.xyaicpzs.util.UserAuthValidator;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/dlt/ball-analysis")
|
||||
@Tag(name = "大乐透球号分析", description = "大乐透球号分析算法API")
|
||||
public class DltBallAnalysisController {
|
||||
|
||||
@Autowired
|
||||
private FirstBallPredictor firstBallPredictor;
|
||||
|
||||
@Autowired
|
||||
private FollowerBallPredictor followerBallPredictor;
|
||||
|
||||
@Autowired
|
||||
private BackBallPredictor backBallPredictor;
|
||||
|
||||
@Autowired
|
||||
private FollowBackBallPredictor followBackBallPredictor;
|
||||
|
||||
@Autowired
|
||||
private DltPredictRecordService dltPredictRecordService;
|
||||
|
||||
@Autowired
|
||||
private DltCombinationAnalysisService dltCombinationAnalysisService;
|
||||
|
||||
@Autowired
|
||||
private DltPersistenceAnalysisService dltPersistenceAnalysisService;
|
||||
|
||||
@Autowired
|
||||
private UserAuthValidator userAuthValidator;
|
||||
|
||||
@PostMapping("/predict-first-ball")
|
||||
@Operation(summary = "前区首球预测", description = "根据输入的级别和号码,预测前区首球")
|
||||
public ApiResponse<FirstBallPredictionResultVO> predictFirstBall(@RequestBody FirstBallPredictionRequest request) {
|
||||
FirstBallPredictionResult predictionResult = firstBallPredictor.predictFirstBallWithProcess(
|
||||
request.getLevel(), request.getRedBalls(), request.getBlueBalls());
|
||||
|
||||
FirstBallPredictionResultVO resultVO = FirstBallPredictionResultVO.builder()
|
||||
.result(predictionResult.getResult())
|
||||
.filteringProcess(predictionResult.getFilteringProcess())
|
||||
.build();
|
||||
|
||||
return ResultUtils.success(resultVO);
|
||||
}
|
||||
|
||||
@PostMapping("/predict-follower-ball")
|
||||
@Operation(summary = "前区随球预测", description = "根据输入的级别和号码,预测前区随球")
|
||||
public ApiResponse<FollowerBallPredictionResultVO> predictFollowerBall(@RequestBody FollowerBallPredictionRequest request) {
|
||||
FollowerBallPredictor.FollowerBallPredictionResult result = followerBallPredictor.predictFollowerBallWithProcess(request.getLevel(), request.getWellRegardedBalls(), request.getPreviousFrontBalls(), request.getPreviousBackBalls());
|
||||
FollowerBallPredictionResultVO vo = FollowerBallPredictionResultVO.builder()
|
||||
.result(result.getResult())
|
||||
.filteringProcess(result.getFilteringProcess())
|
||||
.build();
|
||||
return ResultUtils.success(vo);
|
||||
}
|
||||
|
||||
@PostMapping("/predict-back-ball")
|
||||
@Operation(summary = "后区首球预测", description = "根据输入的级别和号码,预测后区首球")
|
||||
public ApiResponse<BackBallPredictionResultVO> predictBackBall(@RequestBody BackBallPredictionRequest request) {
|
||||
BackBallPredictor.BackBallPredictionResult result = backBallPredictor.predictBackBallWithProcess(request.getLevel(), request.getNextFrontBalls(), request.getPreviousFrontBalls(), request.getPreviousBackBalls(), request.getNextBackBalls());
|
||||
BackBallPredictionResultVO vo = BackBallPredictionResultVO.builder()
|
||||
.result(result.getResult())
|
||||
.filteringProcess(result.getFilteringProcess())
|
||||
.build();
|
||||
return ResultUtils.success(vo);
|
||||
}
|
||||
|
||||
@PostMapping("/predict-follow-back-ball")
|
||||
@Operation(summary = "后区随球预测", description = "根据输入的级别和号码,预测后区随球")
|
||||
public ApiResponse<FollowBackBallPredictionResultVO> predictFollowBackBall(@RequestBody FollowBackBallPredictionRequest request) {
|
||||
FollowBackBallPredictor.FollowBackBallPredictionResult result = followBackBallPredictor.predictFollowBackBallWithProcess(request.getLevel(), request.getBackFirstBall(), request.getNextFrontBalls(), request.getPreviousFrontBalls(), request.getPreviousBackBalls());
|
||||
FollowBackBallPredictionResultVO vo = FollowBackBallPredictionResultVO.builder()
|
||||
.result(result.getResult())
|
||||
.filteringProcess(result.getFilteringProcess())
|
||||
.build();
|
||||
return ResultUtils.success(vo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建大乐透预测记录
|
||||
* @param userId 用户ID
|
||||
* @param drawId 开奖期号
|
||||
* @param drawDate 开奖日期
|
||||
* @param frontBalls 5个前区球号码,用逗号分隔
|
||||
* @param backBalls 2个后区球号码,用逗号分隔
|
||||
* @return 创建的预测记录
|
||||
*/
|
||||
@PostMapping("/create-dlt-predict")
|
||||
@Operation(summary = "创建大乐透预测记录", description = "向dlt_predict_record表插入一条大乐透预测记录数据")
|
||||
public ApiResponse<DltPredictRecord> createDltPredictRecord(
|
||||
@Parameter(description = "用户ID,例如:1001", required = true)
|
||||
@RequestParam Long userId,
|
||||
|
||||
@Parameter(description = "开奖期号,例如:25001", required = true)
|
||||
@RequestParam Long drawId,
|
||||
|
||||
@Parameter(description = "开奖日期,格式:yyyy-MM-dd", required = false)
|
||||
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date drawDate,
|
||||
|
||||
@Parameter(description = "5个前区球号码,用逗号分隔,例如:1,5,12,18,25", required = true)
|
||||
@RequestParam String frontBalls,
|
||||
|
||||
@Parameter(description = "2个后区球号码,用逗号分隔,例如:8,12", required = true)
|
||||
@RequestParam String backBalls) {
|
||||
|
||||
try {
|
||||
log.info("接收到创建大乐透预测记录请求:用户ID={},期号={},开奖日期={},前区球={},后区球={}",
|
||||
userId, drawId, drawDate, frontBalls, backBalls);
|
||||
|
||||
// 解析前区球号码
|
||||
List<Integer> frontBallList = parseDltBalls(frontBalls, 5, "前区球", 1, 35);
|
||||
|
||||
// 解析后区球号码
|
||||
List<Integer> backBallList = parseDltBalls(backBalls, 2, "后区球", 1, 12);
|
||||
|
||||
// 调用服务创建大乐透预测记录
|
||||
DltPredictRecord result = dltPredictRecordService.createDltPredictRecord(userId, drawId, drawDate, frontBallList, backBallList);
|
||||
|
||||
log.info("创建大乐透预测记录完成,用户ID:{},记录ID:{}", userId, result.getId());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("创建大乐透预测记录失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "创建大乐透预测记录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析大乐透球号码字符串
|
||||
*/
|
||||
private List<Integer> parseDltBalls(String balls, int expectedCount, String ballType, int minValue, int maxValue) {
|
||||
if (balls == null || balls.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException(ballType + "号码不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
String[] parts = balls.split(",");
|
||||
if (parts.length != expectedCount) {
|
||||
throw new IllegalArgumentException(ballType + "数量必须为" + expectedCount + "个,实际:" + parts.length);
|
||||
}
|
||||
|
||||
List<Integer> result = Arrays.stream(parts)
|
||||
.map(String::trim)
|
||||
.map(Integer::parseInt)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
// 验证球号码范围
|
||||
for (Integer ball : result) {
|
||||
if (ball < minValue || ball > maxValue) {
|
||||
throw new IllegalArgumentException(ballType + "号码必须在" + minValue + "-" + maxValue + "范围内,错误值:" + ball);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException(ballType + "号码格式错误,请使用逗号分隔的数字");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 前区与前区的组合性分析
|
||||
* @param masterBall 主球号码(前区)
|
||||
* @param slaveBall 随球号码(前区)
|
||||
* @return 系数分析结果
|
||||
*/
|
||||
@GetMapping("/front-front-combination-analysis")
|
||||
@Operation(summary = "前区与前区的组合性分析", description = "根据前区主球和前区随球号码,查询D5表获取系数,并计算主球与其他球号的组合情况")
|
||||
public ApiResponse<BallCombinationAnalysisVO> frontFrontCombinationAnalysis(
|
||||
@Parameter(description = "主球号码(前区),例如:5", required = true)
|
||||
@RequestParam Integer masterBall,
|
||||
|
||||
@Parameter(description = "随球号码(前区),例如:12", required = true)
|
||||
@RequestParam Integer slaveBall,
|
||||
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 权限验证
|
||||
ApiResponse<BallCombinationAnalysisVO> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到前区与前区的组合性分析请求:主球(前区)={},随球(前区)={}", masterBall, slaveBall);
|
||||
|
||||
BallCombinationAnalysisVO result = dltCombinationAnalysisService.analyzeFrontFrontCombination(masterBall, slaveBall);
|
||||
|
||||
log.info("前区与前区的组合性分析完成:{}", result);
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("前区与前区的组合性分析失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "前区与前区的组合性分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 前区与后区的组合性分析
|
||||
* @param masterBall 主球号码(前区)
|
||||
* @param slaveBall 随球号码(后区)
|
||||
* @return 系数分析结果
|
||||
*/
|
||||
@GetMapping("/front-back-combination-analysis")
|
||||
@Operation(summary = "前区与后区的组合性分析", description = "根据前区主球和后区随球号码,查询D6表获取系数,并返回扩展分析")
|
||||
public ApiResponse<BallCombinationAnalysisVO> frontBackCombinationAnalysis(
|
||||
@Parameter(description = "主球号码(前区),例如:5", required = true)
|
||||
@RequestParam Integer masterBall,
|
||||
|
||||
@Parameter(description = "随球号码(后区),例如:8", required = true)
|
||||
@RequestParam Integer slaveBall,
|
||||
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 权限验证
|
||||
ApiResponse<BallCombinationAnalysisVO> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到前区与后区的组合性分析请求:主球(前区)={},随球(后区)={}", masterBall, slaveBall);
|
||||
|
||||
BallCombinationAnalysisVO result = dltCombinationAnalysisService.analyzeFrontBackCombination(masterBall, slaveBall);
|
||||
|
||||
log.info("前区与后区的组合性分析完成:{}", result);
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("前区与后区的组合性分析失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "前区与后区的组合性分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 后区与后区的组合性分析
|
||||
* @param masterBall 主球号码(后区)
|
||||
* @param slaveBall 随球号码(后区)
|
||||
* @return 系数分析结果
|
||||
*/
|
||||
@GetMapping("/back-back-combination-analysis")
|
||||
@Operation(summary = "后区与后区的组合性分析", description = "根据后区主球和后区随球号码,查询D7表获取系数,并返回扩展分析")
|
||||
public ApiResponse<BallCombinationAnalysisVO> backBackCombinationAnalysis(
|
||||
@Parameter(description = "主球号码(后区),例如:8", required = true)
|
||||
@RequestParam Integer masterBall,
|
||||
|
||||
@Parameter(description = "随球号码(后区),例如:12", required = true)
|
||||
@RequestParam Integer slaveBall,
|
||||
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 权限验证
|
||||
ApiResponse<BallCombinationAnalysisVO> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到后区与后区的组合性分析请求:主球(后区)={},随球(后区)={}", masterBall, slaveBall);
|
||||
|
||||
BallCombinationAnalysisVO result = dltCombinationAnalysisService.analyzeBackBackCombination(masterBall, slaveBall);
|
||||
|
||||
log.info("后区与后区的组合性分析完成:{}", result);
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("后区与后区的组合性分析失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "后区与后区的组合性分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 后区与前区的组合性分析
|
||||
* @param masterBall 主球号码(后区)
|
||||
* @param slaveBall 随球号码(前区)
|
||||
* @return 系数分析结果
|
||||
*/
|
||||
@GetMapping("/back-front-combination-analysis")
|
||||
@Operation(summary = "后区与前区的组合性分析", description = "根据后区主球和前区随球号码,查询D8表获取系数,并返回扩展分析")
|
||||
public ApiResponse<BallCombinationAnalysisVO> backFrontCombinationAnalysis(
|
||||
@Parameter(description = "主球号码(后区),例如:8", required = true)
|
||||
@RequestParam Integer masterBall,
|
||||
|
||||
@Parameter(description = "随球号码(前区),例如:5", required = true)
|
||||
@RequestParam Integer slaveBall,
|
||||
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 权限验证
|
||||
ApiResponse<BallCombinationAnalysisVO> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到后区与前区的组合性分析请求:主球(后区)={},随球(前区)={}", masterBall, slaveBall);
|
||||
|
||||
BallCombinationAnalysisVO result = dltCombinationAnalysisService.analyzeBackFrontCombination(masterBall, slaveBall);
|
||||
|
||||
log.info("后区与前区的组合性分析完成:{}", result);
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("后区与前区的组合性分析失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "后区与前区的组合性分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 前区与前区的持续性分析
|
||||
* @param masterBall 主球号码(前区)
|
||||
* @param slaveBall 随球号码(前区)
|
||||
* @return 线系数分析结果
|
||||
*/
|
||||
@GetMapping("/front-front-persistence-analysis")
|
||||
@Operation(summary = "前区与前区的持续性分析", description = "根据前区主球和前区随球号码,查询D9表获取线系数,并返回扩展分析")
|
||||
public ApiResponse<BallPersistenceAnalysisVO> frontFrontPersistenceAnalysis(
|
||||
@Parameter(description = "主球号码(前区),例如:5", required = true)
|
||||
@RequestParam Integer masterBall,
|
||||
|
||||
@Parameter(description = "随球号码(前区),例如:12", required = true)
|
||||
@RequestParam Integer slaveBall,
|
||||
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 权限验证
|
||||
ApiResponse<BallPersistenceAnalysisVO> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到前区与前区的持续性分析请求:主球(前区)={},随球(前区)={}", masterBall, slaveBall);
|
||||
|
||||
BallPersistenceAnalysisVO result = dltPersistenceAnalysisService.analyzeFrontFrontPersistence(masterBall, slaveBall);
|
||||
|
||||
log.info("前区与前区的持续性分析完成:{}", result);
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("前区与前区的持续性分析失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "前区与前区的持续性分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 后区与后区的持续性分析
|
||||
* @param masterBall 主球号码(后区)
|
||||
* @param slaveBall 随球号码(后区)
|
||||
* @return 线系数分析结果
|
||||
*/
|
||||
@GetMapping("/back-back-persistence-analysis")
|
||||
@Operation(summary = "后区与后区的持续性分析", description = "根据后区主球和后区随球号码,查询D11表获取线系数,并返回扩展分析")
|
||||
public ApiResponse<BallPersistenceAnalysisVO> backBackPersistenceAnalysis(
|
||||
@Parameter(description = "主球号码(后区),例如:8", required = true)
|
||||
@RequestParam Integer masterBall,
|
||||
|
||||
@Parameter(description = "随球号码(后区),例如:12", required = true)
|
||||
@RequestParam Integer slaveBall,
|
||||
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 权限验证
|
||||
ApiResponse<BallPersistenceAnalysisVO> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到后区与后区的持续性分析请求:主球(后区)={},随球(后区)={}", masterBall, slaveBall);
|
||||
|
||||
BallPersistenceAnalysisVO result = dltPersistenceAnalysisService.analyzeBackBackPersistence(masterBall, slaveBall);
|
||||
|
||||
log.info("后区与后区的持续性分析完成:{}", result);
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("后区与后区的持续性分析失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "后区与后区的持续性分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 前区与后区的持续性分析
|
||||
* @param masterBall 主球号码(前区)
|
||||
* @param slaveBall 随球号码(后区)
|
||||
* @return 线系数分析结果
|
||||
*/
|
||||
@GetMapping("/front-back-persistence-analysis")
|
||||
@Operation(summary = "前区与后区的持续性分析", description = "根据前区主球和后区随球号码,查询D10表获取线系数,并返回扩展分析")
|
||||
public ApiResponse<BallPersistenceAnalysisVO> frontBackPersistenceAnalysis(
|
||||
@Parameter(description = "主球号码(前区),例如:5", required = true)
|
||||
@RequestParam Integer masterBall,
|
||||
|
||||
@Parameter(description = "随球号码(后区),例如:8", required = true)
|
||||
@RequestParam Integer slaveBall,
|
||||
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 权限验证
|
||||
ApiResponse<BallPersistenceAnalysisVO> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到前区与后区的持续性分析请求:主球(前区)={},随球(后区)={}", masterBall, slaveBall);
|
||||
|
||||
BallPersistenceAnalysisVO result = dltPersistenceAnalysisService.analyzeFrontBackPersistence(masterBall, slaveBall);
|
||||
|
||||
log.info("前区与后区的持续性分析完成:{}", result);
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("前区与后区的持续性分析失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "前区与后区的持续性分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 后区与前区的持续性分析
|
||||
* @param masterBall 主球号码(后区)
|
||||
* @param slaveBall 随球号码(前区)
|
||||
* @return 线系数分析结果
|
||||
*/
|
||||
@GetMapping("/back-front-persistence-analysis")
|
||||
@Operation(summary = "后区与前区的持续性分析", description = "根据后区主球和前区随球号码,查询D12表获取线系数,并返回扩展分析")
|
||||
public ApiResponse<BallPersistenceAnalysisVO> backFrontPersistenceAnalysis(
|
||||
@Parameter(description = "主球号码(后区),例如:8", required = true)
|
||||
@RequestParam Integer masterBall,
|
||||
|
||||
@Parameter(description = "随球号码(前区),例如:5", required = true)
|
||||
@RequestParam Integer slaveBall,
|
||||
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 权限验证
|
||||
ApiResponse<BallPersistenceAnalysisVO> authResult = userAuthValidator.validateUserAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到后区与前区的持续性分析请求:主球(后区)={},随球(前区)={}", masterBall, slaveBall);
|
||||
|
||||
BallPersistenceAnalysisVO result = dltPersistenceAnalysisService.analyzeBackFrontPersistence(masterBall, slaveBall);
|
||||
|
||||
log.info("后区与前区的持续性分析完成:{}", result);
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("后区与前区的持续性分析失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "后区与前区的持续性分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
305
src/main/java/com/xy/xyaicpzs/controller/DltDrawController.java
Normal file
305
src/main/java/com/xy/xyaicpzs/controller/DltDrawController.java
Normal file
@@ -0,0 +1,305 @@
|
||||
package com.xy.xyaicpzs.controller;
|
||||
|
||||
import com.xy.xyaicpzs.common.ErrorCode;
|
||||
import com.xy.xyaicpzs.common.ResultUtils;
|
||||
import com.xy.xyaicpzs.common.response.ApiResponse;
|
||||
import com.xy.xyaicpzs.domain.entity.DltDrawRecord;
|
||||
import com.xy.xyaicpzs.domain.entity.User;
|
||||
import com.xy.xyaicpzs.service.DltDrawRecordService;
|
||||
import com.xy.xyaicpzs.service.UserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 大乐透开奖记录控制器
|
||||
* 提供大乐透开奖记录查询的API接口
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/dlt-draw")
|
||||
@Tag(name = "大乐透开奖记录", description = "大乐透开奖记录查询API")
|
||||
public class DltDrawController {
|
||||
|
||||
@Autowired
|
||||
private DltDrawRecordService dltDrawRecordService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* 获取近期大乐透开奖信息
|
||||
* @param limit 获取条数,可选参数,默认10条
|
||||
* @return 近期开奖信息列表
|
||||
*/
|
||||
@GetMapping("/recent-draws")
|
||||
@Operation(summary = "获取近期大乐透开奖信息", description = "获取最近的大乐透开奖信息,默认返回10条,按开奖日期倒序排列")
|
||||
public ApiResponse<List<DltDrawRecord>> getRecentDraws(
|
||||
@Parameter(description = "获取条数,默认10条", required = false)
|
||||
@RequestParam(required = false, defaultValue = "10") Integer limit, HttpServletRequest request) {
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null){
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "您还未登录,请登录后查看");
|
||||
}
|
||||
try {
|
||||
log.info("接收到获取近期大乐透开奖信息请求:条数={}", limit);
|
||||
|
||||
// 调用服务获取近期开奖信息,按开奖日期倒序排列
|
||||
List<DltDrawRecord> result = dltDrawRecordService.lambdaQuery()
|
||||
.orderByDesc(DltDrawRecord::getDrawDate)
|
||||
.last("LIMIT " + limit)
|
||||
.list();
|
||||
|
||||
log.info("获取近期大乐透开奖信息完成,返回{}条记录", result.size());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取近期大乐透开奖信息失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取近期大乐透开奖信息失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据日期范围查询大乐透开奖信息
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @return 开奖信息列表
|
||||
*/
|
||||
@GetMapping("/query-draws")
|
||||
@Operation(summary = "按日期范围查询大乐透开奖信息", description = "根据日期范围查询大乐透开奖信息,支持单边日期查询")
|
||||
public ApiResponse<List<DltDrawRecord>> queryDraws(
|
||||
@Parameter(description = "开始日期,格式:yyyy-MM-dd,例如:2025-01-01", required = false)
|
||||
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
|
||||
|
||||
@Parameter(description = "结束日期,格式:yyyy-MM-dd,例如:2025-01-31", required = false)
|
||||
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
|
||||
HttpServletRequest request) {
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null){
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "您还未登录,请登录后查看");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到按日期范围查询大乐透开奖信息请求:开始日期={},结束日期={}", startDate, endDate);
|
||||
|
||||
// 日期范围验证
|
||||
if (startDate != null && endDate != null && startDate.after(endDate)) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "开始日期不能晚于结束日期");
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
var queryWrapper = dltDrawRecordService.lambdaQuery();
|
||||
|
||||
if (startDate != null) {
|
||||
queryWrapper.ge(DltDrawRecord::getDrawDate, startDate);
|
||||
}
|
||||
|
||||
if (endDate != null) {
|
||||
queryWrapper.le(DltDrawRecord::getDrawDate, endDate);
|
||||
}
|
||||
|
||||
// 按开奖日期倒序排列
|
||||
List<DltDrawRecord> result = queryWrapper
|
||||
.orderByDesc(DltDrawRecord::getDrawDate)
|
||||
.list();
|
||||
|
||||
log.info("按日期范围查询大乐透开奖信息完成,返回{}条记录", result.size());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("按日期范围查询大乐透开奖信息失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "查询大乐透开奖信息失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据期号精准查询单条大乐透开奖信息
|
||||
* @param drawId 开奖期号
|
||||
* @return 开奖信息
|
||||
*/
|
||||
@GetMapping("/draw/{drawId}")
|
||||
@Operation(summary = "根据期号查询大乐透开奖信息", description = "根据期号精准查询单条大乐透开奖信息")
|
||||
public ApiResponse<DltDrawRecord> getDrawById(
|
||||
@Parameter(description = "开奖期号,例如:07001", required = true)
|
||||
@PathVariable String drawId,
|
||||
HttpServletRequest request) {
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null){
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "您还未登录,请登录后查看");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到根据期号查询大乐透开奖信息请求:期号={}", drawId);
|
||||
|
||||
// 调用服务查询开奖信息
|
||||
DltDrawRecord result = dltDrawRecordService.lambdaQuery()
|
||||
.eq(DltDrawRecord::getDrawId, drawId)
|
||||
.one();
|
||||
|
||||
if (result == null) {
|
||||
log.warn("未找到期号为{}的大乐透开奖信息", drawId);
|
||||
return ResultUtils.error(ErrorCode.NOT_FOUND_ERROR, "未找到期号为" + drawId + "的大乐透开奖信息");
|
||||
}
|
||||
|
||||
log.info("根据期号查询大乐透开奖信息完成:{}", result.getDrawId());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("根据期号查询大乐透开奖信息失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "查询大乐透开奖信息失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据开奖期号查询大乐透开奖球号
|
||||
* @param drawId 开奖期号
|
||||
* @return 7个中奖球号(5个前区+2个后区)
|
||||
*/
|
||||
@GetMapping("/draw/{drawId}/numbers")
|
||||
@Operation(summary = "根据开奖期号查询大乐透开奖球号", description = "根据开奖期号查询7个中奖球号(5个前区+2个后区)")
|
||||
public ApiResponse<List<Integer>> getDrawNumbersById(
|
||||
@Parameter(description = "开奖期号,例如:07001", required = true)
|
||||
@PathVariable String drawId,
|
||||
HttpServletRequest request) {
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null){
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "您还未登录,请登录后查看");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到根据期号查询大乐透开奖球号请求:期号={}", drawId);
|
||||
|
||||
// 调用服务查询开奖信息
|
||||
DltDrawRecord result = dltDrawRecordService.lambdaQuery()
|
||||
.eq(DltDrawRecord::getDrawId, drawId)
|
||||
.one();
|
||||
|
||||
if (result == null) {
|
||||
log.warn("未找到期号为{}的大乐透开奖信息", drawId);
|
||||
return ResultUtils.error(ErrorCode.NOT_FOUND_ERROR, "未找到期号为" + drawId + "的大乐透开奖信息");
|
||||
}
|
||||
|
||||
List<Integer> winningNumbers = new ArrayList<>();
|
||||
// 前区5个球号
|
||||
winningNumbers.add(result.getFrontBall1());
|
||||
winningNumbers.add(result.getFrontBall2());
|
||||
winningNumbers.add(result.getFrontBall3());
|
||||
winningNumbers.add(result.getFrontBall4());
|
||||
winningNumbers.add(result.getFrontBall5());
|
||||
// 后区2个球号
|
||||
winningNumbers.add(result.getBackBall1());
|
||||
winningNumbers.add(result.getBackBall2());
|
||||
|
||||
log.info("根据期号查询大乐透开奖球号完成:{},球号:{}", drawId, winningNumbers);
|
||||
return ResultUtils.success(winningNumbers);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("根据期号查询大乐透开奖球号失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "查询大乐透开奖球号失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取近10期大乐透开奖期号
|
||||
* @return 近10期开奖期号列表
|
||||
*/
|
||||
@GetMapping("/recent-10-draw-ids")
|
||||
@Operation(summary = "获取近10期大乐透开奖期号", description = "获取dlt_draw_record表中最新的10期开奖期号,按开奖日期倒序排列")
|
||||
public ApiResponse<List<String>> getRecent10DrawIds(HttpServletRequest request) {
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null){
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "您还未登录,请登录后查看");
|
||||
}
|
||||
try {
|
||||
log.info("接收到获取近10期大乐透开奖期号请求");
|
||||
|
||||
// 查询最近10期开奖期号,按开奖日期倒序
|
||||
List<String> drawIds = dltDrawRecordService.lambdaQuery()
|
||||
.select(DltDrawRecord::getDrawId)
|
||||
.orderByDesc(DltDrawRecord::getDrawDate)
|
||||
.last("LIMIT 10")
|
||||
.list()
|
||||
.stream()
|
||||
.map(DltDrawRecord::getDrawId)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
log.info("获取近10期大乐透开奖期号完成,返回{}条记录", drawIds.size());
|
||||
return ResultUtils.success(drawIds);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取近10期大乐透开奖期号失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取近10期大乐透开奖期号失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取近10期大乐透开奖信息
|
||||
* @return 近10期开奖信息列表
|
||||
*/
|
||||
@GetMapping("/recent-10-draws")
|
||||
@Operation(summary = "获取近10期大乐透开奖信息", description = "获取dlt_draw_record表中最新的10期开奖信息,按开奖日期倒序排列")
|
||||
public ApiResponse<List<DltDrawRecord>> getRecent10Draws(HttpServletRequest request) {
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null){
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "您还未登录,请登录后查看");
|
||||
}
|
||||
try {
|
||||
log.info("接收到获取近10期大乐透开奖信息请求");
|
||||
|
||||
// 调用服务获取近10期开奖信息,按开奖日期倒序
|
||||
List<DltDrawRecord> result = dltDrawRecordService.lambdaQuery()
|
||||
.orderByDesc(DltDrawRecord::getDrawDate)
|
||||
.last("LIMIT 10")
|
||||
.list();
|
||||
|
||||
log.info("获取近10期大乐透开奖信息完成,返回{}条记录", result.size());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取近10期大乐透开奖信息失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取近10期大乐透开奖信息失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取近100期大乐透开奖信息
|
||||
* @return 近100期开奖信息列表
|
||||
*/
|
||||
@GetMapping("/recent-100-draws")
|
||||
@Operation(summary = "获取近100期大乐透开奖信息", description = "获取dlt_draw_record表中最新的100期开奖信息,按开奖日期倒序排列")
|
||||
public ApiResponse<List<DltDrawRecord>> getRecent100Draws(HttpServletRequest request) {
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null){
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "您还未登录,请登录后查看");
|
||||
}
|
||||
Date now = new Date();
|
||||
if (loginUser.getVipExpire() == null || loginUser.getVipExpire().before(now)) {
|
||||
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "VIP会员已过期,请续费后使用");
|
||||
}
|
||||
try {
|
||||
log.info("接收到获取近100期大乐透开奖信息请求");
|
||||
|
||||
// 调用服务获取近100期开奖信息,按开奖日期倒序
|
||||
List<DltDrawRecord> result = dltDrawRecordService.lambdaQuery()
|
||||
.orderByDesc(DltDrawRecord::getDrawDate)
|
||||
.last("LIMIT 100")
|
||||
.list();
|
||||
|
||||
log.info("获取近100期大乐透开奖信息完成,返回{}条记录", result.size());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取近100期大乐透开奖信息失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取近100期大乐透开奖信息失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package com.xy.xyaicpzs.controller;
|
||||
|
||||
import com.xy.xyaicpzs.common.ErrorCode;
|
||||
import com.xy.xyaicpzs.common.ResultUtils;
|
||||
import com.xy.xyaicpzs.common.response.ApiResponse;
|
||||
import com.xy.xyaicpzs.domain.entity.User;
|
||||
import com.xy.xyaicpzs.service.OperationHistoryService;
|
||||
import com.xy.xyaicpzs.service.UserService;
|
||||
import com.xy.xyaicpzs.util.DltDataImporter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
/**
|
||||
* 大乐透数据导入控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/dlt")
|
||||
@Tag(name = "大乐透数据导入", description = "大乐透数据导入相关接口")
|
||||
public class DltImportController {
|
||||
|
||||
@Autowired
|
||||
private DltDataImporter dltDataImporter;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private OperationHistoryService operationHistoryService;
|
||||
|
||||
/**
|
||||
* 上传Excel文件并完整导入大乐透数据(D3-D12工作表)
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
@Operation(summary = "上传Excel文件完整导入大乐透数据", description = "上传包含D3-D12工作表的Excel文件,将前区历史数据、后区历史数据和系数数据导入到对应表中")
|
||||
public ApiResponse<String> uploadDltFile(
|
||||
@Parameter(description = "包含D3-D12工作表的Excel文件(.xlsx格式)", required = true)
|
||||
@RequestParam("file") MultipartFile file, HttpServletRequest httpServletRequest) {
|
||||
User loginUser = userService.getLoginUser(httpServletRequest);
|
||||
if (!userService.isAdmin(loginUser)){
|
||||
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "无权限");
|
||||
}
|
||||
Long userId = loginUser.getId();
|
||||
String userName = loginUser.getUserName();
|
||||
|
||||
String fileName = file.getOriginalFilename();
|
||||
log.info("接收到大乐透完整数据上传请求,文件名:{}", fileName);
|
||||
|
||||
// 验证文件格式
|
||||
if (fileName == null || !fileName.toLowerCase().endsWith(".xlsx")) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "只支持.xlsx格式的Excel文件");
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建临时文件
|
||||
Path tempFile = Files.createTempFile("dlt_upload_", ".xlsx");
|
||||
Files.copy(file.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
// 导入完整数据(D3-D12)
|
||||
dltDataImporter.importDltCompleteData(tempFile.toString());
|
||||
|
||||
// 删除临时文件
|
||||
Files.deleteIfExists(tempFile);
|
||||
|
||||
String message = "大乐透完整数据导入成功";
|
||||
|
||||
// 记录操作历史 - 成功
|
||||
String resultMessage = String.format("%s成功上传并导入大乐透完整数据文件:%s", userName, fileName);
|
||||
operationHistoryService.recordOperation(userId, "大乐透完整数据导入", 1, "成功", resultMessage);
|
||||
|
||||
return ResultUtils.success(message);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("大乐透完整数据导入失败,文件名:{},错误:{}", fileName, e.getMessage(), e);
|
||||
|
||||
// 记录操作历史 - 失败
|
||||
String resultMessage = String.format("%s大乐透完整数据导入失败,文件名:%s,错误原因:%s", userName, fileName, e.getMessage());
|
||||
operationHistoryService.recordOperation(userId, "大乐透完整数据导入", 1, "失败", resultMessage);
|
||||
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "大乐透完整数据导入失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传Excel文件并导入大乐透开奖数据(覆盖)
|
||||
*/
|
||||
@PostMapping("/upload-draw-data")
|
||||
@Operation(summary = "上传Excel文件导入大乐透开奖数据", description = "上传包含D1工作表的Excel文件,将大乐透开奖数据导入到dlt_draw_record表中(覆盖现有数据)")
|
||||
public ApiResponse<String> uploadDltDrawData(
|
||||
@Parameter(description = "包含D1工作表的Excel文件(.xlsx格式)", required = true)
|
||||
@RequestParam("file") MultipartFile file, HttpServletRequest httpServletRequest) {
|
||||
User loginUser = userService.getLoginUser(httpServletRequest);
|
||||
if (!userService.isAdmin(loginUser)){
|
||||
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "无权限");
|
||||
}
|
||||
Long userId = loginUser.getId();
|
||||
String userName = loginUser.getUserName();
|
||||
|
||||
String fileName = file.getOriginalFilename();
|
||||
log.info("接收到大乐透开奖数据上传请求,文件名:{}", fileName);
|
||||
|
||||
// 验证文件格式
|
||||
if (fileName == null || !fileName.toLowerCase().endsWith(".xlsx")) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "只支持.xlsx格式的Excel文件");
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建临时文件
|
||||
Path tempFile = Files.createTempFile("dlt_draw_upload_", ".xlsx");
|
||||
Files.copy(file.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
// 导入数据
|
||||
dltDataImporter.importDltDrawData(tempFile.toString());
|
||||
|
||||
// 删除临时文件
|
||||
Files.deleteIfExists(tempFile);
|
||||
|
||||
String message = "大乐透开奖数据导入成功";
|
||||
|
||||
// 记录操作历史 - 成功
|
||||
String resultMessage = String.format("%s成功上传并导入大乐透开奖数据文件:%s", userName, fileName);
|
||||
operationHistoryService.recordOperation(userId, "大乐透开奖数据导入", 1, "成功", resultMessage);
|
||||
|
||||
return ResultUtils.success(message);
|
||||
} catch (Exception e) {
|
||||
log.error("大乐透开奖数据导入失败,文件名:{},错误:{}", fileName, e.getMessage(), e);
|
||||
|
||||
// 记录操作历史 - 失败
|
||||
String resultMessage = String.format("%s大乐透开奖数据导入失败,文件名:%s,错误原因:%s", userName, fileName, e.getMessage());
|
||||
operationHistoryService.recordOperation(userId, "大乐透开奖数据导入", 1, "失败", resultMessage);
|
||||
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "大乐透开奖数据导入失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传Excel文件并追加导入大乐透开奖数据
|
||||
*/
|
||||
@PostMapping("/append-draw-data")
|
||||
@Operation(summary = "追加导入大乐透开奖数据", description = "上传包含D1工作表的Excel文件,将大乐透开奖数据追加导入到dlt_draw_record表中(不清空现有数据,跳过重复记录)")
|
||||
public ApiResponse<String> appendDltDrawData(
|
||||
@Parameter(description = "包含D1工作表的Excel文件(.xlsx格式)", required = true)
|
||||
@RequestParam("file") MultipartFile file, HttpServletRequest httpServletRequest) {
|
||||
User loginUser = userService.getLoginUser(httpServletRequest);
|
||||
if (!userService.isAdmin(loginUser)){
|
||||
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "无权限");
|
||||
}
|
||||
Long userId = loginUser.getId();
|
||||
String userName = loginUser.getUserName();
|
||||
|
||||
String fileName = file.getOriginalFilename();
|
||||
log.info("接收到大乐透开奖数据追加导入请求,文件名:{}", fileName);
|
||||
|
||||
// 验证文件格式
|
||||
if (fileName == null || !fileName.toLowerCase().endsWith(".xlsx")) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "只支持.xlsx格式的Excel文件");
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建临时文件
|
||||
Path tempFile = Files.createTempFile("dlt_append_", ".xlsx");
|
||||
Files.copy(file.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
// 追加导入数据
|
||||
dltDataImporter.appendDltDrawData(tempFile.toString());
|
||||
|
||||
// 删除临时文件
|
||||
Files.deleteIfExists(tempFile);
|
||||
|
||||
String message = "大乐透开奖数据追加导入成功";
|
||||
|
||||
// 记录操作历史 - 成功
|
||||
String resultMessage = String.format("%s成功追加导入大乐透开奖数据文件:%s", userName, fileName);
|
||||
operationHistoryService.recordOperation(userId, "大乐透开奖数据追加导入", 1, "成功", resultMessage);
|
||||
|
||||
return ResultUtils.success(message);
|
||||
} catch (Exception e) {
|
||||
log.error("大乐透开奖数据追加导入失败,文件名:{},错误:{}", fileName, e.getMessage(), e);
|
||||
|
||||
// 记录操作历史 - 失败
|
||||
String resultMessage = String.format("%s大乐透开奖数据追加导入失败,文件名:%s,错误原因:%s", userName, fileName, e.getMessage());
|
||||
operationHistoryService.recordOperation(userId, "大乐透开奖数据追加导入", 1, "失败", resultMessage);
|
||||
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "大乐透开奖数据追加导入失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
package com.xy.xyaicpzs.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.xy.xyaicpzs.common.ErrorCode;
|
||||
import com.xy.xyaicpzs.common.ResultUtils;
|
||||
import com.xy.xyaicpzs.common.requset.DltPredictRecordQueryRequest;
|
||||
import com.xy.xyaicpzs.common.response.ApiResponse;
|
||||
import com.xy.xyaicpzs.common.response.PageResponse;
|
||||
import com.xy.xyaicpzs.domain.entity.DltPredictRecord;
|
||||
import com.xy.xyaicpzs.domain.entity.User;
|
||||
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.DataAnalysisService;
|
||||
import com.xy.xyaicpzs.service.DltDataAnalysisService;
|
||||
import com.xy.xyaicpzs.service.DltPredictRecordService;
|
||||
import com.xy.xyaicpzs.service.UserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 大乐透预测记录控制器
|
||||
* 提供大乐透预测记录查询相关的API接口
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/dlt-predict")
|
||||
@Tag(name = "大乐透预测记录", description = "大乐透预测记录查询API")
|
||||
public class DltPredictController {
|
||||
|
||||
@Autowired
|
||||
private DltPredictRecordService dltPredictRecordService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private DltDataAnalysisService dltDataAnalysisService;
|
||||
|
||||
@Autowired
|
||||
private DataAnalysisService dataAnalysisService;
|
||||
|
||||
/**
|
||||
* 根据用户ID获取大乐透预测记录
|
||||
* @param userId 用户ID
|
||||
* @return 用户的所有大乐透预测记录列表
|
||||
*/
|
||||
@GetMapping("/predict-records/{userId}")
|
||||
@Operation(summary = "获取用户大乐透预测记录", description = "根据用户ID分页获取该用户的大乐透预测记录,按预测时间倒序排列,每页5条")
|
||||
public ApiResponse<PageResponse<DltPredictRecord>> getDltPredictRecordsByUserId(
|
||||
@Parameter(description = "用户ID,例如:1001", required = true)
|
||||
@PathVariable Long userId,
|
||||
@Parameter(description = "页码,从1开始,默认为1", required = false)
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
HttpServletRequest request) {
|
||||
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null){
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
|
||||
}
|
||||
|
||||
// 检查VIP权限
|
||||
Date now = new Date();
|
||||
if (loginUser.getVipExpire() == null || loginUser.getVipExpire().before(now)) {
|
||||
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "VIP会员已过期,请续费后使用");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取用户大乐透预测记录请求:用户ID={},页码={}", userId, page);
|
||||
|
||||
// 参数校验
|
||||
if (page < 1) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
// 获取总记录数
|
||||
Long total = dltPredictRecordService.getDltPredictRecordsCountByUserId(userId);
|
||||
|
||||
// 调用服务获取用户预测记录(分页,每页5条)
|
||||
List<DltPredictRecord> records = dltPredictRecordService.getDltPredictRecordsByUserIdWithPaging(userId, page, 5);
|
||||
|
||||
// 创建分页响应对象
|
||||
PageResponse<DltPredictRecord> pageResponse = PageResponse.of(records, total, page, 5);
|
||||
|
||||
log.info("获取用户大乐透预测记录完成,用户ID:{},页码:{},返回{}条记录,总记录数:{}", userId, page, records.size(), total);
|
||||
return ResultUtils.success(pageResponse);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户大乐透预测记录失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取用户大乐透预测记录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按条件查询大乐透预测记录
|
||||
* @param request 查询条件
|
||||
* @return 分页预测记录
|
||||
*/
|
||||
@PostMapping("/query-predict-records")
|
||||
@Operation(summary = "按条件查询大乐透预测记录", description = "根据用户ID和预测状态(待开奖/已开奖)以及预测结果筛选大乐透预测记录,支持分页查询")
|
||||
public ApiResponse<PageResponse<DltPredictRecord>> queryDltPredictRecords(@RequestBody DltPredictRecordQueryRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
|
||||
User loginUser = userService.getLoginUser(httpRequest);
|
||||
if (loginUser == null){
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到按条件查询大乐透预测记录请求:userId={}, predictStatus={}, predictResult={}, current={}, pageSize={}",
|
||||
request.getUserId(), request.getPredictStatus(), request.getPredictResult(),
|
||||
request.getCurrent(), request.getPageSize());
|
||||
|
||||
// 构建查询条件
|
||||
QueryWrapper<DltPredictRecord> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
// 添加用户ID筛选条件
|
||||
if (request.getUserId() != null) {
|
||||
queryWrapper.eq("userId", request.getUserId());
|
||||
}
|
||||
|
||||
// 添加预测状态筛选条件
|
||||
if (StringUtils.isNotBlank(request.getPredictStatus())) {
|
||||
queryWrapper.eq("predictStatus", request.getPredictStatus());
|
||||
}
|
||||
|
||||
// 添加预测结果筛选条件
|
||||
if (StringUtils.isNotBlank(request.getPredictResult())) {
|
||||
queryWrapper.eq("predictResult", request.getPredictResult());
|
||||
}
|
||||
|
||||
// 按预测时间降序排序
|
||||
queryWrapper.orderByDesc("predictTime");
|
||||
|
||||
// 执行分页查询
|
||||
Page<DltPredictRecord> page = new Page<>(request.getCurrent(), request.getPageSize());
|
||||
Page<DltPredictRecord> resultPage = dltPredictRecordService.page(page, queryWrapper);
|
||||
|
||||
// 构建分页响应对象
|
||||
PageResponse<DltPredictRecord> result = PageResponse.of(
|
||||
resultPage.getRecords(),
|
||||
resultPage.getTotal(),
|
||||
(int) resultPage.getCurrent(),
|
||||
(int) resultPage.getSize()
|
||||
);
|
||||
|
||||
log.info("按条件查询大乐透预测记录完成,总记录数:{}", result.getTotal());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("按条件查询大乐透预测记录失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "查询大乐透预测记录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户大乐透预测统计数据
|
||||
* @param userId 用户ID
|
||||
* @return 用户大乐透预测统计数据
|
||||
*/
|
||||
@GetMapping("/user-predict-stat/{userId}")
|
||||
@Operation(summary = "获取用户大乐透预测统计数据", description = "根据用户ID获取该用户的大乐透预测次数、待开奖次数、命中次数、命中率等统计数据")
|
||||
public ApiResponse<UserPredictStatVO> getUserDltPredictStat(
|
||||
@Parameter(description = "用户ID,例如:1001", required = true)
|
||||
@PathVariable Long userId,
|
||||
HttpServletRequest request) {
|
||||
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null){
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
|
||||
}
|
||||
|
||||
// 检查VIP权限
|
||||
Date now = new Date();
|
||||
if (loginUser.getVipExpire() == null || loginUser.getVipExpire().before(now)) {
|
||||
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "VIP会员已过期,请续费后使用");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取用户大乐透预测统计数据请求:用户ID={}", userId);
|
||||
|
||||
// 调用服务获取用户大乐透预测统计数据
|
||||
UserPredictStatVO result = dltDataAnalysisService.getUserDltPredictStat(userId);
|
||||
|
||||
log.info("获取用户大乐透预测统计数据完成,用户ID:{},预测次数:{},待开奖次数:{},命中次数:{},命中率:{}",
|
||||
userId, result.getPredictCount(), result.getPendingCount(),
|
||||
result.getHitCount(), result.getHitRate());
|
||||
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户大乐透预测统计数据失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取用户大乐透预测统计数据失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 大乐透奖金统计
|
||||
* @return 奖金统计信息(各中奖等级、中奖次数、单奖金额、奖金合计)
|
||||
*/
|
||||
@GetMapping("/prize-statistics")
|
||||
@Operation(summary = "大乐透奖金统计", description = "统计用户所有大乐透中奖记录,按等级汇总各等级的中奖次数和奖金")
|
||||
public ApiResponse<PrizeEstimateVO> getDltPrizeStatistics(HttpServletRequest request) {
|
||||
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null) {
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "您还未登录,请登录后查看");
|
||||
}
|
||||
|
||||
// 检查VIP权限
|
||||
Date now = new Date();
|
||||
if (loginUser.getVipExpire() == null || loginUser.getVipExpire().before(now)) {
|
||||
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "VIP会员已过期,请续费后使用");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到大乐透奖金统计请求");
|
||||
|
||||
// 调用服务进行大乐透奖金统计
|
||||
PrizeEstimateVO result = dltDataAnalysisService.getDltPrizeStatistics(loginUser.getId(), null);
|
||||
|
||||
log.info("大乐透奖金统计完成,总奖金:{}", result.getTotalPrize());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("大乐透奖金统计失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "大乐透奖金统计失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动触发处理待开奖记录(双色球+大乐透)
|
||||
* @return 处理结果
|
||||
*/
|
||||
@PostMapping("/process-pending")
|
||||
@Operation(summary = "手动处理待开奖记录", description = "手动触发处理双色球和大乐透的待开奖预测记录,匹配开奖结果并更新中奖状态")
|
||||
public ApiResponse<String> processPendingPredictions(HttpServletRequest request) {
|
||||
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null){
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
|
||||
}
|
||||
|
||||
// 检查管理员权限
|
||||
if (!userService.isAdmin(loginUser)) {
|
||||
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "无管理员权限");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到手动处理待开奖记录请求(双色球+大乐透)");
|
||||
|
||||
int ssqProcessedCount = 0;
|
||||
int dltProcessedCount = 0;
|
||||
|
||||
// 处理双色球待开奖记录
|
||||
try {
|
||||
log.info("开始处理双色球待开奖记录");
|
||||
ssqProcessedCount = dataAnalysisService.processPendingPredictions();
|
||||
log.info("双色球待开奖记录处理完成,共处理{}条", ssqProcessedCount);
|
||||
} catch (Exception e) {
|
||||
log.error("处理双色球待开奖记录时发生错误:{}", e.getMessage(), e);
|
||||
}
|
||||
|
||||
// 处理大乐透待开奖记录
|
||||
try {
|
||||
log.info("开始处理大乐透待开奖记录");
|
||||
dltProcessedCount = dltDataAnalysisService.processPendingDltPredictions();
|
||||
log.info("大乐透待开奖记录处理完成,共处理{}条", dltProcessedCount);
|
||||
} catch (Exception e) {
|
||||
log.error("处理大乐透待开奖记录时发生错误:{}", e.getMessage(), e);
|
||||
}
|
||||
|
||||
int totalProcessedCount = ssqProcessedCount + dltProcessedCount;
|
||||
String message = String.format("待开奖记录处理完成!双色球:%d条,大乐透:%d条,总计:%d条",
|
||||
ssqProcessedCount, dltProcessedCount, totalProcessedCount);
|
||||
log.info("手动处理待开奖记录完成:{}", message);
|
||||
|
||||
return ResultUtils.success(message);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("手动处理待开奖记录失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "处理待开奖记录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取大乐透前区首球命中率统计
|
||||
* @return 前区首球命中次数和命中率统计
|
||||
*/
|
||||
@GetMapping("/front-first-ball-hit-rate")
|
||||
@Operation(summary = "获取大乐透前区首球命中率统计", description = "统计用户的大乐透前区首球命中次数和命中率")
|
||||
public ApiResponse<RedBallHitRateVO> getFrontFirstBallHitRate(HttpServletRequest request) {
|
||||
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null) {
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "您还未登录,请登录后查看");
|
||||
}
|
||||
|
||||
// 检查VIP权限
|
||||
Date now = new Date();
|
||||
if (loginUser.getVipExpire() == null || loginUser.getVipExpire().before(now)) {
|
||||
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "VIP会员已过期,请续费后使用");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取大乐透前区首球命中率统计请求");
|
||||
|
||||
// 调用服务获取前区首球命中率
|
||||
RedBallHitRateVO result = dltDataAnalysisService.getFrontFirstBallHitRate(loginUser.getId());
|
||||
|
||||
log.info("获取大乐透前区首球命中率统计完成,命中总数:{},预测总数:{},命中率:{}",
|
||||
result.getTotalHitCount(), result.getTotalPredictedCount(), result.getHitRate());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取大乐透前区首球命中率统计失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取大乐透前区首球命中率统计失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取大乐透前区球号命中率统计
|
||||
* @return 前区球号命中总数和命中率统计
|
||||
*/
|
||||
@GetMapping("/front-ball-hit-rate")
|
||||
@Operation(summary = "获取大乐透前区球号命中率统计", description = "统计用户的大乐透前区球号命中总数和命中率")
|
||||
public ApiResponse<RedBallHitRateVO> getFrontBallHitRate(HttpServletRequest request) {
|
||||
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null) {
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "您还未登录,请登录后查看");
|
||||
}
|
||||
|
||||
// 检查VIP权限
|
||||
Date now = new Date();
|
||||
if (loginUser.getVipExpire() == null || loginUser.getVipExpire().before(now)) {
|
||||
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "VIP会员已过期,请续费后使用");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取大乐透前区球号命中率统计请求");
|
||||
|
||||
// 调用服务获取前区球号命中率
|
||||
RedBallHitRateVO result = dltDataAnalysisService.getFrontBallHitRate(loginUser.getId());
|
||||
|
||||
log.info("获取大乐透前区球号命中率统计完成,命中总数:{},预测总数:{},命中率:{}",
|
||||
result.getTotalHitCount(), result.getTotalPredictedCount(), result.getHitRate());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取大乐透前区球号命中率统计失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取大乐透前区球号命中率统计失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取大乐透后区首球命中率统计
|
||||
* @return 后区首球命中次数和命中率统计
|
||||
*/
|
||||
@GetMapping("/back-first-ball-hit-rate")
|
||||
@Operation(summary = "获取大乐透后区首球命中率统计", description = "统计用户的大乐透后区首球命中次数和命中率")
|
||||
public ApiResponse<RedBallHitRateVO> getBackFirstBallHitRate(HttpServletRequest request) {
|
||||
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null) {
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "您还未登录,请登录后查看");
|
||||
}
|
||||
|
||||
// 检查VIP权限
|
||||
Date now = new Date();
|
||||
if (loginUser.getVipExpire() == null || loginUser.getVipExpire().before(now)) {
|
||||
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "VIP会员已过期,请续费后使用");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取大乐透后区首球命中率统计请求");
|
||||
|
||||
// 调用服务获取后区首球命中率
|
||||
RedBallHitRateVO result = dltDataAnalysisService.getBackFirstBallHitRate(loginUser.getId());
|
||||
|
||||
log.info("获取大乐透后区首球命中率统计完成,命中总数:{},预测总数:{},命中率:{}",
|
||||
result.getTotalHitCount(), result.getTotalPredictedCount(), result.getHitRate());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取大乐透后区首球命中率统计失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取大乐透后区首球命中率统计失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取大乐透后区球号命中率统计
|
||||
* @return 后区球号命中次数和命中率统计
|
||||
*/
|
||||
@GetMapping("/back-ball-hit-rate")
|
||||
@Operation(summary = "获取大乐透后区球号命中率统计", description = "统计用户的大乐透后区球号命中次数和命中率")
|
||||
public ApiResponse<RedBallHitRateVO> getBackBallHitRate(HttpServletRequest request) {
|
||||
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (loginUser == null) {
|
||||
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "您还未登录,请登录后查看");
|
||||
}
|
||||
|
||||
// 检查VIP权限
|
||||
Date now = new Date();
|
||||
if (loginUser.getVipExpire() == null || loginUser.getVipExpire().before(now)) {
|
||||
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "VIP会员已过期,请续费后使用");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("接收到获取大乐透后区球号命中率统计请求");
|
||||
|
||||
// 调用服务获取后区球号命中率
|
||||
RedBallHitRateVO result = dltDataAnalysisService.getBackBallHitRate(loginUser.getId());
|
||||
|
||||
log.info("获取大乐透后区球号命中率统计完成,命中总数:{},预测总数:{},命中率:{}",
|
||||
result.getTotalHitCount(), result.getTotalPredictedCount(), result.getHitRate());
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取大乐透后区球号命中率统计失败:{}", e.getMessage(), e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取大乐透后区球号命中率统计失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,18 +56,18 @@ public class ExcelImportController {
|
||||
String message = excelImportService.importExcelFile(file);
|
||||
|
||||
// 记录操作历史 - 成功
|
||||
String resultMessage = String.format("%s成功上传并导入Excel文件:%s,导入结果:%s", userName, fileName, message);
|
||||
operationHistoryService.recordOperation(userId, "Excel数据导入", 1, "成功", resultMessage);
|
||||
String resultMessage = String.format("%s成功上传并导入双色球Excel文件:%s,导入结果:%s", userName, fileName, message);
|
||||
operationHistoryService.recordOperation(userId, "双色球Excel数据导入", 1, "成功", resultMessage);
|
||||
|
||||
return ResultUtils.success(message);
|
||||
} catch (Exception e) {
|
||||
log.error("Excel文件导入失败,文件名:{},错误:{}", fileName, e.getMessage(), e);
|
||||
log.error("双色球Excel文件导入失败,文件名:{},错误:{}", fileName, e.getMessage(), e);
|
||||
|
||||
// 记录操作历史 - 失败
|
||||
String resultMessage = String.format("%sExcel文件导入失败:%s,文件名:%s,错误原因:%s", userName, fileName, fileName, e.getMessage());
|
||||
operationHistoryService.recordOperation(userId, "Excel数据导入", 1, "失败", resultMessage);
|
||||
String resultMessage = String.format("%s双色球Excel文件导入失败:%s,文件名:%s,错误原因:%s", userName, fileName, fileName, e.getMessage());
|
||||
operationHistoryService.recordOperation(userId, "双色球Excel数据导入", 1, "失败", resultMessage);
|
||||
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "Excel文件导入失败:" + e.getMessage());
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "双色球Excel文件导入失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,18 +93,18 @@ public class ExcelImportController {
|
||||
String message = excelImportService.importLotteryDrawsFile(file);
|
||||
|
||||
// 记录操作历史 - 成功
|
||||
String resultMessage = String.format("%s成功上传并导入开奖数据文件:%s,导入结果:%s", userName, fileName, message);
|
||||
operationHistoryService.recordOperation(userId, "开奖数据导入", 1, "成功", resultMessage);
|
||||
String resultMessage = String.format("%s成功上传并导入双色球开奖数据文件:%s,导入结果:%s", userName, fileName, message);
|
||||
operationHistoryService.recordOperation(userId, "双色球开奖数据导入", 1, "成功", resultMessage);
|
||||
|
||||
return ResultUtils.success(message);
|
||||
} catch (Exception e) {
|
||||
log.error("开奖数据文件导入失败,文件名:{},错误:{}", fileName, e.getMessage(), e);
|
||||
|
||||
// 记录操作历史 - 失败
|
||||
String resultMessage = String.format("%s开奖数据文件导入失败:%s,文件名:%s,错误原因:%s", userName, fileName, fileName, e.getMessage());
|
||||
operationHistoryService.recordOperation(userId, "开奖数据导入", 1, "失败", resultMessage);
|
||||
String resultMessage = String.format("%s双色球开奖数据文件导入失败:%s,文件名:%s,错误原因:%s", userName, fileName, fileName, e.getMessage());
|
||||
operationHistoryService.recordOperation(userId, "双色球开奖数据导入", 1, "失败", resultMessage);
|
||||
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "开奖数据文件导入失败:" + e.getMessage());
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "双色球开奖数据文件导入失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,18 +130,18 @@ public class ExcelImportController {
|
||||
String message = excelImportService.appendLotteryDrawsFile(file);
|
||||
|
||||
// 记录操作历史 - 成功
|
||||
String resultMessage = String.format("%s成功追加导入开奖数据文件:%s,导入结果:%s", userName, fileName, message);
|
||||
operationHistoryService.recordOperation(userId, "开奖数据追加导入", 1, "成功", resultMessage);
|
||||
String resultMessage = String.format("%s成功追加导入双色球开奖数据文件:%s,导入结果:%s", userName, fileName, message);
|
||||
operationHistoryService.recordOperation(userId, "双色球开奖数据追加导入", 1, "成功", resultMessage);
|
||||
|
||||
return ResultUtils.success(message);
|
||||
} catch (Exception e) {
|
||||
log.error("开奖数据追加导入失败,文件名:{},错误:{}", fileName, e.getMessage(), e);
|
||||
|
||||
// 记录操作历史 - 失败
|
||||
String resultMessage = String.format("%s开奖数据追加导入失败:%s,文件名:%s,错误原因:%s", userName, fileName, fileName, e.getMessage());
|
||||
operationHistoryService.recordOperation(userId, "开奖数据追加导入", 1, "失败", resultMessage);
|
||||
String resultMessage = String.format("%s双色球开奖数据追加导入失败:%s,文件名:%s,错误原因:%s", userName, fileName, fileName, e.getMessage());
|
||||
operationHistoryService.recordOperation(userId, "双色球开奖数据追加导入", 1, "失败", resultMessage);
|
||||
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "开奖数据追加导入失败:" + e.getMessage());
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "双色球开奖数据追加导入失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
398
src/main/java/com/xy/xyaicpzs/controller/JtDltController.java
Normal file
398
src/main/java/com/xy/xyaicpzs/controller/JtDltController.java
Normal file
@@ -0,0 +1,398 @@
|
||||
package com.xy.xyaicpzs.controller;
|
||||
|
||||
import com.xy.xyaicpzs.common.ErrorCode;
|
||||
import com.xy.xyaicpzs.common.ResultUtils;
|
||||
import com.xy.xyaicpzs.common.response.ApiResponse;
|
||||
import com.xy.xyaicpzs.domain.vo.DLTFirstStepResultVO;
|
||||
import com.xy.xyaicpzs.domain.vo.DLTSecondStepResultVO;
|
||||
import com.xy.xyaicpzs.domain.vo.DLTThirdStepResultVO;
|
||||
import com.xy.xyaicpzs.domain.vo.DLTFourthStepResultVO;
|
||||
import com.xy.xyaicpzs.jt.jtdlt.FirstFrontBallAnalysis;
|
||||
import com.xy.xyaicpzs.jt.jtdlt.FollowFrontBallAnalysis;
|
||||
import com.xy.xyaicpzs.jt.jtdlt.BackBallAnalysis;
|
||||
import com.xy.xyaicpzs.jt.jtdlt.FollowBackendBallAnalysis;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/jtdlt/analysis")
|
||||
@Tag(name = "精推大乐透分析", description = "精推大乐透分析算法API")
|
||||
public class JtDltController {
|
||||
|
||||
@Autowired
|
||||
private FirstFrontBallAnalysis firstFrontBallAnalysis;
|
||||
|
||||
@Autowired
|
||||
private FollowFrontBallAnalysis followFrontBallAnalysis;
|
||||
|
||||
@Autowired
|
||||
private BackBallAnalysis backBallAnalysis;
|
||||
|
||||
@Autowired
|
||||
private FollowBackendBallAnalysis followBackendBallAnalysis;
|
||||
|
||||
/**
|
||||
* 精推大乐透第一步分析
|
||||
*/
|
||||
@PostMapping("/first-step")
|
||||
@Operation(summary = "精推大乐透第一步分析", description = "根据前区5个球号和后区2个球号进行第一步分析")
|
||||
public ApiResponse<DLTFirstStepResultVO> firstStepAnalysis(
|
||||
@Parameter(description = "策略级别(H-高位/M-中位/L-低位)", required = true)
|
||||
@RequestParam String level,
|
||||
@Parameter(description = "前区球号列表(逗号分隔,如:1,5,12,18,22)", required = true)
|
||||
@RequestParam String frontBalls,
|
||||
@Parameter(description = "后区球号列表(逗号分隔,如:3,7)", required = true)
|
||||
@RequestParam String backBalls) {
|
||||
|
||||
try {
|
||||
// 参数验证
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "策略级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
// 解析前区球号
|
||||
String[] frontBallArray = frontBalls.split(",");
|
||||
if (frontBallArray.length != 5) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "前区球号必须为5个");
|
||||
}
|
||||
|
||||
List<Integer> frontBallList = java.util.Arrays.stream(frontBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 35) {
|
||||
throw new IllegalArgumentException("前区球号范围应为1-35");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("前区球号格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
// 解析后区球号
|
||||
String[] backBallArray = backBalls.split(",");
|
||||
if (backBallArray.length != 2) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "后区球号必须为2个");
|
||||
}
|
||||
|
||||
List<Integer> backBallList = java.util.Arrays.stream(backBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 12) {
|
||||
throw new IllegalArgumentException("后区球号范围应为1-12");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("后区球号格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
log.info("精推大乐透第一步分析请求:策略={}, 前区={}, 后区={}", level, frontBallList, backBallList);
|
||||
|
||||
// 调用分析算法
|
||||
DLTFirstStepResultVO result = firstFrontBallAnalysis.analyze(level, frontBallList, backBallList);
|
||||
|
||||
log.info("精推大乐透第一步分析完成,返回{}个结果", result.getResults().size());
|
||||
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("精推大乐透第一步分析失败", e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 精推大乐透第二步分析
|
||||
*/
|
||||
@PostMapping("/second-step")
|
||||
@Operation(summary = "精推大乐透第二步分析", description = "根据上期前区5个球号、上期后区2个球号和本期首球进行第二步分析")
|
||||
public ApiResponse<DLTSecondStepResultVO> secondStepAnalysis(
|
||||
@Parameter(description = "策略级别(H-高位/M-中位/L-低位)", required = true)
|
||||
@RequestParam String level,
|
||||
@Parameter(description = "上期前区球号列表(逗号分隔,如:1,5,12,18,22)", required = true)
|
||||
@RequestParam String previousFrontBalls,
|
||||
@Parameter(description = "上期后区球号列表(逗号分隔,如:3,7)", required = true)
|
||||
@RequestParam String previousBackBalls,
|
||||
@Parameter(description = "本期首球号码", required = true)
|
||||
@RequestParam Integer currentFirstBall) {
|
||||
|
||||
try {
|
||||
// 参数验证
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "策略级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
if (currentFirstBall == null || currentFirstBall < 1 || currentFirstBall > 35) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "本期首球号码范围应为1-35");
|
||||
}
|
||||
|
||||
// 解析上期前区球号
|
||||
String[] previousFrontBallArray = previousFrontBalls.split(",");
|
||||
if (previousFrontBallArray.length != 5) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "上期前区球号必须为5个");
|
||||
}
|
||||
|
||||
List<Integer> previousFrontBallList = java.util.Arrays.stream(previousFrontBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 35) {
|
||||
throw new IllegalArgumentException("上期前区球号范围应为1-35");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("上期前区球号格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
// 解析上期后区球号
|
||||
String[] previousBackBallArray = previousBackBalls.split(",");
|
||||
if (previousBackBallArray.length != 2) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "上期后区球号必须为2个");
|
||||
}
|
||||
|
||||
List<Integer> previousBackBallList = java.util.Arrays.stream(previousBackBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 12) {
|
||||
throw new IllegalArgumentException("上期后区球号范围应为1-12");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("上期后区球号格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
log.info("精推大乐透第二步分析请求:策略={}, 上期前区={}, 上期后区={}, 本期首球={}",
|
||||
level, previousFrontBallList, previousBackBallList, currentFirstBall);
|
||||
|
||||
// 调用分析算法
|
||||
DLTSecondStepResultVO result = followFrontBallAnalysis.analyze(level, previousFrontBallList,
|
||||
previousBackBallList, currentFirstBall);
|
||||
|
||||
log.info("精推大乐透第二步分析完成,返回{}个结果", result.getResults().size());
|
||||
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("精推大乐透第二步分析失败", e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 精推大乐透第三步分析
|
||||
*/
|
||||
@PostMapping("/third-step")
|
||||
@Operation(summary = "精推大乐透第三步分析", description = "根据上期前区5个球号、上期后区2个球号和本期前区5个球号进行第三步分析")
|
||||
public ApiResponse<DLTThirdStepResultVO> thirdStepAnalysis(
|
||||
@Parameter(description = "策略级别(H-高位/M-中位/L-低位)", required = true)
|
||||
@RequestParam String level,
|
||||
@Parameter(description = "上期前区球号列表(逗号分隔,如:1,5,12,18,22)", required = true)
|
||||
@RequestParam String previousFrontBalls,
|
||||
@Parameter(description = "上期后区球号列表(逗号分隔,如:3,7)", required = true)
|
||||
@RequestParam String previousBackBalls,
|
||||
@Parameter(description = "本期前区球号列表(逗号分隔,如:2,9,16,23,29)", required = true)
|
||||
@RequestParam String currentFrontBalls) {
|
||||
|
||||
try {
|
||||
// 参数验证
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "策略级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
// 解析上期前区球号
|
||||
String[] previousFrontBallArray = previousFrontBalls.split(",");
|
||||
if (previousFrontBallArray.length != 5) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "上期前区球号必须为5个");
|
||||
}
|
||||
|
||||
List<Integer> previousFrontBallList = java.util.Arrays.stream(previousFrontBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 35) {
|
||||
throw new IllegalArgumentException("上期前区球号范围应为1-35");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("上期前区球号格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
// 解析上期后区球号
|
||||
String[] previousBackBallArray = previousBackBalls.split(",");
|
||||
if (previousBackBallArray.length != 2) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "上期后区球号必须为2个");
|
||||
}
|
||||
|
||||
List<Integer> previousBackBallList = java.util.Arrays.stream(previousBackBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 12) {
|
||||
throw new IllegalArgumentException("上期后区球号范围应为1-12");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("上期后区球号格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
// 解析本期前区球号
|
||||
String[] currentFrontBallArray = currentFrontBalls.split(",");
|
||||
if (currentFrontBallArray.length != 5) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "本期前区球号必须为5个");
|
||||
}
|
||||
|
||||
List<Integer> currentFrontBallList = java.util.Arrays.stream(currentFrontBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 35) {
|
||||
throw new IllegalArgumentException("本期前区球号范围应为1-35");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("本期前区球号格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
log.info("精推大乐透第三步分析请求:策略={}, 上期前区={}, 上期后区={}, 本期前区={}",
|
||||
level, previousFrontBallList, previousBackBallList, currentFrontBallList);
|
||||
|
||||
// 调用分析算法
|
||||
DLTThirdStepResultVO result = backBallAnalysis.analyze(level, previousFrontBallList,
|
||||
previousBackBallList, currentFrontBallList);
|
||||
|
||||
log.info("精推大乐透第三步分析完成,返回{}个结果", result.getResults().size());
|
||||
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("精推大乐透第三步分析失败", e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 精推大乐透第四步分析
|
||||
*/
|
||||
@PostMapping("/fourth-step")
|
||||
@Operation(summary = "精推大乐透第四步分析", description = "根据上期前区5个球号、上期后区2个球号、本期前区5个球号和本期后区首球进行第四步分析")
|
||||
public ApiResponse<DLTFourthStepResultVO> fourthStepAnalysis(
|
||||
@Parameter(description = "策略级别(H-高位/M-中位/L-低位)", required = true)
|
||||
@RequestParam String level,
|
||||
@Parameter(description = "上期前区球号列表(逗号分隔,如:1,5,12,18,22)", required = true)
|
||||
@RequestParam String previousFrontBalls,
|
||||
@Parameter(description = "上期后区球号列表(逗号分隔,如:3,7)", required = true)
|
||||
@RequestParam String previousBackBalls,
|
||||
@Parameter(description = "本期前区球号列表(逗号分隔,如:2,9,16,23,29)", required = true)
|
||||
@RequestParam String currentFrontBalls,
|
||||
@Parameter(description = "本期后区首球号码", required = true)
|
||||
@RequestParam Integer currentBackFirstBall) {
|
||||
|
||||
try {
|
||||
// 参数验证
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "策略级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
if (currentBackFirstBall == null || currentBackFirstBall < 1 || currentBackFirstBall > 12) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "本期后区首球号码范围应为1-12");
|
||||
}
|
||||
|
||||
// 解析上期前区球号
|
||||
String[] previousFrontBallArray = previousFrontBalls.split(",");
|
||||
if (previousFrontBallArray.length != 5) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "上期前区球号必须为5个");
|
||||
}
|
||||
|
||||
List<Integer> previousFrontBallList = java.util.Arrays.stream(previousFrontBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 35) {
|
||||
throw new IllegalArgumentException("上期前区球号范围应为1-35");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("上期前区球号格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
// 解析上期后区球号
|
||||
String[] previousBackBallArray = previousBackBalls.split(",");
|
||||
if (previousBackBallArray.length != 2) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "上期后区球号必须为2个");
|
||||
}
|
||||
|
||||
List<Integer> previousBackBallList = java.util.Arrays.stream(previousBackBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 12) {
|
||||
throw new IllegalArgumentException("上期后区球号范围应为1-12");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("上期后区球号格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
// 解析本期前区球号
|
||||
String[] currentFrontBallArray = currentFrontBalls.split(",");
|
||||
if (currentFrontBallArray.length != 5) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "本期前区球号必须为5个");
|
||||
}
|
||||
|
||||
List<Integer> currentFrontBallList = java.util.Arrays.stream(currentFrontBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 35) {
|
||||
throw new IllegalArgumentException("本期前区球号范围应为1-35");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("本期前区球号格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
log.info("精推大乐透第四步分析请求:策略={}, 上期前区={}, 上期后区={}, 本期前区={}, 本期后区首球={}",
|
||||
level, previousFrontBallList, previousBackBallList, currentFrontBallList, currentBackFirstBall);
|
||||
|
||||
// 调用分析算法
|
||||
DLTFourthStepResultVO result = followBackendBallAnalysis.analyze(level, previousFrontBallList,
|
||||
previousBackBallList, currentFrontBallList, currentBackFirstBall);
|
||||
|
||||
log.info("精推大乐透第四步分析完成,返回{}个结果", result.getResults().size());
|
||||
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("精推大乐透第四步分析失败", e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
239
src/main/java/com/xy/xyaicpzs/controller/JtSsqCotroller.java
Normal file
239
src/main/java/com/xy/xyaicpzs/controller/JtSsqCotroller.java
Normal file
@@ -0,0 +1,239 @@
|
||||
package com.xy.xyaicpzs.controller;
|
||||
|
||||
import com.xy.xyaicpzs.common.ErrorCode;
|
||||
import com.xy.xyaicpzs.common.ResultUtils;
|
||||
import com.xy.xyaicpzs.common.response.ApiResponse;
|
||||
import com.xy.xyaicpzs.domain.vo.SSQFirstStepResultVO;
|
||||
import com.xy.xyaicpzs.domain.vo.SSQSecondStepResultVO;
|
||||
import com.xy.xyaicpzs.domain.vo.SSQThirdStepResultVO;
|
||||
import com.xy.xyaicpzs.jt.jtssq.FirstBallAnalysis;
|
||||
import com.xy.xyaicpzs.jt.jtssq.FollowBallAnalysis;
|
||||
import com.xy.xyaicpzs.jt.jtssq.BlueBallAnalysis;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/jtssq/analysis")
|
||||
@Tag(name = "精推双色球分析", description = "精推双色球三步分析算法API")
|
||||
public class JtSsqCotroller {
|
||||
|
||||
@Autowired
|
||||
private FirstBallAnalysis firstBallAnalysis;
|
||||
|
||||
@Autowired
|
||||
private FollowBallAnalysis followBallAnalysis;
|
||||
|
||||
@Autowired
|
||||
private BlueBallAnalysis blueBallAnalysis;
|
||||
|
||||
/**
|
||||
* 精推双色球第一步分析
|
||||
*/
|
||||
@PostMapping("/first-step")
|
||||
@Operation(summary = "精推双色球第一步分析", description = "根据红球和蓝球号码进行第一步分析")
|
||||
public ApiResponse<SSQFirstStepResultVO> firstStepAnalysis(
|
||||
@Parameter(description = "策略级别(H-高位/M-中位/L-低位)", required = true)
|
||||
@RequestParam String level,
|
||||
@Parameter(description = "红球号码列表(逗号分隔,如:1,5,12,18,22,30)", required = true)
|
||||
@RequestParam String redBalls,
|
||||
@Parameter(description = "蓝球号码", required = true)
|
||||
@RequestParam Integer blueBall) {
|
||||
|
||||
try {
|
||||
// 参数验证
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "策略级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
if (blueBall == null || blueBall < 1 || blueBall > 16) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "蓝球号码范围应为1-16");
|
||||
}
|
||||
|
||||
// 解析红球号码
|
||||
String[] redBallArray = redBalls.split(",");
|
||||
if (redBallArray.length != 6) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "红球号码必须为6个");
|
||||
}
|
||||
|
||||
List<Integer> redBallList = java.util.Arrays.stream(redBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 33) {
|
||||
throw new IllegalArgumentException("红球号码范围应为1-33");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("红球号码格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
log.info("精推双色球第一步分析请求:策略={}, 红球={}, 蓝球={}", level, redBallList, blueBall);
|
||||
|
||||
// 调用分析算法
|
||||
SSQFirstStepResultVO result = firstBallAnalysis.analyze(level, redBallList, blueBall);
|
||||
|
||||
log.info("精推双色球第一步分析完成,返回{}个结果", result.getResults().size());
|
||||
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("精推双色球第一步分析失败", e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 精推双色球第二步分析
|
||||
*/
|
||||
@PostMapping("/second-step")
|
||||
@Operation(summary = "精推双色球第二步分析", description = "根据红球、蓝球和下期首球号码进行第二步分析")
|
||||
public ApiResponse<SSQSecondStepResultVO> secondStepAnalysis(
|
||||
@Parameter(description = "策略级别(H-高位/M-中位/L-低位)", required = true)
|
||||
@RequestParam String level,
|
||||
@Parameter(description = "红球号码列表(逗号分隔,如:1,5,12,18,22,30)", required = true)
|
||||
@RequestParam String redBalls,
|
||||
@Parameter(description = "蓝球号码", required = true)
|
||||
@RequestParam Integer blueBall,
|
||||
@Parameter(description = "下期首球号码", required = true)
|
||||
@RequestParam Integer nextFirstBall) {
|
||||
|
||||
try {
|
||||
// 参数验证
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "策略级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
if (blueBall == null || blueBall < 1 || blueBall > 16) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "蓝球号码范围应为1-16");
|
||||
}
|
||||
|
||||
if (nextFirstBall == null || nextFirstBall < 1 || nextFirstBall > 33) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "下期首球号码范围应为1-33");
|
||||
}
|
||||
|
||||
// 解析红球号码
|
||||
String[] redBallArray = redBalls.split(",");
|
||||
if (redBallArray.length != 6) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "红球号码必须为6个");
|
||||
}
|
||||
|
||||
List<Integer> redBallList = java.util.Arrays.stream(redBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 33) {
|
||||
throw new IllegalArgumentException("红球号码范围应为1-33");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("红球号码格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
log.info("精推双色球第二步分析请求:策略={}, 红球={}, 蓝球={}, 下期首球={}",
|
||||
level, redBallList, blueBall, nextFirstBall);
|
||||
|
||||
// 调用分析算法
|
||||
SSQSecondStepResultVO result = followBallAnalysis.analyze(level, redBallList, blueBall, nextFirstBall);
|
||||
|
||||
log.info("精推双色球第二步分析完成,返回{}个结果", result.getResults().size());
|
||||
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("精推双色球第二步分析失败", e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 精推双色球第三步分析
|
||||
*/
|
||||
@PostMapping("/third-step")
|
||||
@Operation(summary = "精推双色球第三步分析", description = "根据红球、蓝球和下期红球号码进行第三步分析")
|
||||
public ApiResponse<SSQThirdStepResultVO> thirdStepAnalysis(
|
||||
@Parameter(description = "策略级别(H-高位/M-中位/L-低位)", required = true)
|
||||
@RequestParam String level,
|
||||
@Parameter(description = "红球号码列表(逗号分隔,如:1,5,12,18,22,30)", required = true)
|
||||
@RequestParam String redBalls,
|
||||
@Parameter(description = "蓝球号码", required = true)
|
||||
@RequestParam Integer blueBall,
|
||||
@Parameter(description = "下期红球号码列表(逗号分隔,如:2,9,16,23,29,32)", required = true)
|
||||
@RequestParam String nextRedBalls) {
|
||||
|
||||
try {
|
||||
// 参数验证
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "策略级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
if (blueBall == null || blueBall < 1 || blueBall > 16) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "蓝球号码范围应为1-16");
|
||||
}
|
||||
|
||||
// 解析红球号码
|
||||
String[] redBallArray = redBalls.split(",");
|
||||
if (redBallArray.length != 6) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "红球号码必须为6个");
|
||||
}
|
||||
|
||||
List<Integer> redBallList = java.util.Arrays.stream(redBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 33) {
|
||||
throw new IllegalArgumentException("红球号码范围应为1-33");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("红球号码格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
// 解析下期红球号码
|
||||
String[] nextRedBallArray = nextRedBalls.split(",");
|
||||
if (nextRedBallArray.length != 6) {
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "下期红球号码必须为6个");
|
||||
}
|
||||
|
||||
List<Integer> nextRedBallList = java.util.Arrays.stream(nextRedBallArray)
|
||||
.map(s -> {
|
||||
try {
|
||||
int ball = Integer.parseInt(s.trim());
|
||||
if (ball < 1 || ball > 33) {
|
||||
throw new IllegalArgumentException("下期红球号码范围应为1-33");
|
||||
}
|
||||
return ball;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("下期红球号码格式错误");
|
||||
}
|
||||
})
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
log.info("精推双色球第三步分析请求:策略={}, 红球={}, 蓝球={}, 下期红球={}",
|
||||
level, redBallList, blueBall, nextRedBallList);
|
||||
|
||||
// 调用分析算法
|
||||
SSQThirdStepResultVO result = blueBallAnalysis.analyze(level, redBallList, blueBall, nextRedBallList);
|
||||
|
||||
log.info("精推双色球第三步分析完成,返回{}个结果", result.getResults().size());
|
||||
|
||||
return ResultUtils.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("精推双色球第三步分析失败", e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
@@ -9,11 +9,12 @@ import com.xy.xyaicpzs.common.requset.VipCodeActivateRequest;
|
||||
import com.xy.xyaicpzs.common.response.ApiResponse;
|
||||
import com.xy.xyaicpzs.domain.dto.user.*;
|
||||
import com.xy.xyaicpzs.domain.entity.User;
|
||||
import com.xy.xyaicpzs.domain.vo.UserVO;
|
||||
import com.xy.xyaicpzs.domain.vo.*;
|
||||
import com.xy.xyaicpzs.exception.BusinessException;
|
||||
import com.xy.xyaicpzs.service.UserService;
|
||||
import com.xy.xyaicpzs.service.VipCodeService;
|
||||
import com.xy.xyaicpzs.service.SmsService;
|
||||
import com.xy.xyaicpzs.util.UserAuthValidator;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -23,6 +24,8 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -45,6 +48,9 @@ public class UserController {
|
||||
|
||||
@Resource
|
||||
private SmsService smsService;
|
||||
|
||||
@Resource
|
||||
private UserAuthValidator userAuthValidator;
|
||||
|
||||
// region 登录相关
|
||||
|
||||
@@ -117,6 +123,12 @@ public class UserController {
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "创建用户", description = "管理员创建用户")
|
||||
public ApiResponse<Long> addUser(@RequestBody UserAddRequest userAddRequest, HttpServletRequest request) {
|
||||
// 验证超级管理员权限
|
||||
ApiResponse<Long> authResult = userAuthValidator.validateSuperAdminAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
if (userAddRequest == null) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||
}
|
||||
@@ -169,6 +181,12 @@ public class UserController {
|
||||
@Operation(summary = "修改用户状态", description = "管理员修改用户状态(正常/封禁)")
|
||||
public ApiResponse<Boolean> updateUserStatus(@RequestBody UserStatusUpdateRequest userStatusUpdateRequest,
|
||||
HttpServletRequest request) {
|
||||
// 验证超级管理员权限
|
||||
ApiResponse<Boolean> authResult = userAuthValidator.validateSuperAdminAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
if (userStatusUpdateRequest == null || userStatusUpdateRequest.getId() == null
|
||||
|| userStatusUpdateRequest.getId() <= 0) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户ID不正确");
|
||||
@@ -182,12 +200,6 @@ public class UserController {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "状态值不正确,应为0(正常)或1(封禁)");
|
||||
}
|
||||
|
||||
// 确认操作人员是否为管理员
|
||||
User loginUser = userService.getLoginUser(request);
|
||||
if (!userService.isAdmin(loginUser)) {
|
||||
throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无管理员权限");
|
||||
}
|
||||
|
||||
// 检查目标用户是否存在
|
||||
User user = userService.getById(id);
|
||||
if (user == null) {
|
||||
@@ -214,6 +226,12 @@ public class UserController {
|
||||
@PostMapping("/delete")
|
||||
@Operation(summary = "删除用户", description = "管理员删除用户")
|
||||
public ApiResponse<Boolean> deleteUser(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
|
||||
// 验证超级管理员权限
|
||||
ApiResponse<Boolean> authResult = userAuthValidator.validateSuperAdminAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
if (deleteRequest == null || deleteRequest.getId() <= 0) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||
}
|
||||
@@ -231,10 +249,25 @@ public class UserController {
|
||||
@PostMapping("/update")
|
||||
@Operation(summary = "更新用户", description = "更新用户信息")
|
||||
public ApiResponse<Boolean> updateUser(@RequestBody UserUpdateRequest userUpdateRequest, HttpServletRequest request) {
|
||||
// 验证登录权限
|
||||
ApiResponse<Boolean> authResult = userAuthValidator.validateLoginAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
if (userUpdateRequest == null || userUpdateRequest.getId() == null) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||
}
|
||||
|
||||
// 获取当前登录用户
|
||||
User currentUser = userAuthValidator.getCurrentUser(request);
|
||||
Long targetUserId = userUpdateRequest.getId();
|
||||
|
||||
// 权限检查:用户只能修改自己的信息,管理员可以修改任何用户
|
||||
if (!currentUser.getId().equals(targetUserId) && !userAuthValidator.isAdmin(currentUser)) {
|
||||
throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限修改其他用户信息");
|
||||
}
|
||||
|
||||
// 参数校验
|
||||
String userPassword = userUpdateRequest.getUserPassword();
|
||||
String password = userUpdateRequest.getPassword();
|
||||
@@ -574,5 +607,181 @@ public class UserController {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前登录用户会员是否过期
|
||||
*
|
||||
* @param request HTTP请求
|
||||
* @return 是否过期,true-已过期,false-未过期
|
||||
*/
|
||||
@GetMapping("/check-vip-expire")
|
||||
@Operation(summary = "检查会员是否过期", description = "检查当前登录用户的会员时间是否过期")
|
||||
public ApiResponse<Boolean> checkVipExpire(HttpServletRequest request) {
|
||||
// 获取当前登录用户
|
||||
User user = userService.getLoginUser(request);
|
||||
|
||||
// // 检查是否为会员
|
||||
// if (user.getIsVip() == null || user.getIsVip() != 1) {
|
||||
// // 非会员直接返回已过期
|
||||
// return ResultUtils.success(true);
|
||||
// }
|
||||
|
||||
// 检查会员到期时间
|
||||
Date vipExpire = user.getVipExpire();
|
||||
if (vipExpire == null) {
|
||||
// 会员到期时间为空,视为已过期
|
||||
return ResultUtils.success(true);
|
||||
}
|
||||
|
||||
// 比较当前时间与到期时间
|
||||
Date currentTime = new Date();
|
||||
boolean isExpired = currentTime.after(vipExpire);
|
||||
|
||||
return ResultUtils.success(isExpired);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据时间段获取新增用户统计
|
||||
*
|
||||
* @param startDate 开始日期 (格式: yyyy-MM-dd)
|
||||
* @param endDate 结束日期 (格式: yyyy-MM-dd)
|
||||
* @param request HTTP请求
|
||||
* @return 新增用户统计数据
|
||||
*/
|
||||
@GetMapping("/statistics/new-users")
|
||||
@Operation(summary = "获取新增用户统计", description = "管理员根据时间段获取新增用户统计数据")
|
||||
public ApiResponse<UserStatisticsVO> getNewUsersStatistics(
|
||||
@RequestParam String startDate,
|
||||
@RequestParam String endDate,
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 验证管理员权限
|
||||
ApiResponse<UserStatisticsVO> authResult = userAuthValidator.validateAdminAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
if (StringUtils.isAnyBlank(startDate, endDate)) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "开始日期和结束日期不能为空");
|
||||
}
|
||||
|
||||
UserStatisticsVO result = userService.getNewUsersStatistics(startDate, endDate);
|
||||
return ResultUtils.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据时间段获取新增会员统计
|
||||
*
|
||||
* @param startDate 开始日期 (格式: yyyy-MM-dd)
|
||||
* @param endDate 结束日期 (格式: yyyy-MM-dd)
|
||||
* @param request HTTP请求
|
||||
* @return 新增会员统计数据
|
||||
*/
|
||||
@GetMapping("/statistics/new-vips")
|
||||
@Operation(summary = "获取新增会员统计", description = "管理员根据时间段获取新增会员统计数据")
|
||||
public ApiResponse<VipStatisticsVO> getNewVipsStatistics(
|
||||
@RequestParam String startDate,
|
||||
@RequestParam String endDate,
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 验证管理员权限
|
||||
ApiResponse<VipStatisticsVO> authResult = userAuthValidator.validateAdminAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
if (StringUtils.isAnyBlank(startDate, endDate)) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "开始日期和结束日期不能为空");
|
||||
}
|
||||
|
||||
VipStatisticsVO result = userService.getNewVipsStatistics(startDate, endDate);
|
||||
return ResultUtils.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据时间段获取用户注册趋势
|
||||
*
|
||||
* @param startDate 开始日期 (格式: yyyy-MM-dd)
|
||||
* @param endDate 结束日期 (格式: yyyy-MM-dd)
|
||||
* @param granularity 时间粒度 (day/week/month)
|
||||
* @param request HTTP请求
|
||||
* @return 用户注册趋势数据
|
||||
*/
|
||||
@GetMapping("/statistics/registration-trend")
|
||||
@Operation(summary = "获取用户注册趋势", description = "管理员根据时间段和粒度获取用户注册趋势")
|
||||
public ApiResponse<RegistrationTrendVO> getRegistrationTrend(
|
||||
@RequestParam String startDate,
|
||||
@RequestParam String endDate,
|
||||
@RequestParam(defaultValue = "day") String granularity,
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 验证管理员权限
|
||||
ApiResponse<RegistrationTrendVO> authResult = userAuthValidator.validateAdminAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
if (StringUtils.isAnyBlank(startDate, endDate)) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "开始日期和结束日期不能为空");
|
||||
}
|
||||
|
||||
if (!Arrays.asList("day", "week", "month").contains(granularity)) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "时间粒度只能是day、week或month");
|
||||
}
|
||||
|
||||
RegistrationTrendVO result = userService.getRegistrationTrend(startDate, endDate, granularity);
|
||||
return ResultUtils.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取即将到期的会员列表
|
||||
*
|
||||
* @param days 提前多少天提醒 (默认7天)
|
||||
* @param current 当前页
|
||||
* @param pageSize 页大小
|
||||
* @param request HTTP请求
|
||||
* @return 即将到期的会员列表
|
||||
*/
|
||||
@GetMapping("/statistics/expiring-vips")
|
||||
@Operation(summary = "获取即将到期的会员", description = "管理员获取即将到期的会员列表")
|
||||
public ApiResponse<Page<UserVO>> getExpiringVips(
|
||||
@RequestParam(defaultValue = "7") Integer days,
|
||||
@RequestParam(defaultValue = "1") Long current,
|
||||
@RequestParam(defaultValue = "10") Long pageSize,
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 验证管理员权限
|
||||
ApiResponse<Page<UserVO>> authResult = userAuthValidator.validateAdminAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
if (days <= 0) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "提前天数必须大于0");
|
||||
}
|
||||
|
||||
Page<UserVO> result = userService.getExpiringVips(days, current, pageSize);
|
||||
return ResultUtils.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员状态分布统计
|
||||
*
|
||||
* @param request HTTP请求
|
||||
* @return 会员状态分布数据
|
||||
*/
|
||||
@GetMapping("/statistics/vip-distribution")
|
||||
@Operation(summary = "获取会员状态分布", description = "管理员获取会员状态分布统计")
|
||||
public ApiResponse<VipDistributionVO> getVipDistribution(HttpServletRequest request) {
|
||||
|
||||
// 验证管理员权限
|
||||
ApiResponse<VipDistributionVO> authResult = userAuthValidator.validateAdminAuth(request);
|
||||
if (authResult != null) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
VipDistributionVO result = userService.getVipDistribution();
|
||||
return ResultUtils.success(result);
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
1247
src/main/java/com/xy/xyaicpzs/dlt/BackBallPredictor.java
Normal file
1247
src/main/java/com/xy/xyaicpzs/dlt/BackBallPredictor.java
Normal file
File diff suppressed because it is too large
Load Diff
1355
src/main/java/com/xy/xyaicpzs/dlt/FirstBallPredictor.java
Normal file
1355
src/main/java/com/xy/xyaicpzs/dlt/FirstBallPredictor.java
Normal file
File diff suppressed because it is too large
Load Diff
1289
src/main/java/com/xy/xyaicpzs/dlt/FollowBackBallPredictor.java
Normal file
1289
src/main/java/com/xy/xyaicpzs/dlt/FollowBackBallPredictor.java
Normal file
File diff suppressed because it is too large
Load Diff
1258
src/main/java/com/xy/xyaicpzs/dlt/FollowerBallPredictor.java
Normal file
1258
src/main/java/com/xy/xyaicpzs/dlt/FollowerBallPredictor.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,7 @@
|
||||
package com.xy.xyaicpzs.domain.dto.user;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import com.xy.xyaicpzs.common.PageRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/D10.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/D10.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* d10表
|
||||
* @TableName d10
|
||||
*/
|
||||
@TableName(value ="d10")
|
||||
@Data
|
||||
public class D10 {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 主球
|
||||
*/
|
||||
private Integer masterBallNumber;
|
||||
|
||||
/**
|
||||
* 从球
|
||||
*/
|
||||
private Integer slaveBallNumber;
|
||||
|
||||
/**
|
||||
* 系数
|
||||
*/
|
||||
private Double coefficient;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/D11.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/D11.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* d11表
|
||||
* @TableName d11
|
||||
*/
|
||||
@TableName(value ="d11")
|
||||
@Data
|
||||
public class D11 {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 主球
|
||||
*/
|
||||
private Integer masterBallNumber;
|
||||
|
||||
/**
|
||||
* 从球
|
||||
*/
|
||||
private Integer slaveBallNumber;
|
||||
|
||||
/**
|
||||
* 系数
|
||||
*/
|
||||
private Double coefficient;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/D12.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/D12.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* d12表
|
||||
* @TableName d12
|
||||
*/
|
||||
@TableName(value ="d12")
|
||||
@Data
|
||||
public class D12 {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 主球
|
||||
*/
|
||||
private Integer masterBallNumber;
|
||||
|
||||
/**
|
||||
* 从球
|
||||
*/
|
||||
private Integer slaveBallNumber;
|
||||
|
||||
/**
|
||||
* 系数
|
||||
*/
|
||||
private Double coefficient;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/D5.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/D5.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* d5表
|
||||
* @TableName d5
|
||||
*/
|
||||
@TableName(value ="d5")
|
||||
@Data
|
||||
public class D5 {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 主球
|
||||
*/
|
||||
private Integer masterBallNumber;
|
||||
|
||||
/**
|
||||
* 从球
|
||||
*/
|
||||
private Integer slaveBallNumber;
|
||||
|
||||
/**
|
||||
* 系数
|
||||
*/
|
||||
private Double coefficient;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/D6.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/D6.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* d6表
|
||||
* @TableName d6
|
||||
*/
|
||||
@TableName(value ="d6")
|
||||
@Data
|
||||
public class D6 {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 主球
|
||||
*/
|
||||
private Integer masterBallNumber;
|
||||
|
||||
/**
|
||||
* 从球
|
||||
*/
|
||||
private Integer slaveBallNumber;
|
||||
|
||||
/**
|
||||
* 系数
|
||||
*/
|
||||
private Double coefficient;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/D7.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/D7.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* d7表
|
||||
* @TableName d7
|
||||
*/
|
||||
@TableName(value ="d7")
|
||||
@Data
|
||||
public class D7 {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 主球
|
||||
*/
|
||||
private Integer masterBallNumber;
|
||||
|
||||
/**
|
||||
* 从球
|
||||
*/
|
||||
private Integer slaveBallNumber;
|
||||
|
||||
/**
|
||||
* 系数
|
||||
*/
|
||||
private Double coefficient;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/D8.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/D8.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* d8表
|
||||
* @TableName d8
|
||||
*/
|
||||
@TableName(value ="d8")
|
||||
@Data
|
||||
public class D8 {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 主球
|
||||
*/
|
||||
private Integer masterBallNumber;
|
||||
|
||||
/**
|
||||
* 从球
|
||||
*/
|
||||
private Integer slaveBallNumber;
|
||||
|
||||
/**
|
||||
* 系数
|
||||
*/
|
||||
private Double coefficient;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/D9.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/D9.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* d9表
|
||||
* @TableName d9
|
||||
*/
|
||||
@TableName(value ="d9")
|
||||
@Data
|
||||
public class D9 {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 主球
|
||||
*/
|
||||
private Integer masterBallNumber;
|
||||
|
||||
/**
|
||||
* 从球
|
||||
*/
|
||||
private Integer slaveBallNumber;
|
||||
|
||||
/**
|
||||
* 系数
|
||||
*/
|
||||
private Double coefficient;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 大乐透后区最近100期数据表
|
||||
* @TableName dlt_backend_history_100
|
||||
*/
|
||||
@TableName(value ="dlt_backend_history_100")
|
||||
@Data
|
||||
public class DltBackendHistory100 {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 出现频次
|
||||
*/
|
||||
private Integer frequencyCount;
|
||||
|
||||
/**
|
||||
* 平均隐现期(次)
|
||||
*/
|
||||
private Double averageHiddenAppear;
|
||||
|
||||
/**
|
||||
* 当前隐现期
|
||||
*/
|
||||
private Integer currentHiddenInterval;
|
||||
|
||||
/**
|
||||
* 最多连出期(次)
|
||||
*/
|
||||
private Integer maxConsecutive;
|
||||
|
||||
/**
|
||||
* 活跃系数
|
||||
*/
|
||||
private Double activeCoefficient;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 大乐透后区全部历史数据表
|
||||
* @TableName dlt_backend_history_all
|
||||
*/
|
||||
@TableName(value ="dlt_backend_history_all")
|
||||
@Data
|
||||
public class DltBackendHistoryAll {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 出现频次
|
||||
*/
|
||||
private Integer frequencyCount;
|
||||
|
||||
/**
|
||||
* 出现频率%
|
||||
*/
|
||||
private Double frequencyPercentage;
|
||||
|
||||
/**
|
||||
* 平均隐现期(次)
|
||||
*/
|
||||
private Integer averageHiddenAppear;
|
||||
|
||||
/**
|
||||
* 最长隐现期(次)
|
||||
*/
|
||||
private Integer maxHiddenInterval;
|
||||
|
||||
/**
|
||||
* 最多连出期(次)
|
||||
*/
|
||||
private Integer maxConsecutive;
|
||||
|
||||
/**
|
||||
* 活跃系数
|
||||
*/
|
||||
private Double activeCoefficient;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 大乐透后区历史数据排行表
|
||||
* @TableName dlt_backend_history_top
|
||||
*/
|
||||
@TableName(value ="dlt_backend_history_top")
|
||||
@Data
|
||||
public class DltBackendHistoryTop {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 排位
|
||||
*/
|
||||
private Integer ranking;
|
||||
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 活跃系数
|
||||
*/
|
||||
private Double activeCoefficient;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 大乐透后区百期数据排行表
|
||||
* @TableName dlt_backend_history_top_100
|
||||
*/
|
||||
@TableName(value ="dlt_backend_history_top_100")
|
||||
@Data
|
||||
public class DltBackendHistoryTop100 {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 排位
|
||||
*/
|
||||
private Integer ranking;
|
||||
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 活跃系数
|
||||
*/
|
||||
private Double activeCoefficient;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import java.util.Date;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 大乐透开奖信息表
|
||||
* @TableName dlt_draw_record
|
||||
*/
|
||||
@TableName(value ="dlt_draw_record")
|
||||
@Data
|
||||
public class DltDrawRecord {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 开奖期号
|
||||
*/
|
||||
private String drawId;
|
||||
|
||||
/**
|
||||
* 开奖日期
|
||||
*/
|
||||
private Date drawDate;
|
||||
|
||||
/**
|
||||
* 前区1
|
||||
*/
|
||||
private Integer frontBall1;
|
||||
|
||||
/**
|
||||
* 前区2
|
||||
*/
|
||||
private Integer frontBall2;
|
||||
|
||||
/**
|
||||
* 前区3
|
||||
*/
|
||||
private Integer frontBall3;
|
||||
|
||||
/**
|
||||
* 前区4
|
||||
*/
|
||||
private Integer frontBall4;
|
||||
|
||||
/**
|
||||
* 前区5
|
||||
*/
|
||||
private Integer frontBall5;
|
||||
|
||||
/**
|
||||
* 后区1
|
||||
*/
|
||||
private Integer backBall1;
|
||||
|
||||
/**
|
||||
* 后区2
|
||||
*/
|
||||
private Integer backBall2;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 大乐透前区最近100期数据表
|
||||
* @TableName dlt_frontend_history_100
|
||||
*/
|
||||
@TableName(value ="dlt_frontend_history_100")
|
||||
@Data
|
||||
public class DltFrontendHistory100 {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 出现频次
|
||||
*/
|
||||
private Integer frequencyCount;
|
||||
|
||||
/**
|
||||
* 平均隐现期(次)
|
||||
*/
|
||||
private Double averageHiddenAppear;
|
||||
|
||||
/**
|
||||
* 当前隐现期
|
||||
*/
|
||||
private Integer currentHiddenInterval;
|
||||
|
||||
/**
|
||||
* 最多连出期(次)
|
||||
*/
|
||||
private Integer maxConsecutive;
|
||||
|
||||
/**
|
||||
* 活跃系数
|
||||
*/
|
||||
private Double activeCoefficient;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 大乐透前区全部历史数据表
|
||||
* @TableName dlt_frontend_history_all
|
||||
*/
|
||||
@TableName(value ="dlt_frontend_history_all")
|
||||
@Data
|
||||
public class DltFrontendHistoryAll {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 出现频次
|
||||
*/
|
||||
private Integer frequencyCount;
|
||||
|
||||
/**
|
||||
* 出现频率%
|
||||
*/
|
||||
private Double frequencyPercentage;
|
||||
|
||||
/**
|
||||
* 平均隐现期(次)
|
||||
*/
|
||||
private Integer averageHiddenAppear;
|
||||
|
||||
/**
|
||||
* 最长隐现期(次)
|
||||
*/
|
||||
private Integer maxHiddenInterval;
|
||||
|
||||
/**
|
||||
* 最多连出期(次)
|
||||
*/
|
||||
private Integer maxConsecutive;
|
||||
|
||||
/**
|
||||
* 活跃系数
|
||||
*/
|
||||
private Double activeCoefficient;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 大乐透前区历史数据排行表
|
||||
* @TableName dlt_frontend_history_top
|
||||
*/
|
||||
@TableName(value ="dlt_frontend_history_top")
|
||||
@Data
|
||||
public class DltFrontendHistoryTop {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 排位
|
||||
*/
|
||||
private Integer ranking;
|
||||
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 活跃系数
|
||||
*/
|
||||
private Double activeCoefficient;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 大乐透前区百期数据排行表
|
||||
* @TableName dlt_frontend_history_top_100
|
||||
*/
|
||||
@TableName(value ="dlt_frontend_history_top_100")
|
||||
@Data
|
||||
public class DltFrontendHistoryTop100 {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 排位
|
||||
*/
|
||||
private Integer ranking;
|
||||
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 活跃系数
|
||||
*/
|
||||
private Double activeCoefficient;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import java.util.Date;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 大乐透推测记录表
|
||||
* @TableName dlt_predict_record
|
||||
*/
|
||||
@TableName(value ="dlt_predict_record")
|
||||
@Data
|
||||
public class DltPredictRecord {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 开奖期号
|
||||
*/
|
||||
private Long drawId;
|
||||
|
||||
/**
|
||||
* 开奖日期
|
||||
*/
|
||||
private Date drawDate;
|
||||
|
||||
/**
|
||||
* 前区1
|
||||
*/
|
||||
private Integer frontendBall1;
|
||||
|
||||
/**
|
||||
* 前区2
|
||||
*/
|
||||
private Integer frontendBall2;
|
||||
|
||||
/**
|
||||
* 前区3
|
||||
*/
|
||||
private Integer frontendBall3;
|
||||
|
||||
/**
|
||||
* 前区4
|
||||
*/
|
||||
private Integer frontendBall4;
|
||||
|
||||
/**
|
||||
* 前区5
|
||||
*/
|
||||
private Integer frontendBall5;
|
||||
|
||||
|
||||
/**
|
||||
* 后区1
|
||||
*/
|
||||
private Integer backendBall1;
|
||||
|
||||
/**
|
||||
* 后区2
|
||||
*/
|
||||
private Integer backendBall2;
|
||||
|
||||
/**
|
||||
* 预测状态(待开奖/已开奖)
|
||||
*/
|
||||
private String predictStatus;
|
||||
|
||||
/**
|
||||
* 预测结果(未中奖/三等奖/二等奖/一等奖)
|
||||
*/
|
||||
private String predictResult;
|
||||
|
||||
/**
|
||||
* 预测时间
|
||||
*/
|
||||
private Date predictTime;
|
||||
|
||||
/**
|
||||
* 奖金
|
||||
*/
|
||||
private Long bonus;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class BackBallPredictionResultVO {
|
||||
private List<Integer> result;
|
||||
private String filteringProcess;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 球号分析结果VO(首球算法)
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class BallAnalysisResultVO {
|
||||
|
||||
/**
|
||||
* 分析结果:前11个球号
|
||||
*/
|
||||
private List<Integer> result;
|
||||
|
||||
/**
|
||||
* 筛选过程说明
|
||||
*/
|
||||
private String filteringProcess;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 蓝球分析结果VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class BlueBallAnalysisResultVO {
|
||||
|
||||
/**
|
||||
* 分析结果:前4个蓝球号码
|
||||
*/
|
||||
private List<Integer> result;
|
||||
|
||||
/**
|
||||
* 筛选过程说明
|
||||
*/
|
||||
private String filteringProcess;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 精推大乐透第一步分析结果VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class DLTFirstStepResultVO {
|
||||
|
||||
/**
|
||||
* 分析结果列表
|
||||
*/
|
||||
private List<BallAnalysisResult> results;
|
||||
|
||||
/**
|
||||
* 策略级别(H/M/L)
|
||||
*/
|
||||
private String strategy;
|
||||
|
||||
/**
|
||||
* 前区号码(5个)
|
||||
*/
|
||||
private List<Integer> frontBalls;
|
||||
|
||||
/**
|
||||
* 后区号码(2个)
|
||||
*/
|
||||
private List<Integer> backBalls;
|
||||
|
||||
/**
|
||||
* 球号分析结果
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public static class BallAnalysisResult {
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 出现频次
|
||||
*/
|
||||
private Integer frequency;
|
||||
|
||||
/**
|
||||
* 系数和
|
||||
*/
|
||||
private Double coefficientSum;
|
||||
|
||||
/**
|
||||
* 百期排位(dlt_frontend_history_top_100表排位)
|
||||
*/
|
||||
private Integer top100Ranking;
|
||||
|
||||
/**
|
||||
* 历史排位(dlt_frontend_history_top表排位)
|
||||
*/
|
||||
private Integer historyRanking;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 精推大乐透第四步分析结果VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class DLTFourthStepResultVO {
|
||||
|
||||
/**
|
||||
* 分析结果列表
|
||||
*/
|
||||
private List<BallAnalysisResult> results;
|
||||
|
||||
/**
|
||||
* 策略级别(H/M/L)
|
||||
*/
|
||||
private String strategy;
|
||||
|
||||
/**
|
||||
* 上期前区号码(5个)
|
||||
*/
|
||||
private List<Integer> previousFrontBalls;
|
||||
|
||||
/**
|
||||
* 上期后区号码(2个)
|
||||
*/
|
||||
private List<Integer> previousBackBalls;
|
||||
|
||||
/**
|
||||
* 本期前区号码(5个)
|
||||
*/
|
||||
private List<Integer> currentFrontBalls;
|
||||
|
||||
/**
|
||||
* 本期后区首球号码
|
||||
*/
|
||||
private Integer currentBackFirstBall;
|
||||
|
||||
/**
|
||||
* 球号分析结果
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public static class BallAnalysisResult {
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 出现频次
|
||||
*/
|
||||
private Integer frequency;
|
||||
|
||||
/**
|
||||
* 系数和
|
||||
*/
|
||||
private Double coefficientSum;
|
||||
|
||||
/**
|
||||
* 百期排位(dlt_backend_history_top_100表排位)
|
||||
*/
|
||||
private Integer top100Ranking;
|
||||
|
||||
/**
|
||||
* 历史排位(dlt_backend_history_top表排位)
|
||||
*/
|
||||
private Integer historyRanking;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 精推大乐透第二步分析结果VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class DLTSecondStepResultVO {
|
||||
|
||||
/**
|
||||
* 分析结果列表
|
||||
*/
|
||||
private List<BallAnalysisResult> results;
|
||||
|
||||
/**
|
||||
* 策略级别(H/M/L)
|
||||
*/
|
||||
private String strategy;
|
||||
|
||||
/**
|
||||
* 上期前区号码(5个)
|
||||
*/
|
||||
private List<Integer> previousFrontBalls;
|
||||
|
||||
/**
|
||||
* 上期后区号码(2个)
|
||||
*/
|
||||
private List<Integer> previousBackBalls;
|
||||
|
||||
/**
|
||||
* 本期首球号码
|
||||
*/
|
||||
private Integer currentFirstBall;
|
||||
|
||||
/**
|
||||
* 球号分析结果
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public static class BallAnalysisResult {
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 出现频次
|
||||
*/
|
||||
private Integer frequency;
|
||||
|
||||
/**
|
||||
* 系数和
|
||||
*/
|
||||
private Double coefficientSum;
|
||||
|
||||
/**
|
||||
* 百期排位(dlt_frontend_history_top_100表排位)
|
||||
*/
|
||||
private Integer top100Ranking;
|
||||
|
||||
/**
|
||||
* 历史排位(dlt_frontend_history_top表排位)
|
||||
*/
|
||||
private Integer historyRanking;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 精推大乐透第三步分析结果VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class DLTThirdStepResultVO {
|
||||
|
||||
/**
|
||||
* 分析结果列表
|
||||
*/
|
||||
private List<BallAnalysisResult> results;
|
||||
|
||||
/**
|
||||
* 策略级别(H/M/L)
|
||||
*/
|
||||
private String strategy;
|
||||
|
||||
/**
|
||||
* 上期前区号码(5个)
|
||||
*/
|
||||
private List<Integer> previousFrontBalls;
|
||||
|
||||
/**
|
||||
* 上期后区号码(2个)
|
||||
*/
|
||||
private List<Integer> previousBackBalls;
|
||||
|
||||
/**
|
||||
* 本期前区号码(5个)
|
||||
*/
|
||||
private List<Integer> currentFrontBalls;
|
||||
|
||||
/**
|
||||
* 球号分析结果
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public static class BallAnalysisResult {
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 出现频次
|
||||
*/
|
||||
private Integer frequency;
|
||||
|
||||
/**
|
||||
* 系数和
|
||||
*/
|
||||
private Double coefficientSum;
|
||||
|
||||
/**
|
||||
* 百期排位(dlt_backend_history_top_100表排位)
|
||||
*/
|
||||
private Integer top100Ranking;
|
||||
|
||||
/**
|
||||
* 历史排位(dlt_backend_history_top表排位)
|
||||
*/
|
||||
private Integer historyRanking;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 前区首球预测结果VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class FirstBallPredictionResultVO {
|
||||
|
||||
/**
|
||||
* 预测结果:前12个球号
|
||||
*/
|
||||
private List<Integer> result;
|
||||
|
||||
/**
|
||||
* 筛选过程说明
|
||||
*/
|
||||
private String filteringProcess;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class FollowBackBallPredictionResultVO {
|
||||
private List<Integer> result;
|
||||
private String filteringProcess;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 跟随球号分析结果VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class FollowBallAnalysisResultVO {
|
||||
|
||||
/**
|
||||
* 分析结果:前8位数字
|
||||
*/
|
||||
private List<Integer> result;
|
||||
|
||||
/**
|
||||
* 筛选过程说明
|
||||
*/
|
||||
private String filteringProcess;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class FollowerBallPredictionResultVO {
|
||||
private List<Integer> result;
|
||||
private String filteringProcess;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 用户注册趋势VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class RegistrationTrendVO {
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
private String startDate;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
private String endDate;
|
||||
|
||||
/**
|
||||
* 时间粒度
|
||||
*/
|
||||
private String granularity;
|
||||
|
||||
/**
|
||||
* 用户注册趋势数据 (时间 -> 用户数)
|
||||
*/
|
||||
private Map<String, Long> userTrend;
|
||||
|
||||
/**
|
||||
* 会员注册趋势数据 (时间 -> 会员数)
|
||||
*/
|
||||
private Map<String, Long> vipTrend;
|
||||
|
||||
/**
|
||||
* 总用户数
|
||||
*/
|
||||
private Integer totalUsers;
|
||||
|
||||
/**
|
||||
* 总会员数
|
||||
*/
|
||||
private Long totalVips;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 双色球第一步分析结果VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class SSQFirstStepResultVO {
|
||||
/**
|
||||
* 分析结果列表
|
||||
*/
|
||||
private List<BallAnalysisResult> results;
|
||||
|
||||
/**
|
||||
* 分析策略 (H/M/L)
|
||||
*/
|
||||
private String strategy;
|
||||
|
||||
/**
|
||||
* 输入的红球号码
|
||||
*/
|
||||
private List<Integer> redBalls;
|
||||
|
||||
/**
|
||||
* 输入的蓝球号码
|
||||
*/
|
||||
private Integer blueBall;
|
||||
|
||||
/**
|
||||
* 球号分析结果内部类
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public static class BallAnalysisResult {
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 出现次数
|
||||
*/
|
||||
private Integer frequency;
|
||||
|
||||
/**
|
||||
* 系数和
|
||||
*/
|
||||
private Double coefficientSum;
|
||||
|
||||
/**
|
||||
* 百期排位
|
||||
*/
|
||||
private Integer top100Ranking;
|
||||
|
||||
/**
|
||||
* 历史排位
|
||||
*/
|
||||
private Integer historyRanking;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 双色球第二步分析结果VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class SSQSecondStepResultVO {
|
||||
/**
|
||||
* 分析结果列表
|
||||
*/
|
||||
private List<BallAnalysisResult> results;
|
||||
|
||||
/**
|
||||
* 分析策略 (H/M/L)
|
||||
*/
|
||||
private String strategy;
|
||||
|
||||
/**
|
||||
* 输入的红球号码
|
||||
*/
|
||||
private List<Integer> redBalls;
|
||||
|
||||
/**
|
||||
* 输入的蓝球号码
|
||||
*/
|
||||
private Integer blueBall;
|
||||
|
||||
/**
|
||||
* 下期首球号码
|
||||
*/
|
||||
private Integer nextFirstBall;
|
||||
|
||||
/**
|
||||
* 球号分析结果内部类
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public static class BallAnalysisResult {
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 出现次数
|
||||
*/
|
||||
private Integer frequency;
|
||||
|
||||
/**
|
||||
* 系数和
|
||||
*/
|
||||
private Double coefficientSum;
|
||||
|
||||
/**
|
||||
* 百期排位
|
||||
*/
|
||||
private Integer top100Ranking;
|
||||
|
||||
/**
|
||||
* 历史排位
|
||||
*/
|
||||
private Integer historyRanking;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 双色球第三步分析结果VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class SSQThirdStepResultVO {
|
||||
/**
|
||||
* 分析结果列表
|
||||
*/
|
||||
private List<BallAnalysisResult> results;
|
||||
|
||||
/**
|
||||
* 分析策略 (H/M/L)
|
||||
*/
|
||||
private String strategy;
|
||||
|
||||
/**
|
||||
* 输入的红球号码
|
||||
*/
|
||||
private List<Integer> redBalls;
|
||||
|
||||
/**
|
||||
* 输入的蓝球号码
|
||||
*/
|
||||
private Integer blueBall;
|
||||
|
||||
/**
|
||||
* 下期红球号码
|
||||
*/
|
||||
private List<Integer> nextRedBalls;
|
||||
|
||||
/**
|
||||
* 球号分析结果内部类
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public static class BallAnalysisResult {
|
||||
/**
|
||||
* 球号
|
||||
*/
|
||||
private Integer ballNumber;
|
||||
|
||||
/**
|
||||
* 出现次数
|
||||
*/
|
||||
private Integer frequency;
|
||||
|
||||
/**
|
||||
* 系数和
|
||||
*/
|
||||
private Double coefficientSum;
|
||||
|
||||
/**
|
||||
* 百期排位
|
||||
*/
|
||||
private Integer top100Ranking;
|
||||
|
||||
/**
|
||||
* 历史排位
|
||||
*/
|
||||
private Integer historyRanking;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 新增用户统计VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class UserStatisticsVO {
|
||||
/**
|
||||
* 新增用户总数
|
||||
*/
|
||||
private Long totalNewUsers;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
private String startDate;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
private String endDate;
|
||||
|
||||
/**
|
||||
* 最近的新增用户列表
|
||||
*/
|
||||
private List<UserVO> recentUsers;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 会员状态分布VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class VipDistributionVO {
|
||||
/**
|
||||
* 总用户数
|
||||
*/
|
||||
private Long totalUsers;
|
||||
|
||||
/**
|
||||
* 普通用户数
|
||||
*/
|
||||
private Long normalUsers;
|
||||
|
||||
/**
|
||||
* 普通用户百分比
|
||||
*/
|
||||
private Double normalPercentage;
|
||||
|
||||
/**
|
||||
* 有效会员数
|
||||
*/
|
||||
private Long activeVips;
|
||||
|
||||
/**
|
||||
* 有效会员百分比
|
||||
*/
|
||||
private Double activeVipPercentage;
|
||||
|
||||
/**
|
||||
* 过期会员数
|
||||
*/
|
||||
private Long expiredVips;
|
||||
|
||||
/**
|
||||
* 过期会员百分比
|
||||
*/
|
||||
private Double expiredVipPercentage;
|
||||
|
||||
/**
|
||||
* 即将到期会员数(7天内)
|
||||
*/
|
||||
private Long soonExpireVips;
|
||||
|
||||
/**
|
||||
* 统计时间
|
||||
*/
|
||||
private Date statisticsTime;
|
||||
}
|
||||
|
||||
44
src/main/java/com/xy/xyaicpzs/domain/vo/VipStatisticsVO.java
Normal file
44
src/main/java/com/xy/xyaicpzs/domain/vo/VipStatisticsVO.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package com.xy.xyaicpzs.domain.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 新增会员统计VO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class VipStatisticsVO {
|
||||
/**
|
||||
* 新增会员总数
|
||||
*/
|
||||
private Long totalNewVips;
|
||||
|
||||
/**
|
||||
* 新增用户总数
|
||||
*/
|
||||
private Long totalNewUsers;
|
||||
|
||||
/**
|
||||
* 会员转化率(百分比)
|
||||
*/
|
||||
private Double conversionRate;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
private String startDate;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
private String endDate;
|
||||
|
||||
/**
|
||||
* 最近的新增会员列表
|
||||
*/
|
||||
private List<UserVO> recentVips;
|
||||
}
|
||||
|
||||
788
src/main/java/com/xy/xyaicpzs/jt/jtdlt/BackBallAnalysis.java
Normal file
788
src/main/java/com/xy/xyaicpzs/jt/jtdlt/BackBallAnalysis.java
Normal file
@@ -0,0 +1,788 @@
|
||||
package com.xy.xyaicpzs.jt.jtdlt;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.xy.xyaicpzs.domain.entity.*;
|
||||
import com.xy.xyaicpzs.domain.vo.DLTThirdStepResultVO;
|
||||
import com.xy.xyaicpzs.mapper.*;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 精推大乐透第三步算法分析
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class BackBallAnalysis {
|
||||
|
||||
@Autowired
|
||||
private D10Mapper d10Mapper;
|
||||
|
||||
@Autowired
|
||||
private D11Mapper d11Mapper;
|
||||
|
||||
@Autowired
|
||||
private D6Mapper d6Mapper;
|
||||
|
||||
@Autowired
|
||||
private DltBackendHistoryTopMapper dltBackendHistoryTopMapper;
|
||||
|
||||
@Autowired
|
||||
private DltBackendHistoryTop100Mapper dltBackendHistoryTop100Mapper;
|
||||
|
||||
/**
|
||||
* 球号和系数的关联类
|
||||
*/
|
||||
@Data
|
||||
public static class BallWithCoefficient {
|
||||
private Integer ballNumber;
|
||||
private Double coefficient;
|
||||
private Integer masterBallNumber; // 主球号,用于标识来源
|
||||
|
||||
public BallWithCoefficient(Integer ballNumber, Double coefficient, Integer masterBallNumber) {
|
||||
this.ballNumber = ballNumber;
|
||||
this.coefficient = coefficient;
|
||||
this.masterBallNumber = masterBallNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 精推大乐透第三步算法主方法
|
||||
* @param level 位置级别(H-高位/M-中位/L-低位)
|
||||
* @param previousFrontBalls 上期前区5个球号
|
||||
* @param previousBackBalls 上期后区2个球号
|
||||
* @param currentFrontBalls 本期前区5个球号
|
||||
* @return 分析结果
|
||||
*/
|
||||
public DLTThirdStepResultVO analyze(String level, List<Integer> previousFrontBalls,
|
||||
List<Integer> previousBackBalls, List<Integer> currentFrontBalls) {
|
||||
// 参数验证
|
||||
validateInputParams(level, previousFrontBalls, previousBackBalls, currentFrontBalls);
|
||||
|
||||
log.info("开始精推大乐透第三步分析,策略:{},上期前区:{},上期后区:{},本期前区:{}",
|
||||
level, previousFrontBalls, previousBackBalls, currentFrontBalls);
|
||||
|
||||
// 用于存储所有候选球号和对应的系数
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap = new HashMap<>();
|
||||
List<Integer> allCandidateBalls = new ArrayList<>();
|
||||
|
||||
// Step 1: 根据5个上期前区球号从D10表获取候选球(取前10个)
|
||||
for (Integer previousFrontBall : previousFrontBalls) {
|
||||
List<BallWithCoefficient> ballsWithCoefficients = getD10BallsByLevel(previousFrontBall, level);
|
||||
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: 从dlt_backend_history_top获取前2个球号
|
||||
List<Integer> top2HistoryTop = getTop2FromDltBackendHistoryTop();
|
||||
allCandidateBalls.addAll(top2HistoryTop);
|
||||
|
||||
// Step 3: 从dlt_backend_history_top_100获取前2个球号
|
||||
List<Integer> top2HistoryTop100 = getTop2FromDltBackendHistoryTop100();
|
||||
allCandidateBalls.addAll(top2HistoryTop100);
|
||||
|
||||
// Step 4: 根据2个上期后区球号从D11表获取候选球(取前10个)
|
||||
for (Integer previousBackBall : previousBackBalls) {
|
||||
List<BallWithCoefficient> ballsWithCoefficients = getD11BallsByLevel(previousBackBall, level);
|
||||
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5: 根据5个本期前区球号从D6表获取候选球(根据策略取不同数量)
|
||||
for (Integer currentFrontBall : currentFrontBalls) {
|
||||
List<BallWithCoefficient> ballsFromD6 = getD6BallsByLevel(currentFrontBall, level);
|
||||
for (BallWithCoefficient ball : ballsFromD6) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6: 统计分析并生成结果
|
||||
List<DLTThirdStepResultVO.BallAnalysisResult> results = analyzeResults(allCandidateBalls, ballCoefficientMap);
|
||||
|
||||
return DLTThirdStepResultVO.builder()
|
||||
.results(results)
|
||||
.strategy(level)
|
||||
.previousFrontBalls(previousFrontBalls)
|
||||
.previousBackBalls(previousBackBalls)
|
||||
.currentFrontBalls(currentFrontBalls)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从D10表获取候选球号和系数(取前10个,如果第10个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getD10BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<D10> d10List = d10Mapper.selectList(
|
||||
new QueryWrapper<D10>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("coefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(d10List)) {
|
||||
log.warn("D10表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 所有策略都取前10个,如果第10个系数相同则一并加入
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (d10List.size() <= 10) {
|
||||
// 如果总数不超过10个,全部加入
|
||||
for (D10 d10 : d10List) {
|
||||
result.add(new BallWithCoefficient(d10.getSlaveBallNumber(), d10.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前10个
|
||||
for (int i = 0; i < 10; i++) {
|
||||
D10 d10 = d10List.get(i);
|
||||
result.add(new BallWithCoefficient(d10.getSlaveBallNumber(), d10.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第10个球号的系数
|
||||
Double boundaryCoefficient = d10List.get(9).getCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 10; i < d10List.size(); i++) {
|
||||
D10 d10 = d10List.get(i);
|
||||
if (d10.getCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(d10.getSlaveBallNumber(), d10.getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从D11表获取候选球号和系数(取前10个,如果第10个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getD11BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<D11> d11List = d11Mapper.selectList(
|
||||
new QueryWrapper<D11>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("coefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(d11List)) {
|
||||
log.warn("D11表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 所有策略都取前10个,如果第10个系数相同则一并加入
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (d11List.size() <= 10) {
|
||||
// 如果总数不超过10个,全部加入
|
||||
for (D11 d11 : d11List) {
|
||||
result.add(new BallWithCoefficient(d11.getSlaveBallNumber(), d11.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前10个
|
||||
for (int i = 0; i < 10; i++) {
|
||||
D11 d11 = d11List.get(i);
|
||||
result.add(new BallWithCoefficient(d11.getSlaveBallNumber(), d11.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第10个球号的系数
|
||||
Double boundaryCoefficient = d11List.get(9).getCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 10; i < d11List.size(); i++) {
|
||||
D11 d11 = d11List.get(i);
|
||||
if (d11.getCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(d11.getSlaveBallNumber(), d11.getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从D6表获取候选球号和系数
|
||||
*/
|
||||
private List<BallWithCoefficient> getD6BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<D6> d6List = d6Mapper.selectList(
|
||||
new QueryWrapper<D6>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("coefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(d6List)) {
|
||||
log.warn("D6表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if ("H".equalsIgnoreCase(level)) {
|
||||
return getHighLevelBallsFromD6(d6List, masterBallNumber);
|
||||
} else if ("M".equalsIgnoreCase(level)) {
|
||||
return getMiddleLevelBallsFromD6(d6List, masterBallNumber);
|
||||
} else { // L
|
||||
return getLowLevelBallsFromD6(d6List, masterBallNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 高位策略:从D6表获取系数最大的前5个球号(如果第5个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getHighLevelBallsFromD6(List<D6> d6List, Integer masterBallNumber) {
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (d6List.size() <= 5) {
|
||||
// 如果总数不超过5个,全部加入
|
||||
for (D6 d6 : d6List) {
|
||||
result.add(new BallWithCoefficient(d6.getSlaveBallNumber(), d6.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前5个
|
||||
for (int i = 0; i < 5; i++) {
|
||||
D6 d6 = d6List.get(i);
|
||||
result.add(new BallWithCoefficient(d6.getSlaveBallNumber(), d6.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第5个球号的系数
|
||||
Double boundaryCoefficient = d6List.get(4).getCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 5; i < d6List.size(); i++) {
|
||||
D6 d6 = d6List.get(i);
|
||||
if (d6.getCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(d6.getSlaveBallNumber(), d6.getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 中位策略:从D6表获取平均值附近的5个球号(向上2个,向下2个,边界系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getMiddleLevelBallsFromD6(List<D6> d6List, Integer masterBallNumber) {
|
||||
if (d6List.size() < 5) {
|
||||
log.warn("D6表数据不足5条,实际{}条", d6List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (D6 d6 : d6List) {
|
||||
result.add(new BallWithCoefficient(d6.getSlaveBallNumber(), d6.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 计算平均系数(保留两位小数,截断方式)
|
||||
double avgCoefficient = d6List.stream()
|
||||
.mapToDouble(D6::getCoefficient)
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
// 截断到两位小数
|
||||
avgCoefficient = Math.floor(avgCoefficient * 100) / 100;
|
||||
|
||||
// 找到第一个比平均值大的位置(从下到上遍历)
|
||||
int avgPosition = -1;
|
||||
for (int i = d6List.size() - 1; i >= 0; i--) {
|
||||
if (d6List.get(i).getCoefficient() >= avgCoefficient) {
|
||||
avgPosition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (avgPosition == -1) {
|
||||
avgPosition = 0; // 如果没找到,取第一个
|
||||
}
|
||||
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
// 先加入平均值位置的球号
|
||||
result.add(new BallWithCoefficient(d6List.get(avgPosition).getSlaveBallNumber(),
|
||||
d6List.get(avgPosition).getCoefficient(), masterBallNumber));
|
||||
|
||||
// 向上取2个球号,处理边界系数相同情况
|
||||
int upCount = 0;
|
||||
Double upBoundaryCoeff = null;
|
||||
for (int i = avgPosition - 1; i >= 0 && upCount < 2; i--) {
|
||||
result.add(new BallWithCoefficient(d6List.get(i).getSlaveBallNumber(),
|
||||
d6List.get(i).getCoefficient(), masterBallNumber));
|
||||
upCount++;
|
||||
if (upCount == 2) {
|
||||
upBoundaryCoeff = d6List.get(i).getCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向上边界处理:继续添加与第2个球号系数相同的球号
|
||||
if (upBoundaryCoeff != null) {
|
||||
for (int i = avgPosition - 3; i >= 0; i--) {
|
||||
if (d6List.get(i).getCoefficient().equals(upBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(d6List.get(i).getSlaveBallNumber(),
|
||||
d6List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 向下取2个球号,处理边界系数相同情况
|
||||
int downCount = 0;
|
||||
Double downBoundaryCoeff = null;
|
||||
for (int i = avgPosition + 1; i < d6List.size() && downCount < 2; i++) {
|
||||
result.add(new BallWithCoefficient(d6List.get(i).getSlaveBallNumber(),
|
||||
d6List.get(i).getCoefficient(), masterBallNumber));
|
||||
downCount++;
|
||||
if (downCount == 2) {
|
||||
downBoundaryCoeff = d6List.get(i).getCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向下边界处理:继续添加与第2个球号系数相同的球号
|
||||
if (downBoundaryCoeff != null) {
|
||||
for (int i = avgPosition + 3; i < d6List.size(); i++) {
|
||||
if (d6List.get(i).getCoefficient().equals(downBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(d6List.get(i).getSlaveBallNumber(),
|
||||
d6List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 低位策略:从D6表获取最小值向上5个球号(含最小值,第1个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getLowLevelBallsFromD6(List<D6> d6List, Integer masterBallNumber) {
|
||||
if (d6List.size() < 5) {
|
||||
log.warn("D6表数据不足5条,实际{}条", d6List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (D6 d6 : d6List) {
|
||||
result.add(new BallWithCoefficient(d6.getSlaveBallNumber(), d6.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 从最小值开始向上取5个(d6List已按系数降序排列,最后5个就是最小的)
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
int startIndex = Math.max(0, d6List.size() - 5);
|
||||
|
||||
// 先加入基本的5个球号
|
||||
for (int i = startIndex; i < d6List.size(); i++) {
|
||||
D6 d6 = d6List.get(i);
|
||||
result.add(new BallWithCoefficient(d6.getSlaveBallNumber(), d6.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 处理第1个球号(系数最大的)边界相同情况
|
||||
if (startIndex > 0) {
|
||||
Double firstBallCoeff = d6List.get(startIndex).getCoefficient();
|
||||
|
||||
// 继续向前查找系数相同的球号
|
||||
for (int i = startIndex - 1; i >= 0; i--) {
|
||||
if (d6List.get(i).getCoefficient().equals(firstBallCoeff)) {
|
||||
result.add(new BallWithCoefficient(d6List.get(i).getSlaveBallNumber(),
|
||||
d6List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从dlt_backend_history_top获取前2个球号(按点系数排行,处理边界相同系数)
|
||||
*/
|
||||
private List<Integer> getTop2FromDltBackendHistoryTop() {
|
||||
List<DltBackendHistoryTop> historyTopList = dltBackendHistoryTopMapper.selectList(
|
||||
new QueryWrapper<DltBackendHistoryTop>()
|
||||
.orderByDesc("activeCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(historyTopList)) {
|
||||
log.warn("dlt_backend_history_top数据为空");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
int index = 0;
|
||||
int addedCount = 0;
|
||||
|
||||
while (addedCount < 2 && index < historyTopList.size()) {
|
||||
double currentCoefficient = historyTopList.get(index).getActiveCoefficient();
|
||||
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
||||
|
||||
// 收集所有相同系数的球号
|
||||
while (index < historyTopList.size() &&
|
||||
historyTopList.get(index).getActiveCoefficient().equals(currentCoefficient)) {
|
||||
sameCoefficientBalls.add(historyTopList.get(index).getBallNumber());
|
||||
index++;
|
||||
}
|
||||
|
||||
// 计算还需要多少个球
|
||||
int needCount = Math.min(2 - addedCount, sameCoefficientBalls.size());
|
||||
|
||||
if (sameCoefficientBalls.size() == 1) {
|
||||
// 只有一个球号,直接加入
|
||||
result.add(sameCoefficientBalls.get(0));
|
||||
addedCount++;
|
||||
} else if (needCount == sameCoefficientBalls.size()) {
|
||||
// 需要选择的数量等于可用数量,全部加入
|
||||
result.addAll(sameCoefficientBalls);
|
||||
addedCount += sameCoefficientBalls.size();
|
||||
} else {
|
||||
// 需要从多个相同系数的球号中选择部分,处理边界冲突
|
||||
List<Integer> selectedBalls = handleBackendHistoryBoundaryConflicts(sameCoefficientBalls, needCount);
|
||||
result.addAll(selectedBalls);
|
||||
addedCount += selectedBalls.size();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从dlt_backend_history_top_100获取前2个球号(按点系数排行,处理边界相同系数)
|
||||
*/
|
||||
private List<Integer> getTop2FromDltBackendHistoryTop100() {
|
||||
List<DltBackendHistoryTop100> historyTop100List = dltBackendHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<DltBackendHistoryTop100>()
|
||||
.orderByDesc("activeCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(historyTop100List)) {
|
||||
log.warn("dlt_backend_history_top_100数据为空");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
int index = 0;
|
||||
int addedCount = 0;
|
||||
|
||||
while (addedCount < 2 && index < historyTop100List.size()) {
|
||||
double currentCoefficient = historyTop100List.get(index).getActiveCoefficient();
|
||||
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
||||
|
||||
// 收集所有相同系数的球号
|
||||
while (index < historyTop100List.size() &&
|
||||
historyTop100List.get(index).getActiveCoefficient().equals(currentCoefficient)) {
|
||||
sameCoefficientBalls.add(historyTop100List.get(index).getBallNumber());
|
||||
index++;
|
||||
}
|
||||
|
||||
// 计算还需要多少个球
|
||||
int needCount = Math.min(2 - addedCount, sameCoefficientBalls.size());
|
||||
|
||||
if (sameCoefficientBalls.size() == 1) {
|
||||
// 只有一个球号,直接加入
|
||||
result.add(sameCoefficientBalls.get(0));
|
||||
addedCount++;
|
||||
} else if (needCount == sameCoefficientBalls.size()) {
|
||||
// 需要选择的数量等于可用数量,全部加入
|
||||
result.addAll(sameCoefficientBalls);
|
||||
addedCount += sameCoefficientBalls.size();
|
||||
} else {
|
||||
// 需要从多个相同系数的球号中选择部分,处理边界冲突
|
||||
List<Integer> selectedBalls = handleBackendHistoryBoundaryConflicts(sameCoefficientBalls, needCount);
|
||||
result.addAll(selectedBalls);
|
||||
addedCount += selectedBalls.size();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理后区历史排行边界冲突,选择指定数量的球号
|
||||
* @param candidateBalls 候选球号列表
|
||||
* @param selectCount 需要选择的数量
|
||||
* @return 选中的球号列表
|
||||
*/
|
||||
private List<Integer> handleBackendHistoryBoundaryConflicts(List<Integer> candidateBalls, int selectCount) {
|
||||
if (CollectionUtils.isEmpty(candidateBalls) || selectCount <= 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (selectCount >= candidateBalls.size()) {
|
||||
return new ArrayList<>(candidateBalls);
|
||||
}
|
||||
|
||||
// 1. 先从dlt_backend_history_top_100比较
|
||||
Map<Integer, Double> top100Coefficients = new HashMap<>();
|
||||
for (Integer ball : candidateBalls) {
|
||||
DltBackendHistoryTop100 record = dltBackendHistoryTop100Mapper.selectOne(
|
||||
new QueryWrapper<DltBackendHistoryTop100>()
|
||||
.eq("ballNumber", ball));
|
||||
if (record != null) {
|
||||
top100Coefficients.put(ball, record.getActiveCoefficient());
|
||||
} else {
|
||||
top100Coefficients.put(ball, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// 按系数降序排序
|
||||
List<Map.Entry<Integer, Double>> sortedByTop100 = top100Coefficients.entrySet().stream()
|
||||
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
|
||||
// 按系数分组处理
|
||||
int currentIndex = 0;
|
||||
while (result.size() < selectCount && currentIndex < sortedByTop100.size()) {
|
||||
Double currentCoefficient = sortedByTop100.get(currentIndex).getValue();
|
||||
List<Integer> sameTop100CoefficientBalls = new ArrayList<>();
|
||||
|
||||
// 收集相同系数的球号
|
||||
while (currentIndex < sortedByTop100.size() &&
|
||||
sortedByTop100.get(currentIndex).getValue().equals(currentCoefficient)) {
|
||||
sameTop100CoefficientBalls.add(sortedByTop100.get(currentIndex).getKey());
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
int needFromThisGroup = Math.min(selectCount - result.size(), sameTop100CoefficientBalls.size());
|
||||
|
||||
if (sameTop100CoefficientBalls.size() == 1 || needFromThisGroup == sameTop100CoefficientBalls.size()) {
|
||||
// 只有一个球或者需要全部选择
|
||||
for (int i = 0; i < needFromThisGroup; i++) {
|
||||
result.add(sameTop100CoefficientBalls.get(i));
|
||||
}
|
||||
} else {
|
||||
// 需要进一步筛选,使用dlt_backend_history_top表
|
||||
List<Integer> selectedFromTop = selectFromBackendHistoryTop(sameTop100CoefficientBalls, needFromThisGroup);
|
||||
result.addAll(selectedFromTop);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从dlt_backend_history_top表中选择指定数量的球号
|
||||
*/
|
||||
private List<Integer> selectFromBackendHistoryTop(List<Integer> candidateBalls, int selectCount) {
|
||||
if (CollectionUtils.isEmpty(candidateBalls) || selectCount <= 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (selectCount >= candidateBalls.size()) {
|
||||
return new ArrayList<>(candidateBalls);
|
||||
}
|
||||
|
||||
// 获取在dlt_backend_history_top表中的系数
|
||||
Map<Integer, Double> topCoefficients = new HashMap<>();
|
||||
for (Integer ball : candidateBalls) {
|
||||
DltBackendHistoryTop record = dltBackendHistoryTopMapper.selectOne(
|
||||
new QueryWrapper<DltBackendHistoryTop>()
|
||||
.eq("ballNumber", ball));
|
||||
if (record != null) {
|
||||
topCoefficients.put(ball, record.getActiveCoefficient());
|
||||
} else {
|
||||
topCoefficients.put(ball, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// 按系数降序排序,选择前selectCount个
|
||||
List<Map.Entry<Integer, Double>> sortedByTop = topCoefficients.entrySet().stream()
|
||||
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
for (int i = 0; i < Math.min(selectCount, sortedByTop.size()); i++) {
|
||||
result.add(sortedByTop.get(i).getKey());
|
||||
}
|
||||
|
||||
// 如果仍然不够,按原始顺序补充
|
||||
while (result.size() < selectCount && result.size() < candidateBalls.size()) {
|
||||
for (Integer ball : candidateBalls) {
|
||||
if (!result.contains(ball) && result.size() < selectCount) {
|
||||
result.add(ball);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计分析并生成结果
|
||||
*/
|
||||
private List<DLTThirdStepResultVO.BallAnalysisResult> analyzeResults(
|
||||
List<Integer> allCandidateBalls,
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap) {
|
||||
|
||||
// 统计球号出现次数
|
||||
Map<Integer, Integer> ballFrequencyMap = new HashMap<>();
|
||||
for (Integer ball : allCandidateBalls) {
|
||||
ballFrequencyMap.put(ball, ballFrequencyMap.getOrDefault(ball, 0) + 1);
|
||||
}
|
||||
|
||||
// 计算系数和
|
||||
Map<Integer, Double> ballCoefficientSumMap = new HashMap<>();
|
||||
for (Map.Entry<Integer, List<BallWithCoefficient>> entry : ballCoefficientMap.entrySet()) {
|
||||
Integer ballNumber = entry.getKey();
|
||||
List<BallWithCoefficient> coefficients = entry.getValue();
|
||||
double sum = coefficients.stream()
|
||||
.mapToDouble(BallWithCoefficient::getCoefficient)
|
||||
.sum();
|
||||
ballCoefficientSumMap.put(ballNumber, sum);
|
||||
}
|
||||
|
||||
// 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
Set<Integer> allBallsWithRanking = getAllBallsWithRanking();
|
||||
|
||||
// 获取百期排位和历史排位
|
||||
Map<Integer, Integer> top100RankingMap = getTop100Rankings(allBallsWithRanking);
|
||||
Map<Integer, Integer> historyRankingMap = getHistoryRankings(allBallsWithRanking);
|
||||
|
||||
// 组装结果
|
||||
List<DLTThirdStepResultVO.BallAnalysisResult> results = new ArrayList<>();
|
||||
for (Integer ballNumber : allBallsWithRanking) {
|
||||
DLTThirdStepResultVO.BallAnalysisResult result = DLTThirdStepResultVO.BallAnalysisResult.builder()
|
||||
.ballNumber(ballNumber)
|
||||
.frequency(ballFrequencyMap.getOrDefault(ballNumber, 0))
|
||||
.coefficientSum(ballCoefficientSumMap.getOrDefault(ballNumber, 0.0))
|
||||
.top100Ranking(top100RankingMap.get(ballNumber))
|
||||
.historyRanking(historyRankingMap.get(ballNumber))
|
||||
.build();
|
||||
results.add(result);
|
||||
}
|
||||
|
||||
// 按出现次数降序排列,次数相同的按球号升序排列
|
||||
results.sort((a, b) -> {
|
||||
int frequencyCompare = b.getFrequency().compareTo(a.getFrequency());
|
||||
if (frequencyCompare != 0) {
|
||||
return frequencyCompare;
|
||||
}
|
||||
return a.getBallNumber().compareTo(b.getBallNumber());
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
*/
|
||||
private Set<Integer> getAllBallsWithRanking() {
|
||||
Set<Integer> allBalls = new HashSet<>();
|
||||
|
||||
// 从DLT后区百期排行表获取所有球号
|
||||
List<DltBackendHistoryTop100> top100List = dltBackendHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<DltBackendHistoryTop100>());
|
||||
for (DltBackendHistoryTop100 item : top100List) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
// 从DLT后区历史排行表获取所有球号
|
||||
List<DltBackendHistoryTop> historyTopList = dltBackendHistoryTopMapper.selectList(
|
||||
new QueryWrapper<DltBackendHistoryTop>());
|
||||
for (DltBackendHistoryTop item : historyTopList) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
return allBalls;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在百期排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getTop100Rankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的ranking字段作为排名
|
||||
List<DltBackendHistoryTop100> top100List = dltBackendHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<DltBackendHistoryTop100>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的ranking字段作为排名
|
||||
for (DltBackendHistoryTop100 item : top100List) {
|
||||
result.put(item.getBallNumber(), item.getRanking());
|
||||
}
|
||||
|
||||
// 对于没有在百期排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在历史排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getHistoryRankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的ranking字段作为排名
|
||||
List<DltBackendHistoryTop> historyTopList = dltBackendHistoryTopMapper.selectList(
|
||||
new QueryWrapper<DltBackendHistoryTop>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的ranking字段作为排名
|
||||
for (DltBackendHistoryTop item : historyTopList) {
|
||||
result.put(item.getBallNumber(), item.getRanking());
|
||||
}
|
||||
|
||||
// 对于没有在历史排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数验证
|
||||
*/
|
||||
private void validateInputParams(String level, List<Integer> previousFrontBalls,
|
||||
List<Integer> previousBackBalls, List<Integer> currentFrontBalls) {
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
throw new IllegalArgumentException("位置级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(previousFrontBalls) || previousFrontBalls.size() != 5) {
|
||||
throw new IllegalArgumentException("上期前区球号必须为5个");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(previousBackBalls) || previousBackBalls.size() != 2) {
|
||||
throw new IllegalArgumentException("上期后区球号必须为2个");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(currentFrontBalls) || currentFrontBalls.size() != 5) {
|
||||
throw new IllegalArgumentException("本期前区球号必须为5个");
|
||||
}
|
||||
|
||||
for (Integer frontBall : previousFrontBalls) {
|
||||
if (frontBall < 1 || frontBall > 35) {
|
||||
throw new IllegalArgumentException("上期前区球号范围应为1-35");
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer backBall : previousBackBalls) {
|
||||
if (backBall < 1 || backBall > 12) {
|
||||
throw new IllegalArgumentException("上期后区球号范围应为1-12");
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer frontBall : currentFrontBalls) {
|
||||
if (frontBall < 1 || frontBall > 35) {
|
||||
throw new IllegalArgumentException("本期前区球号范围应为1-35");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,858 @@
|
||||
package com.xy.xyaicpzs.jt.jtdlt;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.xy.xyaicpzs.domain.entity.*;
|
||||
import com.xy.xyaicpzs.domain.vo.DLTFirstStepResultVO;
|
||||
import com.xy.xyaicpzs.mapper.*;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 精推大乐透第一步算法分析
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FirstFrontBallAnalysis {
|
||||
|
||||
@Autowired
|
||||
private D9Mapper d9Mapper;
|
||||
|
||||
@Autowired
|
||||
private D12Mapper d12Mapper;
|
||||
|
||||
@Autowired
|
||||
private DltFrontendHistoryTopMapper dltFrontendHistoryTopMapper;
|
||||
|
||||
@Autowired
|
||||
private DltFrontendHistoryTop100Mapper dltFrontendHistoryTop100Mapper;
|
||||
|
||||
/**
|
||||
* 球号和系数的关联类
|
||||
*/
|
||||
@Data
|
||||
public static class BallWithCoefficient {
|
||||
private Integer ballNumber;
|
||||
private Double coefficient;
|
||||
private Integer masterBallNumber; // 主球号,用于标识来源
|
||||
|
||||
public BallWithCoefficient(Integer ballNumber, Double coefficient, Integer masterBallNumber) {
|
||||
this.ballNumber = ballNumber;
|
||||
this.coefficient = coefficient;
|
||||
this.masterBallNumber = masterBallNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 精推大乐透第一步算法主方法
|
||||
* @param level 位置级别(H-高位/M-中位/L-低位)
|
||||
* @param frontBalls 前区5个球号
|
||||
* @param backBalls 后区2个球号
|
||||
* @return 分析结果
|
||||
*/
|
||||
public DLTFirstStepResultVO analyze(String level, List<Integer> frontBalls, List<Integer> backBalls) {
|
||||
// 参数验证
|
||||
validateInputParams(level, frontBalls, backBalls);
|
||||
|
||||
log.info("开始精推大乐透第一步分析,策略:{},前区:{},后区:{}", level, frontBalls, backBalls);
|
||||
|
||||
// 用于存储所有候选球号和对应的系数
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap = new HashMap<>();
|
||||
List<Integer> allCandidateBalls = new ArrayList<>();
|
||||
|
||||
// Step 1: 根据5个前区球号从D9表获取候选球
|
||||
for (Integer frontBall : frontBalls) {
|
||||
List<BallWithCoefficient> ballsWithCoefficients = getD9BallsByLevel(frontBall, level);
|
||||
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: 从dlt_frontend_history_top获取前3个球号
|
||||
List<Integer> top3HistoryTop = getTop3FromDltFrontendHistoryTop();
|
||||
allCandidateBalls.addAll(top3HistoryTop);
|
||||
|
||||
// Step 3: 从dlt_frontend_history_top_100获取前3个球号
|
||||
List<Integer> top3HistoryTop100 = getTop3FromDltFrontendHistoryTop100();
|
||||
allCandidateBalls.addAll(top3HistoryTop100);
|
||||
|
||||
// Step 4: 根据2个后区球号从D12表获取候选球
|
||||
for (Integer backBall : backBalls) {
|
||||
List<BallWithCoefficient> ballsWithCoefficients = getD12BallsByLevel(backBall, level);
|
||||
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5: 统计分析并生成结果
|
||||
List<DLTFirstStepResultVO.BallAnalysisResult> results = analyzeResults(allCandidateBalls, ballCoefficientMap);
|
||||
|
||||
return DLTFirstStepResultVO.builder()
|
||||
.results(results)
|
||||
.strategy(level)
|
||||
.frontBalls(frontBalls)
|
||||
.backBalls(backBalls)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从D9表获取候选球号和系数
|
||||
*/
|
||||
private List<BallWithCoefficient> getD9BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<D9> d9List = d9Mapper.selectList(
|
||||
new QueryWrapper<D9>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("coefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(d9List)) {
|
||||
log.warn("D9表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if ("H".equalsIgnoreCase(level)) {
|
||||
return getHighLevelBallsFromD9(d9List, masterBallNumber);
|
||||
} else if ("M".equalsIgnoreCase(level)) {
|
||||
return getMiddleLevelBallsFromD9(d9List, masterBallNumber);
|
||||
} else { // L
|
||||
return getLowLevelBallsFromD9(d9List, masterBallNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从D12表获取候选球号和系数
|
||||
*/
|
||||
private List<BallWithCoefficient> getD12BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<D12> d12List = d12Mapper.selectList(
|
||||
new QueryWrapper<D12>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("coefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(d12List)) {
|
||||
log.warn("D12表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if ("H".equalsIgnoreCase(level)) {
|
||||
return getHighLevelBallsFromD12(d12List, masterBallNumber);
|
||||
} else if ("M".equalsIgnoreCase(level)) {
|
||||
return getMiddleLevelBallsFromD12(d12List, masterBallNumber);
|
||||
} else { // L
|
||||
return getLowLevelBallsFromD12(d12List, masterBallNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 高位策略:从D9表获取系数最大的前17个球号(如果第17个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getHighLevelBallsFromD9(List<D9> d9List, Integer masterBallNumber) {
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (d9List.size() <= 17) {
|
||||
// 如果总数不超过17个,全部加入
|
||||
for (D9 d9 : d9List) {
|
||||
result.add(new BallWithCoefficient(d9.getSlaveBallNumber(), d9.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前17个
|
||||
for (int i = 0; i < 17; i++) {
|
||||
D9 d9 = d9List.get(i);
|
||||
result.add(new BallWithCoefficient(d9.getSlaveBallNumber(), d9.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第17个球号的系数
|
||||
Double boundaryCoefficient = d9List.get(16).getCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 17; i < d9List.size(); i++) {
|
||||
D9 d9 = d9List.get(i);
|
||||
if (d9.getCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(d9.getSlaveBallNumber(), d9.getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 高位策略:从D12表获取系数最大的前17个球号(如果第17个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getHighLevelBallsFromD12(List<D12> d12List, Integer masterBallNumber) {
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (d12List.size() <= 17) {
|
||||
// 如果总数不超过17个,全部加入
|
||||
for (D12 d12 : d12List) {
|
||||
result.add(new BallWithCoefficient(d12.getSlaveBallNumber(), d12.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前17个
|
||||
for (int i = 0; i < 17; i++) {
|
||||
D12 d12 = d12List.get(i);
|
||||
result.add(new BallWithCoefficient(d12.getSlaveBallNumber(), d12.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第17个球号的系数
|
||||
Double boundaryCoefficient = d12List.get(16).getCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 17; i < d12List.size(); i++) {
|
||||
D12 d12 = d12List.get(i);
|
||||
if (d12.getCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(d12.getSlaveBallNumber(), d12.getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 中位策略:从D9表获取平均值附近的17个球号(向上8个,向下8个,边界系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getMiddleLevelBallsFromD9(List<D9> d9List, Integer masterBallNumber) {
|
||||
if (d9List.size() < 17) {
|
||||
log.warn("D9表数据不足17条,实际{}条", d9List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (D9 d9 : d9List) {
|
||||
result.add(new BallWithCoefficient(d9.getSlaveBallNumber(), d9.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 计算平均系数(保留两位小数,截断方式)
|
||||
double avgCoefficient = d9List.stream()
|
||||
.mapToDouble(D9::getCoefficient)
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
// 截断到两位小数
|
||||
avgCoefficient = Math.floor(avgCoefficient * 100) / 100;
|
||||
|
||||
// 找到第一个比平均值大的位置(从下到上遍历)
|
||||
int avgPosition = -1;
|
||||
for (int i = d9List.size() - 1; i >= 0; i--) {
|
||||
if (d9List.get(i).getCoefficient() >= avgCoefficient) {
|
||||
avgPosition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (avgPosition == -1) {
|
||||
avgPosition = 0; // 如果没找到,取第一个
|
||||
}
|
||||
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
// 先加入平均值位置的球号
|
||||
result.add(new BallWithCoefficient(d9List.get(avgPosition).getSlaveBallNumber(),
|
||||
d9List.get(avgPosition).getCoefficient(), masterBallNumber));
|
||||
|
||||
// 向上取8个球号,处理边界系数相同情况
|
||||
int upCount = 0;
|
||||
Double upBoundaryCoeff = null;
|
||||
for (int i = avgPosition - 1; i >= 0 && upCount < 8; i--) {
|
||||
result.add(new BallWithCoefficient(d9List.get(i).getSlaveBallNumber(),
|
||||
d9List.get(i).getCoefficient(), masterBallNumber));
|
||||
upCount++;
|
||||
if (upCount == 8) {
|
||||
upBoundaryCoeff = d9List.get(i).getCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向上边界处理:继续添加与第8个球号系数相同的球号
|
||||
if (upBoundaryCoeff != null) {
|
||||
for (int i = avgPosition - 9; i >= 0; i--) {
|
||||
if (d9List.get(i).getCoefficient().equals(upBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(d9List.get(i).getSlaveBallNumber(),
|
||||
d9List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 向下取8个球号,处理边界系数相同情况
|
||||
int downCount = 0;
|
||||
Double downBoundaryCoeff = null;
|
||||
for (int i = avgPosition + 1; i < d9List.size() && downCount < 8; i++) {
|
||||
result.add(new BallWithCoefficient(d9List.get(i).getSlaveBallNumber(),
|
||||
d9List.get(i).getCoefficient(), masterBallNumber));
|
||||
downCount++;
|
||||
if (downCount == 8) {
|
||||
downBoundaryCoeff = d9List.get(i).getCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向下边界处理:继续添加与第8个球号系数相同的球号
|
||||
if (downBoundaryCoeff != null) {
|
||||
for (int i = avgPosition + 9; i < d9List.size(); i++) {
|
||||
if (d9List.get(i).getCoefficient().equals(downBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(d9List.get(i).getSlaveBallNumber(),
|
||||
d9List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 中位策略:从D12表获取平均值附近的17个球号(向上8个,向下8个,边界系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getMiddleLevelBallsFromD12(List<D12> d12List, Integer masterBallNumber) {
|
||||
if (d12List.size() < 17) {
|
||||
log.warn("D12表数据不足17条,实际{}条", d12List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (D12 d12 : d12List) {
|
||||
result.add(new BallWithCoefficient(d12.getSlaveBallNumber(), d12.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 计算平均系数(保留两位小数,截断方式)
|
||||
double avgCoefficient = d12List.stream()
|
||||
.mapToDouble(D12::getCoefficient)
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
// 截断到两位小数
|
||||
avgCoefficient = Math.floor(avgCoefficient * 100) / 100;
|
||||
|
||||
// 找到第一个比平均值大的位置(从下到上遍历)
|
||||
int avgPosition = -1;
|
||||
for (int i = d12List.size() - 1; i >= 0; i--) {
|
||||
if (d12List.get(i).getCoefficient() >= avgCoefficient) {
|
||||
avgPosition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (avgPosition == -1) {
|
||||
avgPosition = 0; // 如果没找到,取第一个
|
||||
}
|
||||
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
// 先加入平均值位置的球号
|
||||
result.add(new BallWithCoefficient(d12List.get(avgPosition).getSlaveBallNumber(),
|
||||
d12List.get(avgPosition).getCoefficient(), masterBallNumber));
|
||||
|
||||
// 向上取8个球号,处理边界系数相同情况
|
||||
int upCount = 0;
|
||||
Double upBoundaryCoeff = null;
|
||||
for (int i = avgPosition - 1; i >= 0 && upCount < 8; i--) {
|
||||
result.add(new BallWithCoefficient(d12List.get(i).getSlaveBallNumber(),
|
||||
d12List.get(i).getCoefficient(), masterBallNumber));
|
||||
upCount++;
|
||||
if (upCount == 8) {
|
||||
upBoundaryCoeff = d12List.get(i).getCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向上边界处理:继续添加与第8个球号系数相同的球号
|
||||
if (upBoundaryCoeff != null) {
|
||||
for (int i = avgPosition - 9; i >= 0; i--) {
|
||||
if (d12List.get(i).getCoefficient().equals(upBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(d12List.get(i).getSlaveBallNumber(),
|
||||
d12List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 向下取8个球号,处理边界系数相同情况
|
||||
int downCount = 0;
|
||||
Double downBoundaryCoeff = null;
|
||||
for (int i = avgPosition + 1; i < d12List.size() && downCount < 8; i++) {
|
||||
result.add(new BallWithCoefficient(d12List.get(i).getSlaveBallNumber(),
|
||||
d12List.get(i).getCoefficient(), masterBallNumber));
|
||||
downCount++;
|
||||
if (downCount == 8) {
|
||||
downBoundaryCoeff = d12List.get(i).getCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向下边界处理:继续添加与第8个球号系数相同的球号
|
||||
if (downBoundaryCoeff != null) {
|
||||
for (int i = avgPosition + 9; i < d12List.size(); i++) {
|
||||
if (d12List.get(i).getCoefficient().equals(downBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(d12List.get(i).getSlaveBallNumber(),
|
||||
d12List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 低位策略:从D9表获取最小值向上17个球号(含最小值,第1个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getLowLevelBallsFromD9(List<D9> d9List, Integer masterBallNumber) {
|
||||
if (d9List.size() < 17) {
|
||||
log.warn("D9表数据不足17条,实际{}条", d9List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (D9 d9 : d9List) {
|
||||
result.add(new BallWithCoefficient(d9.getSlaveBallNumber(), d9.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 从最小值开始向上取17个(d9List已按系数降序排列,最后17个就是最小的)
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
int startIndex = Math.max(0, d9List.size() - 17);
|
||||
|
||||
// 先加入基本的17个球号
|
||||
for (int i = startIndex; i < d9List.size(); i++) {
|
||||
D9 d9 = d9List.get(i);
|
||||
result.add(new BallWithCoefficient(d9.getSlaveBallNumber(), d9.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 处理第1个球号(系数最大的)边界相同情况
|
||||
if (startIndex > 0) {
|
||||
Double firstBallCoeff = d9List.get(startIndex).getCoefficient();
|
||||
|
||||
// 继续向前查找系数相同的球号
|
||||
for (int i = startIndex - 1; i >= 0; i--) {
|
||||
if (d9List.get(i).getCoefficient().equals(firstBallCoeff)) {
|
||||
result.add(new BallWithCoefficient(d9List.get(i).getSlaveBallNumber(),
|
||||
d9List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 低位策略:从D12表获取最小值向上17个球号(含最小值,第1个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getLowLevelBallsFromD12(List<D12> d12List, Integer masterBallNumber) {
|
||||
if (d12List.size() < 17) {
|
||||
log.warn("D12表数据不足17条,实际{}条", d12List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (D12 d12 : d12List) {
|
||||
result.add(new BallWithCoefficient(d12.getSlaveBallNumber(), d12.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 从最小值开始向上取17个(d12List已按系数降序排列,最后17个就是最小的)
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
int startIndex = Math.max(0, d12List.size() - 17);
|
||||
|
||||
// 先加入基本的17个球号
|
||||
for (int i = startIndex; i < d12List.size(); i++) {
|
||||
D12 d12 = d12List.get(i);
|
||||
result.add(new BallWithCoefficient(d12.getSlaveBallNumber(), d12.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 处理第1个球号(系数最大的)边界相同情况
|
||||
if (startIndex > 0) {
|
||||
Double firstBallCoeff = d12List.get(startIndex).getCoefficient();
|
||||
|
||||
// 继续向前查找系数相同的球号
|
||||
for (int i = startIndex - 1; i >= 0; i--) {
|
||||
if (d12List.get(i).getCoefficient().equals(firstBallCoeff)) {
|
||||
result.add(new BallWithCoefficient(d12List.get(i).getSlaveBallNumber(),
|
||||
d12List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从dlt_frontend_history_top获取前3个球号(按点系数排行,处理边界相同系数)
|
||||
*/
|
||||
private List<Integer> getTop3FromDltFrontendHistoryTop() {
|
||||
List<DltFrontendHistoryTop> historyTopList = dltFrontendHistoryTopMapper.selectList(
|
||||
new QueryWrapper<DltFrontendHistoryTop>()
|
||||
.orderByDesc("activeCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(historyTopList)) {
|
||||
log.warn("dlt_frontend_history_top数据为空");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
int index = 0;
|
||||
int addedCount = 0;
|
||||
|
||||
while (addedCount < 3 && index < historyTopList.size()) {
|
||||
double currentCoefficient = historyTopList.get(index).getActiveCoefficient();
|
||||
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
||||
|
||||
// 收集所有相同系数的球号
|
||||
while (index < historyTopList.size() &&
|
||||
historyTopList.get(index).getActiveCoefficient().equals(currentCoefficient)) {
|
||||
sameCoefficientBalls.add(historyTopList.get(index).getBallNumber());
|
||||
index++;
|
||||
}
|
||||
|
||||
// 计算还需要多少个球
|
||||
int needCount = Math.min(3 - addedCount, sameCoefficientBalls.size());
|
||||
|
||||
if (sameCoefficientBalls.size() == 1) {
|
||||
// 只有一个球号,直接加入
|
||||
result.add(sameCoefficientBalls.get(0));
|
||||
addedCount++;
|
||||
} else if (needCount == sameCoefficientBalls.size()) {
|
||||
// 需要选择的数量等于可用数量,全部加入
|
||||
result.addAll(sameCoefficientBalls);
|
||||
addedCount += sameCoefficientBalls.size();
|
||||
} else {
|
||||
// 需要从多个相同系数的球号中选择部分,处理边界冲突
|
||||
List<Integer> selectedBalls = handleFrontendHistoryBoundaryConflicts(sameCoefficientBalls, needCount);
|
||||
result.addAll(selectedBalls);
|
||||
addedCount += selectedBalls.size();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从dlt_frontend_history_top_100获取前3个球号(按点系数排行,处理边界相同系数)
|
||||
*/
|
||||
private List<Integer> getTop3FromDltFrontendHistoryTop100() {
|
||||
List<DltFrontendHistoryTop100> historyTop100List = dltFrontendHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<DltFrontendHistoryTop100>()
|
||||
.orderByDesc("activeCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(historyTop100List)) {
|
||||
log.warn("dlt_frontend_history_top_100数据为空");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
int index = 0;
|
||||
int addedCount = 0;
|
||||
|
||||
while (addedCount < 3 && index < historyTop100List.size()) {
|
||||
double currentCoefficient = historyTop100List.get(index).getActiveCoefficient();
|
||||
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
||||
|
||||
// 收集所有相同系数的球号
|
||||
while (index < historyTop100List.size() &&
|
||||
historyTop100List.get(index).getActiveCoefficient().equals(currentCoefficient)) {
|
||||
sameCoefficientBalls.add(historyTop100List.get(index).getBallNumber());
|
||||
index++;
|
||||
}
|
||||
|
||||
// 计算还需要多少个球
|
||||
int needCount = Math.min(3 - addedCount, sameCoefficientBalls.size());
|
||||
|
||||
if (sameCoefficientBalls.size() == 1) {
|
||||
// 只有一个球号,直接加入
|
||||
result.add(sameCoefficientBalls.get(0));
|
||||
addedCount++;
|
||||
} else if (needCount == sameCoefficientBalls.size()) {
|
||||
// 需要选择的数量等于可用数量,全部加入
|
||||
result.addAll(sameCoefficientBalls);
|
||||
addedCount += sameCoefficientBalls.size();
|
||||
} else {
|
||||
// 需要从多个相同系数的球号中选择部分,处理边界冲突
|
||||
List<Integer> selectedBalls = handleFrontendHistoryBoundaryConflicts(sameCoefficientBalls, needCount);
|
||||
result.addAll(selectedBalls);
|
||||
addedCount += selectedBalls.size();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理前区历史排行边界冲突,选择指定数量的球号
|
||||
* @param candidateBalls 候选球号列表
|
||||
* @param selectCount 需要选择的数量
|
||||
* @return 选中的球号列表
|
||||
*/
|
||||
private List<Integer> handleFrontendHistoryBoundaryConflicts(List<Integer> candidateBalls, int selectCount) {
|
||||
if (CollectionUtils.isEmpty(candidateBalls) || selectCount <= 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (selectCount >= candidateBalls.size()) {
|
||||
return new ArrayList<>(candidateBalls);
|
||||
}
|
||||
|
||||
// 1. 先从dlt_frontend_history_top_100比较
|
||||
Map<Integer, Double> top100Coefficients = new HashMap<>();
|
||||
for (Integer ball : candidateBalls) {
|
||||
DltFrontendHistoryTop100 record = dltFrontendHistoryTop100Mapper.selectOne(
|
||||
new QueryWrapper<DltFrontendHistoryTop100>()
|
||||
.eq("ballNumber", ball));
|
||||
if (record != null) {
|
||||
top100Coefficients.put(ball, record.getActiveCoefficient());
|
||||
} else {
|
||||
top100Coefficients.put(ball, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// 按系数降序排序
|
||||
List<Map.Entry<Integer, Double>> sortedByTop100 = top100Coefficients.entrySet().stream()
|
||||
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
|
||||
// 按系数分组处理
|
||||
int currentIndex = 0;
|
||||
while (result.size() < selectCount && currentIndex < sortedByTop100.size()) {
|
||||
Double currentCoefficient = sortedByTop100.get(currentIndex).getValue();
|
||||
List<Integer> sameTop100CoefficientBalls = new ArrayList<>();
|
||||
|
||||
// 收集相同系数的球号
|
||||
while (currentIndex < sortedByTop100.size() &&
|
||||
sortedByTop100.get(currentIndex).getValue().equals(currentCoefficient)) {
|
||||
sameTop100CoefficientBalls.add(sortedByTop100.get(currentIndex).getKey());
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
int needFromThisGroup = Math.min(selectCount - result.size(), sameTop100CoefficientBalls.size());
|
||||
|
||||
if (sameTop100CoefficientBalls.size() == 1 || needFromThisGroup == sameTop100CoefficientBalls.size()) {
|
||||
// 只有一个球或者需要全部选择
|
||||
for (int i = 0; i < needFromThisGroup; i++) {
|
||||
result.add(sameTop100CoefficientBalls.get(i));
|
||||
}
|
||||
} else {
|
||||
// 需要进一步筛选,使用dlt_frontend_history_top表
|
||||
List<Integer> selectedFromTop = selectFromFrontendHistoryTop(sameTop100CoefficientBalls, needFromThisGroup);
|
||||
result.addAll(selectedFromTop);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从dlt_frontend_history_top表中选择指定数量的球号
|
||||
*/
|
||||
private List<Integer> selectFromFrontendHistoryTop(List<Integer> candidateBalls, int selectCount) {
|
||||
if (CollectionUtils.isEmpty(candidateBalls) || selectCount <= 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (selectCount >= candidateBalls.size()) {
|
||||
return new ArrayList<>(candidateBalls);
|
||||
}
|
||||
|
||||
// 获取在dlt_frontend_history_top表中的系数
|
||||
Map<Integer, Double> topCoefficients = new HashMap<>();
|
||||
for (Integer ball : candidateBalls) {
|
||||
DltFrontendHistoryTop record = dltFrontendHistoryTopMapper.selectOne(
|
||||
new QueryWrapper<DltFrontendHistoryTop>()
|
||||
.eq("ballNumber", ball));
|
||||
if (record != null) {
|
||||
topCoefficients.put(ball, record.getActiveCoefficient());
|
||||
} else {
|
||||
topCoefficients.put(ball, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// 按系数降序排序,选择前selectCount个
|
||||
List<Map.Entry<Integer, Double>> sortedByTop = topCoefficients.entrySet().stream()
|
||||
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
for (int i = 0; i < Math.min(selectCount, sortedByTop.size()); i++) {
|
||||
result.add(sortedByTop.get(i).getKey());
|
||||
}
|
||||
|
||||
// 如果仍然不够,按原始顺序补充
|
||||
while (result.size() < selectCount && result.size() < candidateBalls.size()) {
|
||||
for (Integer ball : candidateBalls) {
|
||||
if (!result.contains(ball) && result.size() < selectCount) {
|
||||
result.add(ball);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计分析并生成结果
|
||||
*/
|
||||
private List<DLTFirstStepResultVO.BallAnalysisResult> analyzeResults(
|
||||
List<Integer> allCandidateBalls,
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap) {
|
||||
|
||||
// 统计球号出现次数
|
||||
Map<Integer, Integer> ballFrequencyMap = new HashMap<>();
|
||||
for (Integer ball : allCandidateBalls) {
|
||||
ballFrequencyMap.put(ball, ballFrequencyMap.getOrDefault(ball, 0) + 1);
|
||||
}
|
||||
|
||||
// 计算系数和
|
||||
Map<Integer, Double> ballCoefficientSumMap = new HashMap<>();
|
||||
for (Map.Entry<Integer, List<BallWithCoefficient>> entry : ballCoefficientMap.entrySet()) {
|
||||
Integer ballNumber = entry.getKey();
|
||||
List<BallWithCoefficient> coefficients = entry.getValue();
|
||||
double sum = coefficients.stream()
|
||||
.mapToDouble(BallWithCoefficient::getCoefficient)
|
||||
.sum();
|
||||
ballCoefficientSumMap.put(ballNumber, sum);
|
||||
}
|
||||
|
||||
// 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
Set<Integer> allBallsWithRanking = getAllBallsWithRanking();
|
||||
|
||||
// 获取百期排位和历史排位
|
||||
Map<Integer, Integer> top100RankingMap = getTop100Rankings(allBallsWithRanking);
|
||||
Map<Integer, Integer> historyRankingMap = getHistoryRankings(allBallsWithRanking);
|
||||
|
||||
// 组装结果
|
||||
List<DLTFirstStepResultVO.BallAnalysisResult> results = new ArrayList<>();
|
||||
for (Integer ballNumber : allBallsWithRanking) {
|
||||
DLTFirstStepResultVO.BallAnalysisResult result = DLTFirstStepResultVO.BallAnalysisResult.builder()
|
||||
.ballNumber(ballNumber)
|
||||
.frequency(ballFrequencyMap.getOrDefault(ballNumber, 0))
|
||||
.coefficientSum(ballCoefficientSumMap.getOrDefault(ballNumber, 0.0))
|
||||
.top100Ranking(top100RankingMap.get(ballNumber))
|
||||
.historyRanking(historyRankingMap.get(ballNumber))
|
||||
.build();
|
||||
results.add(result);
|
||||
}
|
||||
|
||||
// 按出现次数降序排列,次数相同的按球号升序排列
|
||||
results.sort((a, b) -> {
|
||||
int frequencyCompare = b.getFrequency().compareTo(a.getFrequency());
|
||||
if (frequencyCompare != 0) {
|
||||
return frequencyCompare;
|
||||
}
|
||||
return a.getBallNumber().compareTo(b.getBallNumber());
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
*/
|
||||
private Set<Integer> getAllBallsWithRanking() {
|
||||
Set<Integer> allBalls = new HashSet<>();
|
||||
|
||||
// 从DLT前区百期排行表获取所有球号
|
||||
List<DltFrontendHistoryTop100> top100List = dltFrontendHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<DltFrontendHistoryTop100>());
|
||||
for (DltFrontendHistoryTop100 item : top100List) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
// 从DLT前区历史排行表获取所有球号
|
||||
List<DltFrontendHistoryTop> historyTopList = dltFrontendHistoryTopMapper.selectList(
|
||||
new QueryWrapper<DltFrontendHistoryTop>());
|
||||
for (DltFrontendHistoryTop item : historyTopList) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
return allBalls;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在百期排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getTop100Rankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的ranking字段作为排名
|
||||
List<DltFrontendHistoryTop100> top100List = dltFrontendHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<DltFrontendHistoryTop100>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的ranking字段作为排名
|
||||
for (DltFrontendHistoryTop100 item : top100List) {
|
||||
result.put(item.getBallNumber(), item.getRanking());
|
||||
}
|
||||
|
||||
// 对于没有在百期排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在历史排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getHistoryRankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的ranking字段作为排名
|
||||
List<DltFrontendHistoryTop> historyTopList = dltFrontendHistoryTopMapper.selectList(
|
||||
new QueryWrapper<DltFrontendHistoryTop>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的ranking字段作为排名
|
||||
for (DltFrontendHistoryTop item : historyTopList) {
|
||||
result.put(item.getBallNumber(), item.getRanking());
|
||||
}
|
||||
|
||||
// 对于没有在历史排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数验证
|
||||
*/
|
||||
private void validateInputParams(String level, List<Integer> frontBalls, List<Integer> backBalls) {
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
throw new IllegalArgumentException("位置级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(frontBalls) || frontBalls.size() != 5) {
|
||||
throw new IllegalArgumentException("前区球号必须为5个");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(backBalls) || backBalls.size() != 2) {
|
||||
throw new IllegalArgumentException("后区球号必须为2个");
|
||||
}
|
||||
|
||||
for (Integer frontBall : frontBalls) {
|
||||
if (frontBall < 1 || frontBall > 35) {
|
||||
throw new IllegalArgumentException("前区球号范围应为1-35");
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer backBall : backBalls) {
|
||||
if (backBall < 1 || backBall > 12) {
|
||||
throw new IllegalArgumentException("后区球号范围应为1-12");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,769 @@
|
||||
package com.xy.xyaicpzs.jt.jtdlt;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.xy.xyaicpzs.domain.entity.*;
|
||||
import com.xy.xyaicpzs.domain.vo.DLTFourthStepResultVO;
|
||||
import com.xy.xyaicpzs.mapper.*;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 精推大乐透第四步算法分析
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FollowBackendBallAnalysis {
|
||||
|
||||
@Autowired
|
||||
private D10Mapper d10Mapper;
|
||||
|
||||
@Autowired
|
||||
private D11Mapper d11Mapper;
|
||||
|
||||
@Autowired
|
||||
private D6Mapper d6Mapper;
|
||||
|
||||
@Autowired
|
||||
private D7Mapper d7Mapper;
|
||||
|
||||
@Autowired
|
||||
private DltBackendHistoryTopMapper dltBackendHistoryTopMapper;
|
||||
|
||||
@Autowired
|
||||
private DltBackendHistoryTop100Mapper dltBackendHistoryTop100Mapper;
|
||||
|
||||
/**
|
||||
* 球号和系数的关联类
|
||||
*/
|
||||
@Data
|
||||
public static class BallWithCoefficient {
|
||||
private Integer ballNumber;
|
||||
private Double coefficient;
|
||||
private Integer masterBallNumber; // 主球号,用于标识来源
|
||||
|
||||
public BallWithCoefficient(Integer ballNumber, Double coefficient, Integer masterBallNumber) {
|
||||
this.ballNumber = ballNumber;
|
||||
this.coefficient = coefficient;
|
||||
this.masterBallNumber = masterBallNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 精推大乐透第四步算法主方法
|
||||
* @param level 位置级别(H-高位/M-中位/L-低位)
|
||||
* @param previousFrontBalls 上期前区5个球号
|
||||
* @param previousBackBalls 上期后区2个球号
|
||||
* @param currentFrontBalls 本期前区5个球号
|
||||
* @param currentBackFirstBall 本期后区首球号码
|
||||
* @return 分析结果
|
||||
*/
|
||||
public DLTFourthStepResultVO analyze(String level, List<Integer> previousFrontBalls,
|
||||
List<Integer> previousBackBalls, List<Integer> currentFrontBalls,
|
||||
Integer currentBackFirstBall) {
|
||||
// 参数验证
|
||||
validateInputParams(level, previousFrontBalls, previousBackBalls, currentFrontBalls, currentBackFirstBall);
|
||||
|
||||
log.info("开始精推大乐透第四步分析,策略:{},上期前区:{},上期后区:{},本期前区:{},本期后区首球:{}",
|
||||
level, previousFrontBalls, previousBackBalls, currentFrontBalls, currentBackFirstBall);
|
||||
|
||||
// 用于存储所有候选球号和对应的系数
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap = new HashMap<>();
|
||||
List<Integer> allCandidateBalls = new ArrayList<>();
|
||||
|
||||
// Step 1: 根据5个上期前区球号从D10表获取候选球(取前10个)
|
||||
for (Integer previousFrontBall : previousFrontBalls) {
|
||||
List<BallWithCoefficient> ballsWithCoefficients = getD10BallsByLevel(previousFrontBall, level);
|
||||
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: 从dlt_backend_history_top取平均值向上连续2个球号
|
||||
List<Integer> top2HistoryTop = getTop2FromDltBackendHistoryTopByAvg();
|
||||
allCandidateBalls.addAll(top2HistoryTop);
|
||||
|
||||
// Step 3: 从dlt_backend_history_top_100取平均值向上连续2个球号
|
||||
List<Integer> top2HistoryTop100 = getTop2FromDltBackendHistoryTop100ByAvg();
|
||||
allCandidateBalls.addAll(top2HistoryTop100);
|
||||
|
||||
// Step 4: 根据2个上期后区球号从D11表获取候选球(取前10个)
|
||||
for (Integer previousBackBall : previousBackBalls) {
|
||||
List<BallWithCoefficient> ballsWithCoefficients = getD11BallsByLevel(previousBackBall, level);
|
||||
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5: 根据5个本期前区球号从D6表获取候选球(取前10个)
|
||||
for (Integer currentFrontBall : currentFrontBalls) {
|
||||
List<BallWithCoefficient> ballsWithCoefficients = getD6BallsByLevel(currentFrontBall, level);
|
||||
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6: 根据1个本期后区首球从D7表获取候选球(根据策略取不同数量)
|
||||
List<BallWithCoefficient> ballsFromD7 = getD7BallsByLevel(currentBackFirstBall, level);
|
||||
for (BallWithCoefficient ball : ballsFromD7) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
|
||||
// Step 7: 统计分析并生成结果
|
||||
List<DLTFourthStepResultVO.BallAnalysisResult> results = analyzeResults(allCandidateBalls, ballCoefficientMap);
|
||||
|
||||
return DLTFourthStepResultVO.builder()
|
||||
.results(results)
|
||||
.strategy(level)
|
||||
.previousFrontBalls(previousFrontBalls)
|
||||
.previousBackBalls(previousBackBalls)
|
||||
.currentFrontBalls(currentFrontBalls)
|
||||
.currentBackFirstBall(currentBackFirstBall)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从D10表获取候选球号和系数(取前10个,如果第10个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getD10BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<D10> d10List = d10Mapper.selectList(
|
||||
new QueryWrapper<D10>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("coefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(d10List)) {
|
||||
log.warn("D10表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 所有策略都取前10个,如果第10个系数相同则一并加入
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (d10List.size() <= 10) {
|
||||
// 如果总数不超过10个,全部加入
|
||||
for (D10 d10 : d10List) {
|
||||
result.add(new BallWithCoefficient(d10.getSlaveBallNumber(), d10.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前10个
|
||||
for (int i = 0; i < 10; i++) {
|
||||
D10 d10 = d10List.get(i);
|
||||
result.add(new BallWithCoefficient(d10.getSlaveBallNumber(), d10.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第10个球号的系数
|
||||
Double boundaryCoefficient = d10List.get(9).getCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 10; i < d10List.size(); i++) {
|
||||
D10 d10 = d10List.get(i);
|
||||
if (d10.getCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(d10.getSlaveBallNumber(), d10.getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从D11表获取候选球号和系数(取前10个,如果第10个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getD11BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<D11> d11List = d11Mapper.selectList(
|
||||
new QueryWrapper<D11>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("coefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(d11List)) {
|
||||
log.warn("D11表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 所有策略都取前10个,如果第10个系数相同则一并加入
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (d11List.size() <= 10) {
|
||||
// 如果总数不超过10个,全部加入
|
||||
for (D11 d11 : d11List) {
|
||||
result.add(new BallWithCoefficient(d11.getSlaveBallNumber(), d11.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前10个
|
||||
for (int i = 0; i < 10; i++) {
|
||||
D11 d11 = d11List.get(i);
|
||||
result.add(new BallWithCoefficient(d11.getSlaveBallNumber(), d11.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第10个球号的系数
|
||||
Double boundaryCoefficient = d11List.get(9).getCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 10; i < d11List.size(); i++) {
|
||||
D11 d11 = d11List.get(i);
|
||||
if (d11.getCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(d11.getSlaveBallNumber(), d11.getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从D6表获取候选球号和系数(取前10个,如果第10个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getD6BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<D6> d6List = d6Mapper.selectList(
|
||||
new QueryWrapper<D6>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("coefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(d6List)) {
|
||||
log.warn("D6表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 所有策略都取前10个,如果第10个系数相同则一并加入
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (d6List.size() <= 10) {
|
||||
// 如果总数不超过10个,全部加入
|
||||
for (D6 d6 : d6List) {
|
||||
result.add(new BallWithCoefficient(d6.getSlaveBallNumber(), d6.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前10个
|
||||
for (int i = 0; i < 10; i++) {
|
||||
D6 d6 = d6List.get(i);
|
||||
result.add(new BallWithCoefficient(d6.getSlaveBallNumber(), d6.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第10个球号的系数
|
||||
Double boundaryCoefficient = d6List.get(9).getCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 10; i < d6List.size(); i++) {
|
||||
D6 d6 = d6List.get(i);
|
||||
if (d6.getCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(d6.getSlaveBallNumber(), d6.getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从D7表获取候选球号和系数
|
||||
*/
|
||||
private List<BallWithCoefficient> getD7BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<D7> d7List = d7Mapper.selectList(
|
||||
new QueryWrapper<D7>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("coefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(d7List)) {
|
||||
log.warn("D7表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if ("H".equalsIgnoreCase(level)) {
|
||||
return getHighLevelBallsFromD7(d7List, masterBallNumber);
|
||||
} else if ("M".equalsIgnoreCase(level)) {
|
||||
return getMiddleLevelBallsFromD7(d7List, masterBallNumber);
|
||||
} else { // L
|
||||
return getLowLevelBallsFromD7(d7List, masterBallNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 高位策略:从D7表获取系数最大的前5个球号(如果第5个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getHighLevelBallsFromD7(List<D7> d7List, Integer masterBallNumber) {
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (d7List.size() <= 5) {
|
||||
// 如果总数不超过5个,全部加入
|
||||
for (D7 d7 : d7List) {
|
||||
result.add(new BallWithCoefficient(d7.getSlaveBallNumber(), d7.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前5个
|
||||
for (int i = 0; i < 5; i++) {
|
||||
D7 d7 = d7List.get(i);
|
||||
result.add(new BallWithCoefficient(d7.getSlaveBallNumber(), d7.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第5个球号的系数
|
||||
Double boundaryCoefficient = d7List.get(4).getCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 5; i < d7List.size(); i++) {
|
||||
D7 d7 = d7List.get(i);
|
||||
if (d7.getCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(d7.getSlaveBallNumber(), d7.getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 中位策略:从D7表获取平均值附近的5个球号(向上2个,向下2个,边界系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getMiddleLevelBallsFromD7(List<D7> d7List, Integer masterBallNumber) {
|
||||
if (d7List.size() < 5) {
|
||||
log.warn("D7表数据不足5条,实际{}条", d7List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (D7 d7 : d7List) {
|
||||
result.add(new BallWithCoefficient(d7.getSlaveBallNumber(), d7.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 计算平均系数(保留两位小数,截断方式)
|
||||
double avgCoefficient = d7List.stream()
|
||||
.mapToDouble(D7::getCoefficient)
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
// 截断到两位小数
|
||||
avgCoefficient = Math.floor(avgCoefficient * 100) / 100;
|
||||
|
||||
// 找到第一个比平均值大的位置(从下到上遍历)
|
||||
int avgPosition = -1;
|
||||
for (int i = d7List.size() - 1; i >= 0; i--) {
|
||||
if (d7List.get(i).getCoefficient() >= avgCoefficient) {
|
||||
avgPosition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (avgPosition == -1) {
|
||||
avgPosition = 0; // 如果没找到,取第一个
|
||||
}
|
||||
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
// 先加入平均值位置的球号
|
||||
result.add(new BallWithCoefficient(d7List.get(avgPosition).getSlaveBallNumber(),
|
||||
d7List.get(avgPosition).getCoefficient(), masterBallNumber));
|
||||
|
||||
// 向上取2个球号,处理边界系数相同情况
|
||||
int upCount = 0;
|
||||
Double upBoundaryCoeff = null;
|
||||
for (int i = avgPosition - 1; i >= 0 && upCount < 2; i--) {
|
||||
result.add(new BallWithCoefficient(d7List.get(i).getSlaveBallNumber(),
|
||||
d7List.get(i).getCoefficient(), masterBallNumber));
|
||||
upCount++;
|
||||
if (upCount == 2) {
|
||||
upBoundaryCoeff = d7List.get(i).getCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向上边界处理:继续添加与第2个球号系数相同的球号
|
||||
if (upBoundaryCoeff != null) {
|
||||
for (int i = avgPosition - 3; i >= 0; i--) {
|
||||
if (d7List.get(i).getCoefficient().equals(upBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(d7List.get(i).getSlaveBallNumber(),
|
||||
d7List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 向下取2个球号,处理边界系数相同情况
|
||||
int downCount = 0;
|
||||
Double downBoundaryCoeff = null;
|
||||
for (int i = avgPosition + 1; i < d7List.size() && downCount < 2; i++) {
|
||||
result.add(new BallWithCoefficient(d7List.get(i).getSlaveBallNumber(),
|
||||
d7List.get(i).getCoefficient(), masterBallNumber));
|
||||
downCount++;
|
||||
if (downCount == 2) {
|
||||
downBoundaryCoeff = d7List.get(i).getCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向下边界处理:继续添加与第2个球号系数相同的球号
|
||||
if (downBoundaryCoeff != null) {
|
||||
for (int i = avgPosition + 3; i < d7List.size(); i++) {
|
||||
if (d7List.get(i).getCoefficient().equals(downBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(d7List.get(i).getSlaveBallNumber(),
|
||||
d7List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 低位策略:从D7表获取最小值向上5个球号(含最小值,第1个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getLowLevelBallsFromD7(List<D7> d7List, Integer masterBallNumber) {
|
||||
if (d7List.size() < 5) {
|
||||
log.warn("D7表数据不足5条,实际{}条", d7List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (D7 d7 : d7List) {
|
||||
result.add(new BallWithCoefficient(d7.getSlaveBallNumber(), d7.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 从最小值开始向上取5个(d7List已按系数降序排列,最后5个就是最小的)
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
int startIndex = Math.max(0, d7List.size() - 5);
|
||||
|
||||
// 先加入基本的5个球号
|
||||
for (int i = startIndex; i < d7List.size(); i++) {
|
||||
D7 d7 = d7List.get(i);
|
||||
result.add(new BallWithCoefficient(d7.getSlaveBallNumber(), d7.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 处理第1个球号(系数最大的)边界相同情况
|
||||
if (startIndex > 0) {
|
||||
Double firstBallCoeff = d7List.get(startIndex).getCoefficient();
|
||||
|
||||
// 继续向前查找系数相同的球号
|
||||
for (int i = startIndex - 1; i >= 0; i--) {
|
||||
if (d7List.get(i).getCoefficient().equals(firstBallCoeff)) {
|
||||
result.add(new BallWithCoefficient(d7List.get(i).getSlaveBallNumber(),
|
||||
d7List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从dlt_backend_history_top取平均值向上连续2个球号(按系数排行,处理边界相同系数)
|
||||
*/
|
||||
private List<Integer> getTop2FromDltBackendHistoryTopByAvg() {
|
||||
List<DltBackendHistoryTop> allHistoryTopList = dltBackendHistoryTopMapper.selectList(
|
||||
new QueryWrapper<DltBackendHistoryTop>()
|
||||
.orderByDesc("activeCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(allHistoryTopList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 计算平均系数(保留两位小数,截断方式)
|
||||
final double avgCoefficient = Math.floor(allHistoryTopList.stream()
|
||||
.mapToDouble(DltBackendHistoryTop::getActiveCoefficient)
|
||||
.average()
|
||||
.orElse(0.0) * 100) / 100;
|
||||
|
||||
// 筛选出大于或等于平均值的球,并按差值排序
|
||||
List<DltBackendHistoryTop> aboveAvgBalls = allHistoryTopList.stream()
|
||||
.filter(ball -> ball.getActiveCoefficient() >= avgCoefficient)
|
||||
.sorted(Comparator.comparingDouble(ball -> ball.getActiveCoefficient() - avgCoefficient))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 如果没有大于或等于平均值的球号,返回空列表
|
||||
if (aboveAvgBalls.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 逐层添加球号,直到达到2个或更多(相同差值的要一起加入)
|
||||
List<Integer> result = new ArrayList<>();
|
||||
double currentDiff = -1;
|
||||
|
||||
for (DltBackendHistoryTop ball : aboveAvgBalls) {
|
||||
double diff = ball.getActiveCoefficient() - avgCoefficient;
|
||||
|
||||
// 如果已经有2个球了,且当前差值与之前不同,则停止
|
||||
if (result.size() >= 2 && diff != currentDiff) {
|
||||
break;
|
||||
}
|
||||
|
||||
result.add(ball.getBallNumber());
|
||||
currentDiff = diff;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从dlt_backend_history_top_100取平均值向上连续2个球号(按系数排行,处理边界相同系数)
|
||||
*/
|
||||
private List<Integer> getTop2FromDltBackendHistoryTop100ByAvg() {
|
||||
List<DltBackendHistoryTop100> allHistoryTop100List = dltBackendHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<DltBackendHistoryTop100>()
|
||||
.orderByDesc("activeCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(allHistoryTop100List)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 计算平均系数(保留两位小数,截断方式)
|
||||
final double avgCoefficient = Math.floor(allHistoryTop100List.stream()
|
||||
.mapToDouble(DltBackendHistoryTop100::getActiveCoefficient)
|
||||
.average()
|
||||
.orElse(0.0) * 100) / 100;
|
||||
|
||||
// 筛选出大于或等于平均值的球,并按差值排序
|
||||
List<DltBackendHistoryTop100> aboveAvgBalls = allHistoryTop100List.stream()
|
||||
.filter(ball -> ball.getActiveCoefficient() >= avgCoefficient)
|
||||
.sorted(Comparator.comparingDouble(ball -> ball.getActiveCoefficient() - avgCoefficient))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 如果没有大于或等于平均值的球号,返回空列表
|
||||
if (aboveAvgBalls.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 逐层添加球号,直到达到2个或更多(相同差值的要一起加入)
|
||||
List<Integer> result = new ArrayList<>();
|
||||
double currentDiff = -1;
|
||||
|
||||
for (DltBackendHistoryTop100 ball : aboveAvgBalls) {
|
||||
double diff = ball.getActiveCoefficient() - avgCoefficient;
|
||||
|
||||
// 如果已经有2个球了,且当前差值与之前不同,则停止
|
||||
if (result.size() >= 2 && diff != currentDiff) {
|
||||
break;
|
||||
}
|
||||
|
||||
result.add(ball.getBallNumber());
|
||||
currentDiff = diff;
|
||||
}
|
||||
|
||||
// 如果结果球号数量 > 2,通过dlt_backend_history_top表筛选
|
||||
if (result.size() > 2) {
|
||||
return selectTop2FromDltBackendHistoryTop(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从dlt_backend_history_top表中选择前2个球号
|
||||
*/
|
||||
private List<Integer> selectTop2FromDltBackendHistoryTop(List<Integer> candidateBalls) {
|
||||
// 获取候选球号在dlt_backend_history_top表中的活跃系数
|
||||
Map<Integer, Double> ballCoefficientMap = new HashMap<>();
|
||||
for (Integer ballNumber : candidateBalls) {
|
||||
DltBackendHistoryTop record = dltBackendHistoryTopMapper.selectOne(
|
||||
new QueryWrapper<DltBackendHistoryTop>()
|
||||
.eq("ballNumber", ballNumber));
|
||||
double coefficient = (record != null) ? record.getActiveCoefficient() : 0.0;
|
||||
ballCoefficientMap.put(ballNumber, coefficient);
|
||||
}
|
||||
|
||||
// 按活跃系数降序排序,选择前2个
|
||||
List<Map.Entry<Integer, Double>> sortedEntries = ballCoefficientMap.entrySet().stream()
|
||||
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
for (int i = 0; i < Math.min(2, sortedEntries.size()); i++) {
|
||||
result.add(sortedEntries.get(i).getKey());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计分析并生成结果
|
||||
*/
|
||||
private List<DLTFourthStepResultVO.BallAnalysisResult> analyzeResults(
|
||||
List<Integer> allCandidateBalls,
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap) {
|
||||
|
||||
// 统计球号出现次数
|
||||
Map<Integer, Integer> ballFrequencyMap = new HashMap<>();
|
||||
for (Integer ball : allCandidateBalls) {
|
||||
ballFrequencyMap.put(ball, ballFrequencyMap.getOrDefault(ball, 0) + 1);
|
||||
}
|
||||
|
||||
// 计算系数和
|
||||
Map<Integer, Double> ballCoefficientSumMap = new HashMap<>();
|
||||
for (Map.Entry<Integer, List<BallWithCoefficient>> entry : ballCoefficientMap.entrySet()) {
|
||||
Integer ballNumber = entry.getKey();
|
||||
List<BallWithCoefficient> coefficients = entry.getValue();
|
||||
double sum = coefficients.stream()
|
||||
.mapToDouble(BallWithCoefficient::getCoefficient)
|
||||
.sum();
|
||||
ballCoefficientSumMap.put(ballNumber, sum);
|
||||
}
|
||||
|
||||
// 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
Set<Integer> allBallsWithRanking = getAllBallsWithRanking();
|
||||
|
||||
// 获取百期排位和历史排位
|
||||
Map<Integer, Integer> top100RankingMap = getTop100Rankings(allBallsWithRanking);
|
||||
Map<Integer, Integer> historyRankingMap = getHistoryRankings(allBallsWithRanking);
|
||||
|
||||
// 组装结果
|
||||
List<DLTFourthStepResultVO.BallAnalysisResult> results = new ArrayList<>();
|
||||
for (Integer ballNumber : allBallsWithRanking) {
|
||||
DLTFourthStepResultVO.BallAnalysisResult result = DLTFourthStepResultVO.BallAnalysisResult.builder()
|
||||
.ballNumber(ballNumber)
|
||||
.frequency(ballFrequencyMap.getOrDefault(ballNumber, 0))
|
||||
.coefficientSum(ballCoefficientSumMap.getOrDefault(ballNumber, 0.0))
|
||||
.top100Ranking(top100RankingMap.get(ballNumber))
|
||||
.historyRanking(historyRankingMap.get(ballNumber))
|
||||
.build();
|
||||
results.add(result);
|
||||
}
|
||||
|
||||
// 按出现次数降序排列,次数相同的按球号升序排列
|
||||
results.sort((a, b) -> {
|
||||
int frequencyCompare = b.getFrequency().compareTo(a.getFrequency());
|
||||
if (frequencyCompare != 0) {
|
||||
return frequencyCompare;
|
||||
}
|
||||
return a.getBallNumber().compareTo(b.getBallNumber());
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
*/
|
||||
private Set<Integer> getAllBallsWithRanking() {
|
||||
Set<Integer> allBalls = new HashSet<>();
|
||||
|
||||
// 从DLT后区百期排行表获取所有球号
|
||||
List<DltBackendHistoryTop100> top100List = dltBackendHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<DltBackendHistoryTop100>());
|
||||
for (DltBackendHistoryTop100 item : top100List) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
// 从DLT后区历史排行表获取所有球号
|
||||
List<DltBackendHistoryTop> historyTopList = dltBackendHistoryTopMapper.selectList(
|
||||
new QueryWrapper<DltBackendHistoryTop>());
|
||||
for (DltBackendHistoryTop item : historyTopList) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
return allBalls;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在百期排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getTop100Rankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的ranking字段作为排名
|
||||
List<DltBackendHistoryTop100> top100List = dltBackendHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<DltBackendHistoryTop100>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的ranking字段作为排名
|
||||
for (DltBackendHistoryTop100 item : top100List) {
|
||||
result.put(item.getBallNumber(), item.getRanking());
|
||||
}
|
||||
|
||||
// 对于没有在百期排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在历史排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getHistoryRankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的ranking字段作为排名
|
||||
List<DltBackendHistoryTop> historyTopList = dltBackendHistoryTopMapper.selectList(
|
||||
new QueryWrapper<DltBackendHistoryTop>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的ranking字段作为排名
|
||||
for (DltBackendHistoryTop item : historyTopList) {
|
||||
result.put(item.getBallNumber(), item.getRanking());
|
||||
}
|
||||
|
||||
// 对于没有在历史排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数验证
|
||||
*/
|
||||
private void validateInputParams(String level, List<Integer> previousFrontBalls,
|
||||
List<Integer> previousBackBalls, List<Integer> currentFrontBalls,
|
||||
Integer currentBackFirstBall) {
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
throw new IllegalArgumentException("位置级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(previousFrontBalls) || previousFrontBalls.size() != 5) {
|
||||
throw new IllegalArgumentException("上期前区球号必须为5个");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(previousBackBalls) || previousBackBalls.size() != 2) {
|
||||
throw new IllegalArgumentException("上期后区球号必须为2个");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(currentFrontBalls) || currentFrontBalls.size() != 5) {
|
||||
throw new IllegalArgumentException("本期前区球号必须为5个");
|
||||
}
|
||||
|
||||
if (currentBackFirstBall == null || currentBackFirstBall < 1 || currentBackFirstBall > 12) {
|
||||
throw new IllegalArgumentException("本期后区首球号码范围应为1-12");
|
||||
}
|
||||
|
||||
for (Integer frontBall : previousFrontBalls) {
|
||||
if (frontBall < 1 || frontBall > 35) {
|
||||
throw new IllegalArgumentException("上期前区球号范围应为1-35");
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer backBall : previousBackBalls) {
|
||||
if (backBall < 1 || backBall > 12) {
|
||||
throw new IllegalArgumentException("上期后区球号范围应为1-12");
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer frontBall : currentFrontBalls) {
|
||||
if (frontBall < 1 || frontBall > 35) {
|
||||
throw new IllegalArgumentException("本期前区球号范围应为1-35");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,714 @@
|
||||
package com.xy.xyaicpzs.jt.jtdlt;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.xy.xyaicpzs.domain.entity.*;
|
||||
import com.xy.xyaicpzs.domain.vo.DLTSecondStepResultVO;
|
||||
import com.xy.xyaicpzs.mapper.*;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 精推大乐透第二步算法分析
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FollowFrontBallAnalysis {
|
||||
|
||||
@Autowired
|
||||
private D9Mapper d9Mapper;
|
||||
|
||||
@Autowired
|
||||
private D12Mapper d12Mapper;
|
||||
|
||||
@Autowired
|
||||
private D5Mapper d5Mapper;
|
||||
|
||||
@Autowired
|
||||
private DltFrontendHistoryTopMapper dltFrontendHistoryTopMapper;
|
||||
|
||||
@Autowired
|
||||
private DltFrontendHistoryTop100Mapper dltFrontendHistoryTop100Mapper;
|
||||
|
||||
/**
|
||||
* 球号和系数的关联类
|
||||
*/
|
||||
@Data
|
||||
public static class BallWithCoefficient {
|
||||
private Integer ballNumber;
|
||||
private Double coefficient;
|
||||
private Integer masterBallNumber; // 主球号,用于标识来源
|
||||
|
||||
public BallWithCoefficient(Integer ballNumber, Double coefficient, Integer masterBallNumber) {
|
||||
this.ballNumber = ballNumber;
|
||||
this.coefficient = coefficient;
|
||||
this.masterBallNumber = masterBallNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 精推大乐透第二步算法主方法
|
||||
* @param level 位置级别(H-高位/M-中位/L-低位)
|
||||
* @param previousFrontBalls 上期前区5个球号
|
||||
* @param previousBackBalls 上期后区2个球号
|
||||
* @param currentFirstBall 本期首球号码
|
||||
* @return 分析结果
|
||||
*/
|
||||
public DLTSecondStepResultVO analyze(String level, List<Integer> previousFrontBalls,
|
||||
List<Integer> previousBackBalls, Integer currentFirstBall) {
|
||||
// 参数验证
|
||||
validateInputParams(level, previousFrontBalls, previousBackBalls, currentFirstBall);
|
||||
|
||||
log.info("开始精推大乐透第二步分析,策略:{},上期前区:{},上期后区:{},本期首球:{}",
|
||||
level, previousFrontBalls, previousBackBalls, currentFirstBall);
|
||||
|
||||
// 用于存储所有候选球号和对应的系数
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap = new HashMap<>();
|
||||
List<Integer> allCandidateBalls = new ArrayList<>();
|
||||
|
||||
// Step 1: 根据5个上期前区球号从D9表获取候选球(取30个)
|
||||
for (Integer frontBall : previousFrontBalls) {
|
||||
List<BallWithCoefficient> ballsWithCoefficients = getD9BallsByLevel(frontBall, level);
|
||||
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: 从dlt_frontend_history_top取平均值向上连续3个球号
|
||||
List<Integer> top3HistoryTop = getTop3FromDltFrontendHistoryTopByAvg();
|
||||
allCandidateBalls.addAll(top3HistoryTop);
|
||||
|
||||
// Step 3: 从dlt_frontend_history_top_100取平均值向上连续3个球号
|
||||
List<Integer> top3HistoryTop100 = getTop3FromDltFrontendHistoryTop100ByAvg();
|
||||
allCandidateBalls.addAll(top3HistoryTop100);
|
||||
|
||||
// Step 4: 根据2个上期后区球号从D12表获取候选球(取30个)
|
||||
for (Integer backBall : previousBackBalls) {
|
||||
List<BallWithCoefficient> ballsWithCoefficients = getD12BallsByLevel(backBall, level);
|
||||
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5: 根据本期首球从D5表获取候选球(取11个)
|
||||
List<BallWithCoefficient> ballsFromD5 = getD5BallsByLevel(currentFirstBall, level);
|
||||
for (BallWithCoefficient ball : ballsFromD5) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
|
||||
// Step 6: 统计分析并生成结果
|
||||
List<DLTSecondStepResultVO.BallAnalysisResult> results = analyzeResults(allCandidateBalls, ballCoefficientMap);
|
||||
|
||||
return DLTSecondStepResultVO.builder()
|
||||
.results(results)
|
||||
.strategy(level)
|
||||
.previousFrontBalls(previousFrontBalls)
|
||||
.previousBackBalls(previousBackBalls)
|
||||
.currentFirstBall(currentFirstBall)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从D9表获取候选球号和系数(取30个,如果第30个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getD9BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<D9> d9List = d9Mapper.selectList(
|
||||
new QueryWrapper<D9>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("coefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(d9List)) {
|
||||
log.warn("D9表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 所有策略都取前30个,如果第30个系数相同则一并加入
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (d9List.size() <= 30) {
|
||||
// 如果总数不超过30个,全部加入
|
||||
for (D9 d9 : d9List) {
|
||||
result.add(new BallWithCoefficient(d9.getSlaveBallNumber(), d9.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前30个
|
||||
for (int i = 0; i < 30; i++) {
|
||||
D9 d9 = d9List.get(i);
|
||||
result.add(new BallWithCoefficient(d9.getSlaveBallNumber(), d9.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第30个球号的系数
|
||||
Double boundaryCoefficient = d9List.get(29).getCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 30; i < d9List.size(); i++) {
|
||||
D9 d9 = d9List.get(i);
|
||||
if (d9.getCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(d9.getSlaveBallNumber(), d9.getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从D12表获取候选球号和系数(取30个,如果第30个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getD12BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<D12> d12List = d12Mapper.selectList(
|
||||
new QueryWrapper<D12>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("coefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(d12List)) {
|
||||
log.warn("D12表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 所有策略都取前30个,如果第30个系数相同则一并加入
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (d12List.size() <= 30) {
|
||||
// 如果总数不超过30个,全部加入
|
||||
for (D12 d12 : d12List) {
|
||||
result.add(new BallWithCoefficient(d12.getSlaveBallNumber(), d12.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前30个
|
||||
for (int i = 0; i < 30; i++) {
|
||||
D12 d12 = d12List.get(i);
|
||||
result.add(new BallWithCoefficient(d12.getSlaveBallNumber(), d12.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第30个球号的系数
|
||||
Double boundaryCoefficient = d12List.get(29).getCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 30; i < d12List.size(); i++) {
|
||||
D12 d12 = d12List.get(i);
|
||||
if (d12.getCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(d12.getSlaveBallNumber(), d12.getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从D5表获取候选球号和系数(取11个)
|
||||
*/
|
||||
private List<BallWithCoefficient> getD5BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<D5> d5List = d5Mapper.selectList(
|
||||
new QueryWrapper<D5>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("coefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(d5List)) {
|
||||
log.warn("D5表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if ("H".equalsIgnoreCase(level)) {
|
||||
return getHighLevelBallsFromD5(d5List, masterBallNumber);
|
||||
} else if ("M".equalsIgnoreCase(level)) {
|
||||
return getMiddleLevelBallsFromD5(d5List, masterBallNumber);
|
||||
} else { // L
|
||||
return getLowLevelBallsFromD5(d5List, masterBallNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 高位策略:从D5表获取系数最大的前11个球号(如果第11个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getHighLevelBallsFromD5(List<D5> d5List, Integer masterBallNumber) {
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (d5List.size() <= 11) {
|
||||
// 如果总数不超过11个,全部加入
|
||||
for (D5 d5 : d5List) {
|
||||
result.add(new BallWithCoefficient(d5.getSlaveBallNumber(), d5.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前11个
|
||||
for (int i = 0; i < 11; i++) {
|
||||
D5 d5 = d5List.get(i);
|
||||
result.add(new BallWithCoefficient(d5.getSlaveBallNumber(), d5.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第11个球号的系数
|
||||
Double boundaryCoefficient = d5List.get(10).getCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 11; i < d5List.size(); i++) {
|
||||
D5 d5 = d5List.get(i);
|
||||
if (d5.getCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(d5.getSlaveBallNumber(), d5.getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 中位策略:从D5表获取平均值附近的11个球号(向上5个,向下5个,边界系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getMiddleLevelBallsFromD5(List<D5> d5List, Integer masterBallNumber) {
|
||||
if (d5List.size() < 11) {
|
||||
log.warn("D5表数据不足11条,实际{}条", d5List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (D5 d5 : d5List) {
|
||||
result.add(new BallWithCoefficient(d5.getSlaveBallNumber(), d5.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 计算平均系数(保留两位小数,截断方式)
|
||||
double avgCoefficient = d5List.stream()
|
||||
.mapToDouble(D5::getCoefficient)
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
// 截断到两位小数
|
||||
avgCoefficient = Math.floor(avgCoefficient * 100) / 100;
|
||||
|
||||
// 找到第一个比平均值大的位置(从下到上遍历)
|
||||
int avgPosition = -1;
|
||||
for (int i = d5List.size() - 1; i >= 0; i--) {
|
||||
if (d5List.get(i).getCoefficient() >= avgCoefficient) {
|
||||
avgPosition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (avgPosition == -1) {
|
||||
avgPosition = 0; // 如果没找到,取第一个
|
||||
}
|
||||
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
// 先加入平均值位置的球号
|
||||
result.add(new BallWithCoefficient(d5List.get(avgPosition).getSlaveBallNumber(),
|
||||
d5List.get(avgPosition).getCoefficient(), masterBallNumber));
|
||||
|
||||
// 向上取5个球号,处理边界系数相同情况
|
||||
int upCount = 0;
|
||||
Double upBoundaryCoeff = null;
|
||||
for (int i = avgPosition - 1; i >= 0 && upCount < 5; i--) {
|
||||
result.add(new BallWithCoefficient(d5List.get(i).getSlaveBallNumber(),
|
||||
d5List.get(i).getCoefficient(), masterBallNumber));
|
||||
upCount++;
|
||||
if (upCount == 5) {
|
||||
upBoundaryCoeff = d5List.get(i).getCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向上边界处理:继续添加与第5个球号系数相同的球号
|
||||
if (upBoundaryCoeff != null) {
|
||||
for (int i = avgPosition - 6; i >= 0; i--) {
|
||||
if (d5List.get(i).getCoefficient().equals(upBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(d5List.get(i).getSlaveBallNumber(),
|
||||
d5List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 向下取5个球号,处理边界系数相同情况
|
||||
int downCount = 0;
|
||||
Double downBoundaryCoeff = null;
|
||||
for (int i = avgPosition + 1; i < d5List.size() && downCount < 5; i++) {
|
||||
result.add(new BallWithCoefficient(d5List.get(i).getSlaveBallNumber(),
|
||||
d5List.get(i).getCoefficient(), masterBallNumber));
|
||||
downCount++;
|
||||
if (downCount == 5) {
|
||||
downBoundaryCoeff = d5List.get(i).getCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向下边界处理:继续添加与第5个球号系数相同的球号
|
||||
if (downBoundaryCoeff != null) {
|
||||
for (int i = avgPosition + 6; i < d5List.size(); i++) {
|
||||
if (d5List.get(i).getCoefficient().equals(downBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(d5List.get(i).getSlaveBallNumber(),
|
||||
d5List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 低位策略:从D5表获取系数最小值(含最小值)向上第3-13的球,共11个球号(边界系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getLowLevelBallsFromD5(List<D5> d5List, Integer masterBallNumber) {
|
||||
if (d5List.size() < 13) {
|
||||
log.warn("D5表数据不足13条,实际{}条", d5List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (D5 d5 : d5List) {
|
||||
result.add(new BallWithCoefficient(d5.getSlaveBallNumber(), d5.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 从最小值向上第3-13的球(d5List已按系数降序排列)
|
||||
// 最小值在最后,向上第3-13就是从倒数第13个到倒数第3个
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
int size = d5List.size();
|
||||
int startIndex = size - 13; // 倒数第13个
|
||||
int endIndex = size - 3; // 倒数第3个
|
||||
|
||||
// 先加入基本的11个球号
|
||||
for (int i = startIndex; i <= endIndex; i++) {
|
||||
D5 d5 = d5List.get(i);
|
||||
result.add(new BallWithCoefficient(d5.getSlaveBallNumber(), d5.getCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 处理起始边界:第1个球号(startIndex)系数相同情况
|
||||
if (startIndex > 0) {
|
||||
Double startBoundaryCoeff = d5List.get(startIndex).getCoefficient();
|
||||
|
||||
// 继续向前查找系数相同的球号
|
||||
for (int i = startIndex - 1; i >= 0; i--) {
|
||||
if (d5List.get(i).getCoefficient().equals(startBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(d5List.get(i).getSlaveBallNumber(),
|
||||
d5List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理结束边界:第11个球号(endIndex)系数相同情况
|
||||
if (endIndex < d5List.size() - 1) {
|
||||
Double endBoundaryCoeff = d5List.get(endIndex).getCoefficient();
|
||||
|
||||
// 继续向后查找系数相同的球号
|
||||
for (int i = endIndex + 1; i < d5List.size(); i++) {
|
||||
if (d5List.get(i).getCoefficient().equals(endBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(d5List.get(i).getSlaveBallNumber(),
|
||||
d5List.get(i).getCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从dlt_frontend_history_top取平均值向上连续3个球号(按系数排行,处理边界相同系数)
|
||||
*/
|
||||
private List<Integer> getTop3FromDltFrontendHistoryTopByAvg() {
|
||||
List<DltFrontendHistoryTop> allHistoryTopList = dltFrontendHistoryTopMapper.selectList(
|
||||
new QueryWrapper<DltFrontendHistoryTop>()
|
||||
.orderByDesc("activeCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(allHistoryTopList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 计算平均系数(保留两位小数,截断方式)
|
||||
final double avgCoefficient = Math.floor(allHistoryTopList.stream()
|
||||
.mapToDouble(DltFrontendHistoryTop::getActiveCoefficient)
|
||||
.average()
|
||||
.orElse(0.0) * 100) / 100;
|
||||
|
||||
// 筛选出大于或等于平均值的球,并按差值排序
|
||||
List<DltFrontendHistoryTop> aboveAvgBalls = allHistoryTopList.stream()
|
||||
.filter(ball -> ball.getActiveCoefficient() >= avgCoefficient)
|
||||
.sorted(Comparator.comparingDouble(ball -> ball.getActiveCoefficient() - avgCoefficient))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 如果没有大于或等于平均值的球号,返回空列表
|
||||
if (aboveAvgBalls.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 逐层添加球号,直到达到3个或更多(相同差值的要一起加入)
|
||||
List<Integer> result = new ArrayList<>();
|
||||
double currentDiff = -1;
|
||||
|
||||
for (DltFrontendHistoryTop ball : aboveAvgBalls) {
|
||||
double diff = ball.getActiveCoefficient() - avgCoefficient;
|
||||
|
||||
// 如果已经有3个球了,且当前差值与之前不同,则停止
|
||||
if (result.size() >= 3 && diff != currentDiff) {
|
||||
break;
|
||||
}
|
||||
|
||||
result.add(ball.getBallNumber());
|
||||
currentDiff = diff;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从dlt_frontend_history_top_100取平均值向上连续3个球号(按系数排行,处理边界相同系数)
|
||||
*/
|
||||
private List<Integer> getTop3FromDltFrontendHistoryTop100ByAvg() {
|
||||
List<DltFrontendHistoryTop100> allHistoryTop100List = dltFrontendHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<DltFrontendHistoryTop100>()
|
||||
.orderByDesc("activeCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(allHistoryTop100List)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 计算平均系数(保留两位小数,截断方式)
|
||||
final double avgCoefficient = Math.floor(allHistoryTop100List.stream()
|
||||
.mapToDouble(DltFrontendHistoryTop100::getActiveCoefficient)
|
||||
.average()
|
||||
.orElse(0.0) * 100) / 100;
|
||||
|
||||
// 筛选出大于或等于平均值的球,并按差值排序
|
||||
List<DltFrontendHistoryTop100> aboveAvgBalls = allHistoryTop100List.stream()
|
||||
.filter(ball -> ball.getActiveCoefficient() >= avgCoefficient)
|
||||
.sorted(Comparator.comparingDouble(ball -> ball.getActiveCoefficient() - avgCoefficient))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 如果没有大于或等于平均值的球号,返回空列表
|
||||
if (aboveAvgBalls.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 逐层添加球号,直到达到3个或更多(相同差值的要一起加入)
|
||||
List<Integer> result = new ArrayList<>();
|
||||
double currentDiff = -1;
|
||||
|
||||
for (DltFrontendHistoryTop100 ball : aboveAvgBalls) {
|
||||
double diff = ball.getActiveCoefficient() - avgCoefficient;
|
||||
|
||||
// 如果已经有3个球了,且当前差值与之前不同,则停止
|
||||
if (result.size() >= 3 && diff != currentDiff) {
|
||||
break;
|
||||
}
|
||||
|
||||
result.add(ball.getBallNumber());
|
||||
currentDiff = diff;
|
||||
}
|
||||
|
||||
// 如果结果球号数量 > 3,通过dlt_frontend_history_top表筛选
|
||||
if (result.size() > 3) {
|
||||
return selectTop3FromDltFrontendHistoryTop(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从dlt_frontend_history_top表中选择前3个球号
|
||||
*/
|
||||
private List<Integer> selectTop3FromDltFrontendHistoryTop(List<Integer> candidateBalls) {
|
||||
// 获取候选球号在dlt_frontend_history_top表中的活跃系数
|
||||
Map<Integer, Double> ballCoefficientMap = new HashMap<>();
|
||||
for (Integer ballNumber : candidateBalls) {
|
||||
DltFrontendHistoryTop record = dltFrontendHistoryTopMapper.selectOne(
|
||||
new QueryWrapper<DltFrontendHistoryTop>()
|
||||
.eq("ballNumber", ballNumber));
|
||||
double coefficient = (record != null) ? record.getActiveCoefficient() : 0.0;
|
||||
ballCoefficientMap.put(ballNumber, coefficient);
|
||||
}
|
||||
|
||||
// 按活跃系数降序排序,选择前3个
|
||||
List<Map.Entry<Integer, Double>> sortedEntries = ballCoefficientMap.entrySet().stream()
|
||||
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
for (int i = 0; i < Math.min(3, sortedEntries.size()); i++) {
|
||||
result.add(sortedEntries.get(i).getKey());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计分析并生成结果
|
||||
*/
|
||||
private List<DLTSecondStepResultVO.BallAnalysisResult> analyzeResults(
|
||||
List<Integer> allCandidateBalls,
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap) {
|
||||
|
||||
// 统计球号出现次数
|
||||
Map<Integer, Integer> ballFrequencyMap = new HashMap<>();
|
||||
for (Integer ball : allCandidateBalls) {
|
||||
ballFrequencyMap.put(ball, ballFrequencyMap.getOrDefault(ball, 0) + 1);
|
||||
}
|
||||
|
||||
// 计算系数和
|
||||
Map<Integer, Double> ballCoefficientSumMap = new HashMap<>();
|
||||
for (Map.Entry<Integer, List<BallWithCoefficient>> entry : ballCoefficientMap.entrySet()) {
|
||||
Integer ballNumber = entry.getKey();
|
||||
List<BallWithCoefficient> coefficients = entry.getValue();
|
||||
double sum = coefficients.stream()
|
||||
.mapToDouble(BallWithCoefficient::getCoefficient)
|
||||
.sum();
|
||||
ballCoefficientSumMap.put(ballNumber, sum);
|
||||
}
|
||||
|
||||
// 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
Set<Integer> allBallsWithRanking = getAllBallsWithRanking();
|
||||
|
||||
// 获取百期排位和历史排位
|
||||
Map<Integer, Integer> top100RankingMap = getTop100Rankings(allBallsWithRanking);
|
||||
Map<Integer, Integer> historyRankingMap = getHistoryRankings(allBallsWithRanking);
|
||||
|
||||
// 组装结果
|
||||
List<DLTSecondStepResultVO.BallAnalysisResult> results = new ArrayList<>();
|
||||
for (Integer ballNumber : allBallsWithRanking) {
|
||||
DLTSecondStepResultVO.BallAnalysisResult result = DLTSecondStepResultVO.BallAnalysisResult.builder()
|
||||
.ballNumber(ballNumber)
|
||||
.frequency(ballFrequencyMap.getOrDefault(ballNumber, 0))
|
||||
.coefficientSum(ballCoefficientSumMap.getOrDefault(ballNumber, 0.0))
|
||||
.top100Ranking(top100RankingMap.get(ballNumber))
|
||||
.historyRanking(historyRankingMap.get(ballNumber))
|
||||
.build();
|
||||
results.add(result);
|
||||
}
|
||||
|
||||
// 按出现次数降序排列,次数相同的按球号升序排列
|
||||
results.sort((a, b) -> {
|
||||
int frequencyCompare = b.getFrequency().compareTo(a.getFrequency());
|
||||
if (frequencyCompare != 0) {
|
||||
return frequencyCompare;
|
||||
}
|
||||
return a.getBallNumber().compareTo(b.getBallNumber());
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
*/
|
||||
private Set<Integer> getAllBallsWithRanking() {
|
||||
Set<Integer> allBalls = new HashSet<>();
|
||||
|
||||
// 从DLT前区百期排行表获取所有球号
|
||||
List<DltFrontendHistoryTop100> top100List = dltFrontendHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<DltFrontendHistoryTop100>());
|
||||
for (DltFrontendHistoryTop100 item : top100List) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
// 从DLT前区历史排行表获取所有球号
|
||||
List<DltFrontendHistoryTop> historyTopList = dltFrontendHistoryTopMapper.selectList(
|
||||
new QueryWrapper<DltFrontendHistoryTop>());
|
||||
for (DltFrontendHistoryTop item : historyTopList) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
return allBalls;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在百期排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getTop100Rankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的ranking字段作为排名
|
||||
List<DltFrontendHistoryTop100> top100List = dltFrontendHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<DltFrontendHistoryTop100>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的ranking字段作为排名
|
||||
for (DltFrontendHistoryTop100 item : top100List) {
|
||||
result.put(item.getBallNumber(), item.getRanking());
|
||||
}
|
||||
|
||||
// 对于没有在百期排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在历史排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getHistoryRankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的ranking字段作为排名
|
||||
List<DltFrontendHistoryTop> historyTopList = dltFrontendHistoryTopMapper.selectList(
|
||||
new QueryWrapper<DltFrontendHistoryTop>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的ranking字段作为排名
|
||||
for (DltFrontendHistoryTop item : historyTopList) {
|
||||
result.put(item.getBallNumber(), item.getRanking());
|
||||
}
|
||||
|
||||
// 对于没有在历史排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数验证
|
||||
*/
|
||||
private void validateInputParams(String level, List<Integer> previousFrontBalls,
|
||||
List<Integer> previousBackBalls, Integer currentFirstBall) {
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
throw new IllegalArgumentException("位置级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(previousFrontBalls) || previousFrontBalls.size() != 5) {
|
||||
throw new IllegalArgumentException("上期前区球号必须为5个");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(previousBackBalls) || previousBackBalls.size() != 2) {
|
||||
throw new IllegalArgumentException("上期后区球号必须为2个");
|
||||
}
|
||||
|
||||
if (currentFirstBall == null || currentFirstBall < 1 || currentFirstBall > 35) {
|
||||
throw new IllegalArgumentException("本期首球号码范围应为1-35");
|
||||
}
|
||||
|
||||
for (Integer frontBall : previousFrontBalls) {
|
||||
if (frontBall < 1 || frontBall > 35) {
|
||||
throw new IllegalArgumentException("上期前区球号范围应为1-35");
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer backBall : previousBackBalls) {
|
||||
if (backBall < 1 || backBall > 12) {
|
||||
throw new IllegalArgumentException("上期后区球号范围应为1-12");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
693
src/main/java/com/xy/xyaicpzs/jt/jtssq/BlueBallAnalysis.java
Normal file
693
src/main/java/com/xy/xyaicpzs/jt/jtssq/BlueBallAnalysis.java
Normal file
@@ -0,0 +1,693 @@
|
||||
package com.xy.xyaicpzs.jt.jtssq;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.xy.xyaicpzs.domain.entity.*;
|
||||
import com.xy.xyaicpzs.domain.vo.SSQThirdStepResultVO;
|
||||
import com.xy.xyaicpzs.mapper.*;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 精推双色球第三步算法分析
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class BlueBallAnalysis {
|
||||
|
||||
@Autowired
|
||||
private T6Mapper t6Mapper;
|
||||
|
||||
@Autowired
|
||||
private T5Mapper t5Mapper;
|
||||
|
||||
@Autowired
|
||||
private T8Mapper t8Mapper;
|
||||
|
||||
@Autowired
|
||||
private BlueHistoryTopMapper blueHistoryTopMapper;
|
||||
|
||||
@Autowired
|
||||
private BlueHistoryTop100Mapper blueHistoryTop100Mapper;
|
||||
|
||||
/**
|
||||
* 球号和系数的关联类
|
||||
*/
|
||||
@Data
|
||||
public static class BallWithCoefficient {
|
||||
private Integer ballNumber;
|
||||
private Double coefficient;
|
||||
private Integer masterBallNumber; // 主球号,用于标识来源
|
||||
|
||||
public BallWithCoefficient(Integer ballNumber, Double coefficient, Integer masterBallNumber) {
|
||||
this.ballNumber = ballNumber;
|
||||
this.coefficient = coefficient;
|
||||
this.masterBallNumber = masterBallNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 精推双色球第三步算法主方法
|
||||
* @param level 位置级别(H-高位/M-中位/L-低位)
|
||||
* @param redBalls 前6个红球号码
|
||||
* @param blueBall 蓝球号码
|
||||
* @param nextRedBalls 下期6个红球号码
|
||||
* @return 分析结果
|
||||
*/
|
||||
public SSQThirdStepResultVO analyze(String level, List<Integer> redBalls, Integer blueBall, List<Integer> nextRedBalls) {
|
||||
// 参数验证
|
||||
validateInputParams(level, redBalls, blueBall, nextRedBalls);
|
||||
|
||||
log.info("开始精推双色球第三步分析,策略:{},红球:{},蓝球:{},下期红球:{}",
|
||||
level, redBalls, blueBall, nextRedBalls);
|
||||
|
||||
// 用于存储所有候选球号和对应的系数
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap = new HashMap<>();
|
||||
List<Integer> allCandidateBalls = new ArrayList<>();
|
||||
|
||||
// Step 1: 根据6个红球号码获取候选球(T6表前12个)
|
||||
for (Integer redBall : redBalls) {
|
||||
List<BallWithCoefficient> ballsWithCoefficients = getTop12FromT6(redBall);
|
||||
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: 从blue_history_top_100百期排行获取前2个球号
|
||||
List<Integer> top2HistoryTop100 = getTop2FromHistoryTop100();
|
||||
allCandidateBalls.addAll(top2HistoryTop100);
|
||||
|
||||
// Step 3: 根据蓝球号码获取候选球(T5表前12个)
|
||||
List<BallWithCoefficient> blueBallsWithCoefficients = getTop12FromT5(blueBall);
|
||||
for (BallWithCoefficient ball : blueBallsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
|
||||
// Step 4: 根据下期6个红球号码获取候选球(T8表,根据策略不同)
|
||||
for (Integer nextRedBall : nextRedBalls) {
|
||||
List<BallWithCoefficient> nextRedBallsWithCoefficients = getT8BallsByLevel(nextRedBall, level);
|
||||
for (BallWithCoefficient ball : nextRedBallsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5: 统计分析并生成结果
|
||||
List<SSQThirdStepResultVO.BallAnalysisResult> results = analyzeResults(allCandidateBalls, ballCoefficientMap);
|
||||
|
||||
return SSQThirdStepResultVO.builder()
|
||||
.results(results)
|
||||
.strategy(level)
|
||||
.redBalls(redBalls)
|
||||
.blueBall(blueBall)
|
||||
.nextRedBalls(nextRedBalls)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从T6表获取前12个球号和系数(如果第12个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getTop12FromT6(Integer masterBallNumber) {
|
||||
List<T6> t6List = t6Mapper.selectList(
|
||||
new QueryWrapper<T6>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("lineCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(t6List)) {
|
||||
log.warn("T6表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (t6List.size() <= 12) {
|
||||
// 如果总数不超过12个,全部加入
|
||||
for (T6 t6 : t6List) {
|
||||
result.add(new BallWithCoefficient(t6.getSlaveBallNumber(), t6.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前12个
|
||||
for (int i = 0; i < 12; i++) {
|
||||
T6 t6 = t6List.get(i);
|
||||
result.add(new BallWithCoefficient(t6.getSlaveBallNumber(), t6.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第12个球号的系数
|
||||
Double boundaryCoefficient = t6List.get(11).getLineCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 12; i < t6List.size(); i++) {
|
||||
T6 t6 = t6List.get(i);
|
||||
if (t6.getLineCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(t6.getSlaveBallNumber(), t6.getLineCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从T5表获取前12个球号和系数(如果第12个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getTop12FromT5(Integer masterBallNumber) {
|
||||
List<T5> t5List = t5Mapper.selectList(
|
||||
new QueryWrapper<T5>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("lineCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(t5List)) {
|
||||
log.warn("T5表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (t5List.size() <= 12) {
|
||||
// 如果总数不超过12个,全部加入
|
||||
for (T5 t5 : t5List) {
|
||||
result.add(new BallWithCoefficient(t5.getSlaveBallNumber(), t5.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前12个
|
||||
for (int i = 0; i < 12; i++) {
|
||||
T5 t5 = t5List.get(i);
|
||||
result.add(new BallWithCoefficient(t5.getSlaveBallNumber(), t5.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第12个球号的系数
|
||||
Double boundaryCoefficient = t5List.get(11).getLineCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 12; i < t5List.size(); i++) {
|
||||
T5 t5 = t5List.get(i);
|
||||
if (t5.getLineCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(t5.getSlaveBallNumber(), t5.getLineCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从blue_history_top_100获取前2个球号(按点系数排行,处理边界相同系数)
|
||||
*/
|
||||
private List<Integer> getTop2FromHistoryTop100() {
|
||||
List<BlueHistoryTop100> blueHistoryTop100List = blueHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<BlueHistoryTop100>()
|
||||
.orderByDesc("pointCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(blueHistoryTop100List)) {
|
||||
log.warn("蓝球百期排行表数据为空");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
int index = 0;
|
||||
int addedCount = 0;
|
||||
|
||||
while (addedCount < 2 && index < blueHistoryTop100List.size()) {
|
||||
double currentCoefficient = blueHistoryTop100List.get(index).getPointCoefficient();
|
||||
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
||||
|
||||
// 收集所有相同系数的球号
|
||||
while (index < blueHistoryTop100List.size() &&
|
||||
blueHistoryTop100List.get(index).getPointCoefficient().equals(currentCoefficient)) {
|
||||
sameCoefficientBalls.add(blueHistoryTop100List.get(index).getBallNumber());
|
||||
index++;
|
||||
}
|
||||
|
||||
// 计算还需要多少个球
|
||||
int needCount = Math.min(2 - addedCount, sameCoefficientBalls.size());
|
||||
|
||||
if (sameCoefficientBalls.size() == 1) {
|
||||
// 只有一个球号,直接加入
|
||||
result.add(sameCoefficientBalls.get(0));
|
||||
addedCount++;
|
||||
} else if (needCount == sameCoefficientBalls.size()) {
|
||||
// 需要选择的数量等于可用数量,全部加入
|
||||
result.addAll(sameCoefficientBalls);
|
||||
addedCount += sameCoefficientBalls.size();
|
||||
} else {
|
||||
// 需要从多个相同系数的球号中选择部分,处理边界冲突
|
||||
List<Integer> selectedBalls = handleBlueHistoryBoundaryConflicts(sameCoefficientBalls, needCount);
|
||||
result.addAll(selectedBalls);
|
||||
addedCount += selectedBalls.size();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从T8表获取候选球号和系数
|
||||
*/
|
||||
private List<BallWithCoefficient> getT8BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<T8> t8List = t8Mapper.selectList(
|
||||
new QueryWrapper<T8>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("faceCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(t8List)) {
|
||||
log.warn("T8表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if ("H".equalsIgnoreCase(level)) {
|
||||
return getHighLevelBallsFromT8(t8List, masterBallNumber);
|
||||
} else if ("M".equalsIgnoreCase(level)) {
|
||||
return getMiddleLevelBallsFromT8(t8List, masterBallNumber);
|
||||
} else { // L
|
||||
return getLowLevelBallsFromT8(t8List, masterBallNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 高位策略:从T8表获取系数最大的前5个球号(如果第5个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getHighLevelBallsFromT8(List<T8> t8List, Integer masterBallNumber) {
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (t8List.size() <= 5) {
|
||||
// 如果总数不超过5个,全部加入
|
||||
for (T8 t8 : t8List) {
|
||||
result.add(new BallWithCoefficient(t8.getSlaveBallNumber(), t8.getFaceCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前5个
|
||||
for (int i = 0; i < 5; i++) {
|
||||
T8 t8 = t8List.get(i);
|
||||
result.add(new BallWithCoefficient(t8.getSlaveBallNumber(), t8.getFaceCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第5个球号的系数
|
||||
Double boundaryCoefficient = t8List.get(4).getFaceCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 5; i < t8List.size(); i++) {
|
||||
T8 t8 = t8List.get(i);
|
||||
if (t8.getFaceCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(t8.getSlaveBallNumber(), t8.getFaceCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 中位策略:从T8表获取平均值附近5个球号(向上2个,向下2个,边界系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getMiddleLevelBallsFromT8(List<T8> t8List, Integer masterBallNumber) {
|
||||
if (t8List.size() < 5) {
|
||||
log.warn("T8表数据不足5条,实际{}条", t8List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (T8 t8 : t8List) {
|
||||
result.add(new BallWithCoefficient(t8.getSlaveBallNumber(), t8.getFaceCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 计算平均系数(保留两位小数,截断方式)
|
||||
double avgCoefficient = t8List.stream()
|
||||
.mapToDouble(T8::getFaceCoefficient)
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
// 截断到两位小数
|
||||
avgCoefficient = Math.floor(avgCoefficient * 100) / 100;
|
||||
|
||||
// 找到第一个比平均值大的位置(从下到上遍历)
|
||||
int avgPosition = -1;
|
||||
for (int i = t8List.size() - 1; i >= 0; i--) {
|
||||
if (t8List.get(i).getFaceCoefficient() >= avgCoefficient) {
|
||||
avgPosition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (avgPosition == -1) {
|
||||
avgPosition = 0; // 如果没找到,取第一个
|
||||
}
|
||||
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
// 先加入平均值位置的球号
|
||||
result.add(new BallWithCoefficient(t8List.get(avgPosition).getSlaveBallNumber(),
|
||||
t8List.get(avgPosition).getFaceCoefficient(), masterBallNumber));
|
||||
|
||||
// 向上取2个球号,处理边界系数相同情况
|
||||
int upCount = 0;
|
||||
Double upBoundaryCoeff = null;
|
||||
for (int i = avgPosition - 1; i >= 0 && upCount < 2; i--) {
|
||||
result.add(new BallWithCoefficient(t8List.get(i).getSlaveBallNumber(),
|
||||
t8List.get(i).getFaceCoefficient(), masterBallNumber));
|
||||
upCount++;
|
||||
if (upCount == 2) {
|
||||
upBoundaryCoeff = t8List.get(i).getFaceCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向上边界处理:继续添加与第2个球号系数相同的球号
|
||||
if (upBoundaryCoeff != null) {
|
||||
for (int i = avgPosition - 3; i >= 0; i--) {
|
||||
if (t8List.get(i).getFaceCoefficient().equals(upBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(t8List.get(i).getSlaveBallNumber(),
|
||||
t8List.get(i).getFaceCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 向下取2个球号,处理边界系数相同情况
|
||||
int downCount = 0;
|
||||
Double downBoundaryCoeff = null;
|
||||
for (int i = avgPosition + 1; i < t8List.size() && downCount < 2; i++) {
|
||||
result.add(new BallWithCoefficient(t8List.get(i).getSlaveBallNumber(),
|
||||
t8List.get(i).getFaceCoefficient(), masterBallNumber));
|
||||
downCount++;
|
||||
if (downCount == 2) {
|
||||
downBoundaryCoeff = t8List.get(i).getFaceCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向下边界处理:继续添加与第2个球号系数相同的球号
|
||||
if (downBoundaryCoeff != null) {
|
||||
for (int i = avgPosition + 3; i < t8List.size(); i++) {
|
||||
if (t8List.get(i).getFaceCoefficient().equals(downBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(t8List.get(i).getSlaveBallNumber(),
|
||||
t8List.get(i).getFaceCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 低位策略:从T8表获取最小值向上第2-6个球(共5个球,边界系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getLowLevelBallsFromT8(List<T8> t8List, Integer masterBallNumber) {
|
||||
if (t8List.size() < 6) {
|
||||
log.warn("T8表数据不足6条,实际{}条", t8List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (T8 t8 : t8List) {
|
||||
result.add(new BallWithCoefficient(t8.getSlaveBallNumber(), t8.getFaceCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 取最小值向上第2-6个球(t8List已按系数降序排列,最后的是最小的)
|
||||
// 最小值在最后,向上第2个就是值数组的第 size-2 个,向上第6个就是值数组的第 size-6 个
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
int startIndex = Math.max(0, t8List.size() - 6); // 第2位
|
||||
int endIndex = t8List.size() - 2; // 第6位
|
||||
|
||||
// 先加入基本的5个球号
|
||||
for (int i = startIndex; i <= endIndex; i++) {
|
||||
T8 t8 = t8List.get(i);
|
||||
result.add(new BallWithCoefficient(t8.getSlaveBallNumber(), t8.getFaceCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 处理起始边界:第1个球号(startIndex)系数相同情况
|
||||
if (startIndex > 0) {
|
||||
Double startBoundaryCoeff = t8List.get(startIndex).getFaceCoefficient();
|
||||
|
||||
// 继续向前查找系数相同的球号
|
||||
for (int i = startIndex - 1; i >= 0; i--) {
|
||||
if (t8List.get(i).getFaceCoefficient().equals(startBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(t8List.get(i).getSlaveBallNumber(),
|
||||
t8List.get(i).getFaceCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理结束边界:第5个球号(endIndex)系数相同情况
|
||||
if (endIndex < t8List.size() - 1) {
|
||||
Double endBoundaryCoeff = t8List.get(endIndex).getFaceCoefficient();
|
||||
|
||||
// 继续向后查找系数相同的球号
|
||||
for (int i = endIndex + 1; i < t8List.size(); i++) {
|
||||
if (t8List.get(i).getFaceCoefficient().equals(endBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(t8List.get(i).getSlaveBallNumber(),
|
||||
t8List.get(i).getFaceCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理蓝球历史排行边界冲突,选择指定数量的球号
|
||||
* @param candidateBalls 候选球号列表
|
||||
* @param selectCount 需要选择的数量
|
||||
* @return 选中的球号列表
|
||||
*/
|
||||
private List<Integer> handleBlueHistoryBoundaryConflicts(List<Integer> candidateBalls, int selectCount) {
|
||||
if (CollectionUtils.isEmpty(candidateBalls) || selectCount <= 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (selectCount >= candidateBalls.size()) {
|
||||
return new ArrayList<>(candidateBalls);
|
||||
}
|
||||
|
||||
// 使用blue_history_top表进行筛选
|
||||
return selectFromBlueHistoryTop(candidateBalls, selectCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从blue_history_top表中选择指定数量的球号
|
||||
*/
|
||||
private List<Integer> selectFromBlueHistoryTop(List<Integer> candidateBalls, int selectCount) {
|
||||
if (CollectionUtils.isEmpty(candidateBalls) || selectCount <= 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (selectCount >= candidateBalls.size()) {
|
||||
return new ArrayList<>(candidateBalls);
|
||||
}
|
||||
|
||||
// 获取在blue_history_top表中的系数
|
||||
Map<Integer, Double> topCoefficients = new HashMap<>();
|
||||
for (Integer ball : candidateBalls) {
|
||||
BlueHistoryTop record = blueHistoryTopMapper.selectOne(
|
||||
new QueryWrapper<BlueHistoryTop>()
|
||||
.eq("ballNumber", ball));
|
||||
if (record != null) {
|
||||
topCoefficients.put(ball, record.getPointCoefficient());
|
||||
} else {
|
||||
topCoefficients.put(ball, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// 按系数降序排序,选择前selectCount个
|
||||
List<Map.Entry<Integer, Double>> sortedByTop = topCoefficients.entrySet().stream()
|
||||
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
for (int i = 0; i < Math.min(selectCount, sortedByTop.size()); i++) {
|
||||
result.add(sortedByTop.get(i).getKey());
|
||||
}
|
||||
|
||||
// 如果仍然不够,按原始顺序补充
|
||||
while (result.size() < selectCount && result.size() < candidateBalls.size()) {
|
||||
for (Integer ball : candidateBalls) {
|
||||
if (!result.contains(ball) && result.size() < selectCount) {
|
||||
result.add(ball);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计分析并生成结果
|
||||
*/
|
||||
private List<SSQThirdStepResultVO.BallAnalysisResult> analyzeResults(
|
||||
List<Integer> allCandidateBalls,
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap) {
|
||||
|
||||
// 统计球号出现次数
|
||||
Map<Integer, Integer> ballFrequencyMap = new HashMap<>();
|
||||
for (Integer ball : allCandidateBalls) {
|
||||
ballFrequencyMap.put(ball, ballFrequencyMap.getOrDefault(ball, 0) + 1);
|
||||
}
|
||||
|
||||
// 计算系数和
|
||||
Map<Integer, Double> ballCoefficientSumMap = new HashMap<>();
|
||||
for (Map.Entry<Integer, List<BallWithCoefficient>> entry : ballCoefficientMap.entrySet()) {
|
||||
Integer ballNumber = entry.getKey();
|
||||
List<BallWithCoefficient> coefficients = entry.getValue();
|
||||
double sum = coefficients.stream()
|
||||
.mapToDouble(BallWithCoefficient::getCoefficient)
|
||||
.sum();
|
||||
ballCoefficientSumMap.put(ballNumber, sum);
|
||||
}
|
||||
|
||||
// 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
Set<Integer> allBallsWithRanking = getAllBallsWithRanking();
|
||||
|
||||
// 获取百期排位和历史排位
|
||||
Map<Integer, Integer> top100RankingMap = getTop100Rankings(allBallsWithRanking);
|
||||
Map<Integer, Integer> historyRankingMap = getHistoryRankings(allBallsWithRanking);
|
||||
|
||||
// 组装结果
|
||||
List<SSQThirdStepResultVO.BallAnalysisResult> results = new ArrayList<>();
|
||||
for (Integer ballNumber : allBallsWithRanking) {
|
||||
SSQThirdStepResultVO.BallAnalysisResult result = SSQThirdStepResultVO.BallAnalysisResult.builder()
|
||||
.ballNumber(ballNumber)
|
||||
.frequency(ballFrequencyMap.getOrDefault(ballNumber, 0))
|
||||
.coefficientSum(ballCoefficientSumMap.getOrDefault(ballNumber, 0.0))
|
||||
.top100Ranking(top100RankingMap.get(ballNumber))
|
||||
.historyRanking(historyRankingMap.get(ballNumber))
|
||||
.build();
|
||||
results.add(result);
|
||||
}
|
||||
|
||||
// 按出现次数降序排列,次数相同的按球号升序排列
|
||||
results.sort((a, b) -> {
|
||||
int frequencyCompare = b.getFrequency().compareTo(a.getFrequency());
|
||||
if (frequencyCompare != 0) {
|
||||
return frequencyCompare;
|
||||
}
|
||||
return a.getBallNumber().compareTo(b.getBallNumber());
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
*/
|
||||
private Set<Integer> getAllBallsWithRanking() {
|
||||
Set<Integer> allBalls = new HashSet<>();
|
||||
|
||||
// 从蓝球百期排行表获取所有球号
|
||||
List<BlueHistoryTop100> blueHistoryTop100List = blueHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<BlueHistoryTop100>());
|
||||
for (BlueHistoryTop100 item : blueHistoryTop100List) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
// 从蓝球历史排行表获取所有球号
|
||||
List<BlueHistoryTop> blueHistoryTopList = blueHistoryTopMapper.selectList(
|
||||
new QueryWrapper<BlueHistoryTop>());
|
||||
for (BlueHistoryTop item : blueHistoryTopList) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
return allBalls;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在蓝球百期排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getTop100Rankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的no字段作为排名
|
||||
List<BlueHistoryTop100> blueHistoryTop100List = blueHistoryTop100Mapper.selectList(
|
||||
new QueryWrapper<BlueHistoryTop100>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的no字段作为排名
|
||||
for (BlueHistoryTop100 item : blueHistoryTop100List) {
|
||||
result.put(item.getBallNumber(), item.getNo());
|
||||
}
|
||||
|
||||
// 对于没有在蓝球百期排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在蓝球历史排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getHistoryRankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的no字段作为排名
|
||||
List<BlueHistoryTop> blueHistoryTopList = blueHistoryTopMapper.selectList(
|
||||
new QueryWrapper<BlueHistoryTop>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的no字段作为排名
|
||||
for (BlueHistoryTop item : blueHistoryTopList) {
|
||||
result.put(item.getBallNumber(), item.getNo());
|
||||
}
|
||||
|
||||
// 对于没有在蓝球历史排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数验证
|
||||
*/
|
||||
private void validateInputParams(String level, List<Integer> redBalls, Integer blueBall, List<Integer> nextRedBalls) {
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
throw new IllegalArgumentException("位置级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(redBalls) || redBalls.size() != 6) {
|
||||
throw new IllegalArgumentException("红球号码必须为6个");
|
||||
}
|
||||
|
||||
if (blueBall == null || blueBall < 1 || blueBall > 16) {
|
||||
throw new IllegalArgumentException("蓝球号码范围应为1-16");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(nextRedBalls) || nextRedBalls.size() != 6) {
|
||||
throw new IllegalArgumentException("下期红球号码必须为6个");
|
||||
}
|
||||
|
||||
for (Integer redBall : redBalls) {
|
||||
if (redBall < 1 || redBall > 33) {
|
||||
throw new IllegalArgumentException("红球号码范围应为1-33");
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer nextRedBall : nextRedBalls) {
|
||||
if (nextRedBall < 1 || nextRedBall > 33) {
|
||||
throw new IllegalArgumentException("下期红球号码范围应为1-33");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
653
src/main/java/com/xy/xyaicpzs/jt/jtssq/FirstBallAnalysis.java
Normal file
653
src/main/java/com/xy/xyaicpzs/jt/jtssq/FirstBallAnalysis.java
Normal file
@@ -0,0 +1,653 @@
|
||||
package com.xy.xyaicpzs.jt.jtssq;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.xy.xyaicpzs.domain.entity.*;
|
||||
import com.xy.xyaicpzs.domain.vo.SSQFirstStepResultVO;
|
||||
import com.xy.xyaicpzs.mapper.*;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 精推双色球第一步算法分析
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FirstBallAnalysis {
|
||||
|
||||
@Autowired
|
||||
private T3Mapper t3Mapper;
|
||||
|
||||
@Autowired
|
||||
private T4Mapper t4Mapper;
|
||||
|
||||
@Autowired
|
||||
private HistoryTopMapper historyTopMapper;
|
||||
|
||||
@Autowired
|
||||
private HistoryTop100Mapper historyTop100Mapper;
|
||||
|
||||
/**
|
||||
* 球号和系数的关联类
|
||||
*/
|
||||
@Data
|
||||
public static class BallWithCoefficient {
|
||||
private Integer ballNumber;
|
||||
private Double coefficient;
|
||||
private Integer masterBallNumber; // 主球号,用于标识来源
|
||||
|
||||
public BallWithCoefficient(Integer ballNumber, Double coefficient, Integer masterBallNumber) {
|
||||
this.ballNumber = ballNumber;
|
||||
this.coefficient = coefficient;
|
||||
this.masterBallNumber = masterBallNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 精推双色球第一步算法主方法
|
||||
* @param level 位置级别(H-高位/M-中位/L-低位)
|
||||
* @param redBalls 前6个红球号码
|
||||
* @param blueBall 蓝球号码
|
||||
* @return 分析结果
|
||||
*/
|
||||
public SSQFirstStepResultVO analyze(String level, List<Integer> redBalls, Integer blueBall) {
|
||||
// 参数验证
|
||||
validateInputParams(level, redBalls, blueBall);
|
||||
|
||||
log.info("开始精推双色球第一步分析,策略:{},红球:{},蓝球:{}", level, redBalls, blueBall);
|
||||
|
||||
// 用于存储所有候选球号和对应的系数
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap = new HashMap<>();
|
||||
List<Integer> allCandidateBalls = new ArrayList<>();
|
||||
|
||||
// Step 1: 根据6个红球号码获取候选球
|
||||
for (Integer redBall : redBalls) {
|
||||
List<BallWithCoefficient> ballsWithCoefficients = getT3BallsByLevel(redBall, level);
|
||||
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: 从history_top获取前3个球号
|
||||
List<Integer> top3HistoryTop = getTop3FromHistoryTop();
|
||||
allCandidateBalls.addAll(top3HistoryTop);
|
||||
|
||||
// Step 3: 根据蓝球号码获取候选球
|
||||
List<BallWithCoefficient> blueBallsWithCoefficients = getT4BallsByLevel(blueBall, level);
|
||||
for (BallWithCoefficient ball : blueBallsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
|
||||
// Step 4: 统计分析并生成结果
|
||||
List<SSQFirstStepResultVO.BallAnalysisResult> results = analyzeResults(allCandidateBalls, ballCoefficientMap);
|
||||
|
||||
return SSQFirstStepResultVO.builder()
|
||||
.results(results)
|
||||
.strategy(level)
|
||||
.redBalls(redBalls)
|
||||
.blueBall(blueBall)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从T3表获取候选球号和系数
|
||||
*/
|
||||
private List<BallWithCoefficient> getT3BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<T3> t3List = t3Mapper.selectList(
|
||||
new QueryWrapper<T3>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("lineCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(t3List)) {
|
||||
log.warn("T3表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if ("H".equalsIgnoreCase(level)) {
|
||||
return getHighLevelBallsFromT3(t3List, masterBallNumber);
|
||||
} else if ("M".equalsIgnoreCase(level)) {
|
||||
return getMiddleLevelBallsFromT3(t3List, masterBallNumber);
|
||||
} else { // L
|
||||
return getLowLevelBallsFromT3(t3List, masterBallNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从T4表获取候选球号和系数
|
||||
*/
|
||||
private List<BallWithCoefficient> getT4BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<T4> t4List = t4Mapper.selectList(
|
||||
new QueryWrapper<T4>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("lineCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(t4List)) {
|
||||
log.warn("T4表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if ("H".equalsIgnoreCase(level)) {
|
||||
return getHighLevelBallsFromT4(t4List, masterBallNumber);
|
||||
} else if ("M".equalsIgnoreCase(level)) {
|
||||
return getMiddleLevelBallsFromT4(t4List, masterBallNumber);
|
||||
} else { // L
|
||||
return getLowLevelBallsFromT4(t4List, masterBallNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 高位策略:从T3表获取系数最大的前17个球号(如果第17个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getHighLevelBallsFromT3(List<T3> t3List, Integer masterBallNumber) {
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (t3List.size() <= 17) {
|
||||
// 如果总数不超过17个,全部加入
|
||||
for (T3 t3 : t3List) {
|
||||
result.add(new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前17个
|
||||
for (int i = 0; i < 17; i++) {
|
||||
T3 t3 = t3List.get(i);
|
||||
result.add(new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第17个球号的系数
|
||||
Double boundaryCoefficient = t3List.get(16).getLineCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 17; i < t3List.size(); i++) {
|
||||
T3 t3 = t3List.get(i);
|
||||
if (t3.getLineCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 高位策略:从T4表获取系数最大的前17个球号(如果第17个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getHighLevelBallsFromT4(List<T4> t4List, Integer masterBallNumber) {
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (t4List.size() <= 17) {
|
||||
// 如果总数不超过17个,全部加入
|
||||
for (T4 t4 : t4List) {
|
||||
result.add(new BallWithCoefficient(t4.getSlaveBallNumber(), t4.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前17个
|
||||
for (int i = 0; i < 17; i++) {
|
||||
T4 t4 = t4List.get(i);
|
||||
result.add(new BallWithCoefficient(t4.getSlaveBallNumber(), t4.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第17个球号的系数
|
||||
Double boundaryCoefficient = t4List.get(16).getLineCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 17; i < t4List.size(); i++) {
|
||||
T4 t4 = t4List.get(i);
|
||||
if (t4.getLineCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(t4.getSlaveBallNumber(), t4.getLineCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 中位策略:从T3表获取平均值附近的17个球号(向上8个,向下8个,边界系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getMiddleLevelBallsFromT3(List<T3> t3List, Integer masterBallNumber) {
|
||||
if (t3List.size() < 17) {
|
||||
log.warn("T3表数据不足17条,实际{}条", t3List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (T3 t3 : t3List) {
|
||||
result.add(new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 计算平均系数(保留两位小数,截断方式)
|
||||
double avgCoefficient = t3List.stream()
|
||||
.mapToDouble(T3::getLineCoefficient)
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
// 截断到两位小数
|
||||
avgCoefficient = Math.floor(avgCoefficient * 100) / 100;
|
||||
|
||||
// 找到第一个比平均值大的位置(从下到上遍历)
|
||||
int avgPosition = -1;
|
||||
for (int i = t3List.size() - 1; i >= 0; i--) {
|
||||
if (t3List.get(i).getLineCoefficient() >= avgCoefficient) {
|
||||
avgPosition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (avgPosition == -1) {
|
||||
avgPosition = 0; // 如果没找到,取第一个
|
||||
}
|
||||
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
// 先加入平均值位置的球号
|
||||
result.add(new BallWithCoefficient(t3List.get(avgPosition).getSlaveBallNumber(),
|
||||
t3List.get(avgPosition).getLineCoefficient(), masterBallNumber));
|
||||
|
||||
// 向上取8个球号,处理边界系数相同情况
|
||||
int upCount = 0;
|
||||
Double upBoundaryCoeff = null;
|
||||
for (int i = avgPosition - 1; i >= 0 && upCount < 8; i--) {
|
||||
result.add(new BallWithCoefficient(t3List.get(i).getSlaveBallNumber(),
|
||||
t3List.get(i).getLineCoefficient(), masterBallNumber));
|
||||
upCount++;
|
||||
if (upCount == 8) {
|
||||
upBoundaryCoeff = t3List.get(i).getLineCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向上边界处理:继续添加与第8个球号系数相同的球号
|
||||
if (upBoundaryCoeff != null) {
|
||||
for (int i = avgPosition - 9; i >= 0; i--) {
|
||||
if (t3List.get(i).getLineCoefficient().equals(upBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(t3List.get(i).getSlaveBallNumber(),
|
||||
t3List.get(i).getLineCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 向下取8个球号,处理边界系数相同情况
|
||||
int downCount = 0;
|
||||
Double downBoundaryCoeff = null;
|
||||
for (int i = avgPosition + 1; i < t3List.size() && downCount < 8; i++) {
|
||||
result.add(new BallWithCoefficient(t3List.get(i).getSlaveBallNumber(),
|
||||
t3List.get(i).getLineCoefficient(), masterBallNumber));
|
||||
downCount++;
|
||||
if (downCount == 8) {
|
||||
downBoundaryCoeff = t3List.get(i).getLineCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向下边界处理:继续添加与第8个球号系数相同的球号
|
||||
if (downBoundaryCoeff != null) {
|
||||
for (int i = avgPosition + 9; i < t3List.size(); i++) {
|
||||
if (t3List.get(i).getLineCoefficient().equals(downBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(t3List.get(i).getSlaveBallNumber(),
|
||||
t3List.get(i).getLineCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 中位策略:从T4表获取平均值附近的17个球号(向上8个,向下8个,边界系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getMiddleLevelBallsFromT4(List<T4> t4List, Integer masterBallNumber) {
|
||||
if (t4List.size() < 17) {
|
||||
log.warn("T4表数据不足17条,实际{}条", t4List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (T4 t4 : t4List) {
|
||||
result.add(new BallWithCoefficient(t4.getSlaveBallNumber(), t4.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 计算平均系数(保留两位小数,截断方式)
|
||||
double avgCoefficient = t4List.stream()
|
||||
.mapToDouble(T4::getLineCoefficient)
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
// 截断到两位小数
|
||||
avgCoefficient = Math.floor(avgCoefficient * 100) / 100;
|
||||
|
||||
// 找到第一个比平均值大的位置(从下到上遍历)
|
||||
int avgPosition = -1;
|
||||
for (int i = t4List.size() - 1; i >= 0; i--) {
|
||||
if (t4List.get(i).getLineCoefficient() >= avgCoefficient) {
|
||||
avgPosition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (avgPosition == -1) {
|
||||
avgPosition = 0; // 如果没找到,取第一个
|
||||
}
|
||||
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
// 先加入平均值位置的球号
|
||||
result.add(new BallWithCoefficient(t4List.get(avgPosition).getSlaveBallNumber(),
|
||||
t4List.get(avgPosition).getLineCoefficient(), masterBallNumber));
|
||||
|
||||
// 向上取8个球号,处理边界系数相同情况
|
||||
int upCount = 0;
|
||||
Double upBoundaryCoeff = null;
|
||||
for (int i = avgPosition - 1; i >= 0 && upCount < 8; i--) {
|
||||
result.add(new BallWithCoefficient(t4List.get(i).getSlaveBallNumber(),
|
||||
t4List.get(i).getLineCoefficient(), masterBallNumber));
|
||||
upCount++;
|
||||
if (upCount == 8) {
|
||||
upBoundaryCoeff = t4List.get(i).getLineCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向上边界处理:继续添加与第8个球号系数相同的球号
|
||||
if (upBoundaryCoeff != null) {
|
||||
for (int i = avgPosition - 9; i >= 0; i--) {
|
||||
if (t4List.get(i).getLineCoefficient().equals(upBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(t4List.get(i).getSlaveBallNumber(),
|
||||
t4List.get(i).getLineCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 向下取8个球号,处理边界系数相同情况
|
||||
int downCount = 0;
|
||||
Double downBoundaryCoeff = null;
|
||||
for (int i = avgPosition + 1; i < t4List.size() && downCount < 8; i++) {
|
||||
result.add(new BallWithCoefficient(t4List.get(i).getSlaveBallNumber(),
|
||||
t4List.get(i).getLineCoefficient(), masterBallNumber));
|
||||
downCount++;
|
||||
if (downCount == 8) {
|
||||
downBoundaryCoeff = t4List.get(i).getLineCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向下边界处理:继续添加与第8个球号系数相同的球号
|
||||
if (downBoundaryCoeff != null) {
|
||||
for (int i = avgPosition + 9; i < t4List.size(); i++) {
|
||||
if (t4List.get(i).getLineCoefficient().equals(downBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(t4List.get(i).getSlaveBallNumber(),
|
||||
t4List.get(i).getLineCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 低位策略:从T3表获取最小值向上17个球号(含最小值,第1个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getLowLevelBallsFromT3(List<T3> t3List, Integer masterBallNumber) {
|
||||
if (t3List.size() < 17) {
|
||||
log.warn("T3表数据不足17条,实际{}条", t3List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (T3 t3 : t3List) {
|
||||
result.add(new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 从最小值开始向上取17个(t3List已按系数降序排列,最后17个就是最小的)
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
int startIndex = Math.max(0, t3List.size() - 17);
|
||||
|
||||
// 先加入基本的17个球号
|
||||
for (int i = startIndex; i < t3List.size(); i++) {
|
||||
T3 t3 = t3List.get(i);
|
||||
result.add(new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 处理第1个球号(系数最大的)边界相同情况
|
||||
if (startIndex > 0) {
|
||||
Double firstBallCoeff = t3List.get(startIndex).getLineCoefficient();
|
||||
|
||||
// 继续向前查找系数相同的球号
|
||||
for (int i = startIndex - 1; i >= 0; i--) {
|
||||
if (t3List.get(i).getLineCoefficient().equals(firstBallCoeff)) {
|
||||
result.add(new BallWithCoefficient(t3List.get(i).getSlaveBallNumber(),
|
||||
t3List.get(i).getLineCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 低位策略:从T4表获取最小值向上17个球号(含最小值,第1个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getLowLevelBallsFromT4(List<T4> t4List, Integer masterBallNumber) {
|
||||
if (t4List.size() < 17) {
|
||||
log.warn("T4表数据不足17条,实际{}条", t4List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (T4 t4 : t4List) {
|
||||
result.add(new BallWithCoefficient(t4.getSlaveBallNumber(), t4.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 从最小值开始向上取17个(t4List已按系数降序排列,最后17个就是最小的)
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
int startIndex = Math.max(0, t4List.size() - 17);
|
||||
|
||||
// 先加入基本的17个球号
|
||||
for (int i = startIndex; i < t4List.size(); i++) {
|
||||
T4 t4 = t4List.get(i);
|
||||
result.add(new BallWithCoefficient(t4.getSlaveBallNumber(), t4.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 处理第1个球号(系数最大的)边界相同情况
|
||||
if (startIndex > 0) {
|
||||
Double firstBallCoeff = t4List.get(startIndex).getLineCoefficient();
|
||||
|
||||
// 继续向前查找系数相同的球号
|
||||
for (int i = startIndex - 1; i >= 0; i--) {
|
||||
if (t4List.get(i).getLineCoefficient().equals(firstBallCoeff)) {
|
||||
result.add(new BallWithCoefficient(t4List.get(i).getSlaveBallNumber(),
|
||||
t4List.get(i).getLineCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从历史排行表获取前3个球号(按点系数排行)
|
||||
*/
|
||||
private List<Integer> getTop3FromHistoryTop() {
|
||||
List<HistoryTop> historyTopList = historyTopMapper.selectList(
|
||||
new QueryWrapper<HistoryTop>()
|
||||
.orderByDesc("pointCoefficient")
|
||||
.last("LIMIT 3"));
|
||||
|
||||
if (CollectionUtils.isEmpty(historyTopList)) {
|
||||
log.warn("历史排行表数据不足3条,实际{}条", historyTopList.size());
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return historyTopList.stream()
|
||||
.map(HistoryTop::getBallNumber)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计分析并生成结果
|
||||
*/
|
||||
private List<SSQFirstStepResultVO.BallAnalysisResult> analyzeResults(
|
||||
List<Integer> allCandidateBalls,
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap) {
|
||||
|
||||
// 统计球号出现次数
|
||||
Map<Integer, Integer> ballFrequencyMap = new HashMap<>();
|
||||
for (Integer ball : allCandidateBalls) {
|
||||
ballFrequencyMap.put(ball, ballFrequencyMap.getOrDefault(ball, 0) + 1);
|
||||
}
|
||||
|
||||
// 计算系数和
|
||||
Map<Integer, Double> ballCoefficientSumMap = new HashMap<>();
|
||||
for (Map.Entry<Integer, List<BallWithCoefficient>> entry : ballCoefficientMap.entrySet()) {
|
||||
Integer ballNumber = entry.getKey();
|
||||
List<BallWithCoefficient> coefficients = entry.getValue();
|
||||
double sum = coefficients.stream()
|
||||
.mapToDouble(BallWithCoefficient::getCoefficient)
|
||||
.sum();
|
||||
ballCoefficientSumMap.put(ballNumber, sum);
|
||||
}
|
||||
|
||||
// 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
Set<Integer> allBallsWithRanking = getAllBallsWithRanking();
|
||||
|
||||
// 获取百期排位和历史排位
|
||||
Map<Integer, Integer> top100RankingMap = getTop100Rankings(allBallsWithRanking);
|
||||
Map<Integer, Integer> historyRankingMap = getHistoryRankings(allBallsWithRanking);
|
||||
|
||||
// 组装结果
|
||||
List<SSQFirstStepResultVO.BallAnalysisResult> results = new ArrayList<>();
|
||||
for (Integer ballNumber : allBallsWithRanking) {
|
||||
SSQFirstStepResultVO.BallAnalysisResult result = SSQFirstStepResultVO.BallAnalysisResult.builder()
|
||||
.ballNumber(ballNumber)
|
||||
.frequency(ballFrequencyMap.getOrDefault(ballNumber, 0))
|
||||
.coefficientSum(ballCoefficientSumMap.getOrDefault(ballNumber, 0.0))
|
||||
.top100Ranking(top100RankingMap.get(ballNumber))
|
||||
.historyRanking(historyRankingMap.get(ballNumber))
|
||||
.build();
|
||||
results.add(result);
|
||||
}
|
||||
|
||||
// 按出现次数降序排列,次数相同的按球号升序排列
|
||||
results.sort((a, b) -> {
|
||||
int frequencyCompare = b.getFrequency().compareTo(a.getFrequency());
|
||||
if (frequencyCompare != 0) {
|
||||
return frequencyCompare;
|
||||
}
|
||||
return a.getBallNumber().compareTo(b.getBallNumber());
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
*/
|
||||
private Set<Integer> getAllBallsWithRanking() {
|
||||
Set<Integer> allBalls = new HashSet<>();
|
||||
|
||||
// 从百期排行表获取所有球号
|
||||
List<HistoryTop100> top100List = historyTop100Mapper.selectList(
|
||||
new QueryWrapper<HistoryTop100>());
|
||||
for (HistoryTop100 item : top100List) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
// 从历史排行表获取所有球号
|
||||
List<HistoryTop> historyTopList = historyTopMapper.selectList(
|
||||
new QueryWrapper<HistoryTop>());
|
||||
for (HistoryTop item : historyTopList) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
return allBalls;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在百期排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getTop100Rankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的no字段作为排名
|
||||
List<HistoryTop100> top100List = historyTop100Mapper.selectList(
|
||||
new QueryWrapper<HistoryTop100>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的no字段作为排名
|
||||
for (HistoryTop100 item : top100List) {
|
||||
result.put(item.getBallNumber(), item.getNo());
|
||||
}
|
||||
|
||||
// 对于没有在百期排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在历史排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getHistoryRankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的no字段作为排名
|
||||
List<HistoryTop> historyTopList = historyTopMapper.selectList(
|
||||
new QueryWrapper<HistoryTop>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的no字段作为排名
|
||||
for (HistoryTop item : historyTopList) {
|
||||
result.put(item.getBallNumber(), item.getNo());
|
||||
}
|
||||
|
||||
// 对于没有在历史排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数验证
|
||||
*/
|
||||
private void validateInputParams(String level, List<Integer> redBalls, Integer blueBall) {
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
throw new IllegalArgumentException("位置级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(redBalls) || redBalls.size() != 6) {
|
||||
throw new IllegalArgumentException("红球号码必须为6个");
|
||||
}
|
||||
|
||||
if (blueBall == null || blueBall < 1 || blueBall > 16) {
|
||||
throw new IllegalArgumentException("蓝球号码范围应为1-16");
|
||||
}
|
||||
|
||||
for (Integer redBall : redBalls) {
|
||||
if (redBall < 1 || redBall > 33) {
|
||||
throw new IllegalArgumentException("红球号码范围应为1-33");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
685
src/main/java/com/xy/xyaicpzs/jt/jtssq/FollowBallAnalysis.java
Normal file
685
src/main/java/com/xy/xyaicpzs/jt/jtssq/FollowBallAnalysis.java
Normal file
@@ -0,0 +1,685 @@
|
||||
package com.xy.xyaicpzs.jt.jtssq;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.xy.xyaicpzs.domain.entity.*;
|
||||
import com.xy.xyaicpzs.domain.vo.SSQSecondStepResultVO;
|
||||
import com.xy.xyaicpzs.mapper.*;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 精推双色球第二步算法分析
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FollowBallAnalysis {
|
||||
|
||||
@Autowired
|
||||
private T3Mapper t3Mapper;
|
||||
|
||||
@Autowired
|
||||
private T4Mapper t4Mapper;
|
||||
|
||||
@Autowired
|
||||
private T7Mapper t7Mapper;
|
||||
|
||||
@Autowired
|
||||
private HistoryTopMapper historyTopMapper;
|
||||
|
||||
@Autowired
|
||||
private HistoryTop100Mapper historyTop100Mapper;
|
||||
|
||||
/**
|
||||
* 球号和系数的关联类
|
||||
*/
|
||||
@Data
|
||||
public static class BallWithCoefficient {
|
||||
private Integer ballNumber;
|
||||
private Double coefficient;
|
||||
private Integer masterBallNumber; // 主球号,用于标识来源
|
||||
|
||||
public BallWithCoefficient(Integer ballNumber, Double coefficient, Integer masterBallNumber) {
|
||||
this.ballNumber = ballNumber;
|
||||
this.coefficient = coefficient;
|
||||
this.masterBallNumber = masterBallNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 精推双色球第二步算法主方法
|
||||
* @param level 位置级别(H-高位/M-中位/L-低位)
|
||||
* @param redBalls 前6个红球号码
|
||||
* @param blueBall 蓝球号码
|
||||
* @param nextFirstBall 下期首球号码
|
||||
* @return 分析结果
|
||||
*/
|
||||
public SSQSecondStepResultVO analyze(String level, List<Integer> redBalls, Integer blueBall, Integer nextFirstBall) {
|
||||
// 参数验证
|
||||
validateInputParams(level, redBalls, blueBall, nextFirstBall);
|
||||
|
||||
log.info("开始精推双色球第二步分析,策略:{},红球:{},蓝球:{},下期首球:{}",
|
||||
level, redBalls, blueBall, nextFirstBall);
|
||||
|
||||
// 用于存储所有候选球号和对应的系数
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap = new HashMap<>();
|
||||
List<Integer> allCandidateBalls = new ArrayList<>();
|
||||
|
||||
// Step 1: 根据6个红球号码获取候选球(T3表前26个)
|
||||
for (Integer redBall : redBalls) {
|
||||
List<BallWithCoefficient> ballsWithCoefficients = getTop26FromT3(redBall);
|
||||
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: 从history_top_100百期排行获取前3个球号
|
||||
List<Integer> top3HistoryTop100 = getTop3FromHistoryTop100();
|
||||
allCandidateBalls.addAll(top3HistoryTop100);
|
||||
|
||||
// Step 3: 根据蓝球号码获取候选球(T4表前26个)
|
||||
List<BallWithCoefficient> blueBallsWithCoefficients = getTop26FromT4(blueBall);
|
||||
for (BallWithCoefficient ball : blueBallsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
|
||||
// Step 4: 根据下期首球号码获取候选球(T7表,根据策略不同)
|
||||
List<BallWithCoefficient> nextFirstBallsWithCoefficients = getT7BallsByLevel(nextFirstBall, level);
|
||||
for (BallWithCoefficient ball : nextFirstBallsWithCoefficients) {
|
||||
ballCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
||||
allCandidateBalls.add(ball.getBallNumber());
|
||||
}
|
||||
|
||||
// Step 5: 统计分析并生成结果
|
||||
List<SSQSecondStepResultVO.BallAnalysisResult> results = analyzeResults(allCandidateBalls, ballCoefficientMap);
|
||||
|
||||
return SSQSecondStepResultVO.builder()
|
||||
.results(results)
|
||||
.strategy(level)
|
||||
.redBalls(redBalls)
|
||||
.blueBall(blueBall)
|
||||
.nextFirstBall(nextFirstBall)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从T3表获取前26个球号和系数(如果第26个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getTop26FromT3(Integer masterBallNumber) {
|
||||
List<T3> t3List = t3Mapper.selectList(
|
||||
new QueryWrapper<T3>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("lineCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(t3List)) {
|
||||
log.warn("T3表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (t3List.size() <= 26) {
|
||||
// 如果总数不超过26个,全部加入
|
||||
for (T3 t3 : t3List) {
|
||||
result.add(new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前26个
|
||||
for (int i = 0; i < 26; i++) {
|
||||
T3 t3 = t3List.get(i);
|
||||
result.add(new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第26个球号的系数
|
||||
Double boundaryCoefficient = t3List.get(25).getLineCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 26; i < t3List.size(); i++) {
|
||||
T3 t3 = t3List.get(i);
|
||||
if (t3.getLineCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从T4表获取前26个球号和系数(如果第26个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getTop26FromT4(Integer masterBallNumber) {
|
||||
List<T4> t4List = t4Mapper.selectList(
|
||||
new QueryWrapper<T4>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("lineCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(t4List)) {
|
||||
log.warn("T4表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (t4List.size() <= 26) {
|
||||
// 如果总数不超过26个,全部加入
|
||||
for (T4 t4 : t4List) {
|
||||
result.add(new BallWithCoefficient(t4.getSlaveBallNumber(), t4.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前26个
|
||||
for (int i = 0; i < 26; i++) {
|
||||
T4 t4 = t4List.get(i);
|
||||
result.add(new BallWithCoefficient(t4.getSlaveBallNumber(), t4.getLineCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第26个球号的系数
|
||||
Double boundaryCoefficient = t4List.get(25).getLineCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 26; i < t4List.size(); i++) {
|
||||
T4 t4 = t4List.get(i);
|
||||
if (t4.getLineCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(t4.getSlaveBallNumber(), t4.getLineCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从history_top_100获取前3个球号(按点系数排行,处理边界相同系数)
|
||||
*/
|
||||
private List<Integer> getTop3FromHistoryTop100() {
|
||||
List<HistoryTop100> historyTop100List = historyTop100Mapper.selectList(
|
||||
new QueryWrapper<HistoryTop100>()
|
||||
.orderByDesc("pointCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(historyTop100List)) {
|
||||
log.warn("百期排行表数据为空");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
int index = 0;
|
||||
int addedCount = 0;
|
||||
|
||||
while (addedCount < 3 && index < historyTop100List.size()) {
|
||||
double currentCoefficient = historyTop100List.get(index).getPointCoefficient();
|
||||
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
||||
|
||||
// 收集所有相同系数的球号
|
||||
while (index < historyTop100List.size() &&
|
||||
historyTop100List.get(index).getPointCoefficient().equals(currentCoefficient)) {
|
||||
sameCoefficientBalls.add(historyTop100List.get(index).getBallNumber());
|
||||
index++;
|
||||
}
|
||||
|
||||
// 计算还需要多少个球
|
||||
int needCount = Math.min(3 - addedCount, sameCoefficientBalls.size());
|
||||
|
||||
if (sameCoefficientBalls.size() == 1) {
|
||||
// 只有一个球号,直接加入
|
||||
result.add(sameCoefficientBalls.get(0));
|
||||
addedCount++;
|
||||
} else if (needCount == sameCoefficientBalls.size()) {
|
||||
// 需要选择的数量等于可用数量,全部加入
|
||||
result.addAll(sameCoefficientBalls);
|
||||
addedCount += sameCoefficientBalls.size();
|
||||
} else {
|
||||
// 需要从多个相同系数的球号中选择部分,处理边界冲突
|
||||
List<Integer> selectedBalls = handleSSQHistoryBoundaryConflicts(sameCoefficientBalls, needCount);
|
||||
result.addAll(selectedBalls);
|
||||
addedCount += selectedBalls.size();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据级别从T7表获取候选球号和系数
|
||||
*/
|
||||
private List<BallWithCoefficient> getT7BallsByLevel(Integer masterBallNumber, String level) {
|
||||
List<T7> t7List = t7Mapper.selectList(
|
||||
new QueryWrapper<T7>()
|
||||
.eq("masterBallNumber", masterBallNumber)
|
||||
.orderByDesc("faceCoefficient"));
|
||||
|
||||
if (CollectionUtils.isEmpty(t7List)) {
|
||||
log.warn("T7表中主球{}没有数据", masterBallNumber);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if ("H".equalsIgnoreCase(level)) {
|
||||
return getHighLevelBallsFromT7(t7List, masterBallNumber);
|
||||
} else if ("M".equalsIgnoreCase(level)) {
|
||||
return getMiddleLevelBallsFromT7(t7List, masterBallNumber);
|
||||
} else { // L
|
||||
return getLowLevelBallsFromT7(t7List, masterBallNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 高位策略:从T7表获取系数最大的前10个球号(如果第10个系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getHighLevelBallsFromT7(List<T7> t7List, Integer masterBallNumber) {
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
if (t7List.size() <= 10) {
|
||||
// 如果总数不超过10个,全部加入
|
||||
for (T7 t7 : t7List) {
|
||||
result.add(new BallWithCoefficient(t7.getSlaveBallNumber(), t7.getFaceCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 先加入前10个
|
||||
for (int i = 0; i < 10; i++) {
|
||||
T7 t7 = t7List.get(i);
|
||||
result.add(new BallWithCoefficient(t7.getSlaveBallNumber(), t7.getFaceCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 获取第10个球号的系数
|
||||
Double boundaryCoefficient = t7List.get(9).getFaceCoefficient();
|
||||
|
||||
// 继续查找后面系数相同的球号
|
||||
for (int i = 10; i < t7List.size(); i++) {
|
||||
T7 t7 = t7List.get(i);
|
||||
if (t7.getFaceCoefficient().equals(boundaryCoefficient)) {
|
||||
result.add(new BallWithCoefficient(t7.getSlaveBallNumber(), t7.getFaceCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 中位策略:从T7表获取平均值附近10个球号(向上5个,向下4个,边界系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getMiddleLevelBallsFromT7(List<T7> t7List, Integer masterBallNumber) {
|
||||
if (t7List.size() < 10) {
|
||||
log.warn("T7表数据不足10条,实际{}条", t7List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (T7 t7 : t7List) {
|
||||
result.add(new BallWithCoefficient(t7.getSlaveBallNumber(), t7.getFaceCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 计算平均系数(保留两位小数,截断方式)
|
||||
double avgCoefficient = t7List.stream()
|
||||
.mapToDouble(T7::getFaceCoefficient)
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
// 截断到两位小数
|
||||
avgCoefficient = Math.floor(avgCoefficient * 100) / 100;
|
||||
|
||||
// 找到第一个比平均值大的位置(从下到上遍历)
|
||||
int avgPosition = -1;
|
||||
for (int i = t7List.size() - 1; i >= 0; i--) {
|
||||
if (t7List.get(i).getFaceCoefficient() >= avgCoefficient) {
|
||||
avgPosition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (avgPosition == -1) {
|
||||
avgPosition = 0; // 如果没找到,取第一个
|
||||
}
|
||||
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
|
||||
// 先加入平均值位置的球号
|
||||
result.add(new BallWithCoefficient(t7List.get(avgPosition).getSlaveBallNumber(),
|
||||
t7List.get(avgPosition).getFaceCoefficient(), masterBallNumber));
|
||||
|
||||
// 向上取5个球号,处理边界系数相同情况
|
||||
int upCount = 0;
|
||||
Double upBoundaryCoeff = null;
|
||||
for (int i = avgPosition - 1; i >= 0 && upCount < 5; i--) {
|
||||
result.add(new BallWithCoefficient(t7List.get(i).getSlaveBallNumber(),
|
||||
t7List.get(i).getFaceCoefficient(), masterBallNumber));
|
||||
upCount++;
|
||||
if (upCount == 5) {
|
||||
upBoundaryCoeff = t7List.get(i).getFaceCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向上边界处理:继续添加与第5个球号系数相同的球号
|
||||
if (upBoundaryCoeff != null) {
|
||||
for (int i = avgPosition - 6; i >= 0; i--) {
|
||||
if (t7List.get(i).getFaceCoefficient().equals(upBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(t7List.get(i).getSlaveBallNumber(),
|
||||
t7List.get(i).getFaceCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 向下取4个球号,处理边界系数相同情况
|
||||
int downCount = 0;
|
||||
Double downBoundaryCoeff = null;
|
||||
for (int i = avgPosition + 1; i < t7List.size() && downCount < 4; i++) {
|
||||
result.add(new BallWithCoefficient(t7List.get(i).getSlaveBallNumber(),
|
||||
t7List.get(i).getFaceCoefficient(), masterBallNumber));
|
||||
downCount++;
|
||||
if (downCount == 4) {
|
||||
downBoundaryCoeff = t7List.get(i).getFaceCoefficient();
|
||||
}
|
||||
}
|
||||
|
||||
// 向下边界处理:继续添加与第4个球号系数相同的球号
|
||||
if (downBoundaryCoeff != null) {
|
||||
for (int i = avgPosition + 5; i < t7List.size(); i++) {
|
||||
if (t7List.get(i).getFaceCoefficient().equals(downBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(t7List.get(i).getSlaveBallNumber(),
|
||||
t7List.get(i).getFaceCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 低位策略:从T7表获取最小值向上第3-12个球(共10个球,边界系数相同则一并加入)
|
||||
*/
|
||||
private List<BallWithCoefficient> getLowLevelBallsFromT7(List<T7> t7List, Integer masterBallNumber) {
|
||||
if (t7List.size() < 12) {
|
||||
log.warn("T7表数据不足12条,实际{}条", t7List.size());
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
for (T7 t7 : t7List) {
|
||||
result.add(new BallWithCoefficient(t7.getSlaveBallNumber(), t7.getFaceCoefficient(), masterBallNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 取最小值向上第3-12个球(t7List已按系数降序排列,最后的是最小的)
|
||||
// 最小值在最后,向上第3个就是值数组的第 size-3 个,向上第12个就是值数组的第 size-12 个
|
||||
List<BallWithCoefficient> result = new ArrayList<>();
|
||||
int startIndex = Math.max(0, t7List.size() - 12); // 第3位
|
||||
int endIndex = t7List.size() - 3; // 第12位
|
||||
|
||||
// 先加入基本的10个球号
|
||||
for (int i = startIndex; i <= endIndex; i++) {
|
||||
T7 t7 = t7List.get(i);
|
||||
result.add(new BallWithCoefficient(t7.getSlaveBallNumber(), t7.getFaceCoefficient(), masterBallNumber));
|
||||
}
|
||||
|
||||
// 处理起始边界:第1个球号(startIndex)系数相同情况
|
||||
if (startIndex > 0) {
|
||||
Double startBoundaryCoeff = t7List.get(startIndex).getFaceCoefficient();
|
||||
|
||||
// 继续向前查找系数相同的球号
|
||||
for (int i = startIndex - 1; i >= 0; i--) {
|
||||
if (t7List.get(i).getFaceCoefficient().equals(startBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(t7List.get(i).getSlaveBallNumber(),
|
||||
t7List.get(i).getFaceCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理结束边界:第10个球号(endIndex)系数相同情况
|
||||
if (endIndex < t7List.size() - 1) {
|
||||
Double endBoundaryCoeff = t7List.get(endIndex).getFaceCoefficient();
|
||||
|
||||
// 继续向后查找系数相同的球号
|
||||
for (int i = endIndex + 1; i < t7List.size(); i++) {
|
||||
if (t7List.get(i).getFaceCoefficient().equals(endBoundaryCoeff)) {
|
||||
result.add(new BallWithCoefficient(t7List.get(i).getSlaveBallNumber(),
|
||||
t7List.get(i).getFaceCoefficient(), masterBallNumber));
|
||||
} else {
|
||||
break; // 系数不同,停止查找
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理SSQ历史排行边界冲突,选择指定数量的球号
|
||||
* @param candidateBalls 候选球号列表
|
||||
* @param selectCount 需要选择的数量
|
||||
* @return 选中的球号列表
|
||||
*/
|
||||
private List<Integer> handleSSQHistoryBoundaryConflicts(List<Integer> candidateBalls, int selectCount) {
|
||||
if (CollectionUtils.isEmpty(candidateBalls) || selectCount <= 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (selectCount >= candidateBalls.size()) {
|
||||
return new ArrayList<>(candidateBalls);
|
||||
}
|
||||
|
||||
// 使用history_top表进行筛选
|
||||
return selectFromHistoryTop(candidateBalls, selectCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从history_top表中选择指定数量的球号
|
||||
*/
|
||||
private List<Integer> selectFromHistoryTop(List<Integer> candidateBalls, int selectCount) {
|
||||
if (CollectionUtils.isEmpty(candidateBalls) || selectCount <= 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (selectCount >= candidateBalls.size()) {
|
||||
return new ArrayList<>(candidateBalls);
|
||||
}
|
||||
|
||||
// 获取在history_top表中的系数
|
||||
Map<Integer, Double> topCoefficients = new HashMap<>();
|
||||
for (Integer ball : candidateBalls) {
|
||||
HistoryTop record = historyTopMapper.selectOne(
|
||||
new QueryWrapper<HistoryTop>()
|
||||
.eq("ballNumber", ball));
|
||||
if (record != null) {
|
||||
topCoefficients.put(ball, record.getPointCoefficient());
|
||||
} else {
|
||||
topCoefficients.put(ball, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// 按系数降序排序,选择前selectCount个
|
||||
List<Map.Entry<Integer, Double>> sortedByTop = topCoefficients.entrySet().stream()
|
||||
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Integer> result = new ArrayList<>();
|
||||
for (int i = 0; i < Math.min(selectCount, sortedByTop.size()); i++) {
|
||||
result.add(sortedByTop.get(i).getKey());
|
||||
}
|
||||
|
||||
// 如果仍然不够,按原始顺序补充
|
||||
while (result.size() < selectCount && result.size() < candidateBalls.size()) {
|
||||
for (Integer ball : candidateBalls) {
|
||||
if (!result.contains(ball) && result.size() < selectCount) {
|
||||
result.add(ball);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计分析并生成结果
|
||||
*/
|
||||
private List<SSQSecondStepResultVO.BallAnalysisResult> analyzeResults(
|
||||
List<Integer> allCandidateBalls,
|
||||
Map<Integer, List<BallWithCoefficient>> ballCoefficientMap) {
|
||||
|
||||
// 统计球号出现次数
|
||||
Map<Integer, Integer> ballFrequencyMap = new HashMap<>();
|
||||
for (Integer ball : allCandidateBalls) {
|
||||
ballFrequencyMap.put(ball, ballFrequencyMap.getOrDefault(ball, 0) + 1);
|
||||
}
|
||||
|
||||
// 计算系数和
|
||||
Map<Integer, Double> ballCoefficientSumMap = new HashMap<>();
|
||||
for (Map.Entry<Integer, List<BallWithCoefficient>> entry : ballCoefficientMap.entrySet()) {
|
||||
Integer ballNumber = entry.getKey();
|
||||
List<BallWithCoefficient> coefficients = entry.getValue();
|
||||
double sum = coefficients.stream()
|
||||
.mapToDouble(BallWithCoefficient::getCoefficient)
|
||||
.sum();
|
||||
ballCoefficientSumMap.put(ballNumber, sum);
|
||||
}
|
||||
|
||||
// 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
Set<Integer> allBallsWithRanking = getAllBallsWithRanking();
|
||||
|
||||
// 获取百期排位和历史排位
|
||||
Map<Integer, Integer> top100RankingMap = getTop100Rankings(allBallsWithRanking);
|
||||
Map<Integer, Integer> historyRankingMap = getHistoryRankings(allBallsWithRanking);
|
||||
|
||||
// 组装结果
|
||||
List<SSQSecondStepResultVO.BallAnalysisResult> results = new ArrayList<>();
|
||||
for (Integer ballNumber : allBallsWithRanking) {
|
||||
SSQSecondStepResultVO.BallAnalysisResult result = SSQSecondStepResultVO.BallAnalysisResult.builder()
|
||||
.ballNumber(ballNumber)
|
||||
.frequency(ballFrequencyMap.getOrDefault(ballNumber, 0))
|
||||
.coefficientSum(ballCoefficientSumMap.getOrDefault(ballNumber, 0.0))
|
||||
.top100Ranking(top100RankingMap.get(ballNumber))
|
||||
.historyRanking(historyRankingMap.get(ballNumber))
|
||||
.build();
|
||||
results.add(result);
|
||||
}
|
||||
|
||||
// 按出现次数降序排列,次数相同的按球号升序排列
|
||||
results.sort((a, b) -> {
|
||||
int frequencyCompare = b.getFrequency().compareTo(a.getFrequency());
|
||||
if (frequencyCompare != 0) {
|
||||
return frequencyCompare;
|
||||
}
|
||||
return a.getBallNumber().compareTo(b.getBallNumber());
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有有排行数据的球号(包括频次为0的球号)
|
||||
*/
|
||||
private Set<Integer> getAllBallsWithRanking() {
|
||||
Set<Integer> allBalls = new HashSet<>();
|
||||
|
||||
// 从百期排行表获取所有球号
|
||||
List<HistoryTop100> top100List = historyTop100Mapper.selectList(
|
||||
new QueryWrapper<HistoryTop100>());
|
||||
for (HistoryTop100 item : top100List) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
// 从历史排行表获取所有球号
|
||||
List<HistoryTop> historyTopList = historyTopMapper.selectList(
|
||||
new QueryWrapper<HistoryTop>());
|
||||
for (HistoryTop item : historyTopList) {
|
||||
allBalls.add(item.getBallNumber());
|
||||
}
|
||||
|
||||
return allBalls;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在百期排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getTop100Rankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的no字段作为排名
|
||||
List<HistoryTop100> top100List = historyTop100Mapper.selectList(
|
||||
new QueryWrapper<HistoryTop100>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的no字段作为排名
|
||||
for (HistoryTop100 item : top100List) {
|
||||
result.put(item.getBallNumber(), item.getNo());
|
||||
}
|
||||
|
||||
// 对于没有在百期排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取球号在历史排行表中的排位
|
||||
*/
|
||||
private Map<Integer, Integer> getHistoryRankings(Set<Integer> ballNumbers) {
|
||||
Map<Integer, Integer> result = new HashMap<>();
|
||||
|
||||
// 直接查询指定球号的排行数据,使用表中的no字段作为排名
|
||||
List<HistoryTop> historyTopList = historyTopMapper.selectList(
|
||||
new QueryWrapper<HistoryTop>()
|
||||
.in("ballNumber", ballNumbers));
|
||||
|
||||
// 使用表中的no字段作为排名
|
||||
for (HistoryTop item : historyTopList) {
|
||||
result.put(item.getBallNumber(), item.getNo());
|
||||
}
|
||||
|
||||
// 对于没有在历史排行表中的球号,设置为null
|
||||
for (Integer ballNumber : ballNumbers) {
|
||||
if (!result.containsKey(ballNumber)) {
|
||||
result.put(ballNumber, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数验证
|
||||
*/
|
||||
private void validateInputParams(String level, List<Integer> redBalls, Integer blueBall, Integer nextFirstBall) {
|
||||
if (!"H".equalsIgnoreCase(level) && !"M".equalsIgnoreCase(level) && !"L".equalsIgnoreCase(level)) {
|
||||
throw new IllegalArgumentException("位置级别必须是H/M/L之一");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(redBalls) || redBalls.size() != 6) {
|
||||
throw new IllegalArgumentException("红球号码必须为6个");
|
||||
}
|
||||
|
||||
if (blueBall == null || blueBall < 1 || blueBall > 16) {
|
||||
throw new IllegalArgumentException("蓝球号码范围应为1-16");
|
||||
}
|
||||
|
||||
if (nextFirstBall == null || nextFirstBall < 1 || nextFirstBall > 33) {
|
||||
throw new IllegalArgumentException("下期首球号码范围应为1-33");
|
||||
}
|
||||
|
||||
for (Integer redBall : redBalls) {
|
||||
if (redBall < 1 || redBall > 33) {
|
||||
throw new IllegalArgumentException("红球号码范围应为1-33");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/main/java/com/xy/xyaicpzs/mapper/D10Mapper.java
Normal file
15
src/main/java/com/xy/xyaicpzs/mapper/D10Mapper.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.D10;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【d10(d10表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-21 15:06:10
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.D10
|
||||
*/
|
||||
public interface D10Mapper extends BaseMapper<D10> {
|
||||
}
|
||||
15
src/main/java/com/xy/xyaicpzs/mapper/D11Mapper.java
Normal file
15
src/main/java/com/xy/xyaicpzs/mapper/D11Mapper.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.D11;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【d11(d11表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-21 15:06:10
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.D11
|
||||
*/
|
||||
public interface D11Mapper extends BaseMapper<D11> {
|
||||
}
|
||||
16
src/main/java/com/xy/xyaicpzs/mapper/D12Mapper.java
Normal file
16
src/main/java/com/xy/xyaicpzs/mapper/D12Mapper.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.D12;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【d12(d12表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-21 15:06:10
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.D12
|
||||
*/
|
||||
public interface D12Mapper extends BaseMapper<D12> {
|
||||
}
|
||||
|
||||
15
src/main/java/com/xy/xyaicpzs/mapper/D5Mapper.java
Normal file
15
src/main/java/com/xy/xyaicpzs/mapper/D5Mapper.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.D5;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【d5(d5表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-21 15:06:10
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.D5
|
||||
*/
|
||||
public interface D5Mapper extends BaseMapper<D5> {
|
||||
}
|
||||
15
src/main/java/com/xy/xyaicpzs/mapper/D6Mapper.java
Normal file
15
src/main/java/com/xy/xyaicpzs/mapper/D6Mapper.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.D6;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【d6(d6表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-21 15:06:10
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.D6
|
||||
*/
|
||||
public interface D6Mapper extends BaseMapper<D6> {
|
||||
}
|
||||
19
src/main/java/com/xy/xyaicpzs/mapper/D7Mapper.java
Normal file
19
src/main/java/com/xy/xyaicpzs/mapper/D7Mapper.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.D7;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【d7(d7表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-21 15:06:10
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.D7
|
||||
*/
|
||||
public interface D7Mapper extends BaseMapper<D7> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
18
src/main/java/com/xy/xyaicpzs/mapper/D8Mapper.java
Normal file
18
src/main/java/com/xy/xyaicpzs/mapper/D8Mapper.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.D8;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【d8(d8表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-21 15:06:10
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.D8
|
||||
*/
|
||||
public interface D8Mapper extends BaseMapper<D8> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
17
src/main/java/com/xy/xyaicpzs/mapper/D9Mapper.java
Normal file
17
src/main/java/com/xy/xyaicpzs/mapper/D9Mapper.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.D9;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【d9(d9表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-21 15:06:10
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.D9
|
||||
*/
|
||||
public interface D9Mapper extends BaseMapper<D9> {
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.DltBackendHistory100;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【dlt_backend_history_100(大乐透后区最近100期数据表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-21 11:35:47
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.DltBackendHistory100
|
||||
*/
|
||||
public interface DltBackendHistory100Mapper extends BaseMapper<DltBackendHistory100> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.DltBackendHistoryAll;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【dlt_backend_history_all(大乐透后区全部历史数据表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-21 11:35:47
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.DltBackendHistoryAll
|
||||
*/
|
||||
public interface DltBackendHistoryAllMapper extends BaseMapper<DltBackendHistoryAll> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.DltBackendHistoryTop100;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【dlt_backend_history_top_100(大乐透后区百期数据排行表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-20 16:24:40
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.DltBackendHistoryTop100
|
||||
*/
|
||||
public interface DltBackendHistoryTop100Mapper extends BaseMapper<DltBackendHistoryTop100> {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.DltBackendHistoryTop;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【dlt_backend_history_top(大乐透后区历史数据排行表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-20 16:24:40
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.DltBackendHistoryTop
|
||||
*/
|
||||
public interface DltBackendHistoryTopMapper extends BaseMapper<DltBackendHistoryTop> {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.DltDrawRecord;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【dlt_draw_record(大乐透开奖信息表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-20 15:55:06
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.DltDrawRecord
|
||||
*/
|
||||
public interface DltDrawRecordMapper extends BaseMapper<DltDrawRecord> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.DltFrontendHistory100;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【dlt_frontend_history_100(大乐透前区最近100期数据表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-20 16:24:40
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.DltFrontendHistory100
|
||||
*/
|
||||
public interface DltFrontendHistory100Mapper extends BaseMapper<DltFrontendHistory100> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.DltFrontendHistoryAll;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【dlt_frontend_history_all(大乐透前区全部历史数据表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-20 16:24:40
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.DltFrontendHistoryAll
|
||||
*/
|
||||
public interface DltFrontendHistoryAllMapper extends BaseMapper<DltFrontendHistoryAll> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.DltFrontendHistoryTop100;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【dlt_frontend_history_top_100(大乐透前区百期数据排行表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-20 16:24:40
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.DltFrontendHistoryTop100
|
||||
*/
|
||||
public interface DltFrontendHistoryTop100Mapper extends BaseMapper<DltFrontendHistoryTop100> {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.DltFrontendHistoryTop;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【dlt_frontend_history_top(大乐透前区历史数据排行表)】的数据库操作Mapper
|
||||
* @createDate 2025-08-20 16:24:40
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.DltFrontendHistoryTop
|
||||
*/
|
||||
public interface DltFrontendHistoryTopMapper extends BaseMapper<DltFrontendHistoryTop> {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.mapper;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.DltPredictRecord;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【dlt_predict_record(大乐透推测记录表)】的数据库操作Mapper
|
||||
* @createDate 2025-09-08 14:01:12
|
||||
* @Entity com.xy.xyaicpzs.domain.entity.DltPredictRecord
|
||||
*/
|
||||
public interface DltPredictRecordMapper extends BaseMapper<DltPredictRecord> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
13
src/main/java/com/xy/xyaicpzs/service/D10Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/D10Service.java
Normal 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> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/D11Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/D11Service.java
Normal 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> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/D12Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/D12Service.java
Normal 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> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/D5Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/D5Service.java
Normal 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> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/D6Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/D6Service.java
Normal 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> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/D7Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/D7Service.java
Normal 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> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/D8Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/D8Service.java
Normal 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> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/D9Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/D9Service.java
Normal 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> {
|
||||
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user