4.发布帖子,评论帖子

目录

一:发布帖子

二:帖子详情

三:查看评论

四:评论帖子

4.发布帖子,评论帖子_第1张图片


一:发布帖子

用到的表:DiscussPost

方法:用到AJAX,网页能将增量更新呈现在页面上,而不需要刷新整个页面 

异步通信技术,虽然X代表XML,但目前JSON使用的比XML更加普遍

思路 

  1. 在首页点击 “我要发布”,填写标题和正文,点击发布,会执行index.js中的publish()方法,触发ajax异步发送帖子请求,映射到DiscussPostController(/discuss)层的 /add 路径。执行控制器类 DiscussPostController 的 addDiscussPost()方法。里面调用 discussPostService.addDiscussPost 方法,将post 中的 title,content 转义HTML标记,,然后调用 discussPostMapper.insertDiscussPost(post) 存入数据。通过其对应的SQL语句将帖子内容插进 discuss_post表中。
  2. 简单来说就是 用post方式提交接json格式数据给目标URL,controller层接收到请求后,处理/add请求,前端根据返回的状态码以及提示信息判断是否添加成功。
     

 开发流程

1.我们从最简单的工具类开始,在里面写上了我们需要的一些工具方法;

在util.CommunityUtil类中添加新的工具方法,用于转换json字符串:返回状态码,在贴子发布后,显示发布成功。

    //得到JSON格式的字符串
    //输入为:编号、提示、业务数据
    public static String getJSONString(int code, String msg, Map map){
        JSONObject json = new JSONObject();
        json.put("code",code);
        json.put("msg",msg);
        if (map!=null){
            for (String key: map.keySet()) {
                json.put(key, map.get(key));
            }
        }
        return json.toJSONString();
    }
 
    //得到JSON格式的字符串(重载1:无业务数据)
    public static String getJSONString(int code, String msg){
        return getJSONString(code, msg, null);
    }
 
    //得到JSON格式的字符串(重载2:无提示、业务数据)
    public static String getJSONString(int code){
        return getJSONString(code, null, null);
    }

2. 在数据层dao中的DiscussPostMapper接口新添加方法,并在对应的discusspost-mapper添加对应的SQL语句

//添加帖子
int insertDiscussPost(DiscussPost discussPost);

//SQL语句
    
        insert into discuss_post()
        values (#{userId},#{title},#{content},#{type},#{status},#{createTime},#{commentCount},#{score})
    

3. 业务的核心逻辑都在Service层,在service类中编写了一些需要的业务逻辑,业务层需要定义一个对帖子进行保存的方法,最后调用dao里的方法,实现对数据层的更新。

在service.DiscussPostService类下新建方法:addDiscussPost()。

    @Autowired
    private SensitiveFilter sensitiveFilter;
 
    public int addDiscussPost(DiscussPost post){
        if(post==null){
            throw new IllegalArgumentException("参数不能为空!");
        }
        //转义HTML标记:防止人家发布的内容中包含html的标签,导致破坏页面
        //只用对主题、评论进行转义、过滤操作
        post.setTitle(HtmlUtils.htmlEscape(post.getTitle()));
        post.setContent(HtmlUtils.htmlEscape(post.getContent()));
        //过滤敏感词
        post.setTitle(sensitiveFilter.filter(post.getTitle()));
        post.setContent(sensitiveFilter.filter(post.getContent()));
        return discussPostMapper.insertDiscussPost(post);
    }


4. Service之后,最后就是视图层的编写,分为两个部分:控制器 + 页面。

在controller目录下新建:DiscussPostController,实现增加帖子的功能,以后所有与发帖相关的请求都在这里处理。

package com.nowcoder.mycommunity.controller;
 
//处理所有与发帖相关的请求
@Controller
@RequestMapping("/discuss")
public class DiscussPostController {
    @Autowired
    private DiscussPostService discussPostService;
    @Autowired    //获取当前用户
    private HostHolder hostHolder;
 
    @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());
        discussPostService.addDiscussPost(post);
 
        return CommunityUtil.getJSONString(0, "发布成功");
    }
}

再完成JS部分的编写,index.html中101行的【发布按钮】绑定了一个函数publish(),可以在Index.js中查看(双击shift搜索)。

$(function(){
	$("#publishBtn").click(publish);
});
 
