彩票助手后端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

View File

@@ -0,0 +1,42 @@
package com.xy.xyaicpzs;
import com.alibaba.dashscope.app.*;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import io.reactivex.Flowable;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void streamCall() throws NoApiKeyException, InputRequiredException {
ApplicationParam param = ApplicationParam.builder()
// 若没有配置环境变量可用百炼API Key将下行替换为.apiKey("sk-xxx")。但不建议在生产环境中直接将API Key硬编码到代码中以减少API Key泄露风险。
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
// 替换为实际的应用 ID
.appId("ec08d5b81ca248e8834228c1133e2c78")
.prompt("你是谁")
// 增量输出
.incrementalOutput(true)
.build();
Application application = new Application();
// .streamCall流式输出内容
Flowable<ApplicationResult> result = application.streamCall(param);
result.blockingForEach(data -> {
System.out.printf("%s\n",
data.getOutput().getText());
// System.out.println("session_id: " + data.getOutput().getSessionId());
});
}
public static void main(String[] args) {
try {
streamCall();
} catch (ApiException | NoApiKeyException | InputRequiredException e) {
System.out.printf("Exception: %s", e.getMessage());
System.out.println("请参考文档https://help.aliyun.com/zh/model-studio/developer-reference/error-code");
}
System.exit(0);
}
}

View File

@@ -0,0 +1,54 @@
// This file is auto-generated, don't edit it. Thanks.
package com.xy.xyaicpzs;
import com.aliyun.tea.*;
public class Sample {
/**
* <b>description</b> :
* <p>使用凭据初始化账号Client</p>
* @return Client
*
* @throws Exception
*/
public static com.aliyun.dysmsapi20170525.Client createClient() throws Exception {
// 工程代码建议使用更安全的无AK方式凭据配置方式请参见https://help.aliyun.com/document_detail/378657.html。
com.aliyun.credentials.Client credential = new com.aliyun.credentials.Client();
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
.setCredential(credential);
// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
config.endpoint = "dysmsapi.aliyuncs.com";
return new com.aliyun.dysmsapi20170525.Client(config);
}
public static void main(String[] args_) throws Exception {
com.aliyun.dysmsapi20170525.Client client = Sample.createClient();
com.aliyun.dysmsapi20170525.models.SendSmsRequest sendSmsRequest = new com.aliyun.dysmsapi20170525.models.SendSmsRequest()
.setSignName("西安精彩数据服务社")
.setTemplateCode("SMS_489840017")
.setPhoneNumbers("13868246742")
.setTemplateParam("{\"code\":\"1234\"}");
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
try {
// 复制代码运行请自行打印 API 的返回值
client.sendSmsWithOptions(sendSmsRequest, runtime);
} catch (TeaException error) {
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
System.out.println(error.getMessage());
// 诊断地址
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
} catch (Exception _error) {
TeaException error = new TeaException(_error.getMessage(), _error);
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
System.out.println(error.getMessage());
// 诊断地址
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
}
}
}

View File

@@ -0,0 +1,17 @@
package com.xy.xyaicpzs;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.xy.xyaicpzs.mapper")
public class XyAiCpzsApplication {
public static void main(String[] args) {
SpringApplication.run(XyAiCpzsApplication.class, args);
}
}

View File

@@ -0,0 +1,19 @@
package com.xy.xyaicpzs.common;
import lombok.Data;
import java.io.Serializable;
/**
* 删除请求
*/
@Data
public class DeleteRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
}

View File

@@ -0,0 +1,39 @@
package com.xy.xyaicpzs.common;
/**
* 自定义错误码
*/
public enum ErrorCode {
SUCCESS(0, "ok"),
PARAMS_ERROR(40000, "请求参数错误"),
NOT_LOGIN_ERROR(40100, "您还未登录,请登录后操作"),
NO_AUTH_ERROR(40101, "无权限"),
NOT_FOUND_ERROR(40400, "请求数据不存在"),
FORBIDDEN_ERROR(40300, "禁止访问"),
SYSTEM_ERROR(50000, "系统内部异常"),
OPERATION_ERROR(50001, "操作失败");
/**
* 状态码
*/
private final int code;
/**
* 信息
*/
private final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}

View File

@@ -0,0 +1,20 @@
package com.xy.xyaicpzs.common;
import lombok.Data;
/**
* 分页请求
*/
@Data
public class PageRequest {
/**
* 当前页号
*/
private long current = 1;
/**
* 页面大小
*/
private long pageSize = 10;
}

View File

@@ -0,0 +1,51 @@
package com.xy.xyaicpzs.common;
import com.xy.xyaicpzs.common.response.ApiResponse;
/**
* 返回工具类
*/
public class ResultUtils {
/**
* 成功
*
* @param data
* @param <T>
* @return
*/
public static <T> ApiResponse<T> success(T data) {
return ApiResponse.success(data);
}
/**
* 失败
*
* @param errorCode
* @return
*/
public static <T> ApiResponse<T> error(ErrorCode errorCode) {
return ApiResponse.error(errorCode.getMessage());
}
/**
* 失败
*
* @param code
* @param message
* @return
*/
public static <T> ApiResponse<T> error(int code, String message) {
return ApiResponse.error(message);
}
/**
* 失败
*
* @param errorCode
* @return
*/
public static <T> ApiResponse<T> error(ErrorCode errorCode, String message) {
return ApiResponse.error(message);
}
}

View File

@@ -0,0 +1,24 @@
package com.xy.xyaicpzs.common.requset;
import lombok.Data;
import java.util.List;
/**
* 球号分析请求对象
*/
@Data
public class BallAnalysisRequest {
private String level;
private List<Integer> redBalls;
private Integer blueBall;
@Override
public String toString() {
return "BallAnalysisRequest{" +
"level='" + level + '\'' +
", redBalls=" + redBalls +
", blueBall=" + blueBall +
'}';
}
}

View File

@@ -0,0 +1,29 @@
package com.xy.xyaicpzs.common.requset;
import lombok.Data;
import java.util.List;
/**
* 蓝球分析请求对象
*/
@Data
public class BlueBallAnalysisRequest {
private String level;
private List<Integer> predictedRedBalls;
private List<Integer> predictedBlueBalls;
private List<Integer> lastRedBalls;
private Integer lastBlueBall;
@Override
public String toString() {
return "BlueBallAnalysisRequest{" +
"level='" + level + '\'' +
", predictedRedBalls=" + predictedRedBalls +
", predictedBlueBalls=" + predictedBlueBalls +
", lastRedBalls=" + lastRedBalls +
", lastBlueBall=" + lastBlueBall +
'}';
}
}

View File

@@ -0,0 +1,26 @@
package com.xy.xyaicpzs.common.requset;
import lombok.Data;
import java.util.List;
/**
* 跟随球号分析请求对象
*/
@Data
public class FallowBallAnalysisRequest {
private String level;
private List<Integer> firstThreeRedBalls;
private List<Integer> lastSixRedBalls;
private Integer blueBall;
@Override
public String toString() {
return "FallowBallAnalysisRequest{" +
"level='" + level + '\'' +
", firstThreeRedBalls=" + firstThreeRedBalls +
", lastSixRedBalls=" + lastSixRedBalls +
", blueBall=" + blueBall +
'}';
}
}

View File

@@ -0,0 +1,20 @@
package com.xy.xyaicpzs.common.requset;
import lombok.Data;
/**
* 生成会员码请求
*/
@Data
public class GenerateVipCodesRequest {
/**
* 生成数量
*/
private Integer numCodes;
/**
* 会员有效月数
*/
private Integer vipExpireTime;
}

View File

@@ -0,0 +1,23 @@
package com.xy.xyaicpzs.common.requset;
import com.xy.xyaicpzs.common.PageRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 预测记录查询请求
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class PredictRecordQueryRequest extends PageRequest {
/**
* 用户ID
*/
private Long userId;
/**
* 预测状态(待开奖/未中奖/已中奖)
*/
private String predictStatus;
}

View File

@@ -0,0 +1,20 @@
package com.xy.xyaicpzs.common.requset;
import lombok.Data;
/**
* 会员码激活请求
*/
@Data
public class VipCodeActivateRequest {
/**
* 用户ID
*/
private Long userId;
/**
* 会员码
*/
private String code;
}

View File

@@ -0,0 +1,74 @@
package com.xy.xyaicpzs.common.requset;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
/**
* 会员码查询请求
*/
@Data
@Schema(description = "会员码查询请求")
public class VipCodeQueryRequest {
/**
* 当前页码
*/
@Schema(description = "当前页码")
private long current = 1;
/**
* 页面大小
*/
@Schema(description = "页面大小")
private long pageSize = 10;
/**
* 会员码
*/
@Schema(description = "会员码")
private String code;
/**
* 会员有效月数(1/12)
*/
@Schema(description = "会员有效月数")
private Integer vipExpireTime;
/**
* 是否使用0-未使用1-已使用
*/
@Schema(description = "是否使用0-未使用1-已使用")
private Integer isUse;
/**
* 创建人ID
*/
@Schema(description = "创建人ID")
private Long createdUserId;
/**
* 创建人名称
*/
@Schema(description = "创建人名称")
private String createdUserName;
/**
* 使用人ID
*/
@Schema(description = "使用人ID")
private Long usedUserId;
/**
* 开始时间
*/
@Schema(description = "开始时间")
private Date startTime;
/**
* 结束时间
*/
@Schema(description = "结束时间")
private Date endTime;
}

View File

@@ -0,0 +1,29 @@
package com.xy.xyaicpzs.common.response;
import lombok.Data;
/**
* API响应对象
*/
@Data
public class ApiResponse<T> {
private boolean success;
private String message;
private T data;
public static <T> ApiResponse<T> success(T data) {
ApiResponse<T> response = new ApiResponse<>();
response.success = true;
response.message = "操作成功";
response.data = data;
return response;
}
public static <T> ApiResponse<T> error(String message) {
ApiResponse<T> response = new ApiResponse<>();
response.success = false;
response.message = message;
return response;
}
}

View File

@@ -0,0 +1,67 @@
package com.xy.xyaicpzs.common.response;
import lombok.Data;
import java.util.List;
/**
* 分页响应对象
* @param <T> 数据类型
*/
@Data
public class PageResponse<T> {
/**
* 数据列表
*/
private List<T> records;
/**
* 总记录数
*/
private Long total;
/**
* 当前页码
*/
private Integer page;
/**
* 每页大小
*/
private Integer size;
/**
* 总页数
*/
private Integer totalPages;
/**
* 是否有下一页
*/
private Boolean hasNext;
/**
* 是否有上一页
*/
private Boolean hasPrevious;
public PageResponse() {}
public PageResponse(List<T> records, Long total, Integer page, Integer size) {
this.records = records;
this.total = total;
this.page = page;
this.size = size;
this.totalPages = (int) Math.ceil((double) total / size);
this.hasNext = page < totalPages;
this.hasPrevious = page > 1;
}
/**
* 创建分页响应对象
*/
public static <T> PageResponse<T> of(List<T> records, Long total, Integer page, Integer size) {
return new PageResponse<>(records, total, page, size);
}
}

View File

@@ -0,0 +1,27 @@
package com.xy.xyaicpzs.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
// * 全局跨域配置
*/
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 覆盖所有请求
registry.addMapping("/**")
// 允许发送 Cookie
.allowCredentials(true)
// 放行哪些域名(必须用 patterns否则 * 会和 allowCredentials 冲突)
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
// 确保SSE相关头信息能被客户端访问
.exposedHeaders("*", HttpHeaders.CACHE_CONTROL, HttpHeaders.CONTENT_TYPE);
}
}

View File

