彩票助手后端1.0

This commit is contained in:
lihanqi
2025-08-01 19:09:57 +08:00
commit 93c8ace8e7
204 changed files with 18805 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
package com.xy.xyaicpzs.service;
import com.xy.xyaicpzs.domain.entity.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> {
}

View File

@@ -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> {
}

View File

@@ -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> {
}

View File

@@ -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> {
}

View File

@@ -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> {
}

View 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());
}
}
}

View File

@@ -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();
}

View 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个表t333×33=1089条记录\n");
info.append("4. T4工作表 - 蓝球组红球线系数数据1个表t416×33=528条记录\n");
info.append("5. T5工作表 - 蓝球组蓝球线系数数据1个表t516×16=256条记录\n");
info.append("6. T6工作表 - 红球组蓝球线系数数据1个表t633×16=528条记录\n");
info.append("7. T7工作表 - 红球组红球面系数数据1个表t733×33=1089条记录\n");
info.append("8. T8工作表 - 红球组蓝球面系数数据1个表t833×16=528条记录\n");
info.append("9. T10工作表 - 彩票开奖信息数据1个表lottery_draws历史开奖记录\n");
info.append("10. T11工作表 - 蓝球组红球面系数数据1个表t1116×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-6I列蓝球\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();
}
}
}

View 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> {
}

View 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> {
}

View File

@@ -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> {
}

View 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> {
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View 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);
}

View 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> {
}

View 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> {
}

View 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> {
}

View 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> {
}

View 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> {
}

View 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> {
}

View 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> {
}

View 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);
}

View 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);
}

View File

@@ -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);
}

View File

@@ -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{
}

View File

@@ -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{
}

View File

@@ -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{
}

View File

@@ -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{
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.domain.entity.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{
}

View File

@@ -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;
}
}
}

View File

@@ -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{
}

View File

@@ -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{
}

View File

@@ -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{
}

View File

@@ -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{
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View 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;
}
}

View File

@@ -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{
}

View File

@@ -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{
}

View File

@@ -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{
}

View File

@@ -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{
}

View File

@@ -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{
}

View File

@@ -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{
}

View File

@@ -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{
}

View 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());
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}