Redis ZSet数据结构实现排行榜功能

1 篇文章 0 订阅
1 篇文章 0 订阅

Redis ZSet数据结构实现排行榜功能

一. 使用场景

公司新项目要求, 实现每日排行榜以及各省排行榜
在这里插入图片描述
在这里插入图片描述

二. 功能实现(Java)

1. 排行榜数据插入及更新

	/**
     * @date: 2022/11/2
     * @description: 每天凌晨6点生成排行榜数据
     */
    @Override
    @Scheduled(cron = "0 0 6 * * ?")
    public void generate() {
        try {
            // 今日排行榜
            String dailyKey = today + "_PERSON_RANK";
            String today = LocalDate.now().format(DateTimeFormatter.ISO_DATE);
            // 随机获取表里的机器人信息
            Result<ArrayList<UserPlainVO>> userIdResults = userManager.getRankUserIds();
            ArrayList<UserPlainVO> userPlainVOS = userIdResults.getResults();
            if (Objects.equals("200",userIdResults.getCode()) && CollectionUtils.isNotEmpty(userPlainVOS)) {
                for (UserPlainVO userPlainVO : userPlainVOS) {
               		// 作答时间为250到750的随机数
                    int usedTime = getRandomNum(250,750);
               		redisTemplate.opsForZSet().add(dailyKey,userPlainVO.getUserId(),usedTime);
                }
            }
            // 设置次日凌晨6点key失效
            String nextDayMorning = LocalDate.now().plusDays(1).format(DateTimeFormatter.ISO_DATE) + " 06:00:00";
            Date nextDay = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(nextDayMorning);
            redisTemplate.expireAt(dailyKey,nextDay);

            // 获取全部省份名称,初始值为5K-2W的随机数,每日累加3K-5K的随机数
            String provinceKey = "PROVINCE_RANK";
            List<String> provinces = sysBasService.getRankProvinces(); // 从字典表获取
            for (String province : provinces) {
                // 判断该成员省是否已存入缓存中
                Double score = redisTemplate.opsForZSet().score(provinceKey, province);
                if (Objects.isNull(score)) {
                    // 不在赋予初始值(1W-2W)
                    int scoreFirst = getRandomNum(10000,20000);
                    redisTemplate.opsForZSet().add(provinceKey,province,scoreFirst);
                } else {
                    // 存在则累加
                    int scoreAdd = getRandomNum(3000,5000);
                    redisTemplate.opsForZSet().incrementScore(provinceKey,province,scoreAdd);
                }
            }

        } catch (ParseException e) {
            throw new RuntimeException(e);
        }

    }

	/**
     * @date: 2022/11/8
     * @description: 获取范围内的随机整数
     * @param min:
     * @param max:
     */
    public static int getRandomNum(int min, int max) {
        return (int) (Math.random() * (max - min + 1) + min);
    }

2. 排行榜数据获取

2.1 创建排行榜数据接收实体

import io.swagger.annotations.ApiModelProperty;

import java.io.Serializable;
import java.util.List;

/**
 * @author xiazz
 * @Date 2022/10/14
 * @Description:
 */
public class RankInfoVO implements Serializable {
    @ApiModelProperty("用户排名: -1表示未上榜")
    private Long userRank;
    @ApiModelProperty("用户通关总耗时: -1表示未通关")
    private Integer userSumTime;
    @ApiModelProperty("用户所在地区排名: -1表示未上榜")
    private Long userAreaRank;
    @ApiModelProperty("今日排行榜列表")
    private List<DailyRankVO> dailyRankVOS;
    @ApiModelProperty("省份排行榜列表")
    private List<ProvinceRankVO> provinceRankVOS;
    @ApiModelProperty("用户名")
    private String currentUsername;
    @ApiModelProperty("用户头像")
    private String currentImgUrl;

    public Long getUserRank() {
        return userRank;
    }

    public void setUserRank(Long userRank) {
        this.userRank = userRank;
    }

    public Integer getUserSumTime() {
        return userSumTime;
    }

    public void setUserSumTime(Integer userSumTime) {
        this.userSumTime = userSumTime;
    }

