项目1在线交流平台-3.开发交流社区核心功能模块-5.显示评论-分级显示策略

参考牛客网高级项目教程

功能需求及处理策略

在这里插入图片描述

在这里插入图片描述

  • 1.在帖子详情页面中,显示关于这条帖子的评论交流动态

    • 即,要显示评论这条帖子的评论,并显示对应评论者的基本信息
      • 要分页展现
      • 并按照评论时间先后分楼层展现
    • 还要,显示针对每条评论的回复,即展现评论者们的交流动态
      • 为明确谁回复谁,要显示指明对象
  • 2.因此,帖子评论是有级别的,为了区分处理不同级别的评论,

    • 不是采用递归策略,这样比较消耗内存和性能
    • 而是采用指针指向上级评论或帖子id的策略,方便管理维护各级评论

2. Dao层处理数据

1)设计评论数据表

分级策略实现-处理评论和一级回复

为方便描述区分:现设定:

  • 评论:指的是对帖子的评论,只有一级

  • 回复:指的是对评论的评论

    • 一级回复:指的是对评论的回复
    • 非一级回复:指的是指向另一条回复的回复
  • entity_type:评论的目标类型-即上级类型:今后可以扩展,比如对题目、视频等的评论等等

    • 帖子:1
    • 评论:2
  • entity_id:评论的目标id-即上级id

    • 这样,通过上级的类型和id,可以查询到下级所有相关的评论
    • 下级的评论也可以明确指向上级的哪一条评论
分级策略实现-处理非一级回复
  • 非一级回复,只回复target_id指向的回复

    • 因此不严格规定级别,只严格规定target_id指向,维护这个指针即可
  • target_id,回复目标的作者用户的id

    • 0,不设置默认为0,无效的指针,指的是评论和一级回复的目标指向

      • 因评论目标指向已经用entity_type=1,entity_id维护
      • 一级回复的目标指向已经用entity_type=2,entity_id维护,故无需再用target_id维护
    • 其他非0值:即目标回复的id

用status表示评论是否删除的状态
  • status,评论是否有效的状态
    • 0, 表示有效状态,需要显示出来
    • 1,表示无效状态,无需显示出来
