目录
一、点赞
1、思路
2、代码实现
二、点赞排行榜
1、思路
2、代码实现
一、点赞
1、思路
在我们的项目中我们有时候会碰到这样的需求,比如实现一个博客系统,当用户访问到这篇博客时可以进行点赞,那么这个功能如何去实现呢,我们可以在数据库中维护一张点赞表,当用户刚进入这个博客页面时拿着这个博客的id发起请求查询这张点赞表,看是否存在点赞记录,如果存在返回前端,前端获取到后将点赞按钮高亮色展示,当用户再次点击时则发起取消点赞请求将数据库中点赞数-1且删除点赞记录,如果没有点过赞则发起点赞请求操作数据库使得点赞数+1以及插入一条点赞记录,上述流程基本全是数据库操作,我们可以通过redis来对该功能进行优化,那么我们怎么优化呢,可以借鉴之前redis优化秒杀项目一人一单的思路,此处一人只能赞一次与一人一单极为相似,我们可以使用redis中的set数据结构,当用户点赞时可以将该博客的id作为set类型key的组成,将用户id作为value值存入set,然后让数据库中点赞量+1。用户进入页面后查询redis中的该set集合,发现该用户id在其中,则使点赞按钮高亮色再次点击时将数据库中的点赞数-1在将该用户id从set集合中移除,整体思路如上述,下面我们使用代码来实现一下
2、代码实现
此处为了代码全部展示,将部分操作全部放在controller层,其中reidskey的命名为了方便此处阅读使用硬编码而没有使用redis key工具类进行管理,在项目开发中推荐使用自定义redis key的工具类进行管理
@RestController
@RequestMapping("/api/follow")
public class FollowController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private FollowService followService;
/**
* 点赞和取消点赞接口(使用Redis缓存防止重复操作)
* @param id 文章ID,不能为空且必须为正整数
* @param request HTTP请求对象
* @return 返回结果对象
*/
@PostMapping("/like")
public ReturnModel like(@NotNull @Positive Integer id, HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute(ApplicationVariable.USER_SESSION_KRY) == null) {
// 判断用户是否已经登录
return ReturnModel.fail(-1, "您尚未登录");
}
User user = (User) session.getAttribute(ApplicationVariable.USER_SESSION_KRY);
Integer userId = user.getId();
Boolean isLiked = stringRedisTemplate.opsForSet().isMember("like:user:" + id, userId.toString());
if (Boolean.TRUE.equals(isLiked)) {
// 如果已经点赞,将该用户id从set中移除,并将数据库点赞数-1
boolean isSuccess = followService.update(id, -1);
if (isSuccess) {
stringRedisTemplate.opsForSet().remove("like:user:" + id, userId.toString());
}
} else {
// 如果未点赞,将该用户id加入set中,并将数据库点赞数+1
boolean isSuccess = followService.update(id, 1);
if (isSuccess) {
stringRedisTemplate.opsForSet().add("like:user:" + id, userId.toString());
}
}
return ReturnModel.success(200);
}
}
二、点赞排行榜
1、思路
如果我们需要在点赞功能的基础上实现展示最先点赞的前10个人,获取亲密度比较高的前10个人,那么我们如何去修改上述的代码去实现呢?这里我们要实现的功能带有排序的特点,那么在redis中哪些数据结构带有排序的特性呢
List | Set | SortedSet | |
排序 | 按照添加的顺序进行排序 | 无法排序 | 根据score值排序 |
唯一 | 不保证唯一 | 保证唯一性 | 保证唯一性 |
查找 | 按索引查找获取按首尾查找 | 根据元素查找 | 根据元素查找 |
在上述三种结构中满足可排序且保证唯一性的只有SortedSet,我们可以通过它的zscore key命令来查找用户是否存在,如果存在就会返回他的score如果不存在则返回空,我们可以通过返回结果来判断用户是否点赞,可以通过zrange命令来获取最先点赞的前10个用户的id,然后通过数据库查找返回
2、代码实现
public ReturnModel followByRedis(@NotNull @Positive Integer id, HttpServletRequest request) {
// 1.首先获取用户id
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute(ApplicationVariable.USER_SESSION_KRY) == null) {
return ReturnModel.fail(-1,"您尚未登录");
}
User user = (User) session.getAttribute(ApplicationVariable.USER_SESSION_KRY);
Integer userId = user.getId();
// 2.查询当前用户是否点赞
Double score = stringRedisTemplate.opsForZSet().score(RedisUtil.FOLLOW_KEY + id,userId.toString());
if (score != null) {
// 2.1 已点赞,则数据库点赞数-1 将该用户id从set中移除
boolean isSuccess = followService.update(id,-1);
if (isSuccess) {
// 数据库操作成功,set中移除id
stringRedisTemplate.opsForZSet().remove(RedisUtil.FOLLOW_KEY + id,userId.toString());
}
} else {
// 2.2 未点赞,则数据库点赞数+1 将该用户id加入set中
boolean isSuccess = followService.update(id,1);
if (isSuccess) {
// 数据库操作成功,userId加入set
stringRedisTemplate.opsForZSet().add(RedisUtil.FOLLOW_KEY + id,userId.toString(),System.currentTimeMillis());
}
}
// 3.返回结果
return ReturnModel.success(200);
}
后续获取前10时可以通过
stringRedisTemplate.opsForZSet().range(RedisUtil.FOLLOW_KEY + id,0,9);
来获取存在redis中的前10个用户id,然后去查询数据库中对应得用户信息进行返回