彩票助手后端1.0
This commit is contained in:
3154
src/main/java/com/xy/xyaicpzs/service/BallAnalysisService.java
Normal file
3154
src/main/java/com/xy/xyaicpzs/service/BallAnalysisService.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.BlueHistory100;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【blue_history_100(蓝球最近100期数据表)】的数据库操作Service
|
||||
* @createDate 2025-06-14 10:40:04
|
||||
*/
|
||||
public interface BlueHistory100Service extends IService<BlueHistory100> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.BlueHistoryAll;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【blue_history_all(蓝球全部历史数据表)】的数据库操作Service
|
||||
* @createDate 2025-06-14 10:40:07
|
||||
*/
|
||||
public interface BlueHistoryAllService extends IService<BlueHistoryAll> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.BlueHistoryTop100;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【blue_history_top_100(创建蓝球100期数据排行表)】的数据库操作Service
|
||||
* @createDate 2025-06-14 10:40:13
|
||||
*/
|
||||
public interface BlueHistoryTop100Service extends IService<BlueHistoryTop100> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.BlueHistoryTop;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【blue_history_top(蓝球历史数据排行表)】的数据库操作Service
|
||||
* @createDate 2025-06-14 10:40:10
|
||||
*/
|
||||
public interface BlueHistoryTopService extends IService<BlueHistoryTop> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.ChatMessage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【chat_message(聊天消息表)】的数据库操作Service
|
||||
* @createDate 2025-07-07 17:37:15
|
||||
*/
|
||||
public interface ChatMessageService extends IService<ChatMessage> {
|
||||
|
||||
}
|
||||
84
src/main/java/com/xy/xyaicpzs/service/CozeAuthService.java
Normal file
84
src/main/java/com/xy/xyaicpzs/service/CozeAuthService.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Coze认证服务
|
||||
* 提供通过JWT获取访问令牌的功能
|
||||
*/
|
||||
@Service
|
||||
public class CozeAuthService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CozeAuthService.class);
|
||||
private static final String COZE_TOKEN_URL = "https://api.coze.cn/api/permission/oauth2/token";
|
||||
private static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* 通过JWT获取访问令牌
|
||||
*
|
||||
* @param jwt JWT令牌
|
||||
* @param durationSeconds 令牌有效期(秒)
|
||||
* @return 包含访问令牌和过期时间的Map
|
||||
*/
|
||||
public Map<String, Object> getAccessToken(String jwt, Integer durationSeconds) {
|
||||
try {
|
||||
// 设置请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.set("Authorization", "Bearer " + jwt);
|
||||
|
||||
// 设置请求体
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("grant_type", GRANT_TYPE);
|
||||
if (durationSeconds != null && durationSeconds > 0) {
|
||||
requestBody.put("duration_seconds", durationSeconds);
|
||||
}
|
||||
|
||||
// 创建请求实体
|
||||
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
|
||||
|
||||
// 发送请求
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(COZE_TOKEN_URL, requestEntity, String.class);
|
||||
|
||||
// 解析响应
|
||||
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
|
||||
JsonNode jsonNode = objectMapper.readTree(response.getBody());
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("access_token", jsonNode.get("access_token").asText());
|
||||
result.put("expires_in", jsonNode.get("expires_in").asLong());
|
||||
|
||||
return result;
|
||||
} else {
|
||||
throw new RuntimeException("获取访问令牌失败,HTTP状态码:" + response.getStatusCodeValue());
|
||||
}
|
||||
|
||||
} catch (JsonProcessingException e) {
|
||||
logger.error("解析响应JSON失败", e);
|
||||
throw new RuntimeException("解析响应JSON失败: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
logger.error("获取访问令牌失败", e);
|
||||
throw new RuntimeException("获取访问令牌失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.common.requset.PredictRecordQueryRequest;
|
||||
import com.xy.xyaicpzs.common.response.PageResponse;
|
||||
import com.xy.xyaicpzs.domain.entity.PredictRecord;
|
||||
import com.xy.xyaicpzs.domain.vo.UserPredictStatVO;
|
||||
|
||||
/**
|
||||
* 数据分析服务接口
|
||||
*/
|
||||
public interface DataAnalysisService {
|
||||
|
||||
/**
|
||||
* 获取用户预测统计数据
|
||||
* @param userId 用户ID
|
||||
* @return 用户预测统计数据
|
||||
*/
|
||||
UserPredictStatVO getUserPredictStat(Long userId);
|
||||
|
||||
/**
|
||||
* 处理待开奖记录,匹配开奖结果
|
||||
* @return 处理的记录数量
|
||||
*/
|
||||
int processPendingPredictions();
|
||||
|
||||
/**
|
||||
* 按条件查询预测记录
|
||||
* @param request 查询条件
|
||||
* @return 分页预测记录
|
||||
*/
|
||||
PageResponse<PredictRecord> queryPredictRecords(PredictRecordQueryRequest request);
|
||||
|
||||
/**
|
||||
* 获取预测记录总数
|
||||
* @return 预测记录总数
|
||||
*/
|
||||
long getTotalPredictCount();
|
||||
}
|
||||
249
src/main/java/com/xy/xyaicpzs/service/ExcelImportService.java
Normal file
249
src/main/java/com/xy/xyaicpzs/service/ExcelImportService.java
Normal file
@@ -0,0 +1,249 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.util.ExcelDataImporter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Excel导入服务类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ExcelImportService {
|
||||
|
||||
@Autowired
|
||||
private ExcelDataImporter excelDataImporter;
|
||||
|
||||
/**
|
||||
* 导入Excel文件数据
|
||||
* @param file 上传的Excel文件
|
||||
* @return 导入结果消息
|
||||
*/
|
||||
public String importExcelFile(MultipartFile file) {
|
||||
if (file.isEmpty()) {
|
||||
return "文件为空,请选择有效的Excel文件";
|
||||
}
|
||||
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (originalFilename == null || !originalFilename.toLowerCase().endsWith(".xlsx")) {
|
||||
return "请上传.xlsx格式的Excel文件";
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建临时文件
|
||||
File tempFile = File.createTempFile("import_", ".xlsx");
|
||||
file.transferTo(tempFile);
|
||||
|
||||
// 执行数据导入
|
||||
excelDataImporter.importExcelData(tempFile.getAbsolutePath());
|
||||
|
||||
// 删除临时文件
|
||||
tempFile.delete();
|
||||
|
||||
return "数据导入成功";
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("文件处理失败:{}", e.getMessage(), e);
|
||||
return "文件处理失败:" + e.getMessage();
|
||||
} catch (Exception e) {
|
||||
log.error("数据导入失败:{}", e.getMessage(), e);
|
||||
return "数据导入失败:" + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入指定路径的Excel文件
|
||||
* @param filePath 文件路径
|
||||
* @return 导入结果消息
|
||||
*/
|
||||
public String importExcelFileByPath(String filePath) {
|
||||
try {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
return "文件不存在:" + filePath;
|
||||
}
|
||||
|
||||
if (!filePath.toLowerCase().endsWith(".xlsx")) {
|
||||
return "请提供.xlsx格式的Excel文件路径";
|
||||
}
|
||||
|
||||
// 执行数据导入
|
||||
excelDataImporter.importExcelData(filePath);
|
||||
|
||||
return "数据导入成功";
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("数据导入失败:{}", e.getMessage(), e);
|
||||
return "数据导入失败:" + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Excel导入信息说明
|
||||
* @return 导入信息说明
|
||||
*/
|
||||
public String getImportInfo() {
|
||||
StringBuilder info = new StringBuilder();
|
||||
info.append("=== Excel数据导入说明 ===\n");
|
||||
info.append("支持的工作表:\n");
|
||||
info.append("1. T1工作表 - 红球数据(4个表:history_all, history_100, history_top, history_top_100)\n");
|
||||
info.append("2. T2工作表 - 蓝球数据(4个表:blue_history_all, blue_history_100, blue_history_top, blue_history_top_100)\n");
|
||||
info.append("3. T3工作表 - 红球线系数数据(1个表:t3,33×33=1089条记录)\n");
|
||||
info.append("4. T4工作表 - 蓝球组红球线系数数据(1个表:t4,16×33=528条记录)\n");
|
||||
info.append("5. T5工作表 - 蓝球组蓝球线系数数据(1个表:t5,16×16=256条记录)\n");
|
||||
info.append("6. T6工作表 - 红球组蓝球线系数数据(1个表:t6,33×16=528条记录)\n");
|
||||
info.append("7. T7工作表 - 红球组红球面系数数据(1个表:t7,33×33=1089条记录)\n");
|
||||
info.append("8. T8工作表 - 红球组蓝球面系数数据(1个表:t8,33×16=528条记录)\n");
|
||||
info.append("9. T10工作表 - 彩票开奖信息数据(1个表:lottery_draws,历史开奖记录)\n");
|
||||
info.append("10. T11工作表 - 蓝球组红球面系数数据(1个表:t11,16×33=528条记录)\n");
|
||||
info.append("\n数据结构:\n");
|
||||
info.append("- T1/T2:标准表格结构,按列映射到对应字段\n");
|
||||
info.append("- T3:每三列为一组,红球1-33号,线系数在C、F、I、L...列\n");
|
||||
info.append("- T4:每三列为一组,蓝球1-16号,红球1-33号,线系数在C、F、I、L...列\n");
|
||||
info.append("- T5:每三列为一组,蓝球1-16号,蓝球1-16号,线系数在C、F、I、L...列\n");
|
||||
info.append("- T6:每三列为一组,红球1-33号,蓝球1-16号,线系数在C、F、I、L...列\n");
|
||||
info.append("- T7:每三列为一组,红球1-33号,红球1-33号,面系数在C、F、I、L...列\n");
|
||||
info.append("- T8:每三列为一组,红球1-33号,蓝球1-16号,面系数在C、F、I、L...列\n");
|
||||
info.append("- T10:标准表格结构,A列开奖期号,B列开奖日期,C-H列红球1-6,I列蓝球\n");
|
||||
info.append("- T11:每三列为一组,蓝球1-16号,红球1-33号,面系数在C、F、I、L...列\n");
|
||||
info.append("\n导入特性:\n");
|
||||
info.append("- 自动清空现有数据\n");
|
||||
info.append("- 数值自动保留两位小数\n");
|
||||
info.append("- 完善的错误处理和日志记录\n");
|
||||
info.append("- 支持.xlsx和.xls格式\n");
|
||||
return info.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入指定路径的开奖数据Excel文件
|
||||
* @param filePath 文件路径
|
||||
* @return 导入结果消息
|
||||
*/
|
||||
public String importLotteryDrawsByPath(String filePath) {
|
||||
try {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
return "文件不存在:" + filePath;
|
||||
}
|
||||
|
||||
if (!filePath.toLowerCase().endsWith(".xlsx")) {
|
||||
return "请提供.xlsx格式的Excel文件路径";
|
||||
}
|
||||
|
||||
// 执行开奖数据导入
|
||||
excelDataImporter.importLotteryDrawsData(filePath);
|
||||
|
||||
return "开奖数据导入成功";
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("开奖数据导入失败:{}", e.getMessage(), e);
|
||||
return "开奖数据导入失败:" + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入上传的开奖数据Excel文件
|
||||
* @param file 上传的Excel文件
|
||||
* @return 导入结果消息
|
||||
*/
|
||||
public String importLotteryDrawsFile(MultipartFile file) {
|
||||
if (file.isEmpty()) {
|
||||
return "文件为空,请选择有效的Excel文件";
|
||||
}
|
||||
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (originalFilename == null || !originalFilename.toLowerCase().endsWith(".xlsx")) {
|
||||
return "请上传.xlsx格式的Excel文件";
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建临时文件
|
||||
File tempFile = File.createTempFile("lottery_import_", ".xlsx");
|
||||
file.transferTo(tempFile);
|
||||
|
||||
// 执行开奖数据导入
|
||||
excelDataImporter.importLotteryDrawsData(tempFile.getAbsolutePath());
|
||||
|
||||
// 删除临时文件
|
||||
tempFile.delete();
|
||||
|
||||
return "开奖数据导入成功";
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("文件处理失败:{}", e.getMessage(), e);
|
||||
return "文件处理失败:" + e.getMessage();
|
||||
} catch (Exception e) {
|
||||
log.error("开奖数据导入失败:{}", e.getMessage(), e);
|
||||
return "开奖数据导入失败:" + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加导入指定路径的开奖数据Excel文件(不清空现有数据)
|
||||
* @param filePath 文件路径
|
||||
* @return 导入结果消息
|
||||
*/
|
||||
public String appendLotteryDrawsByPath(String filePath) {
|
||||
try {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
return "文件不存在:" + filePath;
|
||||
}
|
||||
|
||||
if (!filePath.toLowerCase().endsWith(".xlsx")) {
|
||||
return "请提供.xlsx格式的Excel文件路径";
|
||||
}
|
||||
|
||||
// 执行开奖数据追加导入
|
||||
excelDataImporter.appendLotteryDrawsData(filePath);
|
||||
|
||||
return "开奖数据追加导入成功";
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("开奖数据追加导入失败:{}", e.getMessage(), e);
|
||||
return "开奖数据追加导入失败:" + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加导入上传的开奖数据Excel文件(不清空现有数据)
|
||||
* @param file 上传的Excel文件
|
||||
* @return 导入结果消息
|
||||
*/
|
||||
public String appendLotteryDrawsFile(MultipartFile file) {
|
||||
if (file.isEmpty()) {
|
||||
return "文件为空,请选择有效的Excel文件";
|
||||
}
|
||||
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (originalFilename == null || !originalFilename.toLowerCase().endsWith(".xlsx")) {
|
||||
return "请上传.xlsx格式的Excel文件";
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建临时文件
|
||||
File tempFile = File.createTempFile("lottery_append_", ".xlsx");
|
||||
file.transferTo(tempFile);
|
||||
|
||||
// 执行开奖数据追加导入
|
||||
excelDataImporter.appendLotteryDrawsData(tempFile.getAbsolutePath());
|
||||
|
||||
// 删除临时文件
|
||||
tempFile.delete();
|
||||
|
||||
return "开奖数据追加导入成功";
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("文件处理失败:{}", e.getMessage(), e);
|
||||
return "文件处理失败:" + e.getMessage();
|
||||
} catch (Exception e) {
|
||||
log.error("开奖数据追加导入失败:{}", e.getMessage(), e);
|
||||
return "开奖数据追加导入失败:" + e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/History100Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/History100Service.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.History100;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【history_100(最近100期数据表)】的数据库操作Service
|
||||
* @createDate 2025-06-14 09:48:05
|
||||
*/
|
||||
public interface History100Service extends IService<History100> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/HistoryAllService.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/HistoryAllService.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.HistoryAll;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【history_all(历史数据表)】的数据库操作Service
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
public interface HistoryAllService extends IService<HistoryAll> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.HistoryTop100;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【history_top_100(创建100期数据排行表)】的数据库操作Service
|
||||
* @createDate 2025-06-14 09:48:16
|
||||
*/
|
||||
public interface HistoryTop100Service extends IService<HistoryTop100> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/HistoryTopService.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/HistoryTopService.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.HistoryTop;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【history_top(历史数据排行表)】的数据库操作Service
|
||||
* @createDate 2025-06-14 09:48:13
|
||||
*/
|
||||
public interface HistoryTopService extends IService<HistoryTop> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.xy.xyaicpzs.domain.entity.LotteryDraws;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【lottery_draws(彩票开奖信息表)】的数据库操作Service
|
||||
* @createDate 2025-06-14 16:41:29
|
||||
*/
|
||||
public interface LotteryDrawsService extends IService<LotteryDraws> {
|
||||
|
||||
/**
|
||||
* 获取近期开奖信息
|
||||
* @param limit 获取条数,默认15条
|
||||
* @return 开奖信息列表,按开奖期号倒序排列
|
||||
*/
|
||||
List<LotteryDraws> getRecentDraws(Integer limit);
|
||||
|
||||
/**
|
||||
* 根据期号精准查询开奖信息
|
||||
* @param drawId 开奖期号
|
||||
* @return 开奖信息
|
||||
*/
|
||||
LotteryDraws getByDrawId(Long drawId);
|
||||
|
||||
/**
|
||||
* 根据日期范围查询开奖信息
|
||||
* @param startDate 开始日期
|
||||
* @param endDate 结束日期
|
||||
* @return 开奖信息列表,按开奖期号倒序排列
|
||||
*/
|
||||
List<LotteryDraws> getByDateRange(Date startDate, Date endDate);
|
||||
|
||||
/**
|
||||
* 综合查询开奖信息
|
||||
* @param drawId 开奖期号(可选)
|
||||
* @param startDate 开始日期(可选)
|
||||
* @param endDate 结束日期(可选)
|
||||
* @return 开奖信息列表
|
||||
*/
|
||||
List<LotteryDraws> queryDraws(Long drawId, Date startDate, Date endDate);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.OperationHistory;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【operation_history(操作历史记录表)】的数据库操作Service
|
||||
* @createDate 2025-06-19 14:51:51
|
||||
*/
|
||||
public interface OperationHistoryService extends IService<OperationHistory> {
|
||||
|
||||
/**
|
||||
* 记录操作历史
|
||||
* @param userId 操作用户ID
|
||||
* @param operationType 操作类型
|
||||
* @param operationModule 操作模块(0-会员码管理/1-Excel导入管理等)
|
||||
* @param operationResult 操作结果(成功/失败)
|
||||
* @param resultMessage 结果消息
|
||||
*/
|
||||
void recordOperation(Long userId, String operationType, Integer operationModule,
|
||||
String operationResult, String resultMessage);
|
||||
|
||||
/**
|
||||
* 根据用户ID和操作模块获取操作历史列表
|
||||
* @param userId 用户ID
|
||||
* @param operationModule 操作模块(0-会员码管理/1-Excel导入管理等)
|
||||
* @return 操作历史列表,按操作时间倒序排列
|
||||
*/
|
||||
List<OperationHistory> getOperationHistoryByUserIdAndModule(Long userId, Integer operationModule);
|
||||
|
||||
/**
|
||||
* 根据操作模块获取操作历史列表
|
||||
* @param operationModule 操作模块(0-会员码管理/1-Excel导入管理等)
|
||||
* @return 操作历史列表,按操作时间倒序排列
|
||||
*/
|
||||
List<OperationHistory> getOperationHistoryByModule(Integer operationModule);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.PredictRecord;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【predict_record(彩票开奖信息表)】的数据库操作Service
|
||||
* @createDate 2025-06-16 13:17:53
|
||||
*/
|
||||
public interface PredictRecordService extends IService<PredictRecord> {
|
||||
|
||||
/**
|
||||
* 创建预测记录
|
||||
* @param userId 用户ID
|
||||
* @param drawId 开奖期号
|
||||
* @param drawDate 开奖日期
|
||||
* @param redBalls 6个红球号码
|
||||
* @param blueBall 蓝球号码
|
||||
* @return 创建的预测记录
|
||||
*/
|
||||
PredictRecord createPredictRecord(Long userId, Long drawId, Date drawDate, List<Integer> redBalls, Integer blueBall);
|
||||
|
||||
/**
|
||||
* 根据用户ID获取所有预测记录
|
||||
* @param userId 用户ID
|
||||
* @return 用户的所有预测记录列表,按预测时间倒序排列
|
||||
*/
|
||||
List<PredictRecord> getPredictRecordsByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 根据用户ID分页获取预测记录
|
||||
* @param userId 用户ID
|
||||
* @param page 页码,从1开始
|
||||
* @param size 每页大小
|
||||
* @return 用户的预测记录列表,按预测时间倒序排列
|
||||
*/
|
||||
List<PredictRecord> getPredictRecordsByUserIdWithPaging(Long userId, Integer page, Integer size);
|
||||
|
||||
/**
|
||||
* 根据用户ID获取预测记录总数
|
||||
* @param userId 用户ID
|
||||
* @return 用户的预测记录总数
|
||||
*/
|
||||
Long getPredictRecordsCountByUserId(Long userId);
|
||||
|
||||
}
|
||||
25
src/main/java/com/xy/xyaicpzs/service/SmsService.java
Normal file
25
src/main/java/com/xy/xyaicpzs/service/SmsService.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
/**
|
||||
* 短信服务接口
|
||||
*/
|
||||
public interface SmsService {
|
||||
|
||||
/**
|
||||
* 发送短信验证码
|
||||
*
|
||||
* @param phoneNumber 手机号码
|
||||
* @return 发送是否成功
|
||||
* @throws Exception 发送异常
|
||||
*/
|
||||
boolean sendVerificationCode(String phoneNumber) throws Exception;
|
||||
|
||||
/**
|
||||
* 验证短信验证码
|
||||
*
|
||||
* @param phoneNumber 手机号码
|
||||
* @param code 验证码
|
||||
* @return 验证是否通过
|
||||
*/
|
||||
boolean verifyCode(String phoneNumber, String code);
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/T11Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/T11Service.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.xy.xyaicpzs.domain.entity.T11;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t11(t11表(蓝球组红球的面系数))】的数据库操作Service
|
||||
* @createDate 2025-06-14 16:25:23
|
||||
*/
|
||||
public interface T11Service extends IService<T11> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/T3Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/T3Service.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.xy.xyaicpzs.domain.entity.T3;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t3(t3表(红球组红球的线系数))】的数据库操作Service
|
||||
* @createDate 2025-06-14 11:02:50
|
||||
*/
|
||||
public interface T3Service extends IService<T3> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/T4Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/T4Service.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.xy.xyaicpzs.domain.entity.T4;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t4(t4表(蓝球组红球的线系数))】的数据库操作Service
|
||||
* @createDate 2025-06-14 11:45:31
|
||||
*/
|
||||
public interface T4Service extends IService<T4> {
|
||||
|
||||
}
|
||||
14
src/main/java/com/xy/xyaicpzs/service/T5Service.java
Normal file
14
src/main/java/com/xy/xyaicpzs/service/T5Service.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.xy.xyaicpzs.domain.entity.T5;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t5(t5表(蓝球组蓝球的线系数))】的数据库操作Service
|
||||
* @createDate 2025-06-14 12:01:16
|
||||
*/
|
||||
public interface T5Service extends IService<T5> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/T6Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/T6Service.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.T6;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t6(t6表(红球组蓝球的线系数))】的数据库操作Service
|
||||
* @createDate 2025-06-14 13:19:12
|
||||
*/
|
||||
public interface T6Service extends IService<T6> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/T7Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/T7Service.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.xy.xyaicpzs.domain.entity.T7;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t7(t7表(红球组红球的面系数))】的数据库操作Service
|
||||
* @createDate 2025-06-14 13:30:50
|
||||
*/
|
||||
public interface T7Service extends IService<T7> {
|
||||
|
||||
}
|
||||
13
src/main/java/com/xy/xyaicpzs/service/T8Service.java
Normal file
13
src/main/java/com/xy/xyaicpzs/service/T8Service.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.T8;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t8(t8表(红球组蓝球的面系数))】的数据库操作Service
|
||||
* @createDate 2025-06-14 16:11:27
|
||||
*/
|
||||
public interface T8Service extends IService<T8> {
|
||||
|
||||
}
|
||||
99
src/main/java/com/xy/xyaicpzs/service/UserService.java
Normal file
99
src/main/java/com/xy/xyaicpzs/service/UserService.java
Normal file
@@ -0,0 +1,99 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.xy.xyaicpzs.domain.dto.user.UserPhoneLoginRequest;
|
||||
import com.xy.xyaicpzs.domain.dto.user.UserPhoneRegisterRequest;
|
||||
import com.xy.xyaicpzs.domain.entity.User;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 用户服务
|
||||
*/
|
||||
public interface UserService extends IService<User> {
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*
|
||||
* @param userAccount 用户账户
|
||||
* @param userPassword 用户密码
|
||||
* @param checkPassword 校验密码
|
||||
* @return 新用户 id
|
||||
*/
|
||||
long userRegister(String userAccount, String userName, String userPassword, String checkPassword);
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*
|
||||
* @param userAccount 用户账户
|
||||
* @param userPassword 用户密码
|
||||
* @param request
|
||||
* @return 脱敏后的用户信息
|
||||
*/
|
||||
User userLogin(String userAccount, String userPassword, HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 获取当前登录用户
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
User getLoginUser(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 用户注销
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
boolean userLogout(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 获取脱敏的用户信息
|
||||
*
|
||||
* @param originUser
|
||||
* @return
|
||||
*/
|
||||
User getSafetyUser(User originUser);
|
||||
|
||||
/**
|
||||
* 是否为管理员
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
boolean isAdmin(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 是否为管理员
|
||||
*
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
boolean isAdmin(User user);
|
||||
|
||||
/**
|
||||
* 手机号注册
|
||||
*
|
||||
* @param userPhoneRegisterRequest 手机号注册请求
|
||||
* @return 新用户 id
|
||||
*/
|
||||
long userPhoneRegister(UserPhoneRegisterRequest userPhoneRegisterRequest);
|
||||
|
||||
/**
|
||||
* 手机号登录
|
||||
*
|
||||
* @param userPhoneLoginRequest 手机号登录请求
|
||||
* @param request HTTP请求
|
||||
* @return 脱敏后的用户信息
|
||||
*/
|
||||
User userPhoneLogin(UserPhoneLoginRequest userPhoneLoginRequest, HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 加密用户密码
|
||||
*
|
||||
* @param password 原始密码
|
||||
* @return 加密后的密码
|
||||
*/
|
||||
String encryptPassword(String password);
|
||||
}
|
||||
40
src/main/java/com/xy/xyaicpzs/service/VipCodeService.java
Normal file
40
src/main/java/com/xy/xyaicpzs/service/VipCodeService.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.xy.xyaicpzs.domain.entity.VipCode;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【vip_code(会员码表)】的数据库操作Service
|
||||
* @createDate 2025-01-15 16:41:29
|
||||
*/
|
||||
public interface VipCodeService extends IService<VipCode> {
|
||||
|
||||
/**
|
||||
* 激活会员码
|
||||
* @param userId 用户ID
|
||||
* @param code 会员码
|
||||
* @return 是否激活成功
|
||||
*/
|
||||
boolean activateVipCode(Long userId, String code);
|
||||
|
||||
/**
|
||||
* 批量生成会员码
|
||||
* @param numCodes 生成数量
|
||||
* @param vipExpireTime 会员有效月数
|
||||
* @param createdUserId 创建人ID
|
||||
* @param createdUserName 创建人名称
|
||||
* @return 生成成功的数量
|
||||
*/
|
||||
int generateVipCodes(int numCodes, int vipExpireTime, Long createdUserId, String createdUserName);
|
||||
|
||||
/**
|
||||
* 获取一个可用的会员码
|
||||
* @param vipExpireTime 会员有效月数(1或12)
|
||||
* @param createdUserId 创建人ID
|
||||
* @param createdUserName 创建人名称
|
||||
* @return 可用的会员码,如果没有则返回null
|
||||
*/
|
||||
String getAvailableVipCode(int vipExpireTime, Long createdUserId, String createdUserName);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.VipExchangeRecord;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【vip_exchange_record(会员兑换表)】的数据库操作Service
|
||||
* @createDate 2025-06-19 11:27:10
|
||||
*/
|
||||
public interface VipExchangeRecordService extends IService<VipExchangeRecord> {
|
||||
|
||||
/**
|
||||
* 根据用户ID获取所有兑换记录
|
||||
* @param userId 用户ID
|
||||
* @return 用户的所有兑换记录列表,按兑换时间倒序排列
|
||||
*/
|
||||
List<VipExchangeRecord> getExchangeRecordsByUserId(Long userId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.BlueHistory100;
|
||||
import com.xy.xyaicpzs.mapper.BlueHistory100Mapper;
|
||||
import com.xy.xyaicpzs.service.BlueHistory100Service;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【blue_history_100(蓝球最近100期历史数据表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class BlueHistory100ServiceImpl extends ServiceImpl<BlueHistory100Mapper, BlueHistory100>
|
||||
implements BlueHistory100Service{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.BlueHistoryAll;
|
||||
import com.xy.xyaicpzs.mapper.BlueHistoryAllMapper;
|
||||
import com.xy.xyaicpzs.service.BlueHistoryAllService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【blue_history_all(蓝球历史数据表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class BlueHistoryAllServiceImpl extends ServiceImpl<BlueHistoryAllMapper, BlueHistoryAll>
|
||||
implements BlueHistoryAllService{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.BlueHistoryTop100;
|
||||
import com.xy.xyaicpzs.mapper.BlueHistoryTop100Mapper;
|
||||
import com.xy.xyaicpzs.service.BlueHistoryTop100Service;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【blue_history_top_100(蓝球100期数据排行表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class BlueHistoryTop100ServiceImpl extends ServiceImpl<BlueHistoryTop100Mapper, BlueHistoryTop100>
|
||||
implements BlueHistoryTop100Service{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.BlueHistoryTop;
|
||||
import com.xy.xyaicpzs.mapper.BlueHistoryTopMapper;
|
||||
import com.xy.xyaicpzs.service.BlueHistoryTopService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【blue_history_top(蓝球历史数据排行表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class BlueHistoryTopServiceImpl extends ServiceImpl<BlueHistoryTopMapper, BlueHistoryTop>
|
||||
implements BlueHistoryTopService{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.ChatMessage;
|
||||
import com.xy.xyaicpzs.service.ChatMessageService;
|
||||
import com.xy.xyaicpzs.mapper.ChatMessageMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【chat_message(聊天消息表)】的数据库操作Service实现
|
||||
* @createDate 2025-07-07 17:37:15
|
||||
*/
|
||||
@Service
|
||||
public class ChatMessageServiceImpl extends ServiceImpl<ChatMessageMapper, ChatMessage>
|
||||
implements ChatMessageService{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.xy.xyaicpzs.common.requset.PredictRecordQueryRequest;
|
||||
import com.xy.xyaicpzs.common.response.PageResponse;
|
||||
import com.xy.xyaicpzs.domain.entity.LotteryDraws;
|
||||
import com.xy.xyaicpzs.domain.entity.PredictRecord;
|
||||
import com.xy.xyaicpzs.domain.vo.UserPredictStatVO;
|
||||
import com.xy.xyaicpzs.mapper.LotteryDrawsMapper;
|
||||
import com.xy.xyaicpzs.mapper.PredictRecordMapper;
|
||||
import com.xy.xyaicpzs.service.DataAnalysisService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据分析服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class DataAnalysisServiceImpl implements DataAnalysisService {
|
||||
|
||||
@Autowired
|
||||
private PredictRecordMapper predictRecordMapper;
|
||||
|
||||
@Autowired
|
||||
private LotteryDrawsMapper lotteryDrawsMapper;
|
||||
|
||||
@Override
|
||||
public UserPredictStatVO getUserPredictStat(Long userId) {
|
||||
UserPredictStatVO statVO = new UserPredictStatVO();
|
||||
|
||||
// 查询用户的所有预测记录
|
||||
QueryWrapper<PredictRecord> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("userId", userId);
|
||||
List<PredictRecord> predictRecords = predictRecordMapper.selectList(queryWrapper);
|
||||
|
||||
if (predictRecords == null || predictRecords.isEmpty()) {
|
||||
statVO.setUserId(userId);
|
||||
statVO.setPredictCount(0L);
|
||||
statVO.setHitCount(0L);
|
||||
statVO.setPendingCount(0L);
|
||||
statVO.setDrawnCount(0L);
|
||||
statVO.setHitRate(java.math.BigDecimal.ZERO);
|
||||
return statVO;
|
||||
}
|
||||
|
||||
// 统计数据
|
||||
long totalPredicts = predictRecords.size();
|
||||
long hitCount = 0L;
|
||||
long pendingCount = 0L;
|
||||
long drawnCount = 0L;
|
||||
|
||||
for (PredictRecord record : predictRecords) {
|
||||
if ("待开奖".equals(record.getPredictStatus())) {
|
||||
pendingCount++;
|
||||
} else if ("已中奖".equals(record.getPredictStatus())) {
|
||||
hitCount++;
|
||||
drawnCount++;
|
||||
} else if ("未中奖".equals(record.getPredictStatus())) {
|
||||
drawnCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算命中率
|
||||
java.math.BigDecimal hitRate = drawnCount > 0 ?
|
||||
java.math.BigDecimal.valueOf(hitCount).divide(java.math.BigDecimal.valueOf(drawnCount), 4, java.math.RoundingMode.HALF_UP) :
|
||||
java.math.BigDecimal.ZERO;
|
||||
|
||||
statVO.setUserId(userId);
|
||||
statVO.setPredictCount(totalPredicts);
|
||||
statVO.setHitCount(hitCount);
|
||||
statVO.setPendingCount(pendingCount);
|
||||
statVO.setDrawnCount(drawnCount);
|
||||
statVO.setHitRate(hitRate);
|
||||
|
||||
return statVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int processPendingPredictions() {
|
||||
// 查询所有待开奖的预测记录
|
||||
QueryWrapper<PredictRecord> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("predictStatus", "待开奖");
|
||||
List<PredictRecord> pendingRecords = predictRecordMapper.selectList(queryWrapper);
|
||||
|
||||
int processedCount = 0;
|
||||
|
||||
for (PredictRecord record : pendingRecords) {
|
||||
// 查询对应期号的开奖结果
|
||||
LotteryDraws draws = lotteryDrawsMapper.selectById(record.getDrawId());
|
||||
if (draws != null) {
|
||||
// 比较预测号码与开奖号码,计算中奖结果
|
||||
String result = calculatePredictResult(record, draws);
|
||||
long bonus = calculateBonus(result);
|
||||
if(result.equals("未中奖")){
|
||||
record.setPredictStatus("未中奖");
|
||||
}else{
|
||||
record.setPredictStatus("已中奖");
|
||||
}
|
||||
|
||||
// 更新预测记录
|
||||
|
||||
record.setPredictResult(result);
|
||||
record.setBonus(bonus);
|
||||
predictRecordMapper.updateById(record);
|
||||
|
||||
processedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return processedCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResponse<PredictRecord> queryPredictRecords(PredictRecordQueryRequest request) {
|
||||
// 构建查询条件
|
||||
QueryWrapper<PredictRecord> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
// 添加用户ID筛选条件
|
||||
if (request.getUserId() != null) {
|
||||
queryWrapper.eq("userId", request.getUserId());
|
||||
}
|
||||
|
||||
// 添加预测状态筛选条件
|
||||
if (StringUtils.hasText(request.getPredictStatus())) {
|
||||
queryWrapper.eq("predictStatus", request.getPredictStatus());
|
||||
}
|
||||
|
||||
// 按预测时间降序排序
|
||||
queryWrapper.orderByDesc("predictTime");
|
||||
|
||||
// 执行分页查询
|
||||
Page<PredictRecord> page = new Page<>(request.getCurrent(), request.getPageSize());
|
||||
Page<PredictRecord> resultPage = predictRecordMapper.selectPage(page, queryWrapper);
|
||||
|
||||
// 构建分页响应对象
|
||||
return PageResponse.of(
|
||||
resultPage.getRecords(),
|
||||
resultPage.getTotal(),
|
||||
(int) resultPage.getCurrent(),
|
||||
(int) resultPage.getSize()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalPredictCount() {
|
||||
// 获取全部预测记录数量
|
||||
return predictRecordMapper.selectCount(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算预测结果
|
||||
*/
|
||||
private String calculatePredictResult(PredictRecord record, LotteryDraws draws) {
|
||||
// 比较红球
|
||||
int redMatches = 0;
|
||||
Integer[] predictReds = {record.getRedBall1(), record.getRedBall2(), record.getRedBall3(),
|
||||
record.getRedBall4(), record.getRedBall5(), record.getRedBall6()};
|
||||
Integer[] drawReds = {draws.getRedBall1(), draws.getRedBall2(), draws.getRedBall3(),
|
||||
draws.getRedBall4(), draws.getRedBall5(), draws.getRedBall6()};
|
||||
|
||||
for (Integer predictRed : predictReds) {
|
||||
for (Integer drawRed : drawReds) {
|
||||
if (predictRed != null && predictRed.equals(drawRed)) {
|
||||
redMatches++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 比较蓝球
|
||||
boolean blueMatch = record.getBlueBall() != null &&
|
||||
record.getBlueBall().equals(draws.getBlueBall());
|
||||
|
||||
// 根据中奖规则判断奖项
|
||||
if (redMatches == 6 && blueMatch) {
|
||||
return "一等奖";
|
||||
} else if (redMatches == 6) {
|
||||
return "二等奖";
|
||||
} else if (redMatches == 5 && blueMatch) {
|
||||
return "三等奖";
|
||||
} else if ((redMatches == 5) || (redMatches == 4 && blueMatch)) {
|
||||
return "四等奖";
|
||||
} else if ((redMatches == 4) || (redMatches == 3 && blueMatch)) {
|
||||
return "五等奖";
|
||||
} else if (blueMatch && (redMatches == 0 || redMatches == 1 || redMatches == 2)) {
|
||||
return "六等奖";
|
||||
} else {
|
||||
return "未中奖";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算奖金
|
||||
*/
|
||||
private long calculateBonus(String result) {
|
||||
switch (result) {
|
||||
case "一等奖":
|
||||
return 5000000L; // 500万
|
||||
case "二等奖":
|
||||
return 1000000L; // 100万
|
||||
case "三等奖":
|
||||
return 3000L; // 3000元
|
||||
case "四等奖":
|
||||
return 200L; // 200元
|
||||
case "五等奖":
|
||||
return 10L; // 10元
|
||||
case "六等奖":
|
||||
return 5L; // 5元
|
||||
default:
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.History100;
|
||||
import com.xy.xyaicpzs.mapper.History100Mapper;
|
||||
import com.xy.xyaicpzs.service.History100Service;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【history_100(最近100期历史数据表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class History100ServiceImpl extends ServiceImpl<History100Mapper, History100>
|
||||
implements History100Service{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.HistoryAll;
|
||||
import com.xy.xyaicpzs.mapper.HistoryAllMapper;
|
||||
import com.xy.xyaicpzs.service.HistoryAllService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【history_all(历史数据表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class HistoryAllServiceImpl extends ServiceImpl<HistoryAllMapper, HistoryAll>
|
||||
implements HistoryAllService{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.HistoryTop100;
|
||||
import com.xy.xyaicpzs.mapper.HistoryTop100Mapper;
|
||||
import com.xy.xyaicpzs.service.HistoryTop100Service;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【history_top_100(100期数据排行表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class HistoryTop100ServiceImpl extends ServiceImpl<HistoryTop100Mapper, HistoryTop100>
|
||||
implements HistoryTop100Service{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.HistoryTop;
|
||||
import com.xy.xyaicpzs.mapper.HistoryTopMapper;
|
||||
import com.xy.xyaicpzs.service.HistoryTopService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【history_top(历史数据排行表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class HistoryTopServiceImpl extends ServiceImpl<HistoryTopMapper, HistoryTop>
|
||||
implements HistoryTopService{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.LotteryDraws;
|
||||
import com.xy.xyaicpzs.mapper.LotteryDrawsMapper;
|
||||
import com.xy.xyaicpzs.service.LotteryDrawsService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【lottery_draws(开奖结果表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class LotteryDrawsServiceImpl extends ServiceImpl<LotteryDrawsMapper, LotteryDraws>
|
||||
implements LotteryDrawsService{
|
||||
|
||||
@Override
|
||||
public List<LotteryDraws> getRecentDraws(Integer limit) {
|
||||
if (limit == null || limit <= 0) {
|
||||
limit = 15;
|
||||
}
|
||||
QueryWrapper<LotteryDraws> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.orderByDesc("drawId").last("LIMIT " + limit);
|
||||
return list(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LotteryDraws getByDrawId(Long drawId) {
|
||||
return getById(drawId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LotteryDraws> getByDateRange(Date startDate, Date endDate) {
|
||||
QueryWrapper<LotteryDraws> queryWrapper = new QueryWrapper<>();
|
||||
if (startDate != null) {
|
||||
queryWrapper.ge("drawDate", startDate);
|
||||
}
|
||||
if (endDate != null) {
|
||||
queryWrapper.le("drawDate", endDate);
|
||||
}
|
||||
queryWrapper.orderByDesc("drawId");
|
||||
return list(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LotteryDraws> queryDraws(Long drawId, Date startDate, Date endDate) {
|
||||
QueryWrapper<LotteryDraws> queryWrapper = new QueryWrapper<>();
|
||||
if (drawId != null) {
|
||||
queryWrapper.eq("drawId", drawId);
|
||||
}
|
||||
if (startDate != null) {
|
||||
queryWrapper.ge("drawDate", startDate);
|
||||
}
|
||||
if (endDate != null) {
|
||||
queryWrapper.le("drawDate", endDate);
|
||||
}
|
||||
queryWrapper.orderByDesc("drawId");
|
||||
return list(queryWrapper);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.OperationHistory;
|
||||
import com.xy.xyaicpzs.service.OperationHistoryService;
|
||||
import com.xy.xyaicpzs.mapper.OperationHistoryMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【operation_history(操作历史记录表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-19 14:51:51
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OperationHistoryServiceImpl extends ServiceImpl<OperationHistoryMapper, OperationHistory>
|
||||
implements OperationHistoryService{
|
||||
|
||||
@Override
|
||||
public void recordOperation(Long userId, String operationType, Integer operationModule,
|
||||
String operationResult, String resultMessage) {
|
||||
try {
|
||||
OperationHistory operationHistory = new OperationHistory();
|
||||
operationHistory.setUserId(userId);
|
||||
operationHistory.setOperationType(operationType);
|
||||
operationHistory.setOperationModule(operationModule);
|
||||
operationHistory.setOperationResult(operationResult);
|
||||
operationHistory.setResultMessage(resultMessage);
|
||||
operationHistory.setOperationTime(new Date());
|
||||
operationHistory.setUpdateTime(new Date());
|
||||
|
||||
save(operationHistory);
|
||||
|
||||
log.info("操作历史记录成功 - 用户ID: {}, 操作类型: {}, 操作模块: {}, 操作结果: {}",
|
||||
userId, operationType, operationModule, operationResult);
|
||||
} catch (Exception e) {
|
||||
log.error("记录操作历史失败 - 用户ID: {}, 操作类型: {}, 错误信息: {}",
|
||||
userId, operationType, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OperationHistory> getOperationHistoryByUserIdAndModule(Long userId, Integer operationModule) {
|
||||
QueryWrapper<OperationHistory> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("userId", userId)
|
||||
.eq("operationModule", operationModule)
|
||||
.orderByDesc("operationTime");
|
||||
return list(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OperationHistory> getOperationHistoryByModule(Integer operationModule) {
|
||||
QueryWrapper<OperationHistory> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("operationModule", operationModule)
|
||||
.orderByDesc("operationTime");
|
||||
return list(queryWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.PredictRecord;
|
||||
import com.xy.xyaicpzs.mapper.PredictRecordMapper;
|
||||
import com.xy.xyaicpzs.service.PredictRecordService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【predict_record(彩票开奖信息表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-16 13:17:53
|
||||
*/
|
||||
@Service
|
||||
public class PredictRecordServiceImpl extends ServiceImpl<PredictRecordMapper, PredictRecord>
|
||||
implements PredictRecordService{
|
||||
|
||||
@Override
|
||||
public PredictRecord createPredictRecord(Long userId, Long drawId, Date drawDate, List<Integer> redBalls, Integer blueBall) {
|
||||
PredictRecord predictRecord = new PredictRecord();
|
||||
predictRecord.setUserId(userId);
|
||||
predictRecord.setDrawId(drawId);
|
||||
predictRecord.setDrawDate(drawDate);
|
||||
predictRecord.setPredictTime(new Date());
|
||||
|
||||
// 设置红球号码
|
||||
if (redBalls != null && redBalls.size() >= 6) {
|
||||
predictRecord.setRedBall1(redBalls.get(0));
|
||||
predictRecord.setRedBall2(redBalls.get(1));
|
||||
predictRecord.setRedBall3(redBalls.get(2));
|
||||
predictRecord.setRedBall4(redBalls.get(3));
|
||||
predictRecord.setRedBall5(redBalls.get(4));
|
||||
predictRecord.setRedBall6(redBalls.get(5));
|
||||
}
|
||||
|
||||
// 设置蓝球号码
|
||||
if (blueBall != null) {
|
||||
predictRecord.setBlueBall(blueBall);
|
||||
}
|
||||
|
||||
// 默认状态为待开奖
|
||||
predictRecord.setPredictStatus("待开奖");
|
||||
|
||||
save(predictRecord);
|
||||
return predictRecord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PredictRecord> getPredictRecordsByUserId(Long userId) {
|
||||
QueryWrapper<PredictRecord> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("userId", userId).orderByDesc("predictTime");
|
||||
return list(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PredictRecord> getPredictRecordsByUserIdWithPaging(Long userId, Integer page, Integer size) {
|
||||
QueryWrapper<PredictRecord> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("userId", userId).orderByDesc("predictTime");
|
||||
|
||||
// 计算偏移量
|
||||
int offset = (page - 1) * size;
|
||||
queryWrapper.last("LIMIT " + offset + ", " + size);
|
||||
|
||||
return list(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getPredictRecordsCountByUserId(Long userId) {
|
||||
QueryWrapper<PredictRecord> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("userId", userId);
|
||||
return count(queryWrapper);
|
||||
}
|
||||
}
|
||||
153
src/main/java/com/xy/xyaicpzs/service/impl/SmsServiceImpl.java
Normal file
153
src/main/java/com/xy/xyaicpzs/service/impl/SmsServiceImpl.java
Normal file
@@ -0,0 +1,153 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.aliyun.dysmsapi20170525.Client;
|
||||
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
|
||||
import com.aliyun.tea.TeaException;
|
||||
import com.aliyun.teautil.models.RuntimeOptions;
|
||||
import com.xy.xyaicpzs.service.SmsService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 短信服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class SmsServiceImpl implements SmsService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SmsServiceImpl.class);
|
||||
|
||||
@Value("${aliyun.sms.sign-name:西安精彩数据服务社}")
|
||||
private String signName;
|
||||
|
||||
@Value("${aliyun.sms.template-code:SMS_489840017}")
|
||||
private String templateCode;
|
||||
|
||||
@Value("${aliyun.sms.access-key-id}")
|
||||
private String accessKeyId;
|
||||
|
||||
@Value("${aliyun.sms.access-key-secret}")
|
||||
private String accessKeySecret;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
// 短信验证码Redis前缀
|
||||
private static final String SMS_CODE_PREFIX = "sms:code:";
|
||||
// 短信发送次数Redis前缀
|
||||
private static final String SMS_COUNT_PREFIX = "sms:count:";
|
||||
// 短信验证码有效期(分钟)
|
||||
private static final long SMS_CODE_EXPIRE = 5;
|
||||
// 每天最大发送次数
|
||||
private static final int MAX_SMS_COUNT_PER_DAY = 10;
|
||||
|
||||
/**
|
||||
* 创建阿里云短信客户端
|
||||
*/
|
||||
private Client createSmsClient() throws Exception {
|
||||
// 从配置文件中获取阿里云AccessKey配置
|
||||
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
|
||||
config.accessKeyId = accessKeyId;
|
||||
config.accessKeySecret = accessKeySecret;
|
||||
config.endpoint = "dysmsapi.aliyuncs.com";
|
||||
return new Client(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成6位随机数字验证码
|
||||
*/
|
||||
private String generateVerificationCode() {
|
||||
Random random = new Random();
|
||||
StringBuilder code = new StringBuilder();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
code.append(random.nextInt(10));
|
||||
}
|
||||
return code.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当天结束时间的剩余秒数
|
||||
*/
|
||||
private long getSecondsUntilEndOfDay() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime endOfDay = now.toLocalDate().atTime(23, 59, 59);
|
||||
Duration duration = Duration.between(now, endOfDay);
|
||||
return duration.getSeconds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendVerificationCode(String phoneNumber) throws Exception {
|
||||
// 检查当天发送次数是否达到上限
|
||||
String countKey = SMS_COUNT_PREFIX + phoneNumber + ":" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
Integer count = (Integer) redisTemplate.opsForValue().get(countKey);
|
||||
|
||||
if (count != null && count >= MAX_SMS_COUNT_PER_DAY) {
|
||||
logger.warn("手机号{}今日短信发送次数已达上限: {}", phoneNumber, MAX_SMS_COUNT_PER_DAY);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 生成6位随机验证码
|
||||
String verificationCode = generateVerificationCode();
|
||||
|
||||
// 构建短信请求
|
||||
Client client = createSmsClient();
|
||||
SendSmsRequest sendSmsRequest = new SendSmsRequest()
|
||||
.setSignName(signName)
|
||||
.setTemplateCode(templateCode)
|
||||
.setPhoneNumbers(phoneNumber)
|
||||
.setTemplateParam("{\"code\":\"" + verificationCode + "\"}");
|
||||
|
||||
RuntimeOptions runtime = new RuntimeOptions();
|
||||
try {
|
||||
// 发送短信
|
||||
client.sendSmsWithOptions(sendSmsRequest, runtime);
|
||||
logger.info("短信验证码发送成功,手机号: {}", phoneNumber);
|
||||
|
||||
// 将验证码保存到Redis,设置过期时间
|
||||
String codeKey = SMS_CODE_PREFIX + phoneNumber;
|
||||
redisTemplate.opsForValue().set(codeKey, verificationCode, SMS_CODE_EXPIRE, TimeUnit.MINUTES);
|
||||
|
||||
// 增加当天发送次数,并设置过期时间为当天结束
|
||||
if (count == null) {
|
||||
count = 0;
|
||||
}
|
||||
redisTemplate.opsForValue().set(countKey, count + 1, getSecondsUntilEndOfDay(), TimeUnit.SECONDS);
|
||||
|
||||
return true;
|
||||
} catch (TeaException error) {
|
||||
logger.error("短信发送失败, 手机号: {}, 错误信息: {}, 诊断信息: {}",
|
||||
phoneNumber, error.getMessage(), error.getData().get("Recommend"));
|
||||
return false;
|
||||
} catch (Exception error) {
|
||||
logger.error("短信发送异常, 手机号: {}", phoneNumber, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyCode(String phoneNumber, String code) {
|
||||
if (phoneNumber == null || code == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String codeKey = SMS_CODE_PREFIX + phoneNumber;
|
||||
String savedCode = (String) redisTemplate.opsForValue().get(codeKey);
|
||||
|
||||
if (savedCode != null && savedCode.equals(code)) {
|
||||
// 验证成功后删除验证码
|
||||
redisTemplate.delete(codeKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.T11;
|
||||
import com.xy.xyaicpzs.mapper.T11Mapper;
|
||||
import com.xy.xyaicpzs.service.T11Service;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t11(蓝球组蓝球面系数表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class T11ServiceImpl extends ServiceImpl<T11Mapper, T11>
|
||||
implements T11Service{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.T3;
|
||||
import com.xy.xyaicpzs.mapper.T3Mapper;
|
||||
import com.xy.xyaicpzs.service.T3Service;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t3(红球线系数表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class T3ServiceImpl extends ServiceImpl<T3Mapper, T3>
|
||||
implements T3Service{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.T4;
|
||||
import com.xy.xyaicpzs.mapper.T4Mapper;
|
||||
import com.xy.xyaicpzs.service.T4Service;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t4(蓝球组红球线系数表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class T4ServiceImpl extends ServiceImpl<T4Mapper, T4>
|
||||
implements T4Service{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.T5;
|
||||
import com.xy.xyaicpzs.mapper.T5Mapper;
|
||||
import com.xy.xyaicpzs.service.T5Service;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t5(蓝球组蓝球线系数表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class T5ServiceImpl extends ServiceImpl<T5Mapper, T5>
|
||||
implements T5Service{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.T6;
|
||||
import com.xy.xyaicpzs.mapper.T6Mapper;
|
||||
import com.xy.xyaicpzs.service.T6Service;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t6(红球组蓝球线系数表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class T6ServiceImpl extends ServiceImpl<T6Mapper, T6>
|
||||
implements T6Service{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.T7;
|
||||
import com.xy.xyaicpzs.mapper.T7Mapper;
|
||||
import com.xy.xyaicpzs.service.T7Service;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t7(红球组红球面系数表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class T7ServiceImpl extends ServiceImpl<T7Mapper, T7>
|
||||
implements T7Service{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.T8;
|
||||
import com.xy.xyaicpzs.mapper.T8Mapper;
|
||||
import com.xy.xyaicpzs.service.T8Service;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【t8(红球组蓝球面系数表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
public class T8ServiceImpl extends ServiceImpl<T8Mapper, T8>
|
||||
implements T8Service{
|
||||
|
||||
}
|
||||
332
src/main/java/com/xy/xyaicpzs/service/impl/UserServiceImpl.java
Normal file
332
src/main/java/com/xy/xyaicpzs/service/impl/UserServiceImpl.java
Normal file
@@ -0,0 +1,332 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.common.ErrorCode;
|
||||
import com.xy.xyaicpzs.constant.UserConstant;
|
||||
import com.xy.xyaicpzs.domain.dto.user.UserPhoneLoginRequest;
|
||||
import com.xy.xyaicpzs.domain.dto.user.UserPhoneRegisterRequest;
|
||||
import com.xy.xyaicpzs.domain.entity.User;
|
||||
import com.xy.xyaicpzs.exception.BusinessException;
|
||||
import com.xy.xyaicpzs.mapper.UserMapper;
|
||||
import com.xy.xyaicpzs.service.SmsService;
|
||||
import com.xy.xyaicpzs.service.UserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.DigestUtils;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Date;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【user(用户表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-14 09:48:10
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
|
||||
implements UserService{
|
||||
|
||||
/**
|
||||
* 盐值,混淆密码
|
||||
*/
|
||||
private static final String SALT = "xy";
|
||||
|
||||
@Autowired
|
||||
private SmsService smsService;
|
||||
|
||||
@Override
|
||||
public long userRegister(String userAccount, String userName, String userPassword, String checkPassword) {
|
||||
// 1. 校验
|
||||
if (StringUtils.isAnyBlank(userAccount, userName, userPassword, checkPassword)) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
|
||||
}
|
||||
if (userAccount.length() < 4) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短");
|
||||
}
|
||||
if (userName.length() > 40) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名过长");
|
||||
}
|
||||
if (userPassword.length() < 8 || checkPassword.length() < 8) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
|
||||
}
|
||||
|
||||
// 密码和校验密码相同
|
||||
if (!userPassword.equals(checkPassword)) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
|
||||
}
|
||||
|
||||
synchronized (userAccount.intern()) {
|
||||
// 账户不能重复
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("userAccount", userAccount);
|
||||
long count = this.baseMapper.selectCount(queryWrapper);
|
||||
if (count > 0) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
|
||||
}
|
||||
|
||||
// 2. 加密
|
||||
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
|
||||
|
||||
// 3. 插入数据
|
||||
User user = new User();
|
||||
user.setUserAccount(userAccount);
|
||||
user.setUserName(userName);
|
||||
user.setUserPassword(encryptPassword);
|
||||
user.setCreateTime(new Date());
|
||||
user.setUpdateTime(new Date());
|
||||
// 设置为VIP用户,有效期10天
|
||||
user.setIsVip(1);
|
||||
Date vipExpireDate = new Date(System.currentTimeMillis() + 10L * 24 * 60 * 60 * 1000);
|
||||
user.setVipExpire(vipExpireDate);
|
||||
boolean saveResult = this.save(user);
|
||||
if (!saveResult) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败,数据库错误");
|
||||
}
|
||||
return user.getId();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User userLogin(String userAccount, String userPassword, HttpServletRequest request) {
|
||||
// 1. 校验
|
||||
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
|
||||
}
|
||||
if (userAccount.length() < 4) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号错误");
|
||||
}
|
||||
if (userPassword.length() < 8) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
|
||||
}
|
||||
|
||||
// 2. 加密
|
||||
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
|
||||
|
||||
// 查询用户是否存在
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("userAccount", userAccount);
|
||||
queryWrapper.eq("userPassword", encryptPassword);
|
||||
User user = this.baseMapper.selectOne(queryWrapper);
|
||||
|
||||
// 用户不存在
|
||||
if (user == null) {
|
||||
log.info("user login failed, userAccount cannot match userPassword");
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
|
||||
}
|
||||
|
||||
// 3. 记录用户的登录态
|
||||
request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, user);
|
||||
return getSafetyUser(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public User getLoginUser(HttpServletRequest request) {
|
||||
// 先判断是否已登录
|
||||
Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
|
||||
User currentUser = (User) userObj;
|
||||
if (currentUser == null || currentUser.getId() == null) {
|
||||
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
|
||||
}
|
||||
// 从数据库查询(追求性能的话可以注释,直接走缓存)
|
||||
long userId = currentUser.getId();
|
||||
currentUser = this.getById(userId);
|
||||
if (currentUser == null) {
|
||||
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
|
||||
}
|
||||
return currentUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注销
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
@Override
|
||||
public boolean userLogout(HttpServletRequest request) {
|
||||
if (request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE) == null) {
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
|
||||
}
|
||||
// 移除登录态
|
||||
request.getSession().removeAttribute(UserConstant.USER_LOGIN_STATE);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getSafetyUser(User originUser) {
|
||||
if (originUser == null) {
|
||||
return null;
|
||||
}
|
||||
User safetyUser = new User();
|
||||
safetyUser.setId(originUser.getId());
|
||||
safetyUser.setUserName(originUser.getUserName());
|
||||
safetyUser.setUserAccount(originUser.getUserAccount());
|
||||
safetyUser.setUserAvatar(originUser.getUserAvatar());
|
||||
safetyUser.setUserRole(originUser.getUserRole());
|
||||
safetyUser.setCreateTime(originUser.getCreateTime());
|
||||
safetyUser.setUpdateTime(originUser.getUpdateTime());
|
||||
return safetyUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdmin(HttpServletRequest request) {
|
||||
// 仅管理员可查询
|
||||
Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
|
||||
User user = (User) userObj;
|
||||
return isAdmin(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdmin(User user) {
|
||||
return user != null && (UserConstant.ADMIN_ROLE.equals(user.getUserRole())
|
||||
|| UserConstant.SUPER_ADMIN_ROLE.equals(user.getUserRole()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long userPhoneRegister(UserPhoneRegisterRequest userPhoneRegisterRequest) {
|
||||
if (userPhoneRegisterRequest == null) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
|
||||
}
|
||||
|
||||
String userAccount = userPhoneRegisterRequest.getUserAccount();
|
||||
String userPassword = userPhoneRegisterRequest.getUserPassword();
|
||||
String checkPassword = userPhoneRegisterRequest.getCheckPassword();
|
||||
String phone = userPhoneRegisterRequest.getPhone();
|
||||
String code = userPhoneRegisterRequest.getCode();
|
||||
String userName = userPhoneRegisterRequest.getUserName();
|
||||
|
||||
// 1. 校验
|
||||
if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword, phone, code)) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
|
||||
}
|
||||
// 用户名可以为空,如果为空则默认使用账号
|
||||
if (StringUtils.isBlank(userName)) {
|
||||
userName = userAccount;
|
||||
}
|
||||
if (userAccount.length() < 4) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短");
|
||||
}
|
||||
if (userPassword.length() < 8 || checkPassword.length() < 8) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
|
||||
}
|
||||
|
||||
// 密码和校验密码相同
|
||||
if (!userPassword.equals(checkPassword)) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
|
||||
}
|
||||
|
||||
// 验证手机号格式
|
||||
String phoneRegex = "^1[3-9]\\d{9}$";
|
||||
if (!phone.matches(phoneRegex)) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号格式错误");
|
||||
}
|
||||
|
||||
// 验证短信验证码
|
||||
boolean isCodeValid = smsService.verifyCode(phone, code);
|
||||
if (!isCodeValid) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误或已过期");
|
||||
}
|
||||
|
||||
synchronized (userAccount.intern()) {
|
||||
// 账户不能重复
|
||||
QueryWrapper<User> accountQueryWrapper = new QueryWrapper<>();
|
||||
accountQueryWrapper.eq("userAccount", userAccount);
|
||||
long accountCount = this.baseMapper.selectCount(accountQueryWrapper);
|
||||
if (accountCount > 0) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号已存在");
|
||||
}
|
||||
|
||||
// 手机号不能重复
|
||||
QueryWrapper<User> phoneQueryWrapper = new QueryWrapper<>();
|
||||
phoneQueryWrapper.eq("phone", phone);
|
||||
long phoneCount = this.baseMapper.selectCount(phoneQueryWrapper);
|
||||
if (phoneCount > 0) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号已注册");
|
||||
}
|
||||
|
||||
// 2. 加密
|
||||
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
|
||||
|
||||
// 3. 插入数据
|
||||
User user = new User();
|
||||
user.setUserAccount(userAccount);
|
||||
user.setUserPassword(encryptPassword);
|
||||
user.setPhone(phone);
|
||||
// 设置用户名
|
||||
user.setUserName(userName);
|
||||
user.setCreateTime(new Date());
|
||||
user.setUpdateTime(new Date());
|
||||
// 设置为VIP用户,有效期10天
|
||||
user.setIsVip(0);
|
||||
Date vipExpireDate = new Date(System.currentTimeMillis() + 10L * 24 * 60 * 60 * 1000);
|
||||
user.setVipExpire(vipExpireDate);
|
||||
|
||||
boolean saveResult = this.save(user);
|
||||
if (!saveResult) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败,数据库错误");
|
||||
}
|
||||
return user.getId();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User userPhoneLogin(UserPhoneLoginRequest userPhoneLoginRequest, HttpServletRequest request) {
|
||||
if (userPhoneLoginRequest == null) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
|
||||
}
|
||||
|
||||
String phone = userPhoneLoginRequest.getPhone();
|
||||
String code = userPhoneLoginRequest.getCode();
|
||||
|
||||
// 1. 校验参数
|
||||
if (StringUtils.isAnyBlank(phone, code)) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
|
||||
}
|
||||
|
||||
// 验证手机号格式
|
||||
String phoneRegex = "^1[3-9]\\d{9}$";
|
||||
if (!phone.matches(phoneRegex)) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号格式错误");
|
||||
}
|
||||
|
||||
// 验证短信验证码
|
||||
boolean isCodeValid = smsService.verifyCode(phone, code);
|
||||
if (!isCodeValid) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误或已过期");
|
||||
}
|
||||
|
||||
// 查询用户是否存在
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("phone", phone);
|
||||
User user = this.baseMapper.selectOne(queryWrapper);
|
||||
|
||||
// 用户不存在
|
||||
if (user == null) {
|
||||
log.info("user login failed, phone number not registered");
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号未注册");
|
||||
}
|
||||
|
||||
// 3. 记录用户的登录态
|
||||
request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, user);
|
||||
return getSafetyUser(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encryptPassword(String password) {
|
||||
if (StringUtils.isBlank(password)) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码不能为空");
|
||||
}
|
||||
return DigestUtils.md5DigestAsHex((SALT + password).getBytes());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.User;
|
||||
import com.xy.xyaicpzs.domain.entity.VipCode;
|
||||
import com.xy.xyaicpzs.domain.entity.VipExchangeRecord;
|
||||
import com.xy.xyaicpzs.mapper.UserMapper;
|
||||
import com.xy.xyaicpzs.mapper.VipCodeMapper;
|
||||
import com.xy.xyaicpzs.service.VipCodeService;
|
||||
import com.xy.xyaicpzs.service.VipExchangeRecordService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【vip_code(会员码表)】的数据库操作Service实现
|
||||
* @createDate 2025-01-15 16:41:29
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class VipCodeServiceImpl extends ServiceImpl<VipCodeMapper, VipCode> implements VipCodeService {
|
||||
|
||||
@Autowired
|
||||
private VipCodeMapper vipCodeMapper;
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Autowired
|
||||
private VipExchangeRecordService vipExchangeRecordService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean activateVipCode(Long userId, String code) {
|
||||
log.info("开始激活会员码,用户ID:{},会员码:{}", userId, code);
|
||||
|
||||
// 1. 校验参数
|
||||
if (userId == null || code == null || code.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("用户ID和会员码不能为空");
|
||||
}
|
||||
|
||||
// 2. 查询会员码信息
|
||||
QueryWrapper<VipCode> vipCodeQuery = new QueryWrapper<>();
|
||||
vipCodeQuery.eq("code", code);
|
||||
VipCode vipCode = vipCodeMapper.selectOne(vipCodeQuery);
|
||||
|
||||
if (vipCode == null) {
|
||||
throw new IllegalArgumentException("会员码不存在");
|
||||
}
|
||||
|
||||
if (vipCode.getIsUse() == 1) {
|
||||
throw new IllegalArgumentException("会员码已被使用");
|
||||
}
|
||||
|
||||
// 3. 查询用户信息
|
||||
User user = userMapper.selectById(userId);
|
||||
if (user == null) {
|
||||
throw new IllegalArgumentException("用户不存在");
|
||||
}
|
||||
|
||||
// 4. 根据会员码的vipExpireTime判断会员类型
|
||||
String memberType;
|
||||
int orderAmount;
|
||||
if (vipCode.getVipExpireTime() == 1) {
|
||||
memberType = "月度会员";
|
||||
orderAmount = 10;
|
||||
} else if (vipCode.getVipExpireTime() == 12) {
|
||||
memberType = "年度会员";
|
||||
orderAmount = 100;
|
||||
} else {
|
||||
throw new IllegalArgumentException("无效的会员有效期:" + vipCode.getVipExpireTime());
|
||||
}
|
||||
|
||||
// 5. 先在vip_exchange_record表插入兑换记录
|
||||
VipExchangeRecord exchangeRecord = new VipExchangeRecord();
|
||||
exchangeRecord.setUserId(userId);
|
||||
exchangeRecord.setType(memberType);
|
||||
exchangeRecord.setExchangeMode(1); // 1表示会员码兑换
|
||||
//生成随机16位订单号
|
||||
exchangeRecord.setOrderNo(generateOrderNumber());
|
||||
exchangeRecord.setOrderAmount(orderAmount);
|
||||
exchangeRecord.setIsUse(1); // 1表示已兑换
|
||||
exchangeRecord.setExchangeTime(new Date());
|
||||
exchangeRecord.setUpdateTime(new Date());
|
||||
|
||||
boolean recordSaved = vipExchangeRecordService.save(exchangeRecord);
|
||||
if (!recordSaved) {
|
||||
log.error("保存兑换记录失败,用户ID:{},会员码:{}", userId, code);
|
||||
throw new RuntimeException("保存兑换记录失败");
|
||||
}
|
||||
|
||||
log.info("成功插入兑换记录,用户ID:{},会员类型:{}", userId, memberType);
|
||||
|
||||
// 6. 计算新的会员到期时间
|
||||
Date newVipExpire = calculateNewVipExpire(user.getVipExpire(), vipCode.getVipExpireTime());
|
||||
|
||||
// 7. 更新用户会员状态
|
||||
User updateUser = new User();
|
||||
updateUser.setId(userId);
|
||||
updateUser.setIsVip(1); // 设置为会员
|
||||
updateUser.setVipExpire(newVipExpire);
|
||||
updateUser.setUpdateTime(new Date());
|
||||
|
||||
int userUpdateResult = userMapper.updateById(updateUser);
|
||||
if (userUpdateResult == 0) {
|
||||
log.error("更新用户会员状态失败,用户ID:{}", userId);
|
||||
throw new RuntimeException("更新用户会员状态失败");
|
||||
}
|
||||
|
||||
// 8. 标记会员码为已使用,并记录使用人信息和使用时间
|
||||
Date now = new Date();
|
||||
VipCode updateVipCode = new VipCode();
|
||||
updateVipCode.setId(vipCode.getId());
|
||||
updateVipCode.setIsUse(1);
|
||||
updateVipCode.setUpdateTime(now);
|
||||
// 添加使用人ID、使用人名称
|
||||
updateVipCode.setUsedUserId(userId);
|
||||
updateVipCode.setUsedUserName(user.getUserName());
|
||||
|
||||
int vipCodeUpdateResult = vipCodeMapper.updateById(updateVipCode);
|
||||
if (vipCodeUpdateResult == 0) {
|
||||
log.error("更新会员码状态失败,会员码:{}", code);
|
||||
throw new RuntimeException("更新会员码状态失败");
|
||||
}
|
||||
|
||||
log.info("会员码激活成功,用户ID:{},会员类型:{},新的到期时间:{}", userId, memberType, newVipExpire);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算新的会员到期时间
|
||||
* @param currentVipExpire 当前会员到期时间
|
||||
* @param vipExpireTime 会员有效月数
|
||||
* @return 新的会员到期时间
|
||||
*/
|
||||
private Date calculateNewVipExpire(Date currentVipExpire, int vipExpireTime) {
|
||||
LocalDate baseDate;
|
||||
|
||||
// 如果当前已经是会员且未过期,则从当前到期时间开始延长
|
||||
if (currentVipExpire != null) {
|
||||
LocalDate currentExpireDate = currentVipExpire.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
if (currentExpireDate.isAfter(today)) {
|
||||
// 当前会员未过期,从到期时间开始延长
|
||||
baseDate = currentExpireDate;
|
||||
} else {
|
||||
// 当前会员已过期,从今天开始计算
|
||||
baseDate = today;
|
||||
}
|
||||
} else {
|
||||
// 从未开通过会员,从今天开始计算
|
||||
baseDate = LocalDate.now();
|
||||
}
|
||||
|
||||
// 增加对应的月数
|
||||
LocalDate newExpireDate = baseDate.plusMonths(vipExpireTime);
|
||||
|
||||
return Date.from(newExpireDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int generateVipCodes(int numCodes, int vipExpireTime, Long createdUserId, String createdUserName) {
|
||||
log.info("开始生成会员码,数量:{},有效月数:{},创建人ID:{}", numCodes, vipExpireTime, createdUserId);
|
||||
|
||||
if (numCodes <= 0) {
|
||||
throw new IllegalArgumentException("生成数量必须大于0");
|
||||
}
|
||||
|
||||
if (vipExpireTime != 1 && vipExpireTime != 12) {
|
||||
throw new IllegalArgumentException("会员有效月数只能是1或12");
|
||||
}
|
||||
|
||||
if (numCodes > 1000) {
|
||||
throw new IllegalArgumentException("单次生成数量不能超过1000");
|
||||
}
|
||||
|
||||
// 获取当前最大的vipNumber
|
||||
Integer maxVipNumber = 100000; // 默认从100001开始(6位数)
|
||||
QueryWrapper<VipCode> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.orderByDesc("vipNumber").last("LIMIT 1");
|
||||
VipCode maxVipCode = vipCodeMapper.selectOne(queryWrapper);
|
||||
if (maxVipCode != null && maxVipCode.getVipNumber() != null) {
|
||||
maxVipNumber = Math.max(maxVipNumber, maxVipCode.getVipNumber());
|
||||
}
|
||||
|
||||
int successCount = 0;
|
||||
Date now = new Date();
|
||||
|
||||
for (int i = 0; i < numCodes; i++) {
|
||||
try {
|
||||
VipCode vipCode = new VipCode();
|
||||
vipCode.setCode(generateUniqueCode());
|
||||
vipCode.setVipExpireTime(vipExpireTime);
|
||||
|
||||
// 设置会员编号,从最大值+1开始,确保是6位数
|
||||
int nextVipNumber = maxVipNumber + i + 1;
|
||||
vipCode.setVipNumber(nextVipNumber);
|
||||
|
||||
vipCode.setIsUse(0); // 0表示未使用
|
||||
vipCode.setCreatedUserId(createdUserId); // 设置创建人ID
|
||||
vipCode.setCreatedUserName(createdUserName); // 设置创建人名称
|
||||
vipCode.setCreateTime(now);
|
||||
/* vipCode.setUpdateTime(now);*/
|
||||
|
||||
int result = vipCodeMapper.insert(vipCode);
|
||||
if (result > 0) {
|
||||
successCount++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("生成第{}个会员码失败:{}", i + 1, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
log.info("会员码生成完成,请求生成:{}个,实际成功:{}个", numCodes, successCount);
|
||||
return successCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAvailableVipCode(int vipExpireTime, Long createdUserId, String createdUserName) {
|
||||
log.info("查找可用会员码,有效月数:{},创建人ID:{}", vipExpireTime, createdUserId);
|
||||
|
||||
if (vipExpireTime != 1 && vipExpireTime != 12) {
|
||||
throw new IllegalArgumentException("会员有效月数只能是1或12");
|
||||
}
|
||||
|
||||
QueryWrapper<VipCode> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("vipExpireTime", vipExpireTime)
|
||||
.eq("isUse", 0)
|
||||
.orderByAsc("createTime")
|
||||
.last("LIMIT 1");
|
||||
|
||||
VipCode vipCode = vipCodeMapper.selectOne(queryWrapper);
|
||||
|
||||
if (vipCode != null) {
|
||||
log.info("找到可用会员码:{}", vipCode.getCode());
|
||||
return vipCode.getCode();
|
||||
} else {
|
||||
log.warn("没有找到有效月数为{}的可用会员码", vipExpireTime);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一的会员码
|
||||
* @return 会员码字符串
|
||||
*/
|
||||
private String generateUniqueCode() {
|
||||
// 生成16位的随机字符串作为会员码
|
||||
String uuid = UUID.randomUUID().toString().replace("-", "");
|
||||
return uuid.substring(0, 16).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成16位随机订单号
|
||||
* @return 16位订单号
|
||||
*/
|
||||
private Long generateOrderNumber() {
|
||||
// 生成16位随机数字订单号
|
||||
// 方法1: 时间戳 + 随机数组合(推荐,避免重复)
|
||||
long timestamp = System.currentTimeMillis() % 1000000L; // 取6位时间戳
|
||||
long random = (long) (Math.random() * 9000000000L) + 1000000000L; // 10位随机数
|
||||
return Long.parseLong(String.valueOf(timestamp) + String.valueOf(random));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.xy.xyaicpzs.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xy.xyaicpzs.domain.entity.VipExchangeRecord;
|
||||
import com.xy.xyaicpzs.service.VipExchangeRecordService;
|
||||
import com.xy.xyaicpzs.mapper.VipExchangeRecordMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【vip_exchange_record(会员兑换表)】的数据库操作Service实现
|
||||
* @createDate 2025-06-19 11:27:10
|
||||
*/
|
||||
@Service
|
||||
public class VipExchangeRecordServiceImpl extends ServiceImpl<VipExchangeRecordMapper, VipExchangeRecord>
|
||||
implements VipExchangeRecordService{
|
||||
|
||||
@Override
|
||||
public List<VipExchangeRecord> getExchangeRecordsByUserId(Long userId) {
|
||||
QueryWrapper<VipExchangeRecord> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("userId", userId)
|
||||
.orderByDesc("exchangeTime");
|
||||
return list(queryWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user