@@ -0,0 +1,31 @@
package com.xy.xyaicpzs.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MyBatis Plus 配置
*
* @author lihanqi
*/
@Configuration
@MapperScan("com.xy.xyaicpzs.mapper")
public class MyBatisPlusConfig {
/**
* 拦截器配置
*
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* ObjectMapper配置类
*/
@Configuration
public class ObjectMapperConfig {
/**
* 配置ObjectMapper Bean
*
* @return ObjectMapper实例
*/
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
}

View File

@@ -0,0 +1,38 @@
package com.xy.xyaicpzs.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis配置类
*/
@Configuration
public class RedisConfig {
/**
* 自定义RedisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 设置连接工厂
redisTemplate.setConnectionFactory(connectionFactory);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// 使用GenericJackson2JsonRedisSerializer来序列化和反序列化redis的value值
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
redisTemplate.setValueSerializer(jsonSerializer);
redisTemplate.setHashValueSerializer(jsonSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* RestTemplate配置类
*/
@Configuration
public class RestTemplateConfig {
/**
* 配置RestTemplate Bean
*
* @return RestTemplate实例
*/
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

View File

@@ -0,0 +1,12 @@
package com.xy.xyaicpzs.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 定时任务配置类
*/
@Configuration
@EnableScheduling
public class ScheduleConfig {
}

View File

@@ -0,0 +1,36 @@
package com.xy.xyaicpzs.constant;
/**
* 用户常量
*/
public interface UserConstant {
/**
* 用户登录态键
*/
String USER_LOGIN_STATE = "userLoginState";
/**
* 系统用户 id虚拟用户
*/
long SYSTEM_USER_ID = 0;
// region 权限
/**
* 默认权限
*/
String DEFAULT_ROLE = "user";
/**
* 管理员权限
*/
String ADMIN_ROLE = "admin";
/**
* 超级管理员权限
*/
String SUPER_ADMIN_ROLE = "superAdmin";
// endregion
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,126 @@
package com.xy.xyaicpzs.controller;
import com.alibaba.dashscope.app.Application;
import com.alibaba.dashscope.app.ApplicationParam;
import com.alibaba.dashscope.app.ApplicationResult;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.xy.xyaicpzs.domain.entity.ChatMessage;
import com.xy.xyaicpzs.service.ChatMessageService;
import io.reactivex.Flowable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.atomic.AtomicReference;
@RestController
public class ChatController {
@Autowired
private ChatMessageService chatMessageService;
@Value("${dashscope.api-key}")
private String dashscopeApiKey;
/**
* SSE流式聊天接口
* @param message 用户消息
* @param conversationId 会话ID
* @param userId 用户ID
* @return SseEmitter
*/
@GetMapping("/chat/sse")
public SseEmitter chatSseEmitter(
@RequestParam String message,
@RequestParam(required = false) String conversationId,
@RequestParam(required = false) String userId) {
// 保存用户消息到数据库
saveMessage(conversationId, userId, "USER", message);
// 创建一个超时时间较长的 SseEmitter
SseEmitter emitter = new SseEmitter(180000L); // 3分钟超时
// 用于收集完整的AI回复
AtomicReference<StringBuilder> fullResponseRef = new AtomicReference<>(new StringBuilder());
try {
// 设置AI参数
ApplicationParam param = ApplicationParam.builder()
.apiKey(dashscopeApiKey) // 使用配置文件中的API密钥
.appId("ec08d5b81ca248e8834228c1133e2c78")
.prompt(message)
.incrementalOutput(true)
.build();
Application application = new Application();
// 流式调用
Flowable<ApplicationResult> result = application.streamCall(param);
result.subscribe(
// 处理每条消息
data -> {
try {
String text = data.getOutput().getText();
// 发送数据到客户端
emitter.send(text);
// 收集完整响应
fullResponseRef.get().append(text);
} catch (IOException e) {
emitter.completeWithError(e);
}
},
// 处理错误
error -> {
emitter.completeWithError(error);
System.out.println("错误: " + error.getMessage());
},
// 处理完成
() -> {
// 保存AI回复到数据库
String fullResponse = fullResponseRef.get().toString();
saveMessage(conversationId, userId, "AI", fullResponse);
emitter.complete();
}
);
} catch (ApiException | NoApiKeyException | InputRequiredException e) {
try {
emitter.send("错误: " + e.getMessage());
emitter.complete();
} catch (IOException ex) {
emitter.completeWithError(ex);
}
System.out.println("异常: " + e.getMessage());
}
return emitter;
}
/**
* 保存消息到数据库
* @param conversationId 会话ID
* @param userId 用户ID
* @param messageType 消息类型(USER/AI)
* @param content 消息内容
*/
private void saveMessage(String conversationId, String userId, String messageType, String content) {
ChatMessage chatMessage = new ChatMessage();
chatMessage.setConversationId(conversationId);
chatMessage.setStudentId(userId);
chatMessage.setMessageType(messageType);
chatMessage.setContent(content);
chatMessage.setCreateTime(new Date());
chatMessage.setUpdateTime(new Date());
chatMessage.setIsDelete(0);
chatMessageService.save(chatMessage);
}
}

View File

@@ -0,0 +1,138 @@
package com.xy.xyaicpzs.controller;
import com.xy.xyaicpzs.common.ErrorCode;
import com.xy.xyaicpzs.common.ResultUtils;
import com.xy.xyaicpzs.common.requset.PredictRecordQueryRequest;
import com.xy.xyaicpzs.common.response.ApiResponse;
import com.xy.xyaicpzs.common.response.PageResponse;
import com.xy.xyaicpzs.domain.entity.PredictRecord;
import com.xy.xyaicpzs.domain.vo.UserPredictStatVO;
import com.xy.xyaicpzs.service.DataAnalysisService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
/**
* 数据分析控制器
* 提供用户预测数据统计分析的API接口
*/
@Slf4j
@RestController
@RequestMapping("/data-analysis")
@Tag(name = "数据分析", description = "用户预测数据统计分析API")
public class DataAnalysisController {
@Autowired
private DataAnalysisService dataAnalysisService;
/**
* 获取用户预测统计数据
* @param userId 用户ID
* @return 用户预测统计数据
*/
@GetMapping("/user-predict-stat/{userId}")
@Operation(summary = "获取用户预测统计数据", description = "根据用户ID获取该用户的预测次数、待开奖次数、命中次数、命中率等统计数据")
public ApiResponse<UserPredictStatVO> getUserPredictStat(
@Parameter(description = "用户ID例如1001", required = true)
@PathVariable Long userId) {
try {
log.info("接收到获取用户预测统计数据请求用户ID={}", userId);
// 调用服务获取用户预测统计数据
UserPredictStatVO result = dataAnalysisService.getUserPredictStat(userId);
log.info("获取用户预测统计数据完成用户ID{},预测次数:{},待开奖次数:{},命中次数:{},命中率:{}",
userId, result.getPredictCount(), result.getPendingCount(),
result.getHitCount(), result.getHitRate());
return ResultUtils.success(result);
} catch (Exception e) {
log.error("获取用户预测统计数据失败:{}", e.getMessage(), e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取用户预测统计数据失败:" + e.getMessage());
}
}
/**
* 手动触发处理待开奖记录
* @return 处理结果
*/
@PostMapping("/process-pending")
@Operation(summary = "手动处理待开奖记录", description = "手动触发处理待开奖的预测记录,匹配开奖结果并更新中奖状态")
public ApiResponse<String> processPendingPredictions() {
try {
log.info("接收到手动处理待开奖记录请求");
// 调用服务处理待开奖记录
int processedCount = dataAnalysisService.processPendingPredictions();
String message = String.format("处理完成,共处理%d条待开奖记录", processedCount);
log.info("手动处理待开奖记录完成:{}", message);
return ResultUtils.success(message);
} catch (Exception e) {
log.error("手动处理待开奖记录失败:{}", e.getMessage(), e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "处理待开奖记录失败:" + e.getMessage());
}
}
/**
* 按条件查询预测记录
* @param request 查询条件
* @return 分页预测记录
*/
@PostMapping("/query-predict-records")
@Operation(summary = "按条件查询预测记录", description = "根据用户ID和预测状态(待开奖/未中奖/已中奖)筛选预测记录,支持分页查询")
public ApiResponse<PageResponse<PredictRecord>> queryPredictRecords(@RequestBody PredictRecordQueryRequest request) {
try {
log.info("接收到按条件查询预测记录请求userId={}, predictStatus={}, current={}, pageSize={}",
request.getUserId(), request.getPredictStatus(), request.getCurrent(), request.getPageSize());
// 调用服务查询预测记录
PageResponse<PredictRecord> result = dataAnalysisService.queryPredictRecords(request);
log.info("按条件查询预测记录完成,总记录数:{}", result.getTotal());
return ResultUtils.success(result);
} catch (Exception e) {
log.error("按条件查询预测记录失败:{}", e.getMessage(), e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "查询预测记录失败:" + e.getMessage());
}
}
/**
* 获取所有预测记录总数
* @return 预测记录总数
*/
@GetMapping("/total-predict-count")
@Operation(summary = "获取预测记录总数", description = "获取系统中所有用户的预测记录总数")
public ApiResponse<Map<String, Long>> getTotalPredictCount() {
try {
log.info("接收到获取预测记录总数请求");
// 调用服务获取预测记录总数
long totalCount = dataAnalysisService.getTotalPredictCount();
Map<String, Long> result = new HashMap<>();
result.put("totalCount", totalCount);
log.info("获取预测记录总数完成,总数:{}", totalCount);
return ResultUtils.success(result);
} catch (Exception e) {
log.error("获取预测记录总数失败:{}", e.getMessage(), e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取预测记录总数失败:" + e.getMessage());
}
}
}

View File

@@ -0,0 +1,156 @@
package com.xy.xyaicpzs.controller;
import com.xy.xyaicpzs.common.ErrorCode;
import com.xy.xyaicpzs.common.ResultUtils;
import com.xy.xyaicpzs.common.response.ApiResponse;
import com.xy.xyaicpzs.domain.entity.User;
import com.xy.xyaicpzs.service.ExcelImportService;
import com.xy.xyaicpzs.service.OperationHistoryService;
import com.xy.xyaicpzs.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
/**
* Excel数据导入控制器
*/
@Slf4j
@RestController
@RequestMapping("/excel")
@Tag(name = "Excel数据导入", description = "Excel数据导入相关接口")
public class ExcelImportController {
@Autowired
private ExcelImportService excelImportService;
@Autowired
private UserService userService;
@Autowired
private OperationHistoryService operationHistoryService;
/**
* 上传Excel文件并导入数据
*/
@PostMapping("/upload")
@Operation(summary = "上传Excel文件导入数据", description = "上传包含T1、T2、T3、T4、T5、T6和T7 sheet的Excel文件将红球、蓝球、线系数和面系数数据分别导入到十二个数据库表中")
public ApiResponse<String> uploadExcelFile(
@Parameter(description = "Excel文件(.xlsx格式)", required = true)
@RequestParam("file") MultipartFile file, HttpServletRequest httpServletRequest) {
User loginUser = userService.getLoginUser(httpServletRequest);
if (!userService.isAdmin(loginUser)){
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "无权限");
}
Long userId = loginUser.getId();
String userName = loginUser.getUserName();
String fileName = file.getOriginalFilename();
log.info("接收到Excel文件上传请求文件名{}", fileName);
try {
String message = excelImportService.importExcelFile(file);
// 记录操作历史 - 成功
String resultMessage = String.format("%s成功上传并导入Excel文件%s导入结果%s", userName, fileName, message);
operationHistoryService.recordOperation(userId, "Excel数据导入", 1, "成功", resultMessage);
return ResultUtils.success(message);
} catch (Exception e) {
log.error("Excel文件导入失败文件名{},错误:{}", fileName, e.getMessage(), e);
// 记录操作历史 - 失败
String resultMessage = String.format("%sExcel文件导入失败%s文件名%s错误原因%s", userName, fileName, fileName, e.getMessage());
operationHistoryService.recordOperation(userId, "Excel数据导入", 1, "失败", resultMessage);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "Excel文件导入失败" + e.getMessage());
}
}
/**
* 上传Excel文件并导入开奖数据
*/
@PostMapping("/upload-lottery-draws")
@Operation(summary = "上传Excel文件导入开奖数据", description = "上传包含T10工作表的Excel文件只导入开奖数据到lottery_draws表")
public ApiResponse<String> uploadLotteryDrawsFile(
@Parameter(description = "包含T10工作表的Excel文件(.xlsx格式)", required = true)
@RequestParam("file") MultipartFile file, HttpServletRequest httpServletRequest) {
User loginUser = userService.getLoginUser(httpServletRequest);
if (!userService.isAdmin(loginUser)){
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "无权限");
}
Long userId = loginUser.getId();
String userName = loginUser.getUserName();
String fileName = file.getOriginalFilename();
log.info("接收到开奖数据上传请求,文件名:{}", fileName);
try {
String message = excelImportService.importLotteryDrawsFile(file);
// 记录操作历史 - 成功
String resultMessage = String.format("%s成功上传并导入开奖数据文件%s导入结果%s", userName, fileName, message);
operationHistoryService.recordOperation(userId, "开奖数据导入", 1, "成功", resultMessage);
return ResultUtils.success(message);
} catch (Exception e) {
log.error("开奖数据文件导入失败,文件名:{},错误:{}", fileName, e.getMessage(), e);
// 记录操作历史 - 失败
String resultMessage = String.format("%s开奖数据文件导入失败%s文件名%s错误原因%s", userName, fileName, fileName, e.getMessage());
operationHistoryService.recordOperation(userId, "开奖数据导入", 1, "失败", resultMessage);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "开奖数据文件导入失败:" + e.getMessage());
}
}
/**
* 上传Excel文件并追加导入开奖数据
*/
@PostMapping("/append-lottery-draws")
@Operation(summary = "上传Excel文件追加导入开奖数据", description = "上传包含T10工作表的Excel文件追加导入开奖数据不清空现有数据跳过重复期号")
public ApiResponse<String> appendLotteryDrawsFile(
@Parameter(description = "包含T10工作表的Excel文件(.xlsx格式)", required = true)
@RequestParam("file") MultipartFile file, HttpServletRequest httpServletRequest) {
User loginUser = userService.getLoginUser(httpServletRequest);
if (!userService.isAdmin(loginUser)){
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "无权限");
}
Long userId = loginUser.getId();
String userName = loginUser.getUserName();
String fileName = file.getOriginalFilename();
log.info("接收到开奖数据追加上传请求,文件名:{}", fileName);
try {
String message = excelImportService.appendLotteryDrawsFile(file);
// 记录操作历史 - 成功
String resultMessage = String.format("%s成功追加导入开奖数据文件%s导入结果%s", userName, fileName, message);
operationHistoryService.recordOperation(userId, "开奖数据追加导入", 1, "成功", resultMessage);
return ResultUtils.success(message);
} catch (Exception e) {
log.error("开奖数据追加导入失败,文件名:{},错误:{}", fileName, e.getMessage(), e);
// 记录操作历史 - 失败
String resultMessage = String.format("%s开奖数据追加导入失败%s文件名%s错误原因%s", userName, fileName, fileName, e.getMessage());
operationHistoryService.recordOperation(userId, "开奖数据追加导入", 1, "失败", resultMessage);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "开奖数据追加导入失败:" + e.getMessage());
}
}
/**
* 获取导入说明
*/
@GetMapping("/import-info")
@Operation(summary = "获取导入说明", description = "获取Excel数据导入的详细说明")
public String getImportInfo() {
return excelImportService.getImportInfo();
}
}

