美文网首页
评论回复功能设计

评论回复功能设计

作者: 盼旺 | 来源:发表于2019-08-29 12:09 被阅读0次

    以评论为主的显示模式,类似于下面的CSDN的评论显示模式

    最终效果图

    效果图

    将评论拆分为评论表和回复表,评论挂在各种主题下面,而回复挂在评论下面
    我是采用的Jpa建表,所以只需要把实体对象写好就行

    评论表
    回复表

    先上项目结构图

    项目结构图

    说一下我的思路

    1.建立俩张表,回复表回复的id分为回复评论还是回复,用一个int标志判断。
    2.想像树状那样显示出来,这里就采取链表的形式存储,一条评论下可能有多人回复,所以存储下一个对象我们使用List来存储,开始的List初始化为private List<ReplayNode> replays = new ArrayList<>();不然replays.add()的时候会报空指针.
    3.因为评论和回复是分开建表的,所以我们还需要单独设置一个评论节点,分别对应上面项目结构图的TopicNodeReplayNode
    4.插入链表和遍历链表都是采用递归方式,有更好的方式欢迎指教。
    5.thymeleaf写递归方式和java一样的思路,就是改变了语法而已

    每个文件源码和注释

    Topic

    package com.wg.springdemo.model;
    import javax.persistence.*;
    @Entity
    @Table(name = "topic")
    public class Topic {
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        private Long tid;//主键ID
        private Long uid;//用户ID
        @Column(length = 50)
        private String uname;//用户名
        @Column(length = 255)
        private String uheadurl;//用户头像
        @Column(length = 800)
        private String tcontent;//评论内容
        @Column(length = 20)
        private String ttime;//评论时间
        public Topic() {
        }
        public Topic(Long uid, String uname, String uheadurl, String tcontent, String ttime) {
            this.uid = uid;
            this.uname = uname;
            this.uheadurl = uheadurl;
            this.tcontent = tcontent;
            this.ttime = ttime;
        }
        public Long getTid() {
            return tid;
        }
        public void setTid(Long tid) {
            this.tid = tid;
        }
        public Long getUid() {
            return uid;
        }
        public void setUid(Long uid) {
            this.uid = uid;
        }
        public String getUname() {
            return uname;
        }
        public void setUname(String uname) {
            this.uname = uname;
        }
        public String getUheadurl() {
            return uheadurl;
        }
        public void setUheadurl(String uheadurl) {
            this.uheadurl = uheadurl;
        }
        public String getTcontent() {
            return tcontent;
        }
        public void setTcontent(String tcontent) {
            this.tcontent = tcontent;
        }
        public String getTtime() {
            return ttime;
        }
        public void setTtime(String ttime) {
            this.ttime = ttime;
        }
        @Override
        public String toString() {
            return "Topic{" +
                    "tid=" + tid +
                    ", uid=" + uid +
                    ", uname='" + uname + '\'' +
                    ", uheadurl='" + uheadurl + '\'' +
                    ", tcontent='" + tcontent + '\'' +
                    ", ttime='" + ttime + '\'' +
                    '}';
        }
    }
    

    Replay

    package com.wg.springdemo.model;
    import javax.persistence.*;
    @Entity
    @Table(name = "replay")
    public class Replay {
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        private Long rid;
        private Long torrid;//评论id或者回复id  就是有可能是回复评论,也可能是给回复的回复
        private int torr;//用来判断上面的哪种情况:1表示回复评论 0表示回复回复
        private Long uid;//用户ID
        private Long touid;//目标用户id 给谁回复
        @Column(length = 50)
        private String uname;//用户名
        @Column(length = 50)
        private String touname;//目标用户名
        @Column(length = 255)
        private String uheadurl;//用户头像
        @Column(length = 800)
        private String rcontent;//回复内容
        @Column(length = 20)
        private String rtime;//回复时间
        public Replay() {
        }
        public Replay(Long torrid, int torr, Long uid, Long touid, String uname, String touname, String uheadurl, String rcontent, String rtime) {
            this.torrid = torrid;
            this.torr = torr;
            this.uid = uid;
            this.touid = touid;
            this.uname = uname;
            this.touname = touname;
            this.uheadurl = uheadurl;
            this.rcontent = rcontent;
            this.rtime = rtime;
        }
        public Long getRid() {
            return rid;
        }
        public void setRid(Long rid) {
            this.rid = rid;
        }
        public Long getTorrid() {
            return torrid;
        }
        public void setTorrid(Long torrid) {
            this.torrid = torrid;
        }
        public int getTorr() {
            return torr;
        }
        public void setTorr(int torr) {
            this.torr = torr;
        }
        public Long getUid() {
            return uid;
        }
        public void setUid(Long uid) {
            this.uid = uid;
        }
        public Long getTouid() {
            return touid;
        }
        public void setTouid(Long touid) {
            this.touid = touid;
        }
        public String getUname() {
            return uname;
        }
        public void setUname(String uname) {
            this.uname = uname;
        }
        public String getTouname() {
            return touname;
        }
        public void setTouname(String touname) {
            this.touname = touname;
        }
        public String getUheadurl() {
            return uheadurl;
        }
        public void setUheadurl(String uheadurl) {
            this.uheadurl = uheadurl;
        }
        public String getRcontent() {
            return rcontent;
        }
        public void setRcontent(String rcontent) {
            this.rcontent = rcontent;
        }
        public String getRtime() {
            return rtime;
        }
        public void setRtime(String rtime) {
            this.rtime = rtime;
        }
        @Override
        public String toString() {
            return "Replay{" +
                    "rid=" + rid +
                    ", torrid=" + torrid +
                    ", torr=" + torr +
                    ", uid=" + uid +
                    ", touid=" + touid +
                    ", uname='" + uname + '\'' +
                    ", touname='" + touname + '\'' +
                    ", uheadurl='" + uheadurl + '\'' +
                    ", rcontent='" + rcontent + '\'' +
                    ", rtime='" + rtime + '\'' +
                    '}';
        }
    }
    

    TopicDao

    package com.wg.springdemo.dao;
    import com.wg.springdemo.model.Topic;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    @Repository
    public interface TopicDao extends JpaRepository<Topic,Long> {
    }
    

    ReplayDao

    package com.wg.springdemo.dao;
    import com.wg.springdemo.model.Replay;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    import java.util.List;
    @Repository
    public interface ReplayDao extends JpaRepository<Replay,Long> {
        //找到所有对某评论的回复 或者对 某回复 的所有回复
        List<Replay> findByTorridAndTorr(Long torrid,int tr);
    }
    

    TopicService

    package com.wg.springdemo.service;
    import com.wg.springdemo.dao.TopicDao;
    import com.wg.springdemo.model.Topic;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import java.util.List;
    @Service
    public class TopicService {
        @Autowired
        TopicDao topicDao;
        //存入一条评论
        public Topic InsertOneTopic(Topic t){
            return topicDao.save(t);
        }
        //查找所有评论
        public List<Topic> FindAllTopic(){
            return topicDao.findAll();
        }
    }
    

    ReplayService

    package com.wg.springdemo.service;
    import com.wg.springdemo.dao.ReplayDao;
    import com.wg.springdemo.model.Replay;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import java.util.List;
    @Service
    public class ReplayService {
        @Autowired
        ReplayDao replayDao;
        //插入一条回复
        public Replay InsertOneReplay(Replay r){
            return replayDao.save(r);
        }
        //找到对某个评论的所有回复
        public List<Replay> FindAllByTid(Long tid){
            return replayDao.findByTorridAndTorr(tid,1);
        }
        //找到对某个回复的所有回复
        public List<Replay> FindAllByRid(Long rid){
            return replayDao.findByTorridAndTorr(rid,0);
        }
    }
    

    TopicNode

    package com.wg.springdemo.util;
    import com.wg.springdemo.model.Replay;
    import com.wg.springdemo.model.Topic;
    import java.util.ArrayList;
    import java.util.List;
    public class TopicNode {
        private Topic topic;
        private List<ReplayNode> replays = new ArrayList<>();
        public TopicNode() {
        }
        public Topic getTopic() {
            return topic;
        }
        public void setTopic(Topic topic) {
            this.topic = topic;
        }
        public List<ReplayNode> getReplays() {
            return replays;
        }
        public void setReplays(List<ReplayNode> replays) {
            this.replays = replays;
        }
    }
    

    ReplayNode

    package com.wg.springdemo.util;
    import com.wg.springdemo.model.Replay;
    import java.util.ArrayList;
    import java.util.List;
    public class ReplayNode {
        private Replay replay;
        private List<ReplayNode> listreplay = new ArrayList<>();
        public ReplayNode() {
        }
        public Replay getReplay() {
            return replay;
        }
        public void setReplay(Replay replay) {
            this.replay = replay;
        }
        public List<ReplayNode> getListreplay() {
            return listreplay;
        }
        public void setListreplay(List<ReplayNode> listreplay) {
            this.listreplay = listreplay;
        }
    }
    
    添加测试数据
    package com.wg.springdemo;
    import com.wg.springdemo.model.Replay;
    import com.wg.springdemo.model.Topic;
    import com.wg.springdemo.service.ReplayService;
    import com.wg.springdemo.service.TopicService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class SpringdemoApplicationTests {
        @Autowired
        ReplayService replayService;
        @Autowired
        TopicService topicService;
        @Test
        public void contextLoads() {
            topicService.InsertOneTopic(new Topic( 1L,"小红","1.jpg","这是第一个评论","2018-01-02"));
            topicService.InsertOneTopic(new Topic( 2L,"小黄","1.jpg","这是第二个评论","2018-01-02"));
            topicService.InsertOneTopic(new Topic( 3L,"小绿","1.jpg","这是第三个评论","2018-01-02"));
            topicService.InsertOneTopic(new Topic( 4L,"小黑","1.jpg","这是第四个评论","2018-01-02"));
            topicService.InsertOneTopic(new Topic( 1L,"小红","1.jpg","这是第五个评论","2018-01-02"));
            replayService.InsertOneReplay(new Replay(1L,1,2L,1L,"小黄","小红","1.jpg","这是第一条回复","2018-01-02"));
            replayService.InsertOneReplay(new Replay(1L,1,3L,1L,"小绿","小红","1.jpg","这是第二条回复","2018-01-02"));
            replayService.InsertOneReplay(new Replay(1L,0,4L,2L,"小黑","小黄","1.jpg","这是第一.一条回复","2018-01-02"));
            replayService.InsertOneReplay(new Replay(3L,0,2L,4L,"小黄","小黑","1.jpg","这是第一.一.一条回复","2018-01-02"));
            replayService.InsertOneReplay(new Replay(4L,0,4L,2L,"小黑","小黄","1.jpg","这是第一.一.一.一条回复","2018-01-02"));
            replayService.InsertOneReplay(new Replay(4L,0,1L,1L,"小红","小黄","1.jpg","这也是第一.一.一.一条回复","2018-01-02"));
            replayService.InsertOneReplay(new Replay(3L,1,2L,3L,"小黄","小绿","1.jpg","这是第三条回复","2018-01-02"));
        }
    }
    

    HomeController

    package com.wg.springdemo.controller;
    import com.wg.springdemo.model.Replay;
    import com.wg.springdemo.model.Topic;
    import com.wg.springdemo.service.ReplayService;
    import com.wg.springdemo.service.TopicService;
    import com.wg.springdemo.util.ReplayNode;
    import com.wg.springdemo.util.TopicNode;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import java.util.ArrayList;
    import java.util.List;
    @Controller
    public class HomeController {
        @Autowired
        ReplayService replayService;
        @Autowired
        TopicService topicService;
        //插入链表 参数分别代表需要待插入的节点list 和这些节点的父亲是谁
        public boolean AddReplayNode(List<Replay> relists,ReplayNode freplay){
            //为空就直接返回
            if(relists.size()==0)return false;
            //挨个遍历list中的节点信息,然后如果节点还有孩子就继续递归
            for(Replay re:relists){
                ReplayNode newreplaynode = new ReplayNode();
                newreplaynode.setReplay(re);
                freplay.getListreplay().add(newreplaynode);
                List<Replay> replayList = new ArrayList<>();
                replayList = replayService.FindAllByRid(re.getRid());
                //有孩子就继续递归,有没有孩子这里是统一进入递归才判断,也可以来个if else
                AddReplayNode(replayList,newreplaynode);
            }
            return false;
        }
        //展示出来 参数表示需要展示的节点list
        public void ShowReplayNodes(List<ReplayNode> replayNodes){
            if(replayNodes.size()==0)return;
            for(ReplayNode temp: replayNodes){
                System.out.println(temp.getReplay().toString());
                ShowReplayNodes(temp.getListreplay());
            }
        }
        @RequestMapping(value = "/", method = RequestMethod.GET)
        public String getHomePostsByPage(Model model) {
            List<Topic> topics = new ArrayList<>();
            topics = topicService.FindAllTopic();//得到所有评论
            List<TopicNode> topicNodes = new ArrayList<>();//装下所有评论的List
            for(Topic temp : topics){
                TopicNode topicNode = new TopicNode();
                topicNode.setTopic(temp);//把每个Topic变成TopicNode
                Long tid = temp.getTid();
                //找到是这个评论的所有回复
                List<Replay> thisreplays = new ArrayList<>();
                thisreplays = replayService.FindAllByTid(tid);
                //遍历每个第一层回复
                for(Replay re:thisreplays){
                    ReplayNode replayNode = new ReplayNode();
                    replayNode.setReplay(re);
                    topicNode.getReplays().add(replayNode);
                    //得到回复的回复
                    List<Replay> replayList = new ArrayList<>();
                    replayList = replayService.FindAllByRid(re.getRid());
                    //递归
                    AddReplayNode(replayList,replayNode);
                }
                topicNodes.add(topicNode);
            }
            //输出
            for(TopicNode tnode:topicNodes){
                //得到评论
                System.out.println(tnode.getTopic().toString());
                ShowReplayNodes(tnode.getReplays());
            }
            model.addAttribute("topics",topicNodes);
            return "index";
        }
    }
    

    index.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Index</title>
        <link rel="stylesheet" href="/css/index.css">
    </head>
    <body>
    <div class="onetopic" th:each="tp : ${topics}">
    <img src="/imgs/1.jpg" class="userhead" th:src="|/imgs/${tp.getTopic().getUheadurl()}|">
        <span class="name" th:text="${tp.getTopic().getUname()}">小虎</span><span class="tr-time" th:text="${tp.getTopic().getTtime()}">2018-01-02</span>
        <p class="tr-content" th:text="${tp.getTopic().getTcontent()}">这是留言内容</p>
        <div th:include="digui::digui(${tp.getReplays()},1)">
            <!--<div class="onereplay" th:each="tr : ${tp.getReplays()}">-->
                <!--<img src="/imgs/1.jpg" class="userhead">-->
                <!--<span class="name">小灰</span><span class="tr-time">2018-01-02</span>-->
                <!--<p class="tr-content">回复 小虎 :这是回复内容</p>-->
                <!--<div class="onereplay">-->
                    <!--<img src="/imgs/1.jpg" class="userhead">-->
                    <!--<span class="name">小灰</span><span class="tr-time">2018-01-02</span>-->
                    <!--<p class="tr-content">回复 小虎 :这是回复内容</p>-->
                <!--</div>-->
            <!--</div>-->
        </div>
        <!--<div class="onereplay">-->
            <!--<img src="/imgs/1.jpg" class="userhead">-->
            <!--<span class="name">小灰</span><span class="tr-time">2018-01-02</span>-->
            <!--<p class="tr-content">回复 小虎 :这是回复内容</p>-->
        <!--</div>-->
    </div>
    </body>
    </html>
    

    digui.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    <div th:fragment="digui(lists,level)">
        <div class="onereplay" th:each="tr : ${lists}">
            <img src="/imgs/1.jpg" class="userhead" th:src="|/imgs/${tr.getReplay().getUheadurl()}|">
            <span class="name" th:text="${tr.getReplay().getUname()}">小灰</span><span class="tr-time" th:text="${tr.getReplay().getRtime()}">2018-01-02</span>
            <p class="tr-content" th:text="'回复 '+${tr.getReplay().getTouname()}+' :'+${tr.getReplay().getRcontent()}">回复 小虎 :这是回复内容</p>
            <div th:unless="${tr.getListreplay().size()==0}" th:include="this::digui(${tr.getListreplay()},${level+1})"></div>
        </div>
    </div>
    </body>
    </html>
    

    index.css

    .onetopic{
        margin: 10px 0;
    }
    .userhead{
        width: 50px;
        border: 1px solid #e8cfcf;
        border-radius: 30px;
    }
    .name{
        position: relative;
        top: -18px;
    }
    .tr-time{
        position: relative;
        top: -18px;
        left: 18px;
        font-size: 12px;
    }
    .tr-content{
        padding: 0 0 20px 0;
        border-bottom: 1px solid #b1a5a5;
        margin: 0;
        margin-left:52px;
    }
    .onereplay{
        margin: 10px 0 10px 40px;
    }
    

    相关文章

      网友评论

          本文标题:评论回复功能设计

          本文链接:https://www.haomeiwen.com/subject/njamectx.html