|
@@ -0,0 +1,267 @@
|
|
|
|
+package com.txz.operating.dubbo.impl;
|
|
|
|
+
|
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
|
+import com.txz.operating.dto.rank.RequestRankDto;
|
|
|
|
+import com.txz.operating.dto.rank.ResponseRankIntro;
|
|
|
|
+import com.txz.operating.dto.rank.ResponseUserRankDetailDTO;
|
|
|
|
+import com.txz.operating.dto.rank.ResponseUserScore;
|
|
|
|
+import com.txz.operating.result.Result;
|
|
|
|
+import com.txz.operating.constants.MyConstants;
|
|
|
|
+import com.txz.operating.constants.RedisConstants;
|
|
|
|
+import com.txz.operating.core.ResultCode;
|
|
|
|
+import com.txz.operating.service.IOperatingRankDubboService;
|
|
|
|
+import com.txz.operating.util.lock.LockNameConst;
|
|
|
|
+import com.txz.operating.util.lock.RedissonLockUtil;
|
|
|
|
+import org.apache.dubbo.config.annotation.Service;
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.data.redis.core.RedisTemplate;
|
|
|
|
+import org.springframework.data.redis.core.ZSetOperations;
|
|
|
|
+
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.Set;
|
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * @Author: Fcx
|
|
|
|
+ * @Date: 2019/11/25 15:07
|
|
|
|
+ * @Version 1.0
|
|
|
|
+ */
|
|
|
|
+@Service
|
|
|
|
+public class RankDubboServiceImpl implements IOperatingRankDubboService {
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private RedisTemplate redisTemplate;
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Result<List<ResponseUserScore>> getRankList(RequestRankDto dto) {
|
|
|
|
+ if (dto.getRankKey() == null || dto.getReverse() == null
|
|
|
|
+ || dto.getPage() == null || dto.getSize() == null) {
|
|
|
|
+ return Result.fail(ResultCode.REQUEST_ARGS_IS_NULL.getCode(), ResultCode.REQUEST_ARGS_IS_NULL.getMessage());
|
|
|
|
+ }
|
|
|
|
+ // rank列表
|
|
|
|
+ Set<ZSetOperations.TypedTuple<String>> rankList;
|
|
|
|
+ if (dto.getReverse()) {
|
|
|
|
+ rankList = redisTemplate.opsForZSet().reverseRangeWithScores(RedisConstants.RANK_LIST
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()), dto.getIndex(), dto.getSize());
|
|
|
|
+ } else {
|
|
|
|
+ rankList = redisTemplate.opsForZSet().rangeWithScores(RedisConstants.RANK_LIST
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()), dto.getIndex(), dto.getSize());
|
|
|
|
+ }
|
|
|
|
+ List<ResponseUserScore> list = new ArrayList<>(rankList.size());
|
|
|
|
+ for (ZSetOperations.TypedTuple<String> tuple : rankList) {
|
|
|
|
+ // key 转换
|
|
|
|
+ String userId = StrUtil.subAfter(tuple.getValue(), RedisConstants.RANK_USER.replace("userId", ""), false);
|
|
|
|
+ int score = tuple.getScore().intValue();
|
|
|
|
+ ResponseUserScore build = ResponseUserScore.builder()
|
|
|
|
+ .userId(userId)
|
|
|
|
+ .score(score)
|
|
|
|
+ .build();
|
|
|
|
+ list.add(build);
|
|
|
|
+ }
|
|
|
|
+ return Result.success(list);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Result<ResponseRankIntro> getRankSizeAndTotalScore(RequestRankDto dto) {
|
|
|
|
+ if (dto.getRankKey() == null || dto.getReverse() == null) {
|
|
|
|
+ return Result.fail(ResultCode.REQUEST_ARGS_IS_NULL.getCode(), ResultCode.REQUEST_ARGS_IS_NULL.getMessage());
|
|
|
|
+ }
|
|
|
|
+ Long rankSize = redisTemplate.opsForZSet().zCard(RedisConstants.RANK_LIST
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey())
|
|
|
|
+ );
|
|
|
|
+ Object o = redisTemplate.opsForValue().get(RedisConstants.RANK_TOTAL_SCORE
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()));
|
|
|
|
+ Long totalScore = 0L;
|
|
|
|
+ if (o != null) {
|
|
|
|
+ totalScore = (Long) o;
|
|
|
|
+ }
|
|
|
|
+ ResponseRankIntro rankIntro = ResponseRankIntro.builder()
|
|
|
|
+ .rankSize(rankSize)
|
|
|
|
+ .totalScore(totalScore)
|
|
|
|
+ .build();
|
|
|
|
+ return Result.success(rankIntro);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Result<ResponseUserRankDetailDTO> getRankByUserId(RequestRankDto dto) {
|
|
|
|
+ if (dto.getRankKey() == null || dto.getUserId() == null || dto.getReverse() == null) {
|
|
|
|
+ return Result.fail(ResultCode.REQUEST_ARGS_IS_NULL.getCode(), ResultCode.REQUEST_ARGS_IS_NULL.getMessage());
|
|
|
|
+ }
|
|
|
|
+ // 用户的分数
|
|
|
|
+ Double score = redisTemplate.opsForZSet().score(
|
|
|
|
+ RedisConstants.RANK_LIST
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()),
|
|
|
|
+ RedisConstants.RANK_USER
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("userId", dto.getUserId()));
|
|
|
|
+ // 用户的排名
|
|
|
|
+ // 取并列map,此得分的用户
|
|
|
|
+
|
|
|
|
+ String parataxisRankUserId = dto.getUserId();
|
|
|
|
+ Long rank = null;
|
|
|
|
+ if (null != score) {
|
|
|
|
+ Object o = redisTemplate.opsForHash().get(
|
|
|
|
+ RedisConstants.RANK_PARATAXIS_MAP
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()),
|
|
|
|
+ // key分数
|
|
|
|
+ RedisConstants.RANK_MAP_SCORE.replace("num", String.valueOf(score.intValue()))
|
|
|
|
+ );
|
|
|
|
+ if (null != o) {
|
|
|
|
+ parataxisRankUserId = (String) o;
|
|
|
|
+ }
|
|
|
|
+ if (dto.getReverse()) {
|
|
|
|
+ rank = redisTemplate.opsForZSet().reverseRank(RedisConstants.RANK_LIST
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()),
|
|
|
|
+ RedisConstants.RANK_USER
|
|
|
|
+ .replaceFirst("userId", parataxisRankUserId));
|
|
|
|
+ } else {
|
|
|
|
+ rank = redisTemplate.opsForZSet().rank(RedisConstants.RANK_LIST
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()),
|
|
|
|
+ RedisConstants.RANK_USER
|
|
|
|
+ .replaceFirst("userId", parataxisRankUserId));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 本排行榜的人数
|
|
|
|
+ Long rankSize = redisTemplate.opsForZSet().zCard(RedisConstants.RANK_LIST
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey())
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ if (null != rank) {
|
|
|
|
+ rank += 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ResponseUserRankDetailDTO detailDTO = ResponseUserRankDetailDTO.builder()
|
|
|
|
+ .score(score)
|
|
|
|
+ .rank(rank)
|
|
|
|
+ .rankSize(rankSize)
|
|
|
|
+ .build();
|
|
|
|
+ return Result.success(detailDTO);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Result saveRankList(RequestRankDto dto) {
|
|
|
|
+ if (dto.getRankKey() == null || dto.getUserId() == null || dto.getReverse() == null || dto.getScore() == null) {
|
|
|
|
+ return Result.fail(ResultCode.REQUEST_ARGS_IS_NULL.getCode(), ResultCode.REQUEST_ARGS_IS_NULL.getMessage());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// 不收集排行榜的总分 2020年11月20日 14:48:33
|
|
|
|
+ // updateRankTotalScore(dto, true);
|
|
|
|
+
|
|
|
|
+ // 更新一个用户在某个排行榜中的分数
|
|
|
|
+ long floatNum;
|
|
|
|
+ if (dto.getReverse()) {
|
|
|
|
+ floatNum = System.currentTimeMillis();
|
|
|
|
+ } else {
|
|
|
|
+ floatNum = MyConstants.RANK_TOTAL_SCORE - System.currentTimeMillis();
|
|
|
|
+ }
|
|
|
|
+ redisTemplate.opsForZSet().add(
|
|
|
|
+ RedisConstants.RANK_LIST
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()),
|
|
|
|
+ RedisConstants.RANK_USER
|
|
|
|
+ .replaceFirst("userId", dto.getUserId()),
|
|
|
|
+
|
|
|
|
+ // 小数点后加一个随时间更新的值, 后面的小于前面的, 所以同分数排名是后来居上
|
|
|
|
+ Double.valueOf(dto.getScore() + "." + floatNum)
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ // 更新并列map,此得分的用户
|
|
|
|
+ redisTemplate.opsForHash().put(
|
|
|
|
+ RedisConstants.RANK_PARATAXIS_MAP
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()),
|
|
|
|
+ // key存分数
|
|
|
|
+ RedisConstants.RANK_MAP_SCORE.replace("num", dto.getScore().toString())
|
|
|
|
+ ,
|
|
|
|
+ // value存userId
|
|
|
|
+ dto.getUserId()
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ return Result.success();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void updateRankTotalScore(RequestRankDto dto, Boolean update) {
|
|
|
|
+ // 加分布式锁
|
|
|
|
+ String lockKey = LockNameConst.LOCK_RANK_TOTAL_SCORE
|
|
|
|
+ .replace("reverse", dto.getReverse().toString())
|
|
|
|
+ .replace("rankKey", dto.getRankKey());
|
|
|
|
+ // 等待5s, 10min释放
|
|
|
|
+ if (!RedissonLockUtil.tryLock(lockKey, TimeUnit.SECONDS, 5, 60 * 10)) {
|
|
|
|
+ throw new RuntimeException("分布式锁冲突");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // #######维护此排行榜的总分 start ######
|
|
|
|
+ Object o = redisTemplate.opsForValue().get(RedisConstants.RANK_TOTAL_SCORE
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()));
|
|
|
|
+ Long totalScore;
|
|
|
|
+ if (o != null) {
|
|
|
|
+ totalScore = (Long) o;
|
|
|
|
+ } else {
|
|
|
|
+ totalScore = 0L;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 查出用户在排行榜里的分数,减去再加上新值
|
|
|
|
+ Double score = redisTemplate.opsForZSet().score(
|
|
|
|
+ RedisConstants.RANK_LIST
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()),
|
|
|
|
+ RedisConstants.RANK_USER
|
|
|
|
+ .replaceFirst("userId", dto.getUserId()));
|
|
|
|
+ if (score != null) {
|
|
|
|
+ totalScore = totalScore - score.longValue();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (update) {
|
|
|
|
+ totalScore = totalScore + dto.getScore();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ redisTemplate.opsForValue().set(
|
|
|
|
+ RedisConstants.RANK_TOTAL_SCORE
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()),
|
|
|
|
+ totalScore
|
|
|
|
+ );
|
|
|
|
+ // #######维护此排行榜的总分 end ######
|
|
|
|
+ RedissonLockUtil.unlock(lockKey);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Result delUserRank(RequestRankDto dto) {
|
|
|
|
+ // 减去用户在此排行榜的总分
|
|
|
|
+ updateRankTotalScore(dto, false);
|
|
|
|
+
|
|
|
|
+ Long remove = redisTemplate.opsForZSet().remove(
|
|
|
|
+ RedisConstants.RANK_LIST
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()),
|
|
|
|
+ RedisConstants.RANK_USER
|
|
|
|
+ .replaceFirst("userId", dto.getUserId()));
|
|
|
|
+
|
|
|
|
+ return Result.success(remove);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Result delRank(RequestRankDto dto) {
|
|
|
|
+ // 删除此排行榜的总分
|
|
|
|
+ Boolean deleteTotalScore = redisTemplate.delete(RedisConstants.RANK_TOTAL_SCORE
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()));
|
|
|
|
+ // 删除排行榜
|
|
|
|
+ Boolean delete = redisTemplate.delete(RedisConstants.RANK_LIST
|
|
|
|
+ .replaceFirst("reverse", dto.getReverse().toString())
|
|
|
|
+ .replaceFirst("rankKey", dto.getRankKey()));
|
|
|
|
+ return Result.success(deleteTotalScore && delete);
|
|
|
|
+ }
|
|
|
|
+}
|