View File

@@ -0,0 +1,15 @@
package com.xy.xyaicpzs.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/health")
public class HealthController {
@GetMapping
public String healthCheck() {
return "ok";
}
}

View File

@@ -0,0 +1,108 @@
package com.xy.xyaicpzs.controller;
import com.xy.xyaicpzs.common.response.ApiResponse;
import com.xy.xyaicpzs.common.ResultUtils;
import com.xy.xyaicpzs.service.CozeAuthService;
import com.xy.xyaicpzs.util.JwtUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
/**
* JWT令牌控制器
*/
@RestController
@RequestMapping("/jwt")
@Tag(name = "JWT接口", description = "提供JWT令牌生成功能")
public class JwtController {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private CozeAuthService cozeAuthService;
/**
* 生成JWT令牌
*
* @param expireSeconds 过期时间(秒)
* @param sessionName 会话名称(可选)
* @param deviceId 设备ID可选
* @return JWT令牌
*/
@GetMapping("/token")
@Operation(summary = "生成JWT令牌", description = "生成Coze API访问所需的JWT令牌")
public ApiResponse<Map<String, String>> generateToken(
@Parameter(description = "过期时间(秒)") @RequestParam(defaultValue = "600") int expireSeconds,
@Parameter(description = "会话名称(可选)") @RequestParam(required = false) String sessionName,
@Parameter(description = "设备ID可选") @RequestParam(required = false) String deviceId) {
try {
String token = jwtUtil.generateToken(expireSeconds, sessionName, deviceId);
Map<String, String> result = new HashMap<>();
result.put("token", token);
return ResultUtils.success(result);
} catch (Exception e) {
return ResultUtils.error(50000, "JWT生成失败: " + e.getMessage());
}
}
/**
* 通过JWT获取访问令牌
*
* @param jwt JWT令牌
* @param durationSeconds 访问令牌有效期默认为86400秒1天
* @return 包含访问令牌和过期时间的信息
*/
@PostMapping("/access-token")
@Operation(summary = "获取访问令牌", description = "通过JWT获取Coze API的OAuth访问令牌")
public ApiResponse<Map<String, Object>> getAccessToken(
@Parameter(description = "JWT令牌") @RequestParam String jwt,
@Parameter(description = "令牌有效期(秒)") @RequestParam(defaultValue = "86400") Integer durationSeconds) {
try {
Map<String, Object> tokenInfo = cozeAuthService.getAccessToken(jwt, durationSeconds);
return ResultUtils.success(tokenInfo);
} catch (Exception e) {
return ResultUtils.error(50000, "获取访问令牌失败: " + e.getMessage());
}
}
/**
* 一站式获取访问令牌生成JWT并立即获取访问令牌
*
* @param jwtExpireSeconds JWT过期时间
* @param sessionName 会话名称(可选)
* @param deviceId 设备ID可选
* @param tokenDurationSeconds 访问令牌有效期(秒)
* @return 包含JWT、访问令牌和过期时间的信息
*/
@PostMapping("/one-step-token")
@Operation(summary = "一站式获取访问令牌", description = "生成JWT并立即获取Coze API的OAuth访问令牌")
public ApiResponse<Map<String, Object>> getOneStepToken(
@Parameter(description = "JWT过期时间") @RequestParam(defaultValue = "600") int jwtExpireSeconds,
@Parameter(description = "会话名称(可选)") @RequestParam(required = false) String sessionName,
@Parameter(description = "设备ID可选") @RequestParam(required = false) String deviceId,
@Parameter(description = "访问令牌有效期(秒)") @RequestParam(defaultValue = "86400") Integer tokenDurationSeconds) {
try {
// 生成JWT令牌
String jwt = jwtUtil.generateToken(jwtExpireSeconds, sessionName, deviceId);
// 获取访问令牌
Map<String, Object> tokenInfo = cozeAuthService.getAccessToken(jwt, tokenDurationSeconds);
// 合并结果
tokenInfo.put("jwt", jwt);
return ResultUtils.success(tokenInfo);
} catch (Exception e) {
return ResultUtils.error(50000, "获取访问令牌失败: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,96 @@
package com.xy.xyaicpzs.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xy.xyaicpzs.common.ErrorCode;
import com.xy.xyaicpzs.common.ResultUtils;
import com.xy.xyaicpzs.common.response.ApiResponse;
import com.xy.xyaicpzs.domain.entity.OperationHistory;
import com.xy.xyaicpzs.domain.entity.User;
import com.xy.xyaicpzs.service.OperationHistoryService;
import com.xy.xyaicpzs.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 操作历史管理控制器
*/
@RestController
@RequestMapping("/operation-history")
@Slf4j
@Tag(name = "操作历史管理", description = "操作历史记录相关接口")
public class OperationHistoryController {
@Autowired
private OperationHistoryService operationHistoryService;
@Autowired
private UserService userService;
/**
* 获取操作历史记录
* 支持按操作模块和操作结果进行筛选
*/
@GetMapping("/list")
@Operation(summary = "获取操作历史记录", description = "获取操作历史记录,支持按操作模块、操作结果筛选和结果信息模糊搜索")
public ApiResponse<List<OperationHistory>> getOperationHistory(
@Parameter(description = "操作模块0-会员码管理/1-Excel导入管理等")
@RequestParam(value = "operationModule", required = false) Integer operationModule,
@Parameter(description = "操作结果(成功/失败)")
@RequestParam(value = "operationResult", required = false) String operationResult,
@Parameter(description = "结果信息关键词(支持模糊搜索)")
@RequestParam(value = "keyword", required = false) String keyword,
HttpServletRequest httpServletRequest) {
try {
// 权限校验:仅管理员可以查看
User loginUser = userService.getLoginUser(httpServletRequest);
if (!userService.isAdmin(loginUser)) {
return ResultUtils.error(ErrorCode.NO_AUTH_ERROR, "无权限查看操作历史");
}
log.info("获取操作历史,操作模块:{},操作结果:{},关键词:{}", operationModule, operationResult, keyword);
// 构建查询条件
QueryWrapper<OperationHistory> queryWrapper = new QueryWrapper<>();
// 添加操作模块筛选条件
if (operationModule != null && operationModule >= 0) {
queryWrapper.eq("operationModule", operationModule);
}
// 添加操作结果筛选条件
if (operationResult != null && !operationResult.trim().isEmpty()) {
if (!operationResult.equals("成功") && !operationResult.equals("失败")) {
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "操作结果只能是'成功'或'失败'");
}
queryWrapper.eq("operationResult", operationResult);
}
// 添加结果信息模糊搜索条件
if (keyword != null && !keyword.trim().isEmpty()) {
queryWrapper.like("resultMessage", keyword.trim());
}
// 按操作时间降序排序
queryWrapper.orderByDesc("operationTime");
// 查询操作历史
List<OperationHistory> records = operationHistoryService.list(queryWrapper);
log.info("操作历史查询成功,共{}条记录", records.size());
return ResultUtils.success(records);
} catch (Exception e) {
log.error("获取操作历史失败", e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取操作历史失败:" + e.getMessage());
}
}
}

View File

@@ -0,0 +1,48 @@
package com.xy.xyaicpzs.controller;
import com.xy.xyaicpzs.common.ResultUtils;
import com.xy.xyaicpzs.common.response.ApiResponse;
import com.xy.xyaicpzs.service.SmsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 短信控制器
*/
@RestController
@RequestMapping("/sms")
@Tag(name = "短信接口", description = "提供短信验证码相关功能")
public class SmsController {
@Autowired
private SmsService smsService;
/**
* 发送短信验证码
*
* @param phoneNumber 手机号
* @return 发送结果
*/
@PostMapping("/sendCode")
@Operation(summary = "发送短信验证码", description = "向指定手机号发送验证码每个手机号每天最多发送3次")
public ApiResponse<Boolean> sendVerificationCode(
@Parameter(description = "手机号码", required = true)
@RequestParam String phoneNumber) {
try {
boolean success = smsService.sendVerificationCode(phoneNumber);
if (success) {
return ResultUtils.success(true);
} else {
return ResultUtils.error(40001, "发送验证码失败,请稍后重试或联系客服");
}
} catch (Exception e) {
return ResultUtils.error(50000, "发送验证码异常:" + e.getMessage());
}
}
}

View File

@@ -0,0 +1,83 @@
package com.xy.xyaicpzs.controller;
import com.xy.xyaicpzs.common.response.ApiResponse;
import com.xy.xyaicpzs.common.ResultUtils;
import com.xy.xyaicpzs.util.SpeechRecognizerDemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* 语音识别控制器
*/
@RestController
@RequestMapping("/api/speech")
public class SpeechRecognitionController {
@Autowired
private SpeechRecognizerDemo speechRecognizer;
/**
* 识别本地语音文件
* @param filePath 文件路径
* @param sampleRate 采样率
* @return 识别结果
*/
@GetMapping("/recognize")
public ApiResponse<Map<String, String>> recognizeSpeech(
@RequestParam("filePath") String filePath,
@RequestParam(value = "sampleRate", defaultValue = "16000") int sampleRate) {
String text = speechRecognizer.speechToText(filePath, sampleRate);
Map<String, String> result = new HashMap<>();
result.put("text", text);
return ResultUtils.success(result);
}
/**
* 上传并识别语音文件
* @param file 上传的语音文件
* @param sampleRate 采样率
* @return 识别结果
*/
@PostMapping("/upload-and-recognize")
public ApiResponse<Map<String, String>> uploadAndRecognize(
@RequestParam("file") MultipartFile file,
@RequestParam(value = "sampleRate", defaultValue = "16000") int sampleRate) {
if (file.isEmpty()) {
return ResultUtils.error(40001, "上传文件不能为空");
}
try {
// 创建临时目录
String tempDir = System.getProperty("java.io.tmpdir");
String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
Path filePath = Paths.get(tempDir, fileName);
// 保存上传的文件
file.transferTo(filePath.toFile());
// 识别语音
String text = speechRecognizer.speechToText(filePath.toString(), sampleRate);
// 删除临时文件
Files.deleteIfExists(filePath);
Map<String, String> result = new HashMap<>();
result.put("text", text);
return ResultUtils.success(result);
} catch (IOException e) {
return ResultUtils.error(50000, "文件处理失败:" + e.getMessage());
}
}
}

View File

@@ -0,0 +1,578 @@
package com.xy.xyaicpzs.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xy.xyaicpzs.common.DeleteRequest;
import com.xy.xyaicpzs.common.ErrorCode;
import com.xy.xyaicpzs.common.ResultUtils;
import com.xy.xyaicpzs.common.requset.VipCodeActivateRequest;
import com.xy.xyaicpzs.common.response.ApiResponse;
import com.xy.xyaicpzs.domain.dto.user.*;
import com.xy.xyaicpzs.domain.entity.User;
import com.xy.xyaicpzs.domain.vo.UserVO;
import com.xy.xyaicpzs.exception.BusinessException;
import com.xy.xyaicpzs.service.UserService;
import com.xy.xyaicpzs.service.VipCodeService;
import com.xy.xyaicpzs.service.SmsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 用户接口
*/
@Slf4j
@RestController
@RequestMapping("/user")
@Tag(name = "用户管理", description = "用户管理相关接口")
public class UserController {
@Resource
private UserService userService;
@Resource
private VipCodeService vipCodeService;
@Resource
private SmsService smsService;
// region 登录相关
/**
* 用户登录
*
* @param userLoginRequest
* @param request
* @return
*/
@PostMapping("/login")
@Operation(summary = "用户登录", description = "用户登录接口")
public ApiResponse<UserVO> userLogin(@RequestBody UserLoginRequest userLoginRequest, HttpServletRequest request) {
if (userLoginRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
String userAccount = userLoginRequest.getUserAccount();
String userPassword = userLoginRequest.getUserPassword();
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User user = userService.userLogin(userAccount, userPassword, request);
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user, userVO);
return ResultUtils.success(userVO);
}
/**
* 用户注销
*
* @param request
* @return
*/
@PostMapping("/logout")
@Operation(summary = "用户注销", description = "用户注销接口")
public ApiResponse<Boolean> userLogout(HttpServletRequest request) {
if (request == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
boolean result = userService.userLogout(request);
return ResultUtils.success(result);
}
/**
* 获取当前登录用户
*
* @param request
* @return
*/
@GetMapping("/get/login")
@Operation(summary = "获取当前登录用户", description = "获取当前登录用户信息")
public ApiResponse<UserVO> getLoginUser(HttpServletRequest request) {
User user = userService.getLoginUser(request);
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user, userVO);
return ResultUtils.success(userVO);
}
// endregion
// region 增删改查
/**
* 创建用户
*
* @param userAddRequest
* @param request
* @return
*/
@PostMapping("/add")
@Operation(summary = "创建用户", description = "管理员创建用户")
public ApiResponse<Long> addUser(@RequestBody UserAddRequest userAddRequest, HttpServletRequest request) {
if (userAddRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 参数校验
String userAccount = userAddRequest.getUserAccount();
String userPassword = userAddRequest.getUserPassword();
String password = userAddRequest.getPassword();
String phone = userAddRequest.getPhone();
// 如果userPassword为空但password不为空则使用password
if (StringUtils.isBlank(userPassword) && StringUtils.isNotBlank(password)) {
userAddRequest.setUserPassword(password);
userPassword = password;
}
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号或密码不能为空");
}
if (phone != null && !phone.isEmpty()) {
// 如果提供了手机号,可以进行手机号格式校验
if (phone.length() != 11) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号格式不正确");
}
}
User user = new User();
BeanUtils.copyProperties(userAddRequest, user);
// 密码加密使用Service层的加密方法
String encryptPassword = userService.encryptPassword(userPassword);
user.setUserPassword(encryptPassword);
boolean result = userService.save(user);
if (!result) {
throw new BusinessException(ErrorCode.OPERATION_ERROR);
}
return ResultUtils.success(user.getId());
}
/**
* 修改用户状态
*
* @param userStatusUpdateRequest 用户状态更新请求
* @param request HTTP请求
* @return 修改结果
*/
@PostMapping("/update-status")
@Operation(summary = "修改用户状态", description = "管理员修改用户状态(正常/封禁)")
public ApiResponse<Boolean> updateUserStatus(@RequestBody UserStatusUpdateRequest userStatusUpdateRequest,
HttpServletRequest request) {
if (userStatusUpdateRequest == null || userStatusUpdateRequest.getId() == null
|| userStatusUpdateRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户ID不正确");
}
Long id = userStatusUpdateRequest.getId();
Integer status = userStatusUpdateRequest.getStatus();
// 校验状态值
if (status == null || (status != 0 && status != 1)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "状态值不正确应为0(正常)或1(封禁)");
}
// 确认操作人员是否为管理员
User loginUser = userService.getLoginUser(request);
if (!userService.isAdmin(loginUser)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无管理员权限");
}
// 检查目标用户是否存在
User user = userService.getById(id);
if (user == null) {
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "用户不存在");
}
// 更新用户状态
user.setStatus(status);
boolean result = userService.updateById(user);
if (!result) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "操作失败");
}
return ResultUtils.success(true);
}
/**
* 删除用户
*
* @param deleteRequest
* @param request
* @return
*/
@PostMapping("/delete")
@Operation(summary = "删除用户", description = "管理员删除用户")
public ApiResponse<Boolean> deleteUser(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
if (deleteRequest == null || deleteRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
boolean b = userService.removeById(deleteRequest.getId());
return ResultUtils.success(b);
}
/**
* 更新用户
*
* @param userUpdateRequest
* @param request
* @return
*/
@PostMapping("/update")
@Operation(summary = "更新用户", description = "更新用户信息")
public ApiResponse<Boolean> updateUser(@RequestBody UserUpdateRequest userUpdateRequest, HttpServletRequest request) {
if (userUpdateRequest == null || userUpdateRequest.getId() == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 参数校验
String userPassword = userUpdateRequest.getUserPassword();
String password = userUpdateRequest.getPassword();
String phone = userUpdateRequest.getPhone();
// 如果userPassword为空但password不为空则使用password
if (StringUtils.isBlank(userPassword) && StringUtils.isNotBlank(password)) {
userUpdateRequest.setUserPassword(password);
userPassword = password;
}
if (phone != null && !phone.isEmpty()) {
// 如果提供了手机号,可以进行手机号格式校验
if (phone.length() != 11) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号格式不正确");
}
}
User user = new User();
BeanUtils.copyProperties(userUpdateRequest, user);
// 如果更新了密码,需要进行加密
if (StringUtils.isNotBlank(userPassword)) {
String encryptPassword = userService.encryptPassword(userPassword);
user.setUserPassword(encryptPassword);
}
boolean result = userService.updateById(user);
return ResultUtils.success(result);
}
/**
* 根据 id 获取用户
*
* @param id
* @param request
* @return
*/
@GetMapping("/get")
@Operation(summary = "根据ID获取用户", description = "根据用户ID获取用户信息")
public ApiResponse<UserVO> getUserById(@RequestParam("id") long id, HttpServletRequest request) {
if (id <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User user = userService.getById(id);
if (user == null) {
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
}
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user, userVO);
return ResultUtils.success(userVO);
}
/**
* 获取用户列表
*
* @param userQueryRequest
* @param request
* @return
*/
@GetMapping("/list")
@Operation(summary = "获取用户列表", description = "获取用户列表,支持用户名/手机号模糊匹配和角色状态筛选")
public ApiResponse<List<UserVO>> listUser(UserQueryRequest userQueryRequest, HttpServletRequest request) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if (userQueryRequest != null) {
// 用户名模糊匹配
if (StringUtils.isNotBlank(userQueryRequest.getUserName())) {
queryWrapper.like("userName", userQueryRequest.getUserName());
}
// 手机号模糊匹配
if (StringUtils.isNotBlank(userQueryRequest.getPhone())) {
queryWrapper.like("phone", userQueryRequest.getPhone());
}
// 账号模糊匹配
if (StringUtils.isNotBlank(userQueryRequest.getUserAccount())) {
queryWrapper.like("userAccount", userQueryRequest.getUserAccount());
}
// 用户角色精确匹配
if (userQueryRequest.getUserRole() != null) {
queryWrapper.eq("userRole", userQueryRequest.getUserRole());
}
// 用户状态精确匹配
if (userQueryRequest.getStatus() != null) {
queryWrapper.eq("status", userQueryRequest.getStatus());
}
// 会员状态匹配
if (userQueryRequest.getIsVip() != null) {
queryWrapper.eq("isVip", userQueryRequest.getIsVip());
}
}
List<User> userList = userService.list(queryWrapper);
List<UserVO> userVOList = userList.stream().map(user -> {
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user, userVO);
return userVO;
}).collect(Collectors.toList());
return ResultUtils.success(userVOList);
}
/**
* 分页获取用户列表
*
* @param userQueryRequest
* @param request
* @return
*/
@GetMapping("/list/page")
@Operation(summary = "分页获取用户列表", description = "分页获取用户列表,支持用户名/手机号模糊匹配和角色状态筛选")
public ApiResponse<Page<UserVO>> listUserByPage(UserQueryRequest userQueryRequest, HttpServletRequest request) {
long current = 1;
long size = 10;
if (userQueryRequest != null) {
current = userQueryRequest.getCurrent();
size = userQueryRequest.getPageSize();
}
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if (userQueryRequest != null) {
// 用户名模糊匹配
if (StringUtils.isNotBlank(userQueryRequest.getUserName())) {
queryWrapper.like("userName", userQueryRequest.getUserName());
}
// 手机号模糊匹配
if (StringUtils.isNotBlank(userQueryRequest.getPhone())) {
queryWrapper.like("phone", userQueryRequest.getPhone());
}
// 账号模糊匹配
if (StringUtils.isNotBlank(userQueryRequest.getUserAccount())) {
queryWrapper.like("userAccount", userQueryRequest.getUserAccount());
}
// 用户角色精确匹配
if (userQueryRequest.getUserRole() != null) {
queryWrapper.eq("userRole", userQueryRequest.getUserRole());
}
// 用户状态精确匹配
if (userQueryRequest.getStatus() != null) {
queryWrapper.eq("status", userQueryRequest.getStatus());
}
// 会员状态匹配
if (userQueryRequest.getIsVip() != null) {
queryWrapper.eq("isVip", userQueryRequest.getIsVip());
}
}
Page<User> userPage = userService.page(new Page<>(current, size), queryWrapper);
// 创建新的Page对象用于返回UserVO
Page<UserVO> userVOPage = new Page<>(userPage.getCurrent(), userPage.getSize(), userPage.getTotal());
List<UserVO> userVOList = userPage.getRecords().stream().map(user -> {
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user, userVO);
return userVO;
}).collect(Collectors.toList());
userVOPage.setRecords(userVOList);
return ResultUtils.success(userVOPage);
}
/**
* 获取用户统计信息
*
* @return 包含总用户数和会员数的统计信息
*/
@GetMapping("/count")
@Operation(summary = "获取用户统计信息", description = "获取系统中总用户数和会员数量")
public ApiResponse<Map<String, Long>> getUserCount() {
// 获取总用户数
long totalUserCount = userService.count();
// 获取会员数量(isVip=1)
QueryWrapper<User> vipQueryWrapper = new QueryWrapper<>();
vipQueryWrapper.eq("isVip", 1);
long vipUserCount = userService.count(vipQueryWrapper);
// 获取正常状态用户数量(status=0)
QueryWrapper<User> normalStatusWrapper = new QueryWrapper<>();
normalStatusWrapper.eq("status", 0);
long normalUserCount = userService.count(normalStatusWrapper);
// 获取封禁状态用户数量(status=1)
QueryWrapper<User> bannedStatusWrapper = new QueryWrapper<>();
bannedStatusWrapper.eq("status", 1);
long bannedUserCount = userService.count(bannedStatusWrapper);
// 构造返回结果
Map<String, Long> countMap = new HashMap<>();
countMap.put("totalUserCount", totalUserCount);
countMap.put("vipUserCount", vipUserCount);
countMap.put("normalUserCount", normalUserCount);
countMap.put("bannedUserCount", bannedUserCount);
return ResultUtils.success(countMap);
}
/**
* 激活会员码
*
* @param request 会员码激活请求
* @return 是否激活成功
*/
@PostMapping("/activate-vip")
@Operation(summary = "激活会员码", description = "用户使用会员码激活会员服务")
public ApiResponse<Boolean> activateVipCode(@RequestBody VipCodeActivateRequest request) {
if (request == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数不能为空");
}
if (request.getUserId() == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户ID不能为空");
}
if (StringUtils.isBlank(request.getCode())) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "会员码不能为空");
}
try {
boolean result = vipCodeService.activateVipCode(request.getUserId(), request.getCode());
return ResultUtils.success(result);
} catch (IllegalArgumentException e) {
log.error("会员码激活失败:{}", e.getMessage());
throw new BusinessException(ErrorCode.PARAMS_ERROR, e.getMessage());
} catch (RuntimeException e) {
log.error("会员码激活系统错误:{}", e.getMessage());
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "会员码激活失败,请稍后重试");
}
}
/**
* 手机号注册
*
* @param userPhoneRegisterRequest 手机号注册请求
* @return 用户ID
*/
@PostMapping("/phone/register")
@Operation(summary = "手机号注册", description = "使用手机号和验证码注册用户")
public ApiResponse<Long> userPhoneRegister(@RequestBody UserPhoneRegisterRequest userPhoneRegisterRequest) {
if (userPhoneRegisterRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
long result = userService.userPhoneRegister(userPhoneRegisterRequest);
return ResultUtils.success(result);
}
/**
* 手机号登录
*
* @param userPhoneLoginRequest 手机号登录请求
* @param request HTTP请求
* @return 用户信息
*/
@PostMapping("/phone/login")
@Operation(summary = "手机号登录", description = "使用手机号和验证码登录")
public ApiResponse<UserVO> userPhoneLogin(@RequestBody UserPhoneLoginRequest userPhoneLoginRequest, HttpServletRequest request) {
if (userPhoneLoginRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User user = userService.userPhoneLogin(userPhoneLoginRequest, request);
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user, userVO);
return ResultUtils.success(userVO);
}
/**
* 重置密码
*
* @param resetPasswordRequest 重置密码请求
* @return 是否重置成功
*/
@PostMapping("/reset-password")
@Operation(summary = "重置密码", description = "使用手机号和验证码重置密码")
public ApiResponse<Boolean> resetPassword(@RequestBody ResetPasswordRequest resetPasswordRequest) {
if (resetPasswordRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数不能为空");
}
String phone = resetPasswordRequest.getPhone();
String code = resetPasswordRequest.getCode();
String newPassword = resetPasswordRequest.getNewPassword();
String confirmPassword = resetPasswordRequest.getConfirmPassword();
// 校验参数
if (StringUtils.isAnyBlank(phone, code, newPassword, confirmPassword)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不能为空");
}
// 校验手机号格式
if (phone.length() != 11) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号格式不正确");
}
// 校验两次密码是否一致
if (!newPassword.equals(confirmPassword)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
}
// 密码长度校验
if (newPassword.length() < 8) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度不能小于8位");
}
// 验证短信验证码
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 = userService.getOne(queryWrapper);
if (user == null) {
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "未找到该手机号注册的用户");
}
// 更新密码
String encryptPassword = userService.encryptPassword(newPassword);
user.setUserPassword(encryptPassword);
boolean result = userService.updateById(user);
if (!result) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "密码重置失败,请稍后重试");
}
ApiResponse<Boolean> response = ResultUtils.success(true);
response.setMessage("密码重置成功");
return response;
}
// endregion
}

