携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情
3.5 显示评论
数据访问层(dao)
- 根据实体查询一页评论数据
- 根据实体查询评论的数量
数据库中的评论表
CREATE TABLE `comment` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`entity_type` int(11) DEFAULT NULL, # 回复的类型,1代表回复帖子,2代表回复评论
`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`),
KEY `index_entity_id` (`entity_id`)
) ENGINE=InnoDB AUTO_INCREMENT=232 DEFAULT CHARSET=utf8;
/*
entity_type 代表评论的类型:比如 评论帖子、评论别人的评论等
entity_id 代表评论的帖子的 id,比如评论的帖子的 id
target_id 代表指向目标评论的人的 id,比如评论别人的帖子,target_id 表示评论的人的 id
content 代表评论的内容
status 代表评论的状态,0 代表正常,1 代表删除或者不可用
*/
comment表对应的实体类
public class Comment {
private int id;
private int userId;
private int entityType;
private int entityId;
private int targetId;
private String content;
private int status;
private Date createTime;
// 为了以免影响阅读体验,get、set、toString方法没有粘上来,但其实是有的
}
dao接口:
@Mapper
public interface CommentMapper {
/*
分页查询评论
参数:1.评论的类型 2.评论的是哪个评论的id 3.起始页 4.每页限制条数
*/
List<Comment> selectCommentsByEntity(int entityType, int entityId, int offset, int limit);
/*
查询评论的总条数
参数:1.评论的类型 2.评论的是哪个评论的id
*/
int selectCountByEntity(int entityType, int entityId);
}
dao接口对应的mapper配置文件:
<?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">
id, user_id, entity_type, entity_id, target_id, content, status, create_time
</sql>
<select id="selectCommentsByEntity" resultType="com.nowcoder.community.entity.Comment" >
select <include refid="selectFields"></include>
from comment
where status = 0 <!--status=0表示这个数据是有效的-->
and entity_type = #{entityType}
and entity_id = #{entityId}
order by create_time
limit #{offset}, #{limit}
</select>
<select id="selectCountByEntity" resultType="Integer" >
select count(id)
from comment
where status = 0
and entity_type = #{entityType}
and entity_id = #{entityId}
</select>
</mapper>
业务层(Service)
- 处理查询评论的业务
- 处理查询评论数量的业务
CommentService:
@Service
public class CommentService {
@Autowired
private CommentMapper commentMapper;
public List<Comment> findCommentsByEntity(int entityType, int entityId, int offset, int limit) {
return commentMapper.selectCommentsByEntity(entityType, entityId, offset, limit);
}
public int findCommentCount(int entityType, int entityId) {
return commentMapper.selectCountByEntity(entityType, entityId);
}
}
表现层(Controller和themeleaf模板)
- 显示帖子详情数据时,同时显示该帖子所有的评论数据
CommunityConstant 常量接口中设置两个常量分别表示评论的类型:
/**
* 回复的实体类型:帖子
*/
int ENTITY_TYPE_POST = 1;
/**
* 回复的实体类型:评论
*/
int ENTITY_TYPE_COMMENT = 2;
对 DiscussPostController 中的getDiscussPost方法补充一些代码去在页面上展示具体的评论
@Component
@RequestMapping("/discuss")
public class DiscussPostController implements CommunityConstant {
@Autowired
private DiscussPostService discussPostService;
@Autowired
private HostHolder hostHolder;
@Autowired
private UserService userService;
@Autowired
private CommentService commentService;
@RequestMapping(path = "/add", method = RequestMethod.POST)
@ResponseBody
public String addDiscussPost(String title, String content){
User user = hostHolder.getUser();
if(user == null){
// 如果没有登录,直接返回错误信息
// 403 代表没有权限
return CommunityUtil.getJSONString(403, "你还没有登录!");
}
// 可以执行到这里说明已经登录了
DiscussPost post = new DiscussPost();
post.setUserId(user.getId());
post.setTitle(title);
post.setContent(content);
post.setCreateTime(new Date());
// 帖子类型和状态、得分等默认就是0,不用设置
discussPostService.addDiscussPost(post);
// 报错的情况将来统一处理
return CommunityUtil.getJSONString(0, "发布成功!"); // 0 表示是一个正确的状态
}
@RequestMapping(path = "/detail/{discussPostId}", method = RequestMethod.GET)
public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model, Page page){
// 由id查询帖子
DiscussPost post = discussPostService.findDiscussPostById(discussPostId);
model.addAttribute("post", post);
// 由于discussPost显示的只有用户的id,我们显示在页面上的肯定是用户的username而不是id,所以我们还需要查一下username
User user = userService.findUserById(post.getUserId());
model.addAttribute("user", user);
/*
评论:给帖子的评论
回复:给评论的评论,因为不止帖子有评论,评论也可能有评论
*/
page.setLimit(5); // 设置每页显示的条数
page.setPath("/discuss/detail/" + discussPostId); // 设置查询的controller方法路径
page.setRows(post.getCommentCount()); // 一共有多少条评论的数据
// 评论列表,因为评论里面有user_id、target_id 我们还需要查user表查到username
List<Comment> commentList = commentService.findCommentsByEntity(
ENTITY_TYPE_POST, post.getId(), page.getOffset(), page.getLimit()
);
// 评论VO列表 (VO 意思是在页面上显示的对象 View Object )
List<Map<String, Object>> commentVoList = new ArrayList<>();
if(commentList != null){
for (Comment comment : commentList) {
// 每个评论的VO
Map<String, Object> commentVo = new HashMap<>();
// 评论
commentVo.put("comment", comment);
// 作者
commentVo.put("user", userService.findUserById(comment.getUserId()));
// 回复列表
List<Comment> replyList = commentService.findCommentsByEntity(
ENTITY_TYPE_COMMENT, comment.getId(), 0, Integer.MAX_VALUE // 这里评论的回复就不分页了
);
// 回复Vo列表
List<Map<String, Object>> replyVoList = new ArrayList<>();
if (replyList != null) {
for (Comment reply : replyList) {
// 评论的每一个回复Vo
Map<String, Object> replyVo = new HashMap<>();
// 回复
replyVo.put("reply", reply);
// 作者
replyVo.put("user", userService.findUserById(reply.getUserId()));
// 回复目标,等于0表示只是普通的评论,
User target = reply.getTargetId() == 0 ? null : userService.findUserById(reply.getTargetId());
replyVo.put("target", target);
replyVoList.add(replyVo);
}
}
commentVo.put("replys", replyVoList);
// 回复数量
int replyCount = commentService.findCommentCount(ENTITY_TYPE_COMMENT, comment.getId());
commentVo.put("replyCount", replyCount);
commentVoList.add(commentVo);
}
}
model.addAttribute("comments", commentVoList);
return "/site/discuss-detail";
}
}
首先让 DiscussPostController 实现常量接口 CommunityConstant
然后是方法的详细内容
接下来我们就需要处理模板了:
先处理一下首页 index.html 帖子的评论数量和分页逻辑复用:
接下来是处理帖子的详细页面 discuss-detail.html :
cvoStat 状态的隐含对象:循环变量名后面加上 Stat
cvoStat.count 循环到了第几次
测试
测试成功