mirror of
https://github.com/201206030/novel-plus.git
synced 2025-07-16 06:06:38 +00:00
feat(novel-front): 增加评论点赞/点踩功能
This commit is contained in:
@ -7,6 +7,7 @@ import com.java2nb.novel.entity.*;
|
||||
import com.java2nb.novel.service.BookContentService;
|
||||
import com.java2nb.novel.service.BookService;
|
||||
import com.java2nb.novel.service.IpLocationService;
|
||||
import com.java2nb.novel.service.LikeService;
|
||||
import com.java2nb.novel.vo.*;
|
||||
import io.github.xxyopen.model.page.PageBean;
|
||||
import io.github.xxyopen.model.page.builder.pagehelper.PageBuilder;
|
||||
@ -35,6 +36,8 @@ public class BookController extends BaseController {
|
||||
|
||||
private final IpLocationService ipLocationService;
|
||||
|
||||
private final LikeService likeService;
|
||||
|
||||
/**
|
||||
* 查询首页小说设置列表数据
|
||||
*/
|
||||
@ -171,6 +174,30 @@ public class BookController extends BaseController {
|
||||
return RestResult.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 评价点赞/取消点赞
|
||||
*/
|
||||
@PostMapping("toggleCommentLike")
|
||||
public RestResult<?> toggleCommentLike(Long commentId, HttpServletRequest request) {
|
||||
UserDetails userDetails = getUserDetails(request);
|
||||
if (userDetails == null) {
|
||||
return RestResult.fail(ResponseStatus.NO_LOGIN);
|
||||
}
|
||||
return RestResult.ok(likeService.toggleCommentLike(commentId, userDetails.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 评价点踩/取消点踩
|
||||
*/
|
||||
@PostMapping("toggleCommentUnLike")
|
||||
public RestResult<?> toggleCommentUnLike(Long commentId, HttpServletRequest request) {
|
||||
UserDetails userDetails = getUserDetails(request);
|
||||
if (userDetails == null) {
|
||||
return RestResult.fail(ResponseStatus.NO_LOGIN);
|
||||
}
|
||||
return RestResult.ok(likeService.toggleCommentUnLike(commentId, userDetails.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增回复
|
||||
*/
|
||||
@ -185,6 +212,30 @@ public class BookController extends BaseController {
|
||||
return RestResult.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复点赞/取消点赞
|
||||
*/
|
||||
@PostMapping("toggleReplyLike")
|
||||
public RestResult<?> toggleReplyLike(Long replyId, HttpServletRequest request) {
|
||||
UserDetails userDetails = getUserDetails(request);
|
||||
if (userDetails == null) {
|
||||
return RestResult.fail(ResponseStatus.NO_LOGIN);
|
||||
}
|
||||
return RestResult.ok(likeService.toggleReplyLike(replyId, userDetails.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复点赞/取消点赞
|
||||
*/
|
||||
@PostMapping("toggleReplyUnLike")
|
||||
public RestResult<?> toggleReplyUnLike(Long replyId, HttpServletRequest request) {
|
||||
UserDetails userDetails = getUserDetails(request);
|
||||
if (userDetails == null) {
|
||||
return RestResult.fail(ResponseStatus.NO_LOGIN);
|
||||
}
|
||||
return RestResult.ok(likeService.toggleReplyUnLike(replyId, userDetails.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据小说ID查询小说前十条最新更新目录集合
|
||||
*/
|
||||
|
@ -0,0 +1,69 @@
|
||||
package com.java2nb.novel.service;
|
||||
|
||||
|
||||
/**
|
||||
* @author 11797
|
||||
*/
|
||||
public interface LikeService {
|
||||
|
||||
/**
|
||||
* 评论点赞或取消点赞
|
||||
* @param commentId 被点赞的评论ID
|
||||
* @param userId 用户ID
|
||||
* @return 返回点赞数量
|
||||
*/
|
||||
public Long toggleCommentLike(Long commentId, Long userId);
|
||||
|
||||
/**
|
||||
* 评论点踩或取消点踩
|
||||
* @param commentId 被点踩的评论ID
|
||||
* @param userId 用户ID
|
||||
* @return 返回点踩数量
|
||||
*/
|
||||
public Long toggleCommentUnLike(Long commentId, Long userId);
|
||||
|
||||
/**
|
||||
* 获取评论的点赞数量
|
||||
* @param commentId 评论ID
|
||||
* @return 点赞数
|
||||
*/
|
||||
public Long getCommentLikesCount(Long commentId);
|
||||
|
||||
/**
|
||||
* 获取评论的点踩赞数量
|
||||
* @param commentId 评论ID
|
||||
* @return 点踩数
|
||||
*/
|
||||
public Long getCommentUnLikesCount(Long commentId);
|
||||
|
||||
/**
|
||||
* 回复点赞或取消点赞
|
||||
* @param replyId 被点赞的回复ID
|
||||
* @param userId 用户ID
|
||||
* @return 返回点赞数量
|
||||
*/
|
||||
public Long toggleReplyLike(Long replyId, Long userId);
|
||||
|
||||
/**
|
||||
* 回复点踩或取消点踩
|
||||
* @param replyId 被点踩的回复ID
|
||||
* @param userId 用户ID
|
||||
* @return 返回点踩数量
|
||||
*/
|
||||
public Long toggleReplyUnLike(Long replyId, Long userId);
|
||||
|
||||
/**
|
||||
* 获取回复的点赞数量
|
||||
* @param replyId 回复ID
|
||||
* @return 点赞数
|
||||
*/
|
||||
public Long getReplyLikesCount(Long replyId);
|
||||
|
||||
/**
|
||||
* 获取回复的点踩数量
|
||||
* @param replyId 回复ID
|
||||
* @return 点踩数
|
||||
*/
|
||||
public Long getReplyUnLikesCount(Long replyId);
|
||||
|
||||
}
|
@ -15,6 +15,7 @@ import com.java2nb.novel.mapper.*;
|
||||
import com.java2nb.novel.service.AuthorService;
|
||||
import com.java2nb.novel.service.BookService;
|
||||
import com.java2nb.novel.service.FileService;
|
||||
import com.java2nb.novel.service.LikeService;
|
||||
import com.java2nb.novel.vo.*;
|
||||
import io.github.xxyopen.model.page.PageBean;
|
||||
import io.github.xxyopen.model.page.builder.pagehelper.PageBuilder;
|
||||
@ -94,6 +95,8 @@ public class BookServiceImpl implements BookService {
|
||||
|
||||
private final FileService fileService;
|
||||
|
||||
private final LikeService likeService;
|
||||
|
||||
private final BookPriceProperties bookPriceConfig;
|
||||
|
||||
private final OpenAiImageModel openAiImageModel;
|
||||
@ -390,7 +393,12 @@ public class BookServiceImpl implements BookService {
|
||||
@Override
|
||||
public PageBean<BookCommentVO> listCommentByPage(Long userId, Long bookId, int page, int pageSize) {
|
||||
PageHelper.startPage(page, pageSize);
|
||||
return PageBuilder.build(bookCommentMapper.listCommentByPage(userId, bookId));
|
||||
PageBean<BookCommentVO> pageBean = PageBuilder.build(bookCommentMapper.listCommentByPage(userId, bookId));
|
||||
for (BookCommentVO bookCommentVO : pageBean.getList()) {
|
||||
bookCommentVO.setLikesCount(likeService.getCommentLikesCount(bookCommentVO.getId()));
|
||||
bookCommentVO.setUnLikesCount(likeService.getCommentUnLikesCount(bookCommentVO.getId()));
|
||||
}
|
||||
return pageBean;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@ -901,7 +909,13 @@ public class BookServiceImpl implements BookService {
|
||||
@Override
|
||||
public PageBean<BookCommentReplyVO> listCommentReplyByPage(Long userId, Long commentId, int page, int pageSize) {
|
||||
PageHelper.startPage(page, pageSize);
|
||||
return PageBuilder.build(bookCommentReplyMapper.listCommentReplyByPage(userId, commentId));
|
||||
PageBean<BookCommentReplyVO> pageBean = PageBuilder.build(
|
||||
bookCommentReplyMapper.listCommentReplyByPage(userId, commentId));
|
||||
pageBean.getList().forEach(commentReply -> {
|
||||
commentReply.setLikesCount(likeService.getReplyLikesCount(commentReply.getId()));
|
||||
commentReply.setUnLikesCount(likeService.getReplyUnLikesCount(commentReply.getId()));
|
||||
});
|
||||
return pageBean;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,94 @@
|
||||
package com.java2nb.novel.service.impl;
|
||||
|
||||
import com.java2nb.novel.service.LikeService;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
||||
import org.springframework.scripting.support.StaticScriptSource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author xiongxiaoyang
|
||||
* @date 2025/7/12
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class LikeServiceImpl implements LikeService {
|
||||
|
||||
private final RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
private DefaultRedisScript<Long> toggleLikeScript;
|
||||
|
||||
private static final String COMMENT_LIKE_KEY_PREFIX = "like:comment:";
|
||||
private static final String COMMENT_REPLY_LIKE_KEY_PREFIX = "like:comment:reply:";
|
||||
private static final String COMMENT_UN_LIKE_KEY_PREFIX = "unlike:comment:";
|
||||
private static final String COMMENT_REPLY_UN_LIKE_KEY_PREFIX = "unlike:comment:reply:";
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
// Lua 脚本保证原子性操作
|
||||
String script = """
|
||||
local key = KEYS[1]
|
||||
local userId = ARGV[1]
|
||||
|
||||
local isLiked = redis.call('SISMEMBER', key, userId)
|
||||
|
||||
if isLiked == 1 then
|
||||
redis.call('SREM', key, userId)
|
||||
else
|
||||
redis.call('SADD', key, userId)
|
||||
end
|
||||
|
||||
return redis.call('SCARD', key)
|
||||
""";
|
||||
|
||||
toggleLikeScript = new DefaultRedisScript<>();
|
||||
toggleLikeScript.setScriptSource(new StaticScriptSource(script));
|
||||
toggleLikeScript.setResultType(Long.class);
|
||||
}
|
||||
|
||||
public Long toggleCommentLike(Long commentId, Long userId) {
|
||||
return executeToggle(COMMENT_LIKE_KEY_PREFIX + commentId, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long toggleCommentUnLike(Long commentId, Long userId) {
|
||||
return executeToggle(COMMENT_UN_LIKE_KEY_PREFIX + commentId, userId);
|
||||
}
|
||||
|
||||
public Long getCommentLikesCount(Long commentId) {
|
||||
return redisTemplate.opsForSet().size(COMMENT_LIKE_KEY_PREFIX + commentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getCommentUnLikesCount(Long commentId) {
|
||||
return redisTemplate.opsForSet().size(COMMENT_UN_LIKE_KEY_PREFIX + commentId);
|
||||
}
|
||||
|
||||
public Long toggleReplyLike(Long replyId, Long userId) {
|
||||
return executeToggle(COMMENT_REPLY_LIKE_KEY_PREFIX + replyId, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long toggleReplyUnLike(Long replyId, Long userId) {
|
||||
return executeToggle(COMMENT_REPLY_UN_LIKE_KEY_PREFIX + replyId, userId);
|
||||
}
|
||||
|
||||
public Long getReplyLikesCount(Long replyId) {
|
||||
return redisTemplate.opsForSet().size(COMMENT_REPLY_LIKE_KEY_PREFIX + replyId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getReplyUnLikesCount(Long replyId) {
|
||||
return redisTemplate.opsForSet().size(COMMENT_REPLY_UN_LIKE_KEY_PREFIX + replyId);
|
||||
}
|
||||
|
||||
private Long executeToggle(String key, Long userId) {
|
||||
return redisTemplate.execute(toggleLikeScript, Collections.singletonList(key), userId);
|
||||
}
|
||||
}
|
@ -23,6 +23,10 @@ public class BookCommentReplyVO extends BookCommentReply {
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
private Long likesCount;
|
||||
|
||||
private Long unLikesCount;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString();
|
||||
|
@ -22,6 +22,10 @@ public class BookCommentVO extends BookComment {
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
private Long likesCount;
|
||||
|
||||
private Long unLikesCount;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString();
|
||||
|
Reference in New Issue
Block a user