View File

@@ -0,0 +1,298 @@
package com.xy.xyaicpzs.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xy.xyaicpzs.common.ErrorCode;
import com.xy.xyaicpzs.common.ResultUtils;
import com.xy.xyaicpzs.common.requset.GenerateVipCodesRequest;
import com.xy.xyaicpzs.common.requset.VipCodeQueryRequest;
import com.xy.xyaicpzs.common.response.ApiResponse;
import com.xy.xyaicpzs.domain.entity.User;
import com.xy.xyaicpzs.domain.entity.VipCode;
import com.xy.xyaicpzs.domain.vo.VipCodeVO;
import com.xy.xyaicpzs.exception.BusinessException;
import com.xy.xyaicpzs.service.OperationHistoryService;
import com.xy.xyaicpzs.service.UserService;
import com.xy.xyaicpzs.service.VipCodeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 会员码管理接口
*/
@Slf4j
@RestController
@RequestMapping("/vip-code")
@Tag(name = "会员码管理", description = "会员码管理相关接口")
public class VipCodeController {
@Resource
private VipCodeService vipCodeService;
@Autowired
private UserService userService;
@Autowired
private OperationHistoryService operationHistoryService;
/**
* 批量生成会员码
*
* @param request 生成会员码请求
* @return 生成成功的数量
*/
@PostMapping("/generate")
@Operation(summary = "批量生成会员码", description = "管理员批量生成会员码")
public ApiResponse<Integer> generateVipCodes(@RequestBody GenerateVipCodesRequest request,
HttpServletRequest httpServletRequest) {
User loginUser = userService.getLoginUser(httpServletRequest);
if (!userService.isAdmin(loginUser)){
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "无权限");
}
Long userId = loginUser.getId();
String userName = loginUser.getUserName();
if (request == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数不能为空");
}
if (request.getNumCodes() == null || request.getNumCodes() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "生成数量必须大于0");
}
if (request.getVipExpireTime() == null || request.getVipExpireTime() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "会员有效月数必须大于0");
}
if (request.getNumCodes() > 1000) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "单次生成数量不能超过1000");
}
try {
int result = vipCodeService.generateVipCodes(request.getNumCodes(), request.getVipExpireTime(), userId, userName);
// 记录操作历史 - 成功
String resultMessage = String.format("%s成功生成%d个会员码有效月数%d", userName, result, request.getVipExpireTime());
operationHistoryService.recordOperation(userId, "批量生成会员码", 0, "成功", resultMessage);
return ResultUtils.success(result);
} catch (IllegalArgumentException e) {
log.error("生成会员码参数错误:{}", e.getMessage());
// 记录操作历史 - 失败
String resultMessage = String.format("%s生成会员码失败%s请求数量%d有效月数%d",
userName, e.getMessage(), request.getNumCodes(), request.getVipExpireTime());
operationHistoryService.recordOperation(userId, "批量生成会员码", 0, "失败", resultMessage);
throw new BusinessException(ErrorCode.PARAMS_ERROR, e.getMessage());
} catch (RuntimeException e) {
log.error("生成会员码系统错误:{}", e.getMessage());
// 记录操作历史 - 失败
String resultMessage = String.format("%s生成会员码系统错误%s请求数量%d有效月数%d",
userName, e.getMessage(), request.getNumCodes(), request.getVipExpireTime());
operationHistoryService.recordOperation(userId, "批量生成会员码", 0, "失败", resultMessage);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "生成会员码失败,请稍后重试");
}
}
/**
* 获取一个可用的会员码
*
* @param vipExpireTime 会员有效月数1或12
* @return 可用的会员码
*/
@GetMapping("/available")
@Operation(summary = "获取可用会员码", description = "根据有效月数获取一个可用的会员码")
public ApiResponse<String> getAvailableVipCode(@RequestParam("vipExpireTime") Integer vipExpireTime,
HttpServletRequest httpServletRequest) {
User loginUser = userService.getLoginUser(httpServletRequest);
if (!userService.isAdmin(loginUser)){
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "无权限");
}
Long userId = loginUser.getId();
String userName = loginUser.getUserName();
if (vipExpireTime == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "会员有效月数不能为空");
}
if (vipExpireTime != 1 && vipExpireTime != 12) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "会员有效月数只能是1或12");
}
try {
String code = vipCodeService.getAvailableVipCode(vipExpireTime, userId, userName);
if (code == null) {
// 记录操作历史 - 失败
String resultMessage = String.format("%s获取可用会员码失败没有找到可用的会员码有效月数%d", userName, vipExpireTime);
operationHistoryService.recordOperation(userId, "获取可用会员码", 0, "失败", resultMessage);
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "没有找到可用的会员码");
}
// 记录操作历史 - 成功
String resultMessage = String.format("%s成功获取可用会员码%s有效月数%d", userName, code, vipExpireTime);
operationHistoryService.recordOperation(userId, "获取可用会员码", 0, "成功", resultMessage);
return ResultUtils.success(code);
} catch (IllegalArgumentException e) {
log.error("获取可用会员码参数错误:{}", e.getMessage());
// 记录操作历史 - 失败
String resultMessage = String.format("%s获取可用会员码参数错误%s有效月数%d", userName, e.getMessage(), vipExpireTime);
operationHistoryService.recordOperation(userId, "获取可用会员码", 0, "失败", resultMessage);
throw new BusinessException(ErrorCode.PARAMS_ERROR, e.getMessage());
} catch (Exception e) {
log.error("获取可用会员码系统错误:{}", e.getMessage());
// 记录操作历史 - 失败
String resultMessage = String.format("%s获取可用会员码系统错误%s有效月数%d", userName, e.getMessage(), vipExpireTime);
operationHistoryService.recordOperation(userId, "获取可用会员码", 0, "失败", resultMessage);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取可用会员码失败,请生成后获取。");
}
}
/**
* 分页获取会员码列表
*
* @param vipCodeQueryRequest 会员码查询请求
* @param httpServletRequest Http请求
* @return 分页会员码列表
*/
@GetMapping("/list/page")
@Operation(summary = "分页获取会员码列表", description = "分页获取会员码列表,支持根据会员码、使用状态和时间筛选")
public ApiResponse<Page<VipCodeVO>> listVipCodesByPage(VipCodeQueryRequest vipCodeQueryRequest,
HttpServletRequest httpServletRequest) {
// 权限校验
User loginUser = userService.getLoginUser(httpServletRequest);
if (!userService.isAdmin(loginUser)){
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "无权限");
}
if (vipCodeQueryRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数不能为空");
}
long current = vipCodeQueryRequest.getCurrent();
long pageSize = vipCodeQueryRequest.getPageSize();
// 构建查询条件
QueryWrapper<VipCode> queryWrapper = new QueryWrapper<>();
// 根据会员码模糊查询
if (StringUtils.isNotBlank(vipCodeQueryRequest.getCode())) {
queryWrapper.like("code", vipCodeQueryRequest.getCode());
}
// 根据使用状态筛选
if (vipCodeQueryRequest.getIsUse() != null) {
queryWrapper.eq("isUse", vipCodeQueryRequest.getIsUse());
}
// 根据会员有效月数筛选
if (vipCodeQueryRequest.getVipExpireTime() != null) {
queryWrapper.eq("vipExpireTime", vipCodeQueryRequest.getVipExpireTime());
}
// 根据创建人ID筛选
if (vipCodeQueryRequest.getCreatedUserId() != null) {
queryWrapper.eq("createdUserId", vipCodeQueryRequest.getCreatedUserId());
}
// 根据创建人名称模糊查询
if (StringUtils.isNotBlank(vipCodeQueryRequest.getCreatedUserName())) {
queryWrapper.like("createdUserName", vipCodeQueryRequest.getCreatedUserName());
}
// 根据使用人ID筛选
if (vipCodeQueryRequest.getUsedUserId() != null) {
queryWrapper.eq("usedUserId", vipCodeQueryRequest.getUsedUserId());
}
// 根据创建时间范围筛选
if (vipCodeQueryRequest.getStartTime() != null && vipCodeQueryRequest.getEndTime() != null) {
queryWrapper.between("createTime", vipCodeQueryRequest.getStartTime(), vipCodeQueryRequest.getEndTime());
} else if (vipCodeQueryRequest.getStartTime() != null) {
queryWrapper.ge("createTime", vipCodeQueryRequest.getStartTime());
} else if (vipCodeQueryRequest.getEndTime() != null) {
queryWrapper.le("createTime", vipCodeQueryRequest.getEndTime());
}
// 按会员编号升序排序(从小到大)
queryWrapper.orderByAsc("vipNumber");
// 执行分页查询
Page<VipCode> vipCodePage = vipCodeService.page(new Page<>(current, pageSize), queryWrapper);
// 转换为VO对象
List<VipCodeVO> vipCodeVOList = vipCodePage.getRecords().stream().map(vipCode -> {
VipCodeVO vipCodeVO = new VipCodeVO();
BeanUtils.copyProperties(vipCode, vipCodeVO);
return vipCodeVO;
}).collect(Collectors.toList());
// 创建VO分页对象确保正确传递所有分页信息
Page<VipCodeVO> vipCodeVOPage = new Page<>(vipCodePage.getCurrent(), vipCodePage.getSize(), vipCodePage.getTotal());
vipCodeVOPage.setRecords(vipCodeVOList);
// 手动设置pages值
vipCodeVOPage.setPages(vipCodePage.getPages());
return ResultUtils.success(vipCodeVOPage);
}
/**
* 获取会员码统计数量
*
* @param httpServletRequest Http请求
* @return 会员码统计数量
*/
@GetMapping("/count")
@Operation(summary = "获取会员码统计数量", description = "获取系统中会员码总数、可用会员码和已使用会员码的数量")
public ApiResponse<Map<String, Long>> getVipCodeCount(HttpServletRequest httpServletRequest) {
// 权限校验
User loginUser = userService.getLoginUser(httpServletRequest);
if (!userService.isAdmin(loginUser)){
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "无权限");
}
try {
// 构建查询条件 - 总数
long totalCount = vipCodeService.count();
// 构建查询条件 - 已使用的会员码
QueryWrapper<VipCode> usedQueryWrapper = new QueryWrapper<>();
usedQueryWrapper.eq("isUse", 1);
long usedCount = vipCodeService.count(usedQueryWrapper);
// 构建查询条件 - 可用的会员码
QueryWrapper<VipCode> availableQueryWrapper = new QueryWrapper<>();
availableQueryWrapper.eq("isUse", 0);
long availableCount = vipCodeService.count(availableQueryWrapper);
// 构造返回结果
Map<String, Long> countMap = new HashMap<>();
countMap.put("totalCount", totalCount);
countMap.put("availableCount", availableCount);
countMap.put("usedCount", usedCount);
return ResultUtils.success(countMap);
} catch (Exception e) {
log.error("获取会员码统计数量失败:{}", e.getMessage());
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取会员码统计数量失败,请稍后重试");
}
}
}

