中软实习培训记录十一(0731)
新闻详情页内的评论功能
实体类设计
@Entity
@Table(name="t_comment")
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nickname;//用户昵称
private String email;//邮箱
private String content;//评论内容
private String avatar;//头像,默认头像
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
//和新闻是一个一一绑定的关系,一个新闻可以有非常多的评论
@ManyToOne
private News news;
//回复的子评论,一条父评论有多条回复的子评论
@OneToMany(mappedBy = "parentComment")
private List<Comment> replyComments=new ArrayList<>();
@ManyToOne
private Comment parentComment;
private boolean adminComment;
public Comment() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public News getNews() {
return news;
}
public void setNews(News news) {
this.news = news;
}
public List<Comment> getReplyComments() {
return replyComments;
}
public void setReplyComments(List<Comment> replyComments) {
this.replyComments = replyComments;
}
public Comment getParentComment() {
return parentComment;
}
public void setParentComment(Comment parentComment) {
this.parentComment = parentComment;
}
public boolean isAdminComment() {
return adminComment;
}
public void setAdminComment(boolean adminComment) {
this.adminComment = adminComment;
}
@Override
public String toString() {
return "Comment{" +
"id=" + id +
", nickname='" + nickname + '\'' +
", email='" + email + '\'' +
", content='" + content + '\'' +
", avatar='" + avatar + '\'' +
", createTime=" + createTime +
", news=" + news +
", replyComments=" + replyComments +
", parentComment=" + parentComment +
", adminComment=" + adminComment +
'}';
}
}
我们在设计评论实体类时添加了新闻表的对应关系,因此在News实体类中也要加上关系,并且添加对应的Getter、Setter函数,更新toString方法。
@OneToMany(mappedBy = "news")
private List<Comment> comments=new ArrayList<>();
评论展示部分
定义评论功能接口
从静态界面可以看到,功能大致分为两个部分,一个是评论的查看,一个是自己评论的保存,上面我们已经完成评论的保存功能,现在开始实现评论的展示【因为评论的保存发布需要得到父评论的信息,因此为了避免一些意外,我先将评论保存的相关方法注释掉】
public interface CommentService {
//通过新闻Id得到该新闻已有的评论数据
//已评数据的展示,需要对内容进行处理,实现回复评论的缩进
List<Comment> listCommentByNewId(Long newId);
//保存评论
// Comment saveComment(Comment comment);
}
在CommentServiceImpl中进行实现
@Service
public class CommentServiceImpl implements CommentService {
@Autowired
private CommentRepository commentRepository;
@Override
public List<Comment> listCommentByNewId(Long newId) {
Sort sort=Sort.by("createTime");
List<Comment> comments=commentRepository.findByNewsIdAndParentCommentNull(newId,sort);
return eachComment(comments);
}
//以下为评论工具类方法
private List<Comment> eachComment(List<Comment> comments){
List<Comment> commentsView=new ArrayList<>();
for(Comment comment:comments){
Comment c=new Comment();
BeanUtils.copyProperties(comment,c);
commentsView.add(c);
}
//合并评论的各层子代到第一级的子代集合中
combineChildren(commentsView);
return commentsView;
}
private void combineChildren(List<Comment> comments){
for(Comment comment:comments){
List<Comment> replys1=comment.getReplyComments();
for(Comment reply1:replys1){
//循环迭代,找出子代
recursively(reply1);
}
comment.setReplyComments(tempReplys);
//清空临时存放区
tempReplys=new ArrayList<>();
}
}
//临时存放子代
private List<Comment> tempReplys=new ArrayList<>();
private void recursively(Comment comment){
tempReplys.add(comment);//顶节点添加到零时存放区
//查看是否有回复
if(comment.getReplyComments().size()>0){
List<Comment> replys=comment.getReplyComments();
//得到的回复是否有回复
for(Comment reply:replys){
tempReplys.add(reply);
if(reply.getReplyComments().size()>0){
recursively(reply);
}
}
}
}
}
定义Repository
根据上述Service方法的定义,对于数据库部分,在CommentRepository定义方法接口
public interface CommentRepository extends JpaRepository<Comment,Long> {
List<Comment> findByNewsIdAndParentCommentNull(Long newId, Sort sort);
}
Controller处理
定义完了数据库部分、定义了Service也已经完成了实现,现在开始在CommentController中进行具体的操作处理。
@GetMapping("/comments/{newId}")
public String comments(@PathVariable Long newId, Model model){
model.addAttribute("comments",commentService.listCommentByNewId(newId));
return "new::commentList";
}
评论保存发布部分
定义评论功能接口
两个部分,一个是评论的查看,一个是自己评论的保存,现在开始实现评论的保存发布:
public interface CommentService {
//通过新闻Id得到该新闻已有的评论数据
//已评数据的展示,需要对内容进行处理,实现回复评论的缩进
List<Comment> listCommentByNewId(Long newId);
//保存评论
Comment saveComment(Comment comment);
}
在CommentServiceImpl中进行实现
@Override
public Comment saveComment(Comment comment) {
//保存父级评论信息
Long parentCommentId=comment.getParentComment().getId();
if(parentCommentId!=-1){
comment.setParentComment(commentRepository.findById(parentCommentId).orElse(null));
}
else{
comment.setParentComment(null);
}
comment.setCreateTime(new Date());
return commentRepository.save(comment);
}
Controller处理
对CommentController中进行操作处理,添加方法post实现评论的发布。
@PostMapping("/comments")
public String post(Comment comment, HttpSession session){
Long newId=comment.getNews().getId();
comment.setNews(newService.getNew(newId));
//需要提取session当中的属性值
User user=(User)session.getAttribute("user");
if(user!=null){
comment.setAdminComment(true);
comment.setAvatar(avatar);
}else{
comment.setAvatar(avatar);
}
commentService.saveComment(comment);
//System.out.println(commentService.saveComment(comment));
return "redirect:/comments/"+newId;
}
}
功能演示
我们可以直接在评论框中输入评论,进行发布,注意昵称金和邮箱也必须填写
发布成功
我们也可以点击具体某一条评论下方回复按钮,就可以看到评论框首先会出现@某个用户,以此表示回复了谁的评论
修改自己的昵称和邮箱,点击发布:
回复成功
主页分类页面功能
在主页上,我们除了首页,我们还设计了“分类”、“标签”、“归档”、“关于我”等部分的浏览
具体地说,对于主页上的“分类”部分,我们希望首先展示所有类别,点击某类别后能显示该类别的新闻信息。
主页分类页面实现
1、构造TypeShowController,其中会涉及到的罗列新闻、新闻排名等方法都已经在之前的首页中有用到过,因此不需要额外的再去定义方法,直接调用即可。
@Controller
public class TypeShowController {
@Autowired
private TypeService typeService;
@Autowired
private NewService newService;
@GetMapping("/types/{id}")
public String types(@PageableDefault(size = 8,sort = {"updateTime"},direction = Sort.Direction.DESC)Pageable pageable,
@PathVariable Long id, Model model){
List<Type> types=typeService.listTypeTop(20);
if(id==-1){
//进入页面,默认显示第一个类别的新闻内容
id=types.get(0).getId();
}
NewQuery newQuery=new NewQuery();
newQuery.setTypeId(id);
model.addAttribute("types",types);
model.addAttribute("page",newService.listNew(pageable,newQuery));
model.addAttribute("activeTypeId",id);
return "types";
}
}
主页分类页面演示
后续继续进行新闻添加,这部分同样会进行更新。
点击选择某一标签下的具体某一篇新闻,同样可以进入新闻的详情页面。
主页标签页面功能
主页标签页面实现
1、标签页面的实现和分类页面类似,但是分类部分因为之前已经实现了通过组合条件查看新闻列表listNew方法,其中包含了NewQuery这个部分,我们不能为了标签页直接去修改NewQuery类的内容,这样很容易造成前面已经实现的很多功能的问题,
因此我们选择在NewService中新定义一个listNew方法,传入的不再是分页和查询条件,而是直接传入tagId和分页
//主页标签页面,根据标签查看新闻
Page<News> listNew(Long tagId,Pageable pageable);
进行实现:
@Override
public Page<News> listNew(Long tagId, Pageable pageable) {
return newRepository.findAll(new Specification<News>() {
@Override
public Predicate toPredicate(Root<News> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Join join=root.join("tags");
return criteriaBuilder.equal(join.get("id"),tagId);
}
},pageable);
}
2、新建一个TagShowController类,该部分与分类页面类似,模仿着写就可完成:
@Controller
public class TagShowController {
@Autowired
private NewService newService;
@Autowired
private TagService tagService;
@GetMapping("/tags/{id}")
public String tags(@PageableDefault(size = 8,sort = {"updateTime"},direction = Sort.Direction.DESC) Pageable pageable,
@PathVariable Long id, Model model){
List<Tag> tags=tagService.listTagTop(20);
if(id==-1){
//进入页面,默认显示第一个类别的新闻内容
id=tags.get(0).getId();
}
// NewQuery newQuery=new NewQuery();
// newQuery.setTypeId(id);
model.addAttribute("tags",tags);
model.addAttribute("page",newService.listNew(id,pageable));
model.addAttribute("activeTagId",id);
return "tags";
}
}
主页标签页面演示
进入标签页面,可以看到页面上会列举出所有我们添加的标签,以及该标签下的新闻篇数,并且刚进入该页面,列举出的新闻是属于第一个标签的。
点击选择某一标签下的具体某一篇新闻,同样可以进入新闻的详情页面。