评论回复功能设计

2019-08-29  本文已影响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;
}
上一篇下一篇

猜你喜欢

热点阅读