View File

@@ -0,0 +1,139 @@
package com.xy.xyaicpzs.controller;
import com.xy.xyaicpzs.common.response.ApiResponse;
import com.xy.xyaicpzs.common.ResultUtils;
import com.xy.xyaicpzs.common.ErrorCode;
import com.xy.xyaicpzs.domain.entity.VipExchangeRecord;
import com.xy.xyaicpzs.service.VipExchangeRecordService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* VIP兑换记录控制器
*/
@RestController
@RequestMapping("/vip-exchange-record")
@Slf4j
@Tag(name = "VIP兑换记录管理", description = "VIP兑换记录相关接口")
public class VipExchangeRecordController {
@Autowired
private VipExchangeRecordService vipExchangeRecordService;
/**
* 根据用户ID获取所有兑换记录
*/
@GetMapping("/user/{userId}")
@Operation(summary = "获取用户兑换记录", description = "根据用户ID获取该用户的所有VIP兑换记录")
public ApiResponse<List<VipExchangeRecord>> getExchangeRecordsByUserId(
@Parameter(description = "用户ID", required = true)
@PathVariable("userId") Long userId) {
try {
log.info("获取用户兑换记录用户ID{}", userId);
// 参数校验
if (userId == null || userId <= 0) {
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "用户ID不能为空且必须大于0");
}
// 查询用户兑换记录
List<VipExchangeRecord> records = vipExchangeRecordService.getExchangeRecordsByUserId(userId);
log.info("用户ID{} 的兑换记录查询成功,共{}条记录", userId, records.size());
return ResultUtils.success(records);
} catch (Exception e) {
log.error("获取用户兑换记录失败用户ID{}", userId, e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取兑换记录失败:" + e.getMessage());
}
}
/**
* 根据用户ID获取兑换记录带分页
*/
@GetMapping("/user/{userId}/page")
@Operation(summary = "分页获取用户兑换记录", description = "根据用户ID分页获取该用户的VIP兑换记录")
public ApiResponse<List<VipExchangeRecord>> getExchangeRecordsByUserIdWithPage(
@Parameter(description = "用户ID", required = true)
@PathVariable("userId") Long userId,
@Parameter(description = "页码从1开始", required = false)
@RequestParam(value = "page", defaultValue = "1") Integer page,
@Parameter(description = "每页大小", required = false)
@RequestParam(value = "size", defaultValue = "10") Integer size) {
try {
log.info("分页获取用户兑换记录用户ID{},页码:{},每页大小:{}", userId, page, size);
// 参数校验
if (userId == null || userId <= 0) {
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "用户ID不能为空且必须大于0");
}
if (page < 1) {
page = 1;
}
if (size < 1 || size > 100) {
size = 10;
}
// 查询用户兑换记录
List<VipExchangeRecord> allRecords = vipExchangeRecordService.getExchangeRecordsByUserId(userId);
// 手动分页
int start = (page - 1) * size;
int end = Math.min(start + size, allRecords.size());
List<VipExchangeRecord> pageRecords = allRecords.subList(start, end);
log.info("用户ID{} 的兑换记录分页查询成功,总记录数:{},当前页记录数:{}",
userId, allRecords.size(), pageRecords.size());
return ResultUtils.success(pageRecords);
} catch (Exception e) {
log.error("分页获取用户兑换记录失败用户ID{}", userId, e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取兑换记录失败:" + e.getMessage());
}
}
/**
* 根据兑换记录ID获取详情
*/
@GetMapping("/{recordId}")
@Operation(summary = "获取兑换记录详情", description = "根据兑换记录ID获取详细信息")
public ApiResponse<VipExchangeRecord> getExchangeRecordById(
@Parameter(description = "兑换记录ID", required = true)
@PathVariable("recordId") Long recordId) {
try {
log.info("获取兑换记录详情记录ID{}", recordId);
// 参数校验
if (recordId == null || recordId <= 0) {
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "兑换记录ID不能为空且必须大于0");
}
// 查询兑换记录
VipExchangeRecord record = vipExchangeRecordService.getById(recordId);
if (record == null) {
return ResultUtils.error(ErrorCode.NOT_FOUND_ERROR, "兑换记录不存在");
}
log.info("兑换记录详情查询成功记录ID{}", recordId);
return ResultUtils.success(record);
} catch (Exception e) {
log.error("获取兑换记录详情失败记录ID{}", recordId, e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "获取兑换记录详情失败:" + e.getMessage());
}
}
}

