zset 可以排序 使用xxl-job实现定时任务 对历史排行榜持久化到数据库
排行榜有当前赛季排行版和历史排行榜
当前赛季排行榜利用redis 中的SortSet 数据结构 获取
每个月的 月初 利用xxl-job的定时任务持久化化上一个月的排行榜信息 并删除redis中的数据
当排行榜数据量巨大时可以 通过对每一个赛季的历史排行榜水平分表 减小单表的数据量和压力
查询历史排行榜 通过持久化的表查询
代码
cotroller
package com.orchids.ranklist.web.controller;
import com.orchids.ranklist.web.domain.query.BoardQuery;
import com.orchids.ranklist.web.domain.vo.PointsBoardVO;
import com.orchids.ranklist.web.service.IPointsBoardService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @ Author qwh
* @ Date 2024/7/6 14:56
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/rank")
@Api(tags = "积分积分排行榜")
public class PointBoardController {
private final IPointsBoardService pointsBoardService;
/**
* 查询指定赛季的排行榜
* @param query
* @return
*/
@GetMapping("boards")
@ApiOperation("分页查询指定赛季的排行榜")
public PointsBoardVO queryPointsBoardBySeasonId(BoardQuery query){
return pointsBoardService.queryPointsBoardBySeasonId(query);
}
}
service
Iservice
package com.orchids.ranklist.web.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.orchids.ranklist.web.domain.po.PointsBoard;
import com.orchids.ranklist.web.domain.query.BoardQuery;
import com.orchids.ranklist.web.domain.vo.PointsBoardVO;
import java.util.List;
/**
* @ Author qwh
* @ Date 2024/7/6 18:55
*/
public interface IPointsBoardService extends IService<PointsBoard> {
PointsBoardVO queryPointsBoardBySeasonId(BoardQuery query);
/**
* 持久化上个月的排行榜信息之前需要创建的表
* @param season
*/
void createPointsBoardTableBySeason(Integer season);
}
servieImpl
package com.orchids.ranklist.web.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.orchids.ranklist.web.domain.po.PointsBoard;
import com.orchids.ranklist.web.domain.query.BoardQuery;
import com.orchids.ranklist.web.domain.vo.PointsBoardVO;
import com.orchids.ranklist.web.mapper.PointsBoardMapper;
import com.orchids.ranklist.web.service.IPointsBoardService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.orchids.ranklist.web.utils.TableNameContext;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* @author nullpointer
* @since 2024-07-06
*/
@Service
@RequiredArgsConstructor
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {
private final PointsBoardMapper pointsBoardMapper;
private final StringRedisTemplate redisTemplate;
// 排行榜
// 位次 id score
// 3 myID myScore
// 1 otherID otherScore
// 2 otherID otherScore
// 3 myID myScore
@Override
public PointsBoardVO queryPointsBoardBySeasonId(BoardQuery query) {
Long seasonId = query.getSeasonId();
//判断是否是当前赛季 seasonId 为 null || 0 就是当前赛季
boolean isCurrent = seasonId == null || seasonId == 0;
//是当前赛季从redis 获取每一个人的积分 积分由zset封装 userId1 score1
//拼接整个榜单的 key point:rank:board:2024:07 userId2 score2 ...
LocalDate localDate = LocalDate.now();
String format = localDate.format(DateTimeFormatter.ofPattern("yyyy:MM"));
String key = "point:rank:board:" + format;
//查询我的积分和排名
PointsBoard pointsBoard = isCurrent ?
//当前赛季查询redis
queryMyCurrentBoard(key):
//历史赛季查数据库 因为每个月初会定时把上个月的排行信息从redis中持久化到数据库中每个月的排行表中
queryMyCountHistoryBoard(seasonId);
//查询整个积分排行榜信息
List<PointsBoard> pointsBoards = isCurrent ?
queryCurrentBoardList(key,query.getPageNo(),query.getPageSize()) :
queryCountHistoryBoardList(query);
//封装排行榜信息
PointsBoardVO result = new PointsBoardVO();
if (pointsBoard!=null){
result.setRank(pointsBoard.getRank());
result.setPoints(pointsBoard.getPoints());
}
if (CollectionUtils.isEmpty(pointsBoards)){
return result;
}
//获取其他人的ID 榜单可以添加用户名信息 通过ID查询 例如
// todo List<Long> UserIds = pointsBoards.stream().map(PointsBoard::getUserId).collect(Collectors.toList());
//封装排行列表
result.setBoardList(pointsBoards);
return result;
}
/**
* 查询当前赛季积分排行榜
* @param key
* @return
*/
private PointsBoard queryMyCurrentBoard(String key) {
//当月排行信息从redis查
//获取redis操作对象
BoundZSetOperations<String, String> ops = redisTemplate.boundZSetOps(key);
//获取当前用户的 积分排行信息
//获取当前用户的UserId 可以从请求头或者token中获取 假设为
Long userId = 13666666L;
Double score = ops.score(userId.toString());
//获取我的排行信息
Long rank = ops.reverseRank(userId.toString());
//封装我的排行版信息
PointsBoard board = new PointsBoard();
board.setRank(rank.intValue()+1);
board.setUserId(userId);
board.setPoints(score.intValue());
//返回PointBoard
return board;
}
/**
* 查询我的历史赛季积分排行信息
* @param seasonId
* @return
*/
private PointsBoard queryMyCountHistoryBoard(Long seasonId) {
//todo 从数据库中查询历史排行表中的排行信息
//获取Id
//获取当前用户的UserId 可以从请求头或者token中获取 假设为
Long userId = 13666666L;
//拼接表名
TableNameContext.setInfo("points_board_"+seasonId);
//因为mybatis动态表名插件在执行查询和修改操作会 从TableNameContext中获取表名
PointsBoard board = lambdaQuery().eq(PointsBoard::getUserId, userId).one();
board.setRank(board.getId().intValue());
TableNameContext.remove();
return board;
}
/**
* 查询当前赛季积分排行榜
* @param key
* @return
*/
private List<PointsBoard> queryCurrentBoardList(String key, Integer pageNo, Integer pageSize) {
//计算分页信息
int from = (pageNo - 1) * pageSize;
int end = from + pageSize + 1;
//从redis中查询
Set<ZSetOperations.TypedTuple<String>> tuples = redisTemplate.opsForZSet().reverseRangeWithScores(key, from, end);
//判断是否为空
if (CollectionUtils.isEmpty(tuples)) {
return new ArrayList<>();
}
//封装排行榜信息
int rank = from + 1;
List<PointsBoard> result = new LinkedList<>();
for (ZSetOperations.TypedTuple<String> tuple : tuples) {
String userId = tuple.getValue(); //用户Id
Double score = tuple.getScore(); //用户积分
if (userId==null||score==null){
continue;
}
PointsBoard board = new PointsBoard();
board.setRank(rank++);
board.setUserId(Long.valueOf(userId));
board.setPoints(score.intValue());
result.add(board);
}
return result;
}
/**
* 查询历史赛季积分排行榜
* @param query
* @return
*/
private List<PointsBoard> queryCountHistoryBoardList(BoardQuery query) {
//todo 后序查询数据库
//获取赛季Id
Long seasonId = query.getSeasonId();
//拼接查询的表
TableNameContext.setInfo("points_board_"+seasonId);
Page<PointsBoard> ipage = new Page<>(query.getPageNo(), query.getPageSize());
Page<PointsBoard> page = pointsBoardMapper.selectPage(ipage, null);
List<PointsBoard> boardList = page.getRecords();
//这里可以 获取用户ID 查询用户信息 修改显示内容
TableNameContext.remove();
return boardList;
}
@Override
public void createPointsBoardTableBySeason(Integer season) {
// 第七赛季的排行榜 表实例 points_board_7
pointsBoardMapper.createPointsBoardTable("points_board_" + season);
}
}
定时任务类
package com.orchids.ranklist.web.handler;
/**
* @ Author qwh
* @ Date 2024/7/6 20:41
*/
import com.orchids.ranklist.web.domain.po.PointsBoard;
import com.orchids.ranklist.web.service.IPointsBoardSeasonService;
import com.orchids.ranklist.web.service.IPointsBoardService;
import com.orchids.ranklist.web.utils.TableNameContext;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
//定时任务 每月初定时创建榜单表 将redis中的数据持久化到数据库
@Slf4j
@Component
@RequiredArgsConstructor
public class PointsBoardPersistentHandler {
//查询赛季信息
private final IPointsBoardSeasonService seasonService;
//创建赛季表
private final IPointsBoardService pointsBoardService;
//删除redis中的上个月榜单
private final StringRedisTemplate redisTemplate;
@XxlJob("xxl_job_time_test")
public void createLocalTime(){
log.debug("xl_job_demo执行器正在执行现在时间是{}",LocalDateTime.now());
System.out.println("xl_job_demo执行器正在执行现在时间是"+LocalDateTime.now());
}
// 每月1号,凌晨3点执行
//todo 添加定时或者 XXL_JOB
@XxlJob("xxl_job_points_board_create_table")
public void createPointBoardTableOfSeason(){
//上个月的凌晨三点
LocalDateTime time = LocalDateTime.now().minusMonths(1);
//查询赛季表获取赛季Id
Integer season = seasonService.querySeasonByTime(time);
if (season == null){
return;
}
//将表名保存到ThreadLocal
TableNameContext.setInfo("points_board_" + season);
//创建对应的表
pointsBoardService.createPointsBoardTableBySeason(season);
}
/**
* 持久化排行榜到数据库
*/
//todo 添加定时或者 XXL_JOB
@XxlJob("xxl_job_points_board_save_mysql")
private void savePointsBoardToDb() {
//拼接redis key
LocalDateTime time = LocalDateTime.now().minusMonths(1);
//计算动态表名
Integer season = seasonService.querySeasonByTime(time);
TableNameContext.setInfo("points_board_"+season);
//拼接key
LocalDateTime now = LocalDateTime.now();
String format = now.format(DateTimeFormatter.ofPattern("yyyy:MM"));
// point:rank:board:2024:06
String key = "point:rank:board:" + format;
//获取redis中的数据
// todo 可以利用分片xxl_job
int pageNo =1;
int pageSize = 1000;
System.out.println(key);
while (true){
List<PointsBoard> pointsBoards = queryCurrentBoardList(key, pageNo, pageSize);
if (CollectionUtils.isEmpty(pointsBoards)){
//当没有数据了跳过循环
break;
}
//持久化到数据库
pointsBoardService.saveBatch(pointsBoards);
System.out.println("持久化成功");
pageNo++;
}
//任务结束 移除表名
TableNameContext.remove();
}
//删除redis中的数据
//todo 添加定时或者 XXL_JOB
@XxlJob("xxl_job_points_board_remove_redis")
public void clearPointsBoardFromRedis(){
//获取上个月的时间
LocalDateTime time = LocalDateTime.now().minusMonths(1);
//拼接key
String key = "point:rank:board:" + time.format(DateTimeFormatter.ofPattern("yyyy:MM"));
//删除上个月的redis缓存数据
redisTemplate.unlink(key);
}
public List<PointsBoard> queryCurrentBoardList(String key, Integer pageNo, Integer pageSize) {
//计算分页信息
int from = (pageNo - 1) * pageSize;
int end = from + pageSize + 1;
//从redis中查询
Set<ZSetOperations.TypedTuple<String>> tuples = redisTemplate.opsForZSet().reverseRangeWithScores(key, from, end);
//判断是否为空
if (CollectionUtils.isEmpty(tuples)) {
return new ArrayList<>();
}
//封装排行榜信息
int rank = from + 1;
List<PointsBoard> result = new LinkedList<>();
for (ZSetOperations.TypedTuple<String> tuple : tuples) {
String userId = tuple.getValue(); //用户Id
Double score = tuple.getScore(); //用户积分
if (userId==null||score==null){
continue;
}
PointsBoard board = new PointsBoard();
board.setId(Long.valueOf(rank++));
board.setUserId(Long.valueOf(userId));
board.setPoints(score.intValue());
result.add(board);
}
return result;
}
}
配置类
package com.orchids.ranklist.web.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.orchids.ranklist.web.utils.TableNameContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @ Author qwh
* @ Date 2024/7/6 14:49
*/
@Configuration
public class MybatisPlusConfiguration {
/**
* 添加动态表明插件
*/
@Bean
public DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {
// 准备一个Map,用于存储TableNameHandler
Map<String, TableNameHandler> map = new HashMap<>(1);
// 存入一个TableNameHandler,用来替换points_board表名称
// 替换方式,就是从TableInfoContext中读取保存好的动态表名
map.put("points_board", (sql, tableName) -> TableNameContext.getInfo() == null ? tableName : TableNameContext.getInfo());
return new DynamicTableNameInnerInterceptor(map);
}
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(@Autowired(required = false) DynamicTableNameInnerInterceptor nameInnerInterceptor) {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//表名替换插件 //todo !!!!!!!!!!!!!!!!!!!!!注意注意(っ °Д °;)っ
interceptor.addInnerInterceptor(nameInnerInterceptor);
//f分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
// 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
return interceptor;
}
}
xxl配置类
package com.orchids.ranklist.web.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @ Author qwh
* @ Date 2024/7/7 9:28
*/
@Slf4j
@Configuration
@EnableConfigurationProperties(XxlJobProperties.class)
public class XxlJobConfig {
/**
* 配置XxlJobSpringExecutor Bean,用于初始化XxlJob的执行器。
* @param prop XxlJobProperties实例,包含XxlJob的配置信息。
* @return 初始化后的XxlJobSpringExecutor实例。
*/
@Bean
public XxlJobSpringExecutor xxlJobExecutor(XxlJobProperties prop) {
// 初始化日志,表示XxlJob配置开始初始化
log.info(">>>>>>>>>>> xxl-job config init.");
// 创建XxlJobSpringExecutor实例
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
// 配置管理员地址
XxlJobProperties.Admin admin = prop.getAdmin();
if (admin != null && admin.getAddress()!=null) {
xxlJobSpringExecutor.setAdminAddresses(admin.getAddress());
}
// 配置执行器信息
XxlJobProperties.Executor executor = prop.getExecutor();
if (executor != null) {
// 配置执行器名称
if (executor.getAppName() != null)
xxlJobSpringExecutor.setAppname(executor.getAppName());
// 配置执行器IP
if (executor.getIp() != null)
xxlJobSpringExecutor.setIp(executor.getIp());
// 配置执行器端口
if (executor.getPort() != null)
xxlJobSpringExecutor.setPort(executor.getPort());
// 配置日志路径
if (executor.getLogPath() != null)
xxlJobSpringExecutor.setLogPath(executor.getLogPath());
// 配置日志保留天数
if (executor.getLogRetentionDays() != null)
xxlJobSpringExecutor.setLogRetentionDays(executor.getLogRetentionDays());
}
// 配置访问令牌
if (prop.getAccessToken() != null)
xxlJobSpringExecutor.setAccessToken(prop.getAccessToken());
// 初始化日志,表示XxlJob配置结束
log.info(">>>>>>>>>>> xxl-job config end.");
// 返回初始化后的执行器实例
return xxlJobSpringExecutor;
}
}
package com.orchids.ranklist.web.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import lombok.Data;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @ Author qwh
* @ Date 2024/7/7 9:28
*/
@Data
//排除没有加入xxl_job的服务避免无法生成bean导致启动失败
@ConditionalOnClass(XxlJobSpringExecutor.class)
@ConfigurationProperties(prefix = "xxl-job")
public class XxlJobProperties {
private String accessToken;
private Admin admin;
private Executor executor;
@Data
public static class Admin {
private String address;
}
@Data
public static class Executor {
private String appName;
private String address;
private String ip;
private Integer port;
private String logPath;
private Integer logRetentionDays;
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--接口的地址com开始到接口名UserMapper-->
<mapper namespace="com.orchids.ranklist.web.mapper.PointsBoardMapper">
<!--sql语句-->
<insert id="createPointsBoardTable">
CREATE TABLE `${tableName}`
(
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '榜单id',
`user_id` BIGINT NOT NULL COMMENT '学生id',
`points` INT NOT NULL COMMENT '积分值',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_user_id` (`user_id`) USING BTREE
)
COMMENT ='学霸天梯榜'
COLLATE = 'utf8mb4_0900_ai_ci'
ENGINE = InnoDB
ROW_FORMAT = DYNAMIC
</insert>
</mapper>
# 应用服务 WEB 访问端口
server:
port: 8081
spring:
application:
name: rank-list
# knife4j 额外配置
mvc:
pathmatch:
matching-strategy: ant_path_matcher
# Redis 配置
redis:
port: 6379
host: localhost
password: 6379
# 数据库 配置
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://localhost:3306/tianji_redis?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2b8
username: root
password: 123123
hikari:
connection-test-query: SELECT 1 # 自动检测连接
connection-timeout: 60000 #数据库连接超时时间,默认30秒
idle-timeout: 500000 #空闲连接存活最大时间,默认600000(10分钟)
max-lifetime: 540000 #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
maximum-pool-size: 12 #连接池最大连接数,默认是10
minimum-idle: 10 #最小空闲连接数量
pool-name: SPHHikariPool # 连接池名称
# Mybatis-plus 配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
xxl-job:
# 访问令牌 不能乱改 要和jar包中的一致
access-token: default_token
admin:
address: http://localhost:8080/xxl-job-admin
executor:
appname: rank-list
# 日志保存时间
log-retention-days: 10
# 日志地址
logPath: rank-list
#自动获取
# port: 9999
#ip会自动获取
# ip: localhost
其他的类
PointsBoard
package com.orchids.ranklist.web.domain.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
* 学霸天梯榜
* </p>
*
* @author nullpointer
* @since 2024-07-06
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
//@TableName("points_board") //每月初 持久化一个赛季的排行信息 到数据库 表为 points_board_seasonId
public class PointsBoard implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 榜单id
*/
@TableId(value = "id")
private Long id;
/**
* 学生id
*/
@TableField("user_id")
private Long userId;
/**
* 积分值
*/
@TableField("points")
private Integer points;
/**
* 名次,只记录赛季前100
*/
@TableField(exist = false)
private Integer rank;
/**
* 赛季,例如 1,就是第一赛季,2-就是第二赛季
*/
@TableField(exist = false)
private Integer season;
}
PointsBoardSeason
package com.orchids.ranklist.web.domain.po;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import java.time.LocalDate;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
*
* </p>
*
* @author nullpointer
* @since 2024-07-06
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("points_board_season")
public class PointsBoardSeason implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 自增长id,season标示
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 赛季名称,例如:第1赛季
*/
private String name;
/**
* 赛季开始时间
*/
private LocalDate beginTime;
/**
* 赛季结束时间
*/
private LocalDate endTime;
}
package com.orchids.ranklist.web.domain.query;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
/**
* @ Author qwh
* @ Date 2024/7/6 19:53
*/
@Data
@ApiModel(description = "分页请求参数")
@Accessors(chain = true)
public class BoardQuery {
public static final Integer DEFAULT_PAGE_SIZE = 20;
public static final Integer DEFAULT_PAGE_NUM =1;
@ApiModelProperty(value = "页码", example = "1")
@Min(value = 1, message = "页码不能小于1")
private Integer pageNo = DEFAULT_PAGE_NUM;
@ApiModelProperty(value = "每页大小", example = "5")
@Min(value = 1, message = "每页查询数量不能小于1")
private Integer pageSize = DEFAULT_PAGE_SIZE;
@ApiModelProperty(value = "赛季id,为null或者0则代表查询当前赛季")
private Long seasonId;
}
package com.orchids.ranklist.web.domain.vo;
import com.orchids.ranklist.web.domain.po.PointsBoard;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* @ Author qwh
* @ Date 2024/7/6 19:01
*/
@Data
@ApiModel(description = "积分榜单汇总信息")
public class PointsBoardVO {
@ApiModelProperty("我的榜单排名")
private Integer rank;
@ApiModelProperty("我的积分值")
private Integer points;
@ApiModelProperty("前100名上榜人信息")
private List<PointsBoard> boardList;
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.orchids</groupId>
<artifactId>rank-list</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rank-list</name>
<description>rank-list</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.6.13</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.xuxueli/xxl-job-core -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.4.1</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.orchids.ranklist.RankListApplication</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
测试
当前赛季
历史赛季