function publish() {
	$("#publishModal").modal("hide");
 
	// 获取标题和内容
	var title = $("#recipient-name").val();
	var content = $("#message-text").val();
 
	// 发送异步请求(POST)
	$.post(
	     CONTEXT_PATH + "/discuss/add",
	    {"title":title,"content":content},
	    function(data) {
	        data = $.parseJSON(data);
	        // 在提示框中显示返回消息
	        $("#hintBody").text(data.msg);
	        // 显示提示框
            $("#hintModal").modal("show");
            // 2秒后,自动隐藏提示框
            setTimeout(function(){
                $("#hintModal").modal("hide");
                // 刷新页面
                if(data.code == 0) {
                    window.location.reload();
                }
            }, 2000);
	    }
	);
 
}

二:帖子详情

比较简单,就是标准的开发流程

1. 大体思路
点击帖子,会打开一个链接,把帖子的内容显示完整。
按照正常的开发流程:数据层 - 服务层 - 页面。

① 在 index 页面点击帖子,映射到DiscussPostController(/discuss)层的 /detail/{discussPostId} 路径,并将帖子的 id 传入。
② 根据 id 利用 discussPostService.findDiscussPostById 方法查出 post。根据userService.findUserById 方法查出 User。将 post帖子,user发帖人 加入model。
(③ 后序新增:将点赞数量和状态查询处理加入model。)
④ 返回 /site/discuss-detail 页面。在 /site/discuss-detail 页面显示帖子和发布帖子的用户信息。
 

2. 过程
①在 dao.DiscussPostMapper类下添加增删改查方法:
selectDiscussPostById 根据主键查询帖子。

//根据主键查询帖子
DiscussPost selectDiscussPostById(int id);

 ②在 resources.mapper.discusspoat-mapper.xml 下添加上面Mapper对应的SQL语句

    

③在service.DiscussPostService 添加方法:

    //根据ID查询帖子
    public DiscussPost findDiscussPostById(int id){
        return discussPostMapper.selectDiscussPostById(id);
    }

④在controller.DiscussPostController 控制器类中添加控制器方法,用来处理点击帖子查看详情时的页面跳转。

    @RequestMapping(path = "/detail/{discussPostId}", method = RequestMethod.GET)
    public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model){
        //查询得到帖子
        DiscussPost post = discussPostService.findDiscussPostById(discussPostId);
        //将帖子传给模板
        model.addAttribute("post",post);
        //查询出发帖人
        User user = userService.findUserById(post.getUserId());
        model.addAttribute("user", user);
 
        return "/site/discuss-detail";
    }

三:查看评论

4.发布帖子,评论帖子_第2张图片

继 帖子详情后显示帖子的评论。同样在 DiscussPostController(/discuss)层的 /detail/{discussPostId} 映射中。我们将 comment 表中的 entity_type 属性在 CommunityConstant 工具类中设置了两个实体常量,(帖子1,评论2),便于分页查询时使用。

新表:评论表Comment,至此已经出现DiscussPost帖子表,User用户表

1.开发思路

这个功能也是比较常规的功能,按照常规流程三层进行开发。
①数据层:
        - 根据实体查询一页评论数据;
        - 根据实体查询评论的数量。
②业务层:
        - 处理查询评论的业务;
        - 处理查询评论数量的业务。
③表现层:
        - 显示帖子详情数据时,同时显示该帖子所有的评论数据。


2.具体过程

评论是显示在帖子详情页面,所以改造DiscussPostController的getDiscussPost方法。同时还给comment帖子表设置了两个常量。(帖子1,评论2),便于分页查询时使用。

1)首先设置评论分页信息。设置为每页显示5条,设置page的路径(/disscuss/detail/ + )和总的评论数。
2)设置完后就可以进行分页查询了。根据该帖子的 id 和类型利用 commentService.findCommentsByEntity 方法查询得到当前帖子的所有评论放到一个集合中。还要呈现其它信息,将该帖子的评论和该评论的作者加入到Map中,当然不仅帖子有评论,评论也有回复的评论,所以也要把它的评论查到显示,这里比较绕,我们称普通评论为评论,评论的评论为回复。

3)刚刚Map封装的是一个评论,这会继续查回复,这里就不用分页了,从第一行开始查,还是在该Map中嵌套插入一个包括该评论的所有回复reply,回复作者和回复目标(target)。再commentService.findCommentCount将回复数量插入Map(因为前端也会展示总的评论数)。完成以后最后将每个评论的Map传入模板Model。(难点)
3)整个逻辑就是查出当前帖子下的所有评论,遍历所有评论,处理用户名等信息,查询评论下的回复,遍历每个回复,处理用户名等信息。最后返回 /site/discuss-detail 页面。

①在entity包下新建实体类:Comment,对应评论的表。

4.发布帖子,评论帖子_第3张图片

package com.nowcoder.mycommunity.entity;
 
import java.util.Date;
 