View File

@@ -0,0 +1,34 @@
package com.xy.xyaicpzs.domain.dto.user;
import lombok.Data;
import java.io.Serializable;
/**
* 重置密码请求
*/
@Data
public class ResetPasswordRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户手机号
*/
private String phone;
/**
* 短信验证码
*/
private String code;
/**
* 新密码
*/
private String newPassword;
/**
* 确认新密码
*/
private String confirmPassword;
}

View File

@@ -0,0 +1,70 @@
package com.xy.xyaicpzs.domain.dto.user;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 用户创建请求
*/
@Data
public class UserAddRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户昵称
*/
private String userName;
/**
* 账号
*/
private String userAccount;
/**
* 电话
*/
private String phone;
/**
* 用户头像
*/
private String userAvatar;
/**
* 性别
*/
private Integer gender;
/**
* 用户角色user / admin
*/
private String userRole;
/**
* 密码
*/
private String userPassword;
/**
* 密码(兼容格式)
*/
private String password;
/**
* 是否会员0-非会员1-会员
*/
private Integer isVip;
/**
* 会员到期时间
*/
private Date vipExpire;
/**
* 状态0-正常1-封禁
*/
private Integer status;
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.domain.dto.user;
import lombok.Data;
import java.io.Serializable;
/**
* 用户登录请求
*/
@Data
public class UserLoginRequest implements Serializable {
private static final long serialVersionUID = 3191241716373120793L;
private String userAccount;
private String userPassword;
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.domain.dto.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 用户手机号登录请求
*/
@Data
@Schema(description = "用户手机号登录请求")
public class UserPhoneLoginRequest implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "手机号")
private String phone;
@Schema(description = "验证码")
private String code;
}

View File

@@ -0,0 +1,34 @@
package com.xy.xyaicpzs.domain.dto.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 用户手机号注册请求
*/
@Data
@Schema(description = "用户手机号注册请求")
public class UserPhoneRegisterRequest implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "用户账号")
private String userAccount;
@Schema(description = "用户名称")
private String userName;
@Schema(description = "用户密码")
private String userPassword;
@Schema(description = "确认密码")
private String checkPassword;
@Schema(description = "手机号")
private String phone;
@Schema(description = "验证码")
private String code;
}

View File

@@ -0,0 +1,57 @@
package com.xy.xyaicpzs.domain.dto.user;
import com.xy.xyaicpzs.common.PageRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* 用户查询请求
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class UserQueryRequest extends PageRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 用户昵称
*/
private String userName;
/**
* 账号
*/
private String userAccount;
/**
* 手机号
*/
private String phone;
/**
* 性别
*/
private Integer gender;
/**
* 用户角色user / admin
*/
private String userRole;
/**
* 是否会员0-非会员1-会员
*/
private Integer isVip;
/**
* 状态0-正常1-封禁
*/
private Integer status;
}

View File

@@ -0,0 +1,22 @@
package com.xy.xyaicpzs.domain.dto.user;
import lombok.Data;
import java.io.Serializable;
/**
* 用户注册请求
*/
@Data
public class UserRegisterRequest implements Serializable {
private static final long serialVersionUID = 3191241716373120793L;
private String userAccount;
private String userName;
private String userPassword;
private String checkPassword;
}

View File

@@ -0,0 +1,24 @@
package com.xy.xyaicpzs.domain.dto.user;
import lombok.Data;
import java.io.Serializable;
/**
* 用户状态更新请求
*/
@Data
public class UserStatusUpdateRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户id
*/
private Long id;
/**
* 状态0-正常1-封禁
*/
private Integer status;
}

View File

@@ -0,0 +1,75 @@
package com.xy.xyaicpzs.domain.dto.user;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 用户更新请求
*/
@Data
public class UserUpdateRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 用户昵称
*/
private String userName;
/**
* 账号
*/
private String userAccount;
/**
* 电话
*/
private String phone;
/**
* 用户头像
*/
private String userAvatar;
/**
* 性别
*/
private Integer gender;
/**
* 用户角色user / admin
*/
private String userRole;
/**
* 密码
*/
private String userPassword;
/**
* 密码(兼容格式)
*/
private String password;
/**
* 是否会员0-非会员1-会员
*/
private Integer isVip;
/**
* 会员到期时间
*/
private Date vipExpire;
/**
* 状态0-正常1-封禁
*/
private Integer status;
}

View File

