Compare commits

..
2 Commits
Author SHA1 Message Date
wonder 02c75583bd 📝Docs: 增加对于点赞接口锁的解释 2025-10-18 23:51:14 +08:00
wonder d47dd63d5f ✨Feat: 增加点赞功能 2025-10-18 23:40:35 +08:00
5 changed files with 105 additions and 1 deletions
+15
View File
@@ -42,3 +42,18 @@ CREATE UNIQUE INDEX idx_userId_blogId ON thumb (userId, blogId);
- 博客服务中需要使用点赞服务查询博客对应的点赞情况列表以返回对应视图。
- 点赞服务中需要使用博客服务更新单条博客的点赞计数器。
- 两个服务之间产生了**循环依赖**,Spring Boot 2.6 以后的版本默认不允许循环依赖,这里可以使用懒加载的方式避免这一问题。
3. 点赞接口加锁对象是什么?
答:
- 加锁目的是为了避免用户并发请求造成数据不一致。
- 不能对帖子加锁,因为帖子是全局的,加锁会导致其他用户无法点赞。
- 因此需要对用户加锁,故而实际上是将每个用户的 id 当作锁。
4. 点赞接口的锁具体如何处理?
答:
`synchronized (("LOCK-USERID-" + loginUser.getId().toString()).intern()) `
- 每个用户持有一个唯一 id,因此考虑把 id 对应的 String 常量池对象加锁。
- 为了避免对程序中其他需要使用该字符串常量的地方产生影响,这里对字符串作出处理 `"LOCK-USERID-" + loginUser.getId().toString()`
- 并且我们需要将字符串放到常量池中,调用 `intern()` 方法。
@@ -0,0 +1,25 @@
package cn.hezhaohui.thumb.controller;
import cn.hezhaohui.thumb.common.BaseResponse;
import cn.hezhaohui.thumb.common.ResultUtils;
import cn.hezhaohui.thumb.model.dto.DoThumbRequest;
import cn.hezhaohui.thumb.service.ThumbService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("thumb")
public class ThumbController {
@Resource
private ThumbService thumbService;
@PostMapping("/do")
public BaseResponse<Boolean> doThumb(@RequestBody DoThumbRequest doThumbRequest, HttpServletRequest request) {
Boolean success = thumbService.doThumb(doThumbRequest, request);
return ResultUtils.sucess(success);
}
}
@@ -0,0 +1,8 @@
package cn.hezhaohui.thumb.model.dto;
import lombok.Data;
@Data
public class DoThumbRequest {
private Long blogId;
}
@@ -1,7 +1,9 @@
package cn.hezhaohui.thumb.service;
import cn.hezhaohui.thumb.model.dto.DoThumbRequest;
import cn.hezhaohui.thumb.model.entity.Thumb;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.servlet.http.HttpServletRequest;
/**
* @author 23117
@@ -10,4 +12,6 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface ThumbService extends IService<Thumb> {
Boolean doThumb(DoThumbRequest doThumbRequest, HttpServletRequest request);
}
@@ -1,10 +1,20 @@
package cn.hezhaohui.thumb.service.impl;
import cn.hezhaohui.thumb.exception.BusinessException;
import cn.hezhaohui.thumb.exception.ErrorCode;
import cn.hezhaohui.thumb.model.dto.DoThumbRequest;
import cn.hezhaohui.thumb.model.entity.Blog;
import cn.hezhaohui.thumb.model.entity.User;
import cn.hezhaohui.thumb.service.BlogService;
import cn.hezhaohui.thumb.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.hezhaohui.thumb.model.entity.Thumb;
import cn.hezhaohui.thumb.service.ThumbService;
import cn.hezhaohui.thumb.mapper.ThumbMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
/**
* @author 23117
@@ -15,6 +25,48 @@ import org.springframework.stereotype.Service;
public class ThumbServiceImpl extends ServiceImpl<ThumbMapper, Thumb>
implements ThumbService{
@Resource
private UserService userService;
@Resource
private BlogService blogService;
@Resource
private TransactionTemplate transactionTemplate;
@Override
public Boolean doThumb(DoThumbRequest doThumbRequest, HttpServletRequest request) {
if (doThumbRequest == null || doThumbRequest.getBlogId() == null) {
throw new RuntimeException("参数异常");
}
User loginUser = userService.getLoginUser(request);
// Lock: 这里使用字符串加锁,获取字符串常量对象才可运行,字符串常量对象具有唯一性
synchronized (("LOCK-USERID-" + loginUser.getId().toString()).intern()) {
// Transaction: 编程式事务
return transactionTemplate.execute(status -> {
Long blogId = doThumbRequest.getBlogId();
// 判断是否已点过赞
boolean exists = this.lambdaQuery()
.eq(Thumb::getUserid, loginUser.getId())
.eq(Thumb::getBlogId, blogId)
.exists();
if (exists) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "用户已点赞");
}
// 更新帖子点赞计数器
boolean update = blogService.lambdaUpdate()
.eq(Blog::getId, blogId)
.setSql("thumbCount = thumbCount + 1")
.update();
// 更新点赞表数据
Thumb thumb = new Thumb();
thumb.setUserid(loginUser.getId());
thumb.setBlogId(blogId);
// 两者一起执行
return update && this.save(thumb);
});
}
}
}