//对应于【评论】表
public class Comment {
    private int id;
    private int userId;
    private int entityType;  //评论类型,1代表帖子,2代表评论
    private int entityId;    //对应回复实体的ID,如帖子id 228,或帖子id 229
    private int targetId;    //回复的对象,即向某个人的评论
    private String content;
    private int status;
    private Date createTime;
 
    // get()、set()
    // toString()
}

②数据层:在dao包下,创建接口CommentMapper,主要有两个方法,一个是查询评论,一个是查询评论数量,这里也需要用到分页查询,用了offeset和limit.
由于涉及到分页,需要用到两个方法:查询某页有多少数据、查询一共有多少条数据

package com.nowcoder.mycommunity.dao;
 
import com.nowcoder.mycommunity.entity.Comment;
import org.apache.ibatis.annotations.Mapper;
 
import java.util.List;
 
@Mapper
public interface CommentMapper {
    //查询评论时,进行分页的两个方法
    List selectCommentsByEntity(int entityType, int entityId, int offset, int limit);
 
    int selectCountByEntity(int entityType, int entityId);
}

在resources.mapper下创建comment-mapper.xml,用来实现上面接口中的SQL语句。




    
        id, user_id, entity_type, entity_id, target_id, content, status, create_time
    
 
    
    
    


③在service目录下创建:CommentService,业务层也比较简单,没有什么需要处理的,直接返回查询结果

package com.nowcoder.mycommunity.service;
 
import com.nowcoder.mycommunity.dao.CommentMapper;
import com.nowcoder.mycommunity.entity.Comment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import java.util.List;
 
@Service
public class CommentService {
    @Autowired
    private CommentMapper commentMapper;
 
    public List 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.DiscussPostController中添加方法

评论是显示在帖子详情页面,所以改造DiscussPostController的getDiscussPost方法。

