有序集合SortedSet实战之充值排行榜

内容:给手机充值

创建数据库表phone_fare

CREATE TABLE `phone_fare` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `phone` varchar(50) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '手机号码',
  `fare` decimal(10,2) DEFAULT NULL COMMENT '充值金额',
  `is_active` tinyint(4) DEFAULT '1' COMMENT '是否有效(1=是;0=否)',
  PRIMARY KEY (`id`),
  KEY `idx_phone` (`phone`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=62 DEFAULT CHARSET=utf8 COMMENT='手机充值记录';

逆向工程生成entity, mapper, mapper.xml

使用hibernate时,在save方法时,报了:org.hibernate.validator.constraints.NotBlank’ validating type 'java.math.BigDecimal,因为@NotBlank是针对String的 。 解决方法是将实体类的注解换成 @NotNull,就行了

SortedSetService
@Service
public class SortedSetService {

    private static final Logger log= LoggerFactory.getLogger(SortedSetService.class);

    @Autowired
    private PhoneFareMapper fareMapper;

    @Autowired
    private RedisTemplate redisTemplate;


    //TODO:新增 手机话费充值记录
    @Transactional(rollbackFor = Exception.class)
    public Integer addRecord(PhoneFare fare) throws Exception{
        log.info("----sorted set话费充值记录新增:{} ",fare);

        fare.setId(null);
        //先插db
        int res = fareMapper.insertSelective(fare);
        if(res > 0){
            //再插Cache
            ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
            //往sortedSet添加一条充值记录,手机号为value,充值金额为score
            zSetOperations.add(Constant.RedisSortedSetKey2, fare.getPhone(), fare.getFare().doubleValue());
        }
        return  fare.getId();
    }

    //TODO:获取充值排行榜-正序
    public List<PhoneFare> getSortFares(){
        List<PhoneFare> list = Lists.newLinkedList();

        ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
        Long total = zSetOperations.size(Constant.RedisSortedSetKey2);

        //得到了排好序的、带分数的成员
        Set<ZSetOperations.TypedTuple<String>> set = zSetOperations.rangeWithScores(Constant.RedisSortedSetKey2, 0L, total);
        set.forEach(tuple->{
            list.add(new PhoneFare(tuple.getValue(), BigDecimal.valueOf(tuple.getScore())));
        });
        return list;
    }


    //TODO:获取充值排行榜-倒序
    public List<PhoneFare> getSortFaresV2(){
        List<PhoneFare> list = Lists.newLinkedList();

        ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
        Long total = zSetOperations.size(Constant.RedisSortedSetKey2);

        //得到了排好序的、带分数的成员
        Set<ZSetOperations.TypedTuple<String>> set = zSetOperations.reverseRangeWithScores(Constant.RedisSortedSetKey2, 0L, total);
        set.forEach(tuple->{
            list.add(new PhoneFare(tuple.getValue(), BigDecimal.valueOf(tuple.getScore())));
        });
        return list;
    }

    //TODO:获取指定手机号的充值金额
    public Double getFareByPhone(final String phone) {
        ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
        return zSetOperations.score(Constant.RedisSortedSetKey2, phone);
    }
}
SortedSetController
@RestController
@RequestMapping("sorted/set")
public class SortedSetController extends AbstractController {

    @Autowired
    private SortedSetService sortedSetService;

    //充值
    @RequestMapping(value = "put",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public BaseResponse put(@RequestBody @Validated PhoneFare fare, BindingResult result){
        String checkRes= ValidatorUtil.checkResult(result);
        if (StrUtil.isNotBlank(checkRes)){
            return new BaseResponse(StatusCode.Fail.getCode(),checkRes);
        }
        BaseResponse response=new BaseResponse(StatusCode.Success);
        try {
            response.setData(sortedSetService.addRecord(fare));
        }catch (Exception e){
            response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
        }
        return response;
    }

    //获取排行榜
    @RequestMapping(value = "rank",method = RequestMethod.GET)
    public BaseResponse get(@RequestParam String phone){
        BaseResponse response=new BaseResponse(StatusCode.Success);
        Map<String,Object> resMap=Maps.newHashMap();
        try {
            resMap.put("RankINfo-正序", sortedSetService.getSortFares());
            resMap.put("RankINfo-倒序", sortedSetService.getSortFaresV2());
            resMap.put("PhoneFare-获取具体的充值金额", sortedSetService.getFareByPhone(phone));

        }catch (Exception e) {
            response = new BaseResponse(StatusCode.Fail.getCode(), e.getMessage());
        }
        response.setData(resMap);
        return response;
    }

}

上述代码的问题:对于同一个手机进行充值的话,不是采取的叠加的形式,而是用最新的覆盖之前的,修改业务逻辑

升级内容:一个手机可以充多次值!

在新增之前,先判断SortSet集合中是否存在响应的元素,如果存在,使用incrementScore方法,否则才使用add方法

//TODO:新增 手机话费充值记录
    @Transactional(rollbackFor = Exception.class)
    public Integer addRecord(PhoneFare fare) throws Exception{
        log.info("----sorted set话费充值记录新增:{} ",fare);

        fare.setId(null);
        //先插db
        int res = fareMapper.insertSelective(fare);
        if(res > 0){
            //再插Cache
            ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();

            //TODO:插入cache之前,需要判断一下cache里面是否有该手机号的充值记录,如果有,则叠加;如果没有,则直接插入(第一次)
            Double curFare = zSetOperations.score(Constant.RedisSortedSetKey2, fare.getPhone());
            if(curFare != null){
                //fare.getFare().doubleValue()由包装类转换为基本数据类型
                zSetOperations.incrementScore(Constant.RedisSortedSetKey2, fare.getPhone(), fare.getFare().doubleValue());
            }else{
                //往sortedSet添加一条充值记录,手机号为value,充值金额为score
                zSetOperations.add(Constant.RedisSortedSetKey2, fare.getPhone(), fare.getFare().doubleValue());
            }
        }
        return  fare.getId();
    }

扩展

游戏充值排行榜、积分排行榜、充币排行榜、比赛得分排行榜、点赞(评论)-微博热搜排行榜......

猜你喜欢

转载自blog.csdn.net/weixin_37841366/article/details/109146812