目录
实现功能
• 点赞
- 支持对帖子、评论点赞。
- 第1次点赞,第2次取消点赞。
controller
@Controller
public class LikeController {
@Autowired
private LikeService likeService;
@Autowired
private HostHolder hostHolder;
@RequestMapping(value = "/like",method = RequestMethod.POST)
@ResponseBody
public String like(int entityType,int entityId){
User user = hostHolder.getUser();
//点赞
likeService.like(user.getId(),entityType,entityId);
//获取点赞数量
long likeCount = likeService.findEntityLikeCount(entityType, entityId);
//获取点赞状态
int likeStatus = likeService.findEntityLikeStatus(user.getId(), entityType, entityId);
//返回的结果
Map<String,Object> map=new HashMap<>();
map.put("likeCount",likeCount);
map.put("likeStatus",likeStatus);
return CommunityUtil.getJsonString(0,null,map);
}
}
service
@Service
public class LikeService {
@Autowired
private RedisTemplate redisTemplate;
//给帖子点赞
public void like(int userId,int entityType,int entityId){
//配置点赞key
String entityLikeKey= RedisKeyUtil.getEntityLikeKey(entityType,entityId);
Boolean isMember = redisTemplate.opsForSet().isMember(entityLikeKey, userId);
if(isMember){
//判断该用户已点过赞
redisTemplate.opsForSet().remove(entityLikeKey,userId);
}else{
//该用户未点过赞
redisTemplate.opsForSet().add(entityLikeKey,userId);
}
}
//查询某实体点赞的数量
public long findEntityLikeCount(int entityType,int entityId){
//配置点赞key
String entityLikeKey= RedisKeyUtil.getEntityLikeKey(entityType,entityId);
return redisTemplate.opsForSet().size(entityLikeKey);
}
//查询某人对某实体点赞状态:已赞-->1;未赞--->0
public int findEntityLikeStatus(int userId,int entityType,int entityId){
//配置点赞key
String entityLikeKey= RedisKeyUtil.getEntityLikeKey(entityType,entityId);
return redisTemplate.opsForSet().isMember(entityLikeKey,userId)?1:0;
}
html
<!--帖子赞-->
<a href="javascript:;" th:onclick="|like(this,1,${post.id});|" class="text-primary">
<b th:text="${likeStatus==0?'赞':'已赞'}">赞</b>
<i th:text="${likeCount}">11</i>
</a>
<!--评论赞-->
<a href="javascript:;" th:onclick="|like(this,2,${cvo.comment.id});|" class="text-primary">
<b th:text="${cvo.likeStatus==0?'赞':'已赞'}">赞</b>
(<i th:text="${cvo.likeCount}">1</i>)
</a>
<!--回复赞-->
<a href="javascript:;" th:onclick="|like(this,2,${rvo.reply.id});|" class="text-primary">
<b th:text="${rvo.likeStatus==0?'赞':'已赞'}">赞</b>
(<i th:text="${rvo.likeCount}">1</i>)
</a>
js
function like(btn,entityType,entityId){
$.post(
CONTEXT_PATH+"/like",
{"entityType":entityType,"entityId":entityId},
function(data){
data=$.parseJSON(data);
if(data.code==0){
$(btn).children("i").text(data.likeCount);
$(btn).children("b").text(data.likeStatus==1?'已赞':'赞');
}else{
alert(data.msg);
}
}
);
}
• 首页点赞数量
- 统计帖子的点赞数量。
controller
@RequestMapping(path = "/index",method = RequestMethod.GET)
public String getIndexPage(Model model, Page page){
//在SpringMVC中,方法参数都是由DispatcherServlet初始化的,
//还会额外把Page对象装进Model中
/*
在方法调用前,SpringMVC会自动实例化Model和Page,并将Page注入Model中
所以在thymeleaf模板中就可以直接访问Page对象中的数据
*/
page.setRows(discussPostService.findDiscussPostRows(0));//设置总行数
page.setPath("/index");//设置返回路径
//首先获取帖子信息
List<DiscussPost> list = discussPostService.findDiscussPosts(0, page.getOffset(), page.getLimit());
List<Map<String,Object>> discussPosts=new ArrayList<>();
for(DiscussPost post:list){
Map<String,Object> map=new HashMap<>();
map.put("post",post);
User user = userService.findUserById(post.getUserId());//再通过获取的帖子信息的UserID找到User完整信息
map.put("user",user);
//获取点赞数量
long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, post.getId());
map.put("likeCount",likeCount);
discussPosts.add(map);
}
model.addAttribute("discussPosts",discussPosts);
//通过
return "/index";
}
html
<!-- 帖子列表 -->
<ul class="list-unstyled">
<li class="media pb-3 pt-3 mb-3 border-bottom" th:each="map:${discussPosts}">
<a href="site/profile.html">
<img th:src="${map.user.headerUrl}" class="mr-4 rounded-circle" alt="用户头像" style="width:50px;height:50px;">
</a>
<div class="media-body">
<h6 class="mt-0 mb-3">
<a th:href="@{|/discuss/detail/${map.post.id}|}" th:utext="${map.post.title}">备战春招,面试刷题跟他复习,一个月全搞定!</a>
<span class="badge badge-secondary bg-primary" th:if="${map.post.type==1}">置顶</span>
<span class="badge badge-secondary bg-danger" th:if="${map.post.status==1}">精华</span>
</h6>
<div class="text-muted font-size-12">
<u class="mr-3" th:utext="${map.user.userName}">寒江雪</u> 发布于 <b th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</b>
<ul class="d-inline float-right">
<li class="d-inline ml-2">
赞 <span th:text="${map.likeCount}"> 11</span>
</li>
<li class="d-inline ml-2">|</li>
<li class="d-inline ml-2">回帖 <span th:text="${map.post.commentCount}">7</span> </li>
</ul>
</div>
</div>
</li>
</ul>
• 详情页点赞数量
- 统计点赞数量。
- 显示点赞状态。
controller
@RequestMapping(value = "/detail/{id}" ,method = RequestMethod.GET)
public String findDiscussPosts(@PathVariable int id, Model model, Page page){
//帖子
DiscussPost post = discussPostService.findDiscussPostById(id);
model.addAttribute("post",post);
//发帖作者(帖子内还要显示作者相关信息:后续可以通过redis数据库进行访问
User user = userService.findUserById(post.getUserId());
model.addAttribute("user",user);
//点赞数量
long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, id);
model.addAttribute("likeCount",likeCount);
//点赞状态:未登录状态只显示赞的数量不显示赞的状态
int likeStatus = hostHolder.getUser()==null?0:
likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_POST, id);
model.addAttribute("likeStatus",likeStatus);
//评论分页信息
page.setLimit(5); //一页显示五条评论
page.setPath("/discuss/detail/"+id);//设置分页查询路径
page.setRows(post.getCommentCount());//一共有多少条帖子
// 评论: 给帖子的评论
// 回复: 给评论的评论
// 评论列表
List<Comment> commentList = commentService.findCommentByEntity(
ENTITY_TYPE_POST, post.getId(), page.getOffset(), page.getLimit());
//评论VO列表
List<Map<String,Object>> commentVOList=new ArrayList<>();
if(commentList != null){
for(Comment comment : commentList){
Map<String,Object> commentVO=new HashMap<>();
//评论
commentVO.put("comment",comment);//加入评论信息
//将评论的作者也加入map中
commentVO.put("user",userService.findUserById(comment.getUserId()));
//点赞数量
likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT, comment.getId());
commentVO.put("likeCount",likeCount);
//点赞状态:未登录状态只显示赞的数量不显示赞的状态
likeStatus = hostHolder.getUser()==null?0:
likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_COMMENT, comment.getId());
commentVO.put("likeStatus",likeStatus);
//回复
List<Comment> replyList = commentService.findCommentByEntity(
ENTITY_TYPE_COMMENT, comment.getId(),0, Integer.MAX_VALUE);
//回复VO列表
List<Map<String,Object>> replyVOList=new ArrayList<>();
if(replyList!=null){
for(Comment reply:replyList){
Map<String,Object> replyVO=new HashMap<>();
//加入回复信息
replyVO.put("reply",reply);
//加入回复作者
replyVO.put("user",userService.findUserById(reply.getUserId()));
//回复目标:给谁回复
User target = reply.getTargetId() == 0 ? null : userService.findUserById(reply.getTargetId());
replyVO.put("target",target);
//点赞数量
likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT, reply.getId());
replyVO.put("likeCount",likeCount);
//点赞状态:未登录状态只显示赞的数量不显示赞的状态
likeStatus = hostHolder.getUser()==null?0:
likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_COMMENT, reply.getId());
replyVO.put("likeStatus",likeStatus);
replyVOList.add(replyVO);
}
}
commentVO.put("replys",replyVOList);
//回复数量
int replyCount = commentService.findCountByEntity(ENTITY_TYPE_COMMENT, comment.getId());
commentVO.put("replyCount",replyCount);
commentVOList.add(commentVO);
}
}
model.addAttribute("comments",commentVOList);
return "/site/discuss-detail";
}
html
<!-- 第1条回帖 -->
<li class="media pb-3 pt-3 mb-3 border-bottom" th:each="cvo:${comments}">
<a href="profile.html">
<img th:src="${cvo.user.headerUrl}" class="align-self-start mr-4 rounded-circle user-header" alt="用户头像" >
</a>
<div class="media-body">
<div class="mt-0">
<span class="font-size-12 text-success" th:utext="${cvo.user.userName}">掉脑袋切切</span>
<span class="badge badge-secondary float-right floor">
<i th:text="${page.offset+cvoStat.count}">1</i>#
</span>
</div>
<div class="mt-2" th:utext="${cvo.comment.content}">
这开课时间是不是有点晚啊。。。
</div>
<div class="mt-4 text-muted font-size-12">
<span>发布于 <b th:text="${#dates.format(cvo.comment.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</b></span>
<ul class="d-inline float-right">
<li class="d-inline ml-2">
<a href="javascript:;" th:onclick="|like(this,2,${cvo.comment.id});|" class="text-primary">
<b th:text="${cvo.likeStatus==0?'赞':'已赞'}">赞</b>(<i th:text="${cvo.likeCount}">1</i>)
</a>
</li>
<li class="d-inline ml-2">|</li>
<li class="d-inline ml-2"><a href="#" class="text-primary">回复(<i th:text="${cvo.replyCount}">2</i>)</a></li>
</ul>
</div>
<!-- 回复列表 -->
<ul class="list-unstyled mt-4 bg-gray p-3 font-size-12 text-muted">
<li class="pb-3 pt-3 mb-3 border-bottom" th:each="rvo:${cvo.replys}">
<div>
<span th:if="${rvo.target==null}">
<b class="text-info" th:text="${rvo.user.userName}">寒江雪</b>:
</span>
<span th:if="${rvo.target!=null}">
<i class="text-info" th:text="${rvo.user.userName}">Sissi</i>回复
<b class="text-info" th:text="${rvo.target.userName}">寒江雪</b>:
</span>
<span th:utext="${rvo.reply.content}">这个是直播时间哈,觉得晚的话可以直接看之前的完整录播的~</span>
</div>
<div class="mt-3">
<span th:text="${#dates.format(rvo.reply.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</span>
<ul class="d-inline float-right">
<li class="d-inline ml-2">
<a href="javascript:;" th:onclick="|like(this,2,${rvo.reply.id});|" class="text-primary">
<b th:text="${rvo.likeStatus==0?'赞':'已赞'}">赞</b>(<i th:text="${rvo.likeCount}">1</i>)
</a>
</li>
<li class="d-inline ml-2">|</li>
<li class="d-inline ml-2"><a th:href="|#huifu-${rvoStat.count}|" data-toggle="collapse" class="text-primary">回复</a></li>
</ul>
<div th:id="|huifu-${rvoStat.count}|" class="mt-4 collapse">
<form method="post" th:action="@{|/comment/add/${post.id}|}">
<div>
<input type="text" class="input-size" name="content" th:placeholder="|回复${rvo.user.userName}|"/>
<input type="hidden" name="entityType" value="2">
<input type="hidden" name="entityId" th:value="${cvo.comment.id}">
<input type="hidden" name="targetId" th:value="${rvo.user.id}">
</div>
<div class="text-right mt-2">
<button type="submit" class="btn btn-primary btn-sm" onclick="#"> 回 复 </button>
</div>
</form>
</div>
</div>
</li>