  1. 注入CommentService
  2. 借助分页,参数添加page
  3. 设置评论分页信息,每页显示5条,设置page的路径和总的评论数。
  4. 得到当前帖子的所有评论
  5. 遍历评论集合,创建评论VO列表,并放入map封装数据

整个逻辑就是查出当前帖子下的所有评论,遍历所有评论,处理用户名等信息,查询评论下的回复,遍历每个回复,处理用户名等信息。
 

@RequestMapping(path = "/detail/{discussPostId}", method = RequestMethod.GET)
    public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model, Page page){
        //查询得到帖子
        DiscussPost post = discussPostService.findDiscussPostById(discussPostId);
        //将帖子传给模板
        model.addAttribute("post",post);
        //查询出发帖人
        User user = userService.findUserById(post.getUserId());
        model.addAttribute("user", user);
 
        //评论的分页信息
        page.setLimit(5);
        page.setPath("/discuss/detail/" + discussPostId);
        page.setRows(post.getCommentCount()); //帖子中的评论数量
 
        //评论:给帖子的评论(楼主)
        //回复:给评论的评论(楼内的互相评论)
        //评论列表
        List commentList =
                commentService.findCommentsByEntity(ENTITY_TYPE_POST, post.getId(), page.getOffset(), page.getLimit());
        //评论VO列表(显示列表)
        List> commentVoList = new ArrayList<>();
        if(commentList != null){
            for (Comment comment : commentList){
                //评论VO
                Map commentVo = new HashMap<>();
                commentVo.put("comment", comment); //向VO中添加评论
                commentVo.put("user", userService.findUserById(comment.getUserId()));  //向VO中添加评论作者
 
                //评论的回复:不分页
                List replyList = commentService.findCommentsByEntity(
                        ENTITY_TYPE_COMMENT, comment.getId(), 0, Integer.MAX_VALUE);
                //回复的VO列表
                List> replyVoList = new ArrayList<>();
                if(replyList != null){
                    for (Comment reply : replyList){
                        Map replyVo = new HashMap<>();
                        //存回复
                        replyVo.put("reply", reply);
                        //作者
                        replyVo.put("user", userService.findUserById(reply.getUserId()));
                        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";
    }

注:由于用到新的常量,在util.CommunityConstant中添加

    //帖子的实体类型
    int ENTITY_TYPE_POST = 1;
    //评论的实体类型
    int ENTITY_TYPE_COMMENT = 2;

四:评论帖子

添加评论的功能也是比较基础的,按照数据层业务层和表现层进行开发,比较特别的就是会用到前面提到的事务管理。
为了效率,在帖子的字段里设计了一个评论数量,那么我们添加评论的时候就要同时更新评论数量。
4.发布帖子,评论帖子_第4张图片

添加 评论分为三种:①回帖  ②回评论  ③回某人评论

  1. 在 discuss-detail 页面点击最下方的回帖,映射到 CommentController 层的 /add/{discussPostId}。addComment方法里声明一个实体来知道给帖子评论的还是给某人回复的,① 传入了 entityType = 1 和 entityId = post.id 。② 在评论下方回复,与①映射相同,传入entityType = 2 和 entityId = comment.id。③ 对某人的评论回复,与①映射相同,传入entityType = 2 和 entityId = comment.id 和 targetId。
  2. 对传入的 comment 进一步设置userId,Status,CreateTime。然后调用commentService.addComment(comment) 方法插入数据库。commentService.addComment 使用了事务注解。该方法对 comment 的 content 进行转义HTML标记。然后使用 commentMapper.insertComment 插入评论。然后通过 commentMapper.selectCountByEntity 查询帖子的评论数量,再使用 discussPostService.updateCommentCount 方法将评论数量插入帖子详情表。
  3. 重定向 return “redirect:/discuss/detail/” + 帖子discussPostId。 刷新帖子详情页面。

①DiscussPostMapper中添加方法UpdatecommentCount:

帖子表因为有一个冗余的显示帖子数量的一个字段,所以帖子更新后,帖子表也要进行一个更新。

//插入评论后,更新对应帖子的评论数量
int updateCommentCount(int id, int commentCount);
对应的discusspost-mapper.xml的SQL语句:

    
        update discuss_post set comment_count = #{commentCount} where id = #{id}
    

②CommentMapper里新增 添加评论的方法insertcomment

package com.nowcoder.mycommunity.dao;
import com.nowcoder.mycommunity.entity.Comment;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
 
@Mapper
public interface CommentMapper {
    //查询评论时,进行分页的两个方法
    List selectCommentsByEntity(int entityType, int entityId, int offset, int limit);
 
    int selectCountByEntity(int entityType, int entityId);
 
    //增加评论
    int insertComment(Comment comment);
}
在resources.mapper创建对应的comment-mapper.xml:




    
    
        id, user_id, entity_type, entity_id, target_id, content, status, create_time
    
 
    
    
        user_id, entity_type, entity_id, target_id, content, status, create_time
    
 
    
    
    
 
    
    
        insert into comment ()
        values (#{userId}, #{entityType}, #{entityId}, #{targetId}, #{content}, #{status}, #{createTime})
    
 

3. 业务层
①DiscussPostService中添加刚才增删改查的业务方法updateCommentCount

  //业务:插入评论后,更新对应帖子的评论数量
    public int updateCommentCount(int id, int commentCount){
        return discussPostMapper.updateCommentCount(id, commentCount);
    }

②创建CommentService,处理增加评论的核心操作,当前都在一个事务之内所以用声明式事务,加上 @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)注解,隔离级别,传播机制;只有更新为帖子的评论时,才更新其数量。

@Service
public class CommentService implements CommunityConstant {
    @Autowired
    private CommentMapper commentMapper;
    @Autowired
    private SensitiveFilter sensitiveFilter;
    @Autowired
    private DiscussPostService discussPostService;
 
    public List 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);
    }
 
    //增加评论
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public int addComment(Comment comment){
        if(comment == null){
            throw new IllegalArgumentException("参数不能为空!");
        }
        //评论的敏感词过滤
        comment.setContent(HtmlUtils.htmlEscape(comment.getContent()));
        comment.setContent(sensitiveFilter.filter(comment.getContent()));
        int rows = commentMapper.insertComment(comment);
        //更新对应帖子的评论数量
        if(comment.getEntityType() == ENTITY_TYPE_POST){
            //
            int count = commentMapper.selectCountByEntity(comment.getEntityType(), comment.getEntityId());
            discussPostService.updateCommentCount(comment.getEntityId(), count);
        }
        return rows;
    }
}

4. 表现层

新建一个CommentController,从地址里获取当前帖子的id,方便评论完重定向,前端传来的评论需要完善数据。

@Controller
@RequestMapping("/comment")
public class CommentController {
    @Autowired
    private CommentService commentService;
    @Autowired
    private HostHolder hostHolder;
 
    //插入评论
    @RequestMapping(path = "/add/{discussPostId}", method = RequestMethod.POST)
    public String addComment(@PathVariable("discussPostId") int discussPostId, Comment comment){
        comment.setUserId(hostHolder.getUser().getId());
        comment.setStatus(0);
        comment.setCreateTime(new Date());
        commentService.addComment(comment);
        return "redirect:/discuss/detail/" + discussPostId;
    }
}

你可能感兴趣的:(校园论坛项目,servlet,javascript,java)