CREATE TABLE `comment` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `entity_type` int(11) DEFAULT NULL,
  `entity_id` int(11) DEFAULT NULL,
  `target_id` int(11) DEFAULT NULL,
  `content` text,
  `status` int(11) DEFAULT NULL,
  `create_time` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_user_id` (`user_id`) /*!80000 INVISIBLE */,
  KEY `index_entity_id` (`entity_id`)
) ENGINE=InnoDB AUTO_INCREMENT=232 DEFAULT CHARSET=utf8

2)接口定义、sql编写

接口中方法声明
  • 为分页显示评论列表信息
    • 根据实体目标类型id分页查询所有评论列表
  • 为分页需要知道要分多少页,需要知道指定目标下的评论总数
    • 查询实体目标类型id下的评论数量
@Mapper
public interface CommentMapper {
    /** 根据实体目标类型id分页查询所有评论列表 */
    List<Comment> selectCommentList(int entityType, int entityId, int offset, int limit);
    
    /** 查询实体目标类型id下的评论数量 */
    int selectComments(int entityType, int entityId);
    
}
sql编写
  • 注意过滤条件,即status要有效
  • 排序规定,默认按照回复的时间正序排列,最先评论的在1楼
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nowcoder.community.dao.CommentMapper">
<!--    定义复用代码块-->
    <sql id="selectFields">
        select id, user_id, entity_type, entity_id, target_id, content, status, create_time from comment
    </sql>
    <sql id="insertFields">
        insert into comment(user_id, entity_type, entity_id, target_id, content, status, create_time)
          values (#{userId}, #{entityType}, #{entityId}, #{targetId}, #{content}, #{status}, #{createTime})
    </sql>

<!--    查询指定目标类型id的评论列表-->
    <select id="selectCommentList" resultType="comment">
        <include refid="selectFields"></include>
        where status = 0
        and entity_type = #{entityType}
        and entity_id = #{entityId}
        order by create_time asc
        limit #{offset}, #{limit}
    </select>

<!--    查询目标类型id一共有多少条评论-->
<!--    返回类型必须要写-->
    <select id="selectComments" resultType="int">
        select count(id) from comment
        where status = 0
        and entity_type = #{entityType}
        and entity_id = #{entityId}
    </select>

</mapper>

2. service层处理分页查询业务

@Service
public class CommentService {
    @Autowired
    CommentMapper commentMapper;

    /** 根据实体目标类型id分页查询所有评论列表 */
    public List<Comment> selectCommentList(int entityType, int entityId, int offset, int limit) {
        return commentMapper.selectCommentList(entityType, entityId, offset, limit);
    }

    /** 查询实体目标类型id下的评论数量 */
    public int selectComments(int entityType, int entityId) {
        return commentMapper.selectComments(entityType, entityId);
    }
}

3. Controller层处理查询请求

处理页面设置

// 处理分页信息-设置页面
page.setLimit(5);
page.setPath("/discuss/detail/" + postId);
page.setRows(discussPost.getCommentCount());    // 直接从帖子表中拿,减少查询次数

分页查询帖子下的所有评论信息

// 查询帖子的评论分页信息-将关联用户信息一并封装
List<Comment> commentList = commentService.selectCommentList(
        ENTITY_TYPE_POST, discussPost.getId(), page.getOffset(), page.getLimit());
List<Map<String, Object>> commentVoList = new ArrayList<>();
if(commentList != null) {   // 有可能没有评论,注意边界条件
    for(Comment comment : commentList) {
        Map<String, Object> commentVoMap = new HashMap<>(); 
        commentVoMap.put("comment", comment);
        commentVoMap.put("user", userService.findUserById(comment.getUserId()));
        
        commentVoList.add(commentVoMap);
    }
}
model.addAttribute("comments", commentVoList);

显示每条评论下的所有回复信息

// 根据每条评论的id,循环查询每条评论下的所有回复信息
List<Comment> replyList = commentService.selectCommentList(
        ENTITY_TYPE_COMMENT, comment.getId(), 0, Integer.MAX_VALUE);
List<Map<String, Object>> replyVoList = new ArrayList<>(); // 封装list
if(replyList != null) {
    for(Comment reply : replyList) {
        Map<String, Object> replyVoMap = new HashMap<>();
        replyVoMap.put("reply", reply);
        replyVoMap.put("user", userService.findUserById(reply.getUserId()));
        // 回复中需要用到targetId
        // 找到回复目标用户作者-注意,如果targetId=0,表示无效,无需查询
        User targetUser = reply.getTargetId() == 0 ? null : 
                userService.findUserById(reply.getTargetId());
        replyVoMap.put("targetUser", targetUser);
        
        replyVoList.add(replyVoMap);
    }
}

commentVoMap.put("replys",replyVoList); // 将当前评论的所有回复封装数据放进评论map中
// 每条评论的回复数量统计
int replyCount = commentService.selectComments(ENTITY_TYPE_COMMENT, 		                                                         comment.getId());
commentVoMap.put("replyCount", replyCount);

整体代码如下:

// 处理分页信息-设置页面
page.setLimit(5);
page.setPath("/discuss/detail/" + postId);
page.setRows(discussPost.getCommentCount());    // 直接从帖子表中拿,减少查询次数

// 查询帖子的评论分页信息-将关联用户信息一并封装
List<Comment> commentList = commentService.selectCommentList(
        ENTITY_TYPE_POST, discussPost.getId(), page.getOffset(), page.getLimit());
List<Map<String, Object>> commentVoList = new ArrayList<>();
if(commentList != null) {   // 有可能没有评论,注意边界条件
    for(Comment comment : commentList) {
        Map<String, Object> commentVoMap = new HashMap<>(); 
        commentVoMap.put("comment", comment);
        commentVoMap.put("user", userService.findUserById(comment.getUserId()));
        
        // 每条评论的回复数量统计
        int replyCount = commentService.selectComments(ENTITY_TYPE_COMMENT, 		                                                         comment.getId());
        commentVoMap.put("replyCount", replyCount);
        // 根据每条评论的id,循环查询每条评论下的所有回复信息
        List<Comment> replyList = commentService.selectCommentList(
                ENTITY_TYPE_COMMENT, comment.getId(), 0, Integer.MAX_VALUE);
        List<Map<String, Object>> replyVoList = new ArrayList<>(); // 封装list
        if(replyList != null) {
            for(Comment reply : replyList) {
                Map<String, Object> replyVoMap = new HashMap<>();
                replyVoMap.put("reply", reply);
                replyVoMap.put("user", userService.findUserById(reply.getUserId()));
                // 回复中需要用到targetId
                // 找到回复目标用户作者-注意,如果targetId=0,表示无效,无需查询
                User targetUser = reply.getTargetId() == 0 ? null : 
                        userService.findUserById(reply.getTargetId());
                replyVoMap.put("targetUser", targetUser);
                
                replyVoList.add(replyVoMap);
            }
        }
        
        commentVoMap.put("replys",replyVoList); // 将当前评论的所有回复封装数据放进评论map中
        
        commentVoList.add(commentVoMap);
    }
}
model.addAttribute("comments", commentVoList);

4. 处理View视图模板

主页中,每条帖子列表动态显示回帖数量

<li class="d-inline ml-2">回帖 <span th:text="${map.post.commentCount}">7</span></li>

帖子详情页面处理

帖子的内容栏,也要显示回帖数量
<li class="d-inline ml-2"><a href="#replyform" class="text-primary">回帖 <span th:text="${post.commentCount}">7</span></a></li
回帖显示栏
总数目显示
<h6><b class="square"></b> <i th:text="${post.commentCount}">30</i>条回帖</h6>
回帖列表分页显示
cvoStat.count
  • cvoStat.count:相当于for循环里的i,记录循环的次数
    • cvoStat,cvo是自定义的循环集合,后拼接Stat.count,记录循环次数
<!-- 分页遍历显示回帖 -->
<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:text="${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="#" class="text-primary">赞(1)</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.targetUser==null}">
            <b class="text-info" th:text="${rvo.user.username}">寒江雪</b>:&nbsp;&nbsp;
         </span>
         <span th:if="${rvo.targetUser!=null}">
            <i class="text-info" th:text="${rvo.user.username}">Sissi</i> 回复
            <b class="text-info" th:text="${rvo.targetUser.username}">寒江雪</b>:&nbsp;&nbsp;
         </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="pagination justify-content-center" th:replace="index::pagination">

测试结果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值