@@ -0,0 +1,50 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 蓝球最近100期数据表
* @TableName blue_history_100
*/
@TableName(value ="blue_history_100")
@Data
public class BlueHistory100 {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 球号
*/
private Integer ballNumber;
/**
* 出现频次
*/
private Integer frequencyCount;
/**
* 平均隐现期(次)
*/
private Double averageInterval;
/**
* 当前隐现期(次)
*/
private Integer nowInterval;
/**
* 最多连出期(次)
*/
private Integer maxConsecutiveCount;
/**
* 点系数
*/
private Double pointCoefficient;
}

View File

@@ -0,0 +1,55 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 蓝球全部历史数据表
* @TableName blue_history_all
*/
@TableName(value ="blue_history_all")
@Data
public class BlueHistoryAll {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 球号
*/
private Integer ballNumber;
/**
* 出现频次
*/
private Integer frequencyCount;
/**
* 出现频率百分比
*/
private Double frequencyPercentage;
/**
* 平均隐现期(次)
*/
private Double averageInterval;
/**
* 最长隐现期(次)
*/
private Integer maxHiddenInterval;
/**
* 最多连出期(次)
*/
private Integer maxConsecutiveCount;
/**
* 点系数
*/
private Double pointCoefficient;
}

View File

@@ -0,0 +1,35 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 蓝球历史数据排行表
* @TableName blue_history_top
*/
@TableName(value ="blue_history_top")
@Data
public class BlueHistoryTop {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 排行
*/
private Integer no;
/**
* 球号
*/
private Integer ballNumber;
/**
* 点系数
*/
private Double pointCoefficient;
}

View File

@@ -0,0 +1,35 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 创建蓝球100期数据排行表
* @TableName blue_history_top_100
*/
@TableName(value ="blue_history_top_100")
@Data
public class BlueHistoryTop100 {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 排行
*/
private Integer no;
/**
* 球号
*/
private Integer ballNumber;
/**
* 点系数
*/
private Double pointCoefficient;
}

View File

@@ -0,0 +1,56 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
import lombok.Data;
/**
* 聊天消息表
* @TableName chat_message
*/
@TableName(value ="chat_message")
@Data
public class ChatMessage {
/**
* id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 会话ID
*/
private String conversationId;
/**
* 用户ID关联用户表
*/
private String studentId;
/**
* 消息类型(如: 用户提问、AI回答
*/
private String messageType;
/**
* 消息内容
*/
private String content;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 是否删除 0-未删除 1-已删除
*/
private Integer isDelete;
}

View File

@@ -0,0 +1,50 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 最近100期数据表
* @TableName history_100
*/
@TableName(value ="history_100")
@Data
public class History100 {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 球号
*/
private Integer ballNumber;
/**
* 出现频次
*/
private Integer frequencyCount;
/**
* 平均隐现期(次)
*/
private Double averageInterval;
/**
* 当前隐现期(次)
*/
private Integer nowInterval;
/**
* 最多连出期(次)
*/
private Integer maxConsecutiveCount;
/**
* 点系数
*/
private Double pointCoefficient;
}

View File

@@ -0,0 +1,55 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 历史数据表
* @TableName history_all
*/
@TableName(value ="history_all")
@Data
public class HistoryAll {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 球号
*/
private Integer ballNumber;
/**
* 出现频次
*/
private Integer frequencyCount;
/**
* 出现频率百分比
*/
private Double frequencyPercentage;
/**
* 平均间隔
*/
private Double averageInterval;
/**
* 最长隐藏间隔
*/
private Integer maxHiddenInterval;
/**
* 最大连续出现次数
*/
private Integer maxConsecutiveCount;
/**
* 点系数
*/
private Double pointCoefficient;
}

View File

@@ -0,0 +1,35 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 历史数据排行表
* @TableName history_top
*/
@TableName(value ="history_top")
@Data
public class HistoryTop {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 排行
*/
private Integer no;
/**
* 球号
*/
private Integer ballNumber;
/**
* 点系数
*/
private Double pointCoefficient;
}

View File

@@ -0,0 +1,35 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 创建100期数据排行表
* @TableName history_top_100
*/
@TableName(value ="history_top_100")
@Data
public class HistoryTop100 {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 排行
*/
private Integer no;
/**
* 球号
*/
private Integer ballNumber;
/**
* 点系数
*/
private Double pointCoefficient;
}

View File

@@ -0,0 +1,60 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
import lombok.Data;
/**
* 彩票开奖信息表
* @TableName lottery_draws
*/
@TableName(value ="lottery_draws")
@Data
public class LotteryDraws {
/**
* 开奖期号
*/
@TableId
private Long drawId;
/**
* 开奖日期
*/
private Date drawDate;
/**
* 红1
*/
private Integer redBall1;
/**
* 红2
*/
private Integer redBall2;
/**
* 红3
*/
private Integer redBall3;
/**
* 红4
*/
private Integer redBall4;
/**
* 红5
*/
private Integer redBall5;
/**
* 红6
*/
private Integer redBall6;
/**
* 蓝球
*/
private Integer blueBall;
}

View File

@@ -0,0 +1,56 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
import lombok.Data;
/**
* 操作历史记录表
* @TableName operation_history
*/
@TableName(value ="operation_history")
@Data
public class OperationHistory {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 操作用户ID
*/
private Long userId;
/**
* 操作类型(批量生成会员码/获取可用会员码/Excel导入等
*/
private String operationType;
/**
* 操作模块(会员码管理/Excel导入管理等
*/
private Integer operationModule;
/**
* 操作结果(成功/失败)
*/
private String operationResult;
/**
* 结果消息
*/
private String resultMessage;
/**
* 操作时间
*/
private Date operationTime;
/**
* 更新时间
*/
private Date updateTime;
}

View File

@@ -0,0 +1,96 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
import lombok.Data;
/**
* 彩票开奖信息表
* @TableName predict_record
*/
@TableName(value ="predict_record")
@Data
public class PredictRecord {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 开奖期号
*/
private Long drawId;
/**
* 开奖日期
*/
private Date drawDate;
/**
* 红1
*/
private Integer redBall1;
/**
* 红2
*/
private Integer redBall2;
/**
* 红3
*/
private Integer redBall3;
/**
* 红4
*/
private Integer redBall4;
/**
* 红5
*/
private Integer redBall5;
/**
* 红6
*/
private Integer redBall6;
/**
* 蓝球
*/
private Integer blueBall;
/**
* 预测状态(待开奖/已开奖)
*/
private String predictStatus;
/**
* 预测结果(未中奖/三等奖/二等奖/一等奖)
*/
private String predictResult;
/**
* 预测时间
*/
private Date predictTime;
/**
* 奖金
*/
private Long bonus;
/**
* 类型
*/
private String type;
}

View File

@@ -0,0 +1,35 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* t11表蓝球组红球的面系数
* @TableName t11
*/
@TableName(value ="t11")
@Data
public class T11 {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 主球
*/
private Integer masterBallNumber;
/**
* 从球
*/
private Integer slaveBallNumber;
/**
* 面系数
*/
private Double faceCoefficient;
}

View File

@@ -0,0 +1,35 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* t3表红球组红球的线系数
* @TableName t3
*/
@TableName(value ="t3")
@Data
public class T3 {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 主球
*/
private Integer masterBallNumber;
/**
* 从球
*/
private Integer slaveBallNumber;
/**
* 线系数
*/
private Double lineCoefficient;
}

View File

@@ -0,0 +1,35 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* t4表蓝球组红球的线系数
* @TableName t4
*/
@TableName(value ="t4")
@Data
public class T4 {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 主球
*/
private Integer masterBallNumber;
/**
* 从球
*/
private Integer slaveBallNumber;
/**
* 线系数
*/
private Double lineCoefficient;
}

View File

@@ -0,0 +1,35 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* t5表蓝球组蓝球的线系数
* @TableName t5
*/
@TableName(value ="t5")
@Data
public class T5 {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 主球
*/
private Integer masterBallNumber;
/**
* 从球
*/
private Integer slaveBallNumber;
/**
* 线系数
*/
private Double lineCoefficient;
}

View File

@@ -0,0 +1,35 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* t6表红球组蓝球的线系数
* @TableName t6
*/
@TableName(value ="t6")
@Data
public class T6 {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 主球
*/
private Integer masterBallNumber;
/**
* 从球
*/
private Integer slaveBallNumber;
/**
* 线系数
*/
private Double lineCoefficient;
}

View File

@@ -0,0 +1,35 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* t7表红球组红球的面系数
* @TableName t7
*/
@TableName(value ="t7")
@Data
public class T7 {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 主球
*/
private Integer masterBallNumber;
/**
* 从球
*/
private Integer slaveBallNumber;
/**
* 面系数
*/
private Double faceCoefficient;
}

View File

@@ -0,0 +1,35 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* t8表红球组蓝球的面系数
* @TableName t8
*/
@TableName(value ="t8")
@Data
public class T8 {
/**
* 唯一标识符
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 主球
*/
private Integer masterBallNumber;
/**
* 从球
*/
private Integer slaveBallNumber;
/**
* 面系数
*/
private Double faceCoefficient;
}

View File

@@ -0,0 +1,86 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
import lombok.Data;
/**
* 用户
* @TableName user
*/
@TableName(value ="user")
@Data
public class User {
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 用户昵称
*/
private String userName;
/**
* 账号
*/
private String userAccount;
/**
* 电话
*/
private String phone;
/**
* 用户头像
*/
private String userAvatar;
/**
* 性别
*/
private Integer gender;
/**
* 用户角色user / admin
*/
private String userRole;
/**
* 密码
*/
private String userPassword;
/**
* 是否会员0-非会员1-会员
*/
private Integer isVip;
/**
* 会员到期时间
*/
private Date vipExpire;
/**
* 状态0-正常1-封禁
*/
private Integer status;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 是否删除
*/
private Integer isDelete;
}

View File

@@ -0,0 +1,72 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
import lombok.Data;
/**
* 会员码表
* @TableName vip_code
*/
@TableName(value ="vip_code")
@Data
public class VipCode {
/**
* 主键
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 会员码
*/
private String code;
/**
* 会员有效月数(1/12)
*/
private Integer vipExpireTime;
/**
* 会员编号
*/
private Integer vipNumber;
/**
* 是否使用0-未使用1-已使用
*/
private Integer isUse;
/**
* 创建的用户id
*/
private Long createdUserId;
/**
* 创建的用户名称
*/
private String createdUserName;
/**
* 使用的用户id
*/
private Long usedUserId;
/**
* 使用的用户名称
*/
private String usedUserName;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}

View File

@@ -0,0 +1,61 @@
package com.xy.xyaicpzs.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
import lombok.Data;
/**
* 会员兑换表
* @TableName vip_exchange_record
*/
@TableName(value ="vip_exchange_record")
@Data
public class VipExchangeRecord {
/**
* 唯一标识符
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 月度会员/年度会员
*/
private String type;
/**
* 兑换方式
*/
private Integer exchangeMode;
/**
* 订单编号
*/
private Long orderNo;
/**
* 订单金额
*/
private Integer orderAmount;
/**
* 是否兑换(未兑换/已兑换)
*/
private Integer isUse;
/**
* 兑换时间
*/
private Date exchangeTime;
/**
* 更新时间
*/
private Date updateTime;
}

View File

@@ -0,0 +1,39 @@
package com.xy.xyaicpzs.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 球号组合分析结果VO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "球号组合分析结果")
public class BallCombinationAnalysisVO {
@Schema(description = "当前两个球的组合系数")
private Double faceCoefficient;
@Schema(description = "与主球组合系数最高的球号")
private Integer highestBall;
@Schema(description = "与主球组合系数最高的值")
private Double highestCoefficient;
@Schema(description = "与主球组合系数最低的球号")
private Integer lowestBall;
@Schema(description = "与主球组合系数最低的值")
private Double lowestCoefficient;
@Schema(description = "与主球组合的所有系数平均值")
private Double averageCoefficient;
@Schema(description = "最新开奖期号")
private Long latestDrawId;
}

View File