    public Long getUserAreaRank() {
        return userAreaRank;
    }

    public void setUserAreaRank(Long userAreaRank) {
        this.userAreaRank = userAreaRank;
    }

    public List<DailyRankVO> getDailyRankVOS() {
        return dailyRankVOS;
    }

    public void setDailyRankVOS(List<DailyRankVO> dailyRankVOS) {
        this.dailyRankVOS = dailyRankVOS;
    }

    public List<ProvinceRankVO> getProvinceRankVOS() {
        return provinceRankVOS;
    }

    public void setProvinceRankVOS(List<ProvinceRankVO> provinceRankVOS) {
        this.provinceRankVOS = provinceRankVOS;
    }

    public String getCurrentUsername() {
        return currentUsername;
    }

    public void setCurrentUsername(String currentUsername) {
        this.currentUsername = currentUsername;
    }

    public String getCurrentImgUrl() {
        return currentImgUrl;
    }

    public void setCurrentImgUrl(String currentImgUrl) {
        this.currentImgUrl = currentImgUrl;
    }
}

日榜数据

import io.swagger.annotations.ApiModelProperty;

import java.io.Serializable;

/**
 * @author xiazz
 * @Date 2022/10/18
 * @Description:
 */
public class DailyRankVO implements Serializable {
    @ApiModelProperty("用户名")
    private String username;
    @ApiModelProperty("通关耗时: -1表示未通关")
    private Integer sumUsedTime;
    @ApiModelProperty("排名: -1表示未上榜")
    private Long rank;
    @ApiModelProperty("用户头像")
    private String imgUrl;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getSumUsedTime() {
        return sumUsedTime;
    }

    public void setSumUsedTime(Integer sumUsedTime) {
        this.sumUsedTime = sumUsedTime;
    }

    public Long getRank() {
        return rank;
    }

    public void setRank(Long rank) {
        this.rank = rank;
    }

    public String getImgUrl() {
        return imgUrl;
    }

    public void setImgUrl(String imgUrl) {
        this.imgUrl = imgUrl;
    }
}

省榜

import io.swagger.annotations.ApiModelProperty;

import java.io.Serializable;

/**
 * @author xiazz
 * @Date 2022/10/18
 * @Description:
 */
public class ProvinceRankVO implements Serializable {
    @ApiModelProperty("省份")
    private String province;
    @ApiModelProperty("排名")
    private Long rank;
    @ApiModelProperty("通关人数")
    private Integer passCount;

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public Long getRank() {
        return rank;
    }

    public void setRank(Long rank) {
        this.rank = rank;
    }

    public Integer getPassCount() {
        return passCount;
    }

    public void setPassCount(Integer passCount) {
        this.passCount = passCount;
    }
}

2.2 获取排行榜数据(日榜前50, 省榜全部)

