彩票助手后端1.0
This commit is contained in:
42
src/main/java/com/xy/xyaicpzs/Main.java
Normal file
42
src/main/java/com/xy/xyaicpzs/Main.java
Normal 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);
|
||||
}
|
||||
}
|
||||
54
src/main/java/com/xy/xyaicpzs/Sample.java
Normal file
54
src/main/java/com/xy/xyaicpzs/Sample.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/main/java/com/xy/xyaicpzs/XyAiCpzsApplication.java
Normal file
17
src/main/java/com/xy/xyaicpzs/XyAiCpzsApplication.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
19
src/main/java/com/xy/xyaicpzs/common/DeleteRequest.java
Normal file
19
src/main/java/com/xy/xyaicpzs/common/DeleteRequest.java
Normal 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;
|
||||
}
|
||||
39
src/main/java/com/xy/xyaicpzs/common/ErrorCode.java
Normal file
39
src/main/java/com/xy/xyaicpzs/common/ErrorCode.java
Normal 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;
|
||||
}
|
||||
}
|
||||
20
src/main/java/com/xy/xyaicpzs/common/PageRequest.java
Normal file
20
src/main/java/com/xy/xyaicpzs/common/PageRequest.java
Normal 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;
|
||||
}
|
||||
51
src/main/java/com/xy/xyaicpzs/common/ResultUtils.java
Normal file
51
src/main/java/com/xy/xyaicpzs/common/ResultUtils.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.xy.xyaicpzs.common.requset;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 生成会员码请求
|
||||
*/
|
||||
@Data
|
||||
public class GenerateVipCodesRequest {
|
||||
|
||||
/**
|
||||
* 生成数量
|
||||
*/
|
||||
private Integer numCodes;
|
||||
|
||||
/**
|
||||
* 会员有效月数
|
||||
*/
|
||||
private Integer vipExpireTime;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.xy.xyaicpzs.common.requset;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 会员码激活请求
|
||||
*/
|
||||
@Data
|
||||
public class VipCodeActivateRequest {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 会员码
|
||||
*/
|
||||
private String code;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
27
src/main/java/com/xy/xyaicpzs/config/CorsConfig.java
Normal file
27
src/main/java/com/xy/xyaicpzs/config/CorsConfig.java
Normal 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);
|
||||
}
|
||||
}
|
||||
31
src/main/java/com/xy/xyaicpzs/config/MyBatisPlusConfig.java
Normal file
31
src/main/java/com/xy/xyaicpzs/config/MyBatisPlusConfig.java
Normal 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;
|
||||
}
|
||||
}
|
||||
22
src/main/java/com/xy/xyaicpzs/config/ObjectMapperConfig.java
Normal file
22
src/main/java/com/xy/xyaicpzs/config/ObjectMapperConfig.java
Normal 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();
|
||||
}
|
||||
}
|
||||
38
src/main/java/com/xy/xyaicpzs/config/RedisConfig.java
Normal file
38
src/main/java/com/xy/xyaicpzs/config/RedisConfig.java
Normal 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;
|
||||
}
|
||||
}
|
||||
22
src/main/java/com/xy/xyaicpzs/config/RestTemplateConfig.java
Normal file
22
src/main/java/com/xy/xyaicpzs/config/RestTemplateConfig.java
Normal 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();
|
||||
}
|
||||
}
|
||||
12
src/main/java/com/xy/xyaicpzs/config/ScheduleConfig.java
Normal file
12
src/main/java/com/xy/xyaicpzs/config/ScheduleConfig.java
Normal 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 {
|
||||
}
|
||||
36
src/main/java/com/xy/xyaicpzs/constant/UserConstant.java
Normal file
36
src/main/java/com/xy/xyaicpzs/constant/UserConstant.java
Normal 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
|
||||
}
|
||||
1805
src/main/java/com/xy/xyaicpzs/controller/BallAnalysisController.java
Normal file
1805
src/main/java/com/xy/xyaicpzs/controller/BallAnalysisController.java
Normal file
File diff suppressed because it is too large
Load Diff
126
src/main/java/com/xy/xyaicpzs/controller/ChatController.java
Normal file
126
src/main/java/com/xy/xyaicpzs/controller/ChatController.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
108
src/main/java/com/xy/xyaicpzs/controller/JwtController.java
Normal file
108
src/main/java/com/xy/xyaicpzs/controller/JwtController.java
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/main/java/com/xy/xyaicpzs/controller/SmsController.java
Normal file
48
src/main/java/com/xy/xyaicpzs/controller/SmsController.java
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
578
src/main/java/com/xy/xyaicpzs/controller/UserController.java
Normal file
578
src/main/java/com/xy/xyaicpzs/controller/UserController.java
Normal 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
|
||||
}
|
||||
298
src/main/java/com/xy/xyaicpzs/controller/VipCodeController.java
Normal file
298
src/main/java/com/xy/xyaicpzs/controller/VipCodeController.java
Normal 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, "获取会员码统计数量失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
56
src/main/java/com/xy/xyaicpzs/domain/entity/ChatMessage.java
Normal file
56
src/main/java/com/xy/xyaicpzs/domain/entity/ChatMessage.java
Normal 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;
|
||||
}
|
||||
50
src/main/java/com/xy/xyaicpzs/domain/entity/History100.java
Normal file
50
src/main/java/com/xy/xyaicpzs/domain/entity/History100.java
Normal 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;
|
||||
}
|
||||
55
src/main/java/com/xy/xyaicpzs/domain/entity/HistoryAll.java
Normal file
55
src/main/java/com/xy/xyaicpzs/domain/entity/HistoryAll.java
Normal 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;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/HistoryTop.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/HistoryTop.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 历史数据排行表
|
||||
* @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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/T11.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/T11.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/T3.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/T3.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/T4.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/T4.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/T5.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/T5.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/T6.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/T6.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/T7.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/T7.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
35
src/main/java/com/xy/xyaicpzs/domain/entity/T8.java
Normal file
35
src/main/java/com/xy/xyaicpzs/domain/entity/T8.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.xy.xyaicpzs.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
86
src/main/java/com/xy/xyaicpzs/domain/entity/User.java
Normal file
86
src/main/java/com/xy/xyaicpzs/domain/entity/User.java
Normal 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;
|
||||
}
|
||||
72
src/main/java/com/xy/xyaicpzs/domain/entity/VipCode.java
Normal file
72
src/main/java/com/xy/xyaicpzs/domain/entity/VipCode.java
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
27
src/main/java/com/xy/xyaicpzs/domain/vo/BallHitRateVO.java
Normal file
27
src/main/java/com/xy/xyaicpzs/domain/vo/BallHitRateVO.java
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
47
src/main/java/com/xy/xyaicpzs/domain/vo/PrizeEstimateVO.java
Normal file
47
src/main/java/com/xy/xyaicpzs/domain/vo/PrizeEstimateVO.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
78
src/main/java/com/xy/xyaicpzs/domain/vo/UserVO.java
Normal file
78
src/main/java/com/xy/xyaicpzs/domain/vo/UserVO.java
Normal 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;
|
||||
}
|
||||
74
src/main/java/com/xy/xyaicpzs/domain/vo/VipCodeVO.java
Normal file
74
src/main/java/com/xy/xyaicpzs/domain/vo/VipCodeVO.java
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
18
src/main/java/com/xy/xyaicpzs/mapper/ChatMessageMapper.java
Normal file
18
src/main/java/com/xy/xyaicpzs/mapper/ChatMessageMapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
18
src/main/java/com/xy/xyaicpzs/mapper/History100Mapper.java
Normal file
18
src/main/java/com/xy/xyaicpzs/mapper/History100Mapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
18
src/main/java/com/xy/xyaicpzs/mapper/HistoryAllMapper.java
Normal file
18
src/main/java/com/xy/xyaicpzs/mapper/HistoryAllMapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
18
src/main/java/com/xy/xyaicpzs/mapper/HistoryTopMapper.java
Normal file
18
src/main/java/com/xy/xyaicpzs/mapper/HistoryTopMapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
18
src/main/java/com/xy/xyaicpzs/mapper/LotteryDrawsMapper.java
Normal file
18
src/main/java/com/xy/xyaicpzs/mapper/LotteryDrawsMapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
18
src/main/java/com/xy/xyaicpzs/mapper/T11Mapper.java
Normal file
18
src/main/java/com/xy/xyaicpzs/mapper/T11Mapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
18
src/main/java/com/xy/xyaicpzs/mapper/T3Mapper.java
Normal file
18
src/main/java/com/xy/xyaicpzs/mapper/T3Mapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
18
src/main/java/com/xy/xyaicpzs/mapper/T4Mapper.java
Normal file
18
src/main/java/com/xy/xyaicpzs/mapper/T4Mapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
18
src/main/java/com/xy/xyaicpzs/mapper/T5Mapper.java
Normal file
18
src/main/java/com/xy/xyaicpzs/mapper/T5Mapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
18
src/main/java/com/xy/xyaicpzs/mapper/T6Mapper.java
Normal file
18
src/main/java/com/xy/xyaicpzs/mapper/T6Mapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
21
src/main/java/com/xy/xyaicpzs/mapper/T7Mapper.java
Normal file
21
src/main/java/com/xy/xyaicpzs/mapper/T7Mapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
18
src/main/java/com/xy/xyaicpzs/mapper/T8Mapper.java
Normal file
18
src/main/java/com/xy/xyaicpzs/mapper/T8Mapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
18
src/main/java/com/xy/xyaicpzs/mapper/UserMapper.java
Normal file
18
src/main/java/com/xy/xyaicpzs/mapper/UserMapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
14
src/main/java/com/xy/xyaicpzs/mapper/VipCodeMapper.java
Normal file
14
src/main/java/com/xy/xyaicpzs/mapper/VipCodeMapper.java
Normal 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> {
|
||||
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
3154
src/main/java/com/xy/xyaicpzs/service/BallAnalysisService.java
Normal file
3154
src/main/java/com/xy/xyaicpzs/service/BallAnalysisService.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
||||
package com.xy.xyaicpzs.service;
|
||||
|
||||
import com.xy.xyaicpzs.domain.entity.BlueHistory100;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author XY003
|
||||
* @description 针对表【blue_history_100(蓝球最近100期数据表)】的数据库操作Service
|
||||
* @createDate 2025-06-14 10:40:04
|
||||
*/
|
||||
public interface BlueHistory100Service extends IService<BlueHistory100> {
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user