php网站登录系统怎么做,如何汉化wordpress插件,橘子seo工具,关键词优化排名要多少钱学习目标#xff1a;
提示#xff1a;学习如何利用Redisson实现点赞排行榜功能#xff0c;按照时间顺序 当用户给某一篇文章点赞后#xff0c;会再数据库中存储一条数据#xff0c;并且在Redis中存储一条数据为当前博客的点赞用户标识#xff0c;来区分哪个用户对文章进…学习目标
提示学习如何利用Redisson实现点赞排行榜功能按照时间顺序 当用户给某一篇文章点赞后会再数据库中存储一条数据并且在Redis中存储一条数据为当前博客的点赞用户标识来区分哪个用户对文章进行了点赞使用ZSet数据结构对点赞用户进行排序来实现排行榜功能 学习产出
解决方案
点赞后的用户记录在Redis的set数据类型中
1. 准备pom环境 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependencydependencygroupIdorg.apache.commons/groupIdartifactIdcommons-pool2/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdscoperuntime/scopeversion5.1.47/version/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.4.3/version/dependency!--hutool--dependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.7.17/version/dependencydependencygroupIdorg.redisson/groupIdartifactIdredisson/artifactIdversion3.23.1/version/dependency2. 配置ThreadLocal和过滤器
public class UserHolder {private static final ThreadLocalUserDTO tl new ThreadLocal();public static void saveUser(UserDTO user){tl.set(user);}public static UserDTO getUser(){return tl.get();}public static void removeUser(){tl.remove();}
}Configuration
public class MvcConfig implements WebMvcConfigurer {Autowiredprivate StringRedisTemplate redis;Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(/user/code,/user/login,/blog/hot,/shop/**,/shop-type/**,/voucher/**).order(2);registry.addInterceptor(new RefreshTokenInterceptor(redis)).addPathPatterns(/**).order(1);}
}
---------------------------------------------
Slf4j
public class LoginInterceptor implements HandlerInterceptor {//controller执行之前Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.判断是否需要拦截ThreadLocalif (UserHolder.getUser()null) {response.setStatus(401);return false;}//7.放行return true;}//渲染后返回给前台数据前Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//移除用户避免内存泄露UserHolder.removeUser();}
}
---------------------------------------------------
Slf4j
public class RefreshTokenInterceptor implements HandlerInterceptor {//这个对象不是由spring管理的所以不能用注解自动注入private StringRedisTemplate redis;public RefreshTokenInterceptor(StringRedisTemplate redis) {this.redis redis;}//controller执行之前Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.获取请求头中的tokenString token request.getHeader(authorization);if (StrUtil.isBlank(token)) {return true;}//2.基于token获取redis中的用户//通过key取到hash中的map集合数据MapObject, Object userMap redis.opsForHash().entries(login:token: token);//3.判断用户是否存在if (userMap.isEmpty()) {return true;}//5.将查询到的hash数据转为userDto对象UserDTO userDTO BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);//6.存在保存用户信息到ThreadLocal中UserHolder.saveUser(userDTO);//7.刷新token有效期redis.expire(LOGIN_USER_KEY token, 30, TimeUnit.MINUTES);log.info(我是第一个拦截器当前拦截所有请求的用户为线程为{},{},UserHolder.getUser(),Thread.currentThread());//8.放行return true;}3. Controller层负责接收请求和向下分配
RestController
RequestMapping(/blog)
public class BlogController{Resourceprivate IBlogService blogService;PutMapping(/like/{id})public Result likeBlog(PathVariable(id) Long id) {return blogService.likeBlog(id);}
}4. Service层负责业务的处理逻辑点赞功能将文章的点赞用户以时间戳为分数存入Redis
Service
public class BlogServiceImpl extends ServiceImplBlogMapper, Blog implements IBlogService {Autowiredprivate IUserService userService;Resourceprivate StringRedisTemplate redis;Overridepublic Result likeBlog(Long id) {//1.获取登录用户Long userId UserHolder.getUser().getId();//2.判断当前用户是否已经点赞String key blog:liked: id;//获取当前登录用户的分数若文章中的用户id分数为null说明未点赞Double score redis.opsForZSet().score(key, userId.toString());if (score null) {//3.如果未点赞可以点赞//3.1 点赞1boolean isSuccess update().setSql(liked liked 1).eq(id, id).update();//3.2保存当前点赞用户到Redis的文章set集合中文章set集合中记录的是点赞用户的id//分数是时间戳可以进行排序if (isSuccess) {//存入Redis的分数值以当前时间戳存入redis.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());}} else {//4.如果已点赞取消点赞//4.1点赞-1boolean isSuccess update().setSql(liked liked -1).eq(id, id).update();//4.2把用户从Redis的set集合移除if (isSuccess) {redis.opsForZSet().remove(key, userId.toString());}}return null;}
}5. 上述为下面的排行榜做铺垫 查询当前文章的点赞排行榜id是文章id号 PutMapping(/likes/{id})public Result likesBlog(PathVariable(id) Long id) {return blogService.queryBlogLikes(id);}Overridepublic Result queryBlogLikes(Long id) {String keyblog:liked: id;//取出前五条数据SetString rangeData redis.opsForZSet().range(key, 0, 4);if (rangeDatanull) {return Result.ok(Collections.emptyList());}//将文章点赞的前五条用户id转换为Long类型ListLong ids rangeData.stream().map(Long::valueOf).collect(Collectors.toList());String idStr StrUtil.join(,, ids);//去数据库把这些用户查询出来并且数据脱敏返回给前端ListUser users userService.query().in(id,ids).last(order by field(id,idStr)).list();UserDTO userData BeanUtil.copyProperties(users, UserDTO.class);return Result.ok(userData);}