/**
     * @param openid: 小程序用户的唯一凭证
     * @param rankType: 1日榜 2省榜
     * @date: 2022/10/18
     * @description: 排行榜
     */
    @Override
    public net.huashitong.ssydt.provider.web.model.Result<RankInfoVO> getRank(String openid, Integer rankType) {
        long t1 = System.currentTimeMillis();
        RankInfoVO rankInfoVO = new RankInfoVO();
        // 用户名
        String userId = "";
        String userProvince = "";
        // 调用用户服务的接口获取用户信息
        Result<User> userResult = userManager.info(openid);
        User user = userResult.getResults();
        if (Objects.equals("200",userResult.getCode()) && Objects.nonNull(user)) {
            userId = user.getUserId();
            userProvince = user.getProvince();
            rankInfoVO.setCurrentUsername(StringUtils.isNotBlank(user.getNickname()) ? user.getNickname() : "");// 用户名
            rankInfoVO.setCurrentImgUrl(StringUtils.isNotBlank(user.getImageUrl()) ? user.getImageUrl() : ""); // 用户头像
        }
        String today = LocalDate.now().format(DateTimeFormatter.ISO_DATE);
        if (rankType == 1) {
            String dailyKey = today + "_PERSON_RANK";
            // 用户排名
            if (StringUtils.isBlank(userId)) {
                rankInfoVO.setUserRank((long) -1);
                rankInfoVO.setUserSumTime(-1);
            } else {
                Long userRank = redisTemplate.opsForZSet().rank(dailyKey, userId);
                rankInfoVO.setUserRank(Objects.isNull(userRank) ? -1 : userRank + 1);
                // 用户通关总耗时
                Double score = redisTemplate.opsForZSet().score(dailyKey, userId);
                rankInfoVO.setUserSumTime(Objects.isNull(score) ? -1 : score.intValue());
            }
            // 获取今日排行榜
            ArrayList<DailyRankVO> dailyRankVOS = new ArrayList<>();
            Set<ZSetOperations.TypedTuple<Object>> set = redisTemplate.opsForZSet().rangeWithScores(dailyKey, 0, 49);
            for (ZSetOperations.TypedTuple<Object> tuple : set) {
                DailyRankVO dailyRankVO = new DailyRankVO();
                String dailyUserId = (String) tuple.getValue(); // 用户id
                Result<User> dailyUserResult = userManager.get(dailyUserId);
                User dailyUser = dailyUserResult.getResults();
                if (!Objects.equals("200",dailyUserResult.getCode()) || Objects.isNull(dailyUser)) {
                    throw new ServiceException("100210","用户不存在");
                }
                dailyRankVO.setUsername(StringUtils.isNotBlank(dailyUser.getNickname()) ? dailyUser.getNickname() : ""); // 用户名
                dailyRankVO.setImgUrl(StringUtils.isNotBlank(dailyUser.getImageUrl()) ? dailyUser.getImageUrl() : ""); // 头像
                Double dailyUserSumTime = tuple.getScore(); // 通关耗时
                dailyRankVO.setSumUsedTime(Objects.isNull(dailyUserSumTime) ? -1 : dailyUserSumTime.intValue());
                // 排名
                Long rank = redisTemplate.opsForZSet().rank(dailyKey, dailyUserId);
                dailyRankVO.setRank(Objects.isNull(rank) ? -1 : rank + 1);
                dailyRankVOS.add(dailyRankVO);
            }
            rankInfoVO.setDailyRankVOS(dailyRankVOS);
        } else {
            // 省份
            String provinceKey = "PROVINCE_RANK";
            // 用户所在省份排名
            if (StringUtils.isBlank(userProvince)) {
                rankInfoVO.setUserAreaRank((long) -1);
            } else {
                Long userAreaRank = redisTemplate.opsForZSet().reverseRank(provinceKey, userProvince);
                rankInfoVO.setUserAreaRank(Objects.isNull(userAreaRank) || StringUtils.isBlank(userProvince) ? -1 : userAreaRank + 1);
            }
            Set<ZSetOperations.TypedTuple<Object>> set = redisTemplate.opsForZSet().reverseRangeWithScores(provinceKey, 0, -1);
            if (CollectionUtils.isNotEmpty(set)) {
                ArrayList<ProvinceRankVO> provinceRankVOS = new ArrayList<>();
                for (ZSetOperations.TypedTuple<Object> tuple : set) {
                    ProvinceRankVO provinceRankVO = new ProvinceRankVO();
                    String province = (String) tuple.getValue();// 省份
                    provinceRankVO.setProvince(province);
                    Double passCount = tuple.getScore(); // 通关人数
                    provinceRankVO.setPassCount(Objects.isNull(passCount) ? -1 : passCount.intValue());
                    // 排名
                    Long rank = redisTemplate.opsForZSet().reverseRank(provinceKey, province);
                    provinceRankVO.setRank(Objects.isNull(rank) ? -1 : rank + 1);
                    provinceRankVOS.add(provinceRankVO);
                }
                rankInfoVO.setProvinceRankVOS(provinceRankVOS);
            }
        }
        long t2 = System.currentTimeMillis();
        logger.info("请求排行榜耗时: " + (t2-t1));
        return ResultUtils.getSuccessResults(rankInfoVO);
    }
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值