@@ -0,0 +1,27 @@
package com.xy.xyaicpzs.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 球号命中率统计VO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "球号命中率统计")
public class BallHitRateVO {
@Schema(description = "命中次数")
private Integer hitCount;
@Schema(description = "总次数")
private Integer totalCount;
@Schema(description = "命中率(百分比)")
private Double hitRate;
}

View File

@@ -0,0 +1,39 @@
package com.xy.xyaicpzs.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 球号持续性分析结果VO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "球号持续性分析结果")
public class BallPersistenceAnalysisVO {
@Schema(description = "当前两个球的组合线系数")
private Double lineCoefficient;
@Schema(description = "与主球组合线系数最高的球号")
private Integer highestBall;
@Schema(description = "与主球组合线系数最高的值")
private Double highestCoefficient;
@Schema(description = "与主球组合线系数最低的球号")
private Integer lowestBall;
@Schema(description = "与主球组合线系数最低的值")
private Double lowestCoefficient;
@Schema(description = "与主球组合的所有线系数平均值")
private Double averageCoefficient;
@Schema(description = "最新开奖期号")
private Long latestDrawId;
}

View File

@@ -0,0 +1,47 @@
package com.xy.xyaicpzs.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
/**
* 奖金估算VO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "奖金估算信息")
public class PrizeEstimateVO {
@Schema(description = "总奖金合计")
private BigDecimal totalPrize;
@Schema(description = "奖项明细")
private List<PrizeDetailItem> prizeDetails;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "奖项明细项")
public static class PrizeDetailItem {
@Schema(description = "中奖等级,例如:一等奖、二等奖等")
private String prizeLevel;
@Schema(description = "中奖注数")
private Integer winningCount;
@Schema(description = "单注奖金(元)")
private BigDecimal singlePrize;
@Schema(description = "该等级奖金小计(元)")
private BigDecimal subtotal;
}
}

View File

@@ -0,0 +1,27 @@
package com.xy.xyaicpzs.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 红球命中率统计VO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "红球命中率统计")
public class RedBallHitRateVO {
@Schema(description = "命中总红球数")
private Integer totalHitCount;
@Schema(description = "总预测红球数")
private Integer totalPredictedCount;
@Schema(description = "红球命中率(百分比)")
private Double hitRate;
}

View File

@@ -0,0 +1,46 @@
package com.xy.xyaicpzs.domain.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.math.BigDecimal;
/**
* 用户预测统计数据VO
*/
@Data
public class UserPredictStatVO {
/**
* 用户ID
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
/**
* 预测次数(总记录数)
*/
private Long predictCount;
/**
* 待开奖次数
*/
private Long pendingCount;
/**
* 命中次数
*/
private Long hitCount;
/**
* 命中率保留4位小数
*/
private BigDecimal hitRate;
/**
* 已开奖次数(总次数 - 待开奖次数)
*/
private Long drawnCount;
}

View File

@@ -0,0 +1,78 @@
package com.xy.xyaicpzs.domain.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 用户视图(脱敏)
*/
@Data
public class UserVO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/**
* 用户昵称
*/
private String userName;
/**
* 用户昵称
*/
private String userAccount;
/**
* 用户头像
*/
private String userAvatar;
/**
* 性别
*/
private Integer gender;
/**
* 用户角色user / admin
*/
private String userRole;
/**
* 用户角色user / admin
*/
private String phone;
/**
* 是否会员0-非会员1-会员
*/
private Integer isVip;
/**
* 会员到期时间
*/
private Date vipExpire;
/**
* 状态0-正常1-封禁
*/
private Integer status;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}

View File

@@ -0,0 +1,74 @@
package com.xy.xyaicpzs.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
/**
* 会员码视图对象
*/
@Data
@Schema(description = "会员码视图对象")
public class VipCodeVO {
/**
* 会员码
*/
@Schema(description = "会员码")
private String code;
/**
* 会员有效月数(1/12)
*/
@Schema(description = "会员有效月数")
private Integer vipExpireTime;
/**
* 会员编号6位数如100001
*/
@Schema(description = "会员编号6位数")
private Integer vipNumber;
/**
* 是否使用0-未使用1-已使用
*/
@Schema(description = "是否使用0-未使用1-已使用")
private Integer isUse;
/**
* 创建人ID
*/
@Schema(description = "创建人ID")
private Long createdUserId;
/**
* 创建人名称
*/
@Schema(description = "创建人名称")
private String createdUserName;
/**
* 使用人ID
*/
@Schema(description = "使用人ID")
private Long usedUserId;
/**
* 使用人名称
*/
@Schema(description = "使用人名称")
private String usedUserName;
/**
* 创建时间
*/
@Schema(description = "创建时间")
private Date createTime;
/**
* 创建时间
*/
@Schema(description = "更新时间")
private Date updateTime;
}

View File

@@ -0,0 +1,33 @@
package com.xy.xyaicpzs.exception;
import com.xy.xyaicpzs.common.ErrorCode;
/**
* 自定义异常类
*/
public class BusinessException extends RuntimeException {
/**
* 错误码
*/
private final int code;
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.code = errorCode.getCode();
}
public BusinessException(ErrorCode errorCode, String message) {
super(message);
this.code = errorCode.getCode();
}
public int getCode() {
return code;
}
}

View File

@@ -0,0 +1,28 @@
package com.xy.xyaicpzs.exception;
import com.xy.xyaicpzs.common.ErrorCode;
import com.xy.xyaicpzs.common.ResultUtils;
import com.xy.xyaicpzs.common.response.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器
*/
//@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ApiResponse<?> businessExceptionHandler(BusinessException e) {
log.error("businessException: " + e.getMessage(), e);
return ResultUtils.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(RuntimeException.class)
public ApiResponse<?> runtimeExceptionHandler(RuntimeException e) {
log.error("runtimeException", e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, e.getMessage());
}
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.BlueHistory100;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【blue_history_100(蓝球最近100期数据表)】的数据库操作Mapper
* @createDate 2025-06-14 10:40:04
* @Entity com.xy.xyaicpzs.domain.entity.BlueHistory100
*/
public interface BlueHistory100Mapper extends BaseMapper<BlueHistory100> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.BlueHistoryAll;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【blue_history_all(蓝球全部历史数据表)】的数据库操作Mapper
* @createDate 2025-06-14 10:40:07
* @Entity com.xy.xyaicpzs.domain.entity.BlueHistoryAll
*/
public interface BlueHistoryAllMapper extends BaseMapper<BlueHistoryAll> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.BlueHistoryTop100;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【blue_history_top_100(创建蓝球100期数据排行表)】的数据库操作Mapper
* @createDate 2025-06-14 10:40:13
* @Entity com.xy.xyaicpzs.domain.entity.BlueHistoryTop100
*/
public interface BlueHistoryTop100Mapper extends BaseMapper<BlueHistoryTop100> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.BlueHistoryTop;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【blue_history_top(蓝球历史数据排行表)】的数据库操作Mapper
* @createDate 2025-06-14 10:40:10
* @Entity com.xy.xyaicpzs.domain.entity.BlueHistoryTop
*/
public interface BlueHistoryTopMapper extends BaseMapper<BlueHistoryTop> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.ChatMessage;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【chat_message(聊天消息表)】的数据库操作Mapper
* @createDate 2025-07-07 17:37:15
* @Entity com.xy.xyaicpzs.domain.entity.ChatMessage
*/
public interface ChatMessageMapper extends BaseMapper<ChatMessage> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.History100;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【history_100(最近100期数据表)】的数据库操作Mapper
* @createDate 2025-06-14 09:48:05
* @Entity generator.domain.History100
*/
public interface History100Mapper extends BaseMapper<History100> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.HistoryAll;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【history_all(历史数据表)】的数据库操作Mapper
* @createDate 2025-06-14 09:48:10
* @Entity generator.domain.HistoryAll
*/
public interface HistoryAllMapper extends BaseMapper<HistoryAll> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.HistoryTop100;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【history_top_100(创建100期数据排行表)】的数据库操作Mapper
* @createDate 2025-06-14 09:48:16
* @Entity generator.domain.HistoryTop100
*/
public interface HistoryTop100Mapper extends BaseMapper<HistoryTop100> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.HistoryTop;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【history_top(历史数据排行表)】的数据库操作Mapper
* @createDate 2025-06-14 09:48:13
* @Entity generator.domain.HistoryTop
*/
public interface HistoryTopMapper extends BaseMapper<HistoryTop> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.LotteryDraws;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【lottery_draws(彩票开奖信息表)】的数据库操作Mapper
* @createDate 2025-06-14 16:41:29
* @Entity com.xy.xyaicpzs.domain.entity.LotteryDraws
*/
public interface LotteryDrawsMapper extends BaseMapper<LotteryDraws> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.OperationHistory;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【operation_history(操作历史记录表)】的数据库操作Mapper
* @createDate 2025-06-19 14:51:51
* @Entity com.xy.xyaicpzs.domain.entity.OperationHistory
*/
public interface OperationHistoryMapper extends BaseMapper<OperationHistory> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.PredictRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【predict_record(彩票开奖信息表)】的数据库操作Mapper
* @createDate 2025-06-16 13:17:53
* @Entity com.xy.xyaicpzs.domain.entity.PredictRecord
*/
public interface PredictRecordMapper extends BaseMapper<PredictRecord> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.T11;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【t11(t11表蓝球组红球的面系数)】的数据库操作Mapper
* @createDate 2025-06-14 16:25:23
* @Entity com.xy.xyaicpzs.domain.entity.T11
*/
public interface T11Mapper extends BaseMapper<T11> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.T3;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【t3(t3表红球组红球的线系数)】的数据库操作Mapper
* @createDate 2025-06-14 11:02:50
* @Entity com.xy.xyaicpzs.domain.entity.T3
*/
public interface T3Mapper extends BaseMapper<T3> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.T4;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【t4(t4表蓝球组红球的线系数)】的数据库操作Mapper
* @createDate 2025-06-14 11:45:31
* @Entity com.xy.xyaicpzs.domain.entity.T4
*/
public interface T4Mapper extends BaseMapper<T4> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.T5;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【t5(t5表蓝球组蓝球的线系数)】的数据库操作Mapper
* @createDate 2025-06-14 12:01:16
* @Entity com.xy.xyaicpzs.domain.entity.T5
*/
public interface T5Mapper extends BaseMapper<T5> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.T6;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【t6(t6表红球组蓝球的线系数)】的数据库操作Mapper
* @createDate 2025-06-14 13:19:12
* @Entity com.xy.xyaicpzs.domain.entity.T6
*/
public interface T6Mapper extends BaseMapper<T6> {
}

View File

@@ -0,0 +1,21 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.T7;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @author XY003
* @description 针对表【t7(t7表红球组红球的面系数)】的数据库操作Mapper
* @createDate 2025-06-14 13:30:50
* @Entity com.xy.xyaicpzs.domain.entity.T7
*/
public interface T7Mapper extends BaseMapper<T7> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.T8;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【t8(t8表红球组蓝球的面系数)】的数据库操作Mapper
* @createDate 2025-06-14 16:11:27
* @Entity com.xy.xyaicpzs.domain.entity.T8
*/
public interface T8Mapper extends BaseMapper<T8> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【user(用户)】的数据库操作Mapper
* @createDate 2025-06-15 18:29:28
* @Entity com.xy.xyaicpzs.domain.entity.User
*/
public interface UserMapper extends BaseMapper<User> {
}

View File

@@ -0,0 +1,14 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.VipCode;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【vip_code(会员码表)】的数据库操作Mapper
* @createDate 2025-01-15 16:41:29
* @Entity com.xy.xyaicpzs.domain.entity.VipCode
*/
public interface VipCodeMapper extends BaseMapper<VipCode> {
}

View File

@@ -0,0 +1,18 @@
package com.xy.xyaicpzs.mapper;
import com.xy.xyaicpzs.domain.entity.VipExchangeRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author XY003
* @description 针对表【vip_exchange_record(会员兑换表)】的数据库操作Mapper
* @createDate 2025-06-19 11:27:10
* @Entity com.xy.xyaicpzs.domain.entity.VipExchangeRecord
*/
public interface VipExchangeRecordMapper extends BaseMapper<VipExchangeRecord> {
}

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

Some files were not shown because too many files have changed in this diff Show More