【Java Web】论坛帖子添加评论

  • 数据层
    • 增加评论数据;
    • 修改帖子评论数量;
  • 业务层
    • 处理添加评论的业务;
    • 先增加评论、在更新帖子的评论数量;
  • 表现层
    • 处理添加评论数据的请求;
    • 设置添加评论的表单。

一、数据层

1.1 CommentMapper.java

java 复制代码
package com.nowcoder.community.dao;

import com.nowcoder.community.entity.Comment;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper

public interface CommentMapper {
    List<Comment> selectCommentsByEntity(int entityType, int entityId, int offset, int limit);

    int selectCountByEntity(int entityType, int entityId);

    int insertComment(Comment comment);
}

1.2 Comment-Mapper.xml

java 复制代码
<?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>

    <sql id="insertFields">
        user_id, entity_type, entity_id, target_id, content, status, create_time
    </sql>

    <select id="selectCommentsByEntity" resultType="Comment">
        select <include refid="selectFields"></include>
        from comment
        where status = 0
        and entity_type = #{entityType}
        and entity_id = #{entityId}
        order by create_time asc
        limit #{offset}, #{limit}
    </select>

    <select id="selectCountByEntity" resultType="int">
        select count(id)
        from comment
        where status = 0
          and entity_type = #{entityType}
          and entity_id = #{entityId}
    </select>

    <insert id="insertComment" parameterType="Comment">
        insert into comment (<include refid="insertFields"></include>)
        values (#{userId},#{entityType},#{entityId},#{targetId},#{content},#{status},#{createTime})
    </insert>



</mapper>

1.3 DiscussPostMapper.java

java 复制代码
package com.nowcoder.community.dao;

import com.nowcoder.community.entity.DiscussPost;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface DiscussPostMapper {

    List<DiscussPost> selectDiscussPosts(int userId, int offset, int limit);

    // @Param注解用于给参数取别名,
    // 如果只有一个参数,并且在<if>里使用,则必须加别名.
    int selectDiscussPostRows(@Param("userId") int userId);

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

    DiscussPost selectDiscussPostById(int id);

    int updateCommentCount(int id, int commentCount);

}

1.4 DiscussPostMapper.xml

java 复制代码
<?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.DiscussPostMapper">

    <sql id="selectFields">
        id, user_id, title, content, type, status, create_time, comment_count, score
    </sql>

    <sql id="insertFields">
        user_id, title, content, type, status, create_time, comment_count, score
    </sql>

    <select id="selectDiscussPosts" resultType="DiscussPost">
        select <include refid="selectFields"></include>
        from discuss_post
        where status != 2
        <if test="userId!=0">
            and user_id = #{userId}
        </if>
        order by type desc, create_time desc
        limit #{offset}, #{limit}
    </select>

    <select id="selectDiscussPostRows" resultType="int">
        select count(id)
        from discuss_post
        where status != 2
        <if test="userId!=0">
            and user_id = #{userId}
        </if>
    </select>

    <insert id="insertDiscussPost" parameterType="DiscussPost">
        insert into discuss_post (<include refid="insertFields"></include>)
        values (#{userId},#{title},#{content},#{type},#{status},#{createTime},#{commentCount},#{score})
    </insert>

    <select id="selectDiscussPostById" resultType="DiscussPost">
        select <include refid="selectFields"></include>
        from discuss_post
        where id = #{id}
    </select>

    <update id="updateCommentCount">
        update discuss_post set comment_count = #{commentCount} where id = #{id}
    </update>

</mapper>

二、业务层

2.1 Service

java 复制代码
package com.nowcoder.community.service;

import com.nowcoder.community.dao.CommentMapper;
import com.nowcoder.community.dao.DiscussPostMapper;
import com.nowcoder.community.entity.Comment;
import com.nowcoder.community.util.CommunityConstant;
import com.nowcoder.community.util.SensitiveFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.util.HtmlUtils;

import java.util.List;

@Service

public class CommentService implements CommunityConstant{

    @Autowired
    private CommentMapper commentMapper;

    @Autowired
    private SensitiveFilter sensitiveFilter;

    @Autowired
    private DiscussPostService discussPostService;


    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);
    }

    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public int addComment(Comment comment){
        if(comment == null){
            throw new IllegalArgumentException("参数不能为空");
        }
        // 添加评论,过滤敏感词和html标签
        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.getTargetId(), count);
        }

        return rows;
    }
}

三、表现层

3.1 Controller

java 复制代码
package com.nowcoder.community.controller;

import com.nowcoder.community.entity.Comment;
import com.nowcoder.community.entity.User;
import com.nowcoder.community.service.CommentService;
import com.nowcoder.community.service.DiscussPostService;
import com.nowcoder.community.util.HostHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.Date;

@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){
        // 获取用户
        User user = hostHolder.getUser();
//        if(user == null) return 0;
        comment.setUserId(user.getId());
        comment.setStatus(0);
        comment.setCreateTime(new Date());
        System.out.println(user.getUsername());
        commentService.addComment(comment);

        return "redirect:/discuss/detail/" + discussPostId;
    }

}

3.2 html

java 复制代码
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
	<link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/>
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous">
	<link rel="stylesheet" th:href="@{/css/global.css}" />
	<link rel="stylesheet" th:href="@{/css/discuss-detail.css}" />
	<title>牛客网-帖子详情</title>
</head>
<body>
<div class="nk-container">
	<!-- 头部 -->
	<header class="bg-dark sticky-top" th:replace="index::header">
		<div class="container">
			<!-- 导航 -->
			<nav class="navbar navbar-expand-lg navbar-dark">
				<!-- logo -->
				<a class="navbar-brand" href="#"></a>
				<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
					<span class="navbar-toggler-icon"></span>
				</button>
				<!-- 功能 -->
				<div class="collapse navbar-collapse" id="navbarSupportedContent">
					<ul class="navbar-nav mr-auto">
						<li class="nav-item ml-3 btn-group-vertical">
							<a class="nav-link" href="../index.html">首页</a>
						</li>
						<li class="nav-item ml-3 btn-group-vertical">
							<a class="nav-link position-relative" href="letter.html">消息<span class="badge badge-danger">12</span></a>
						</li>
						<li class="nav-item ml-3 btn-group-vertical">
							<a class="nav-link" href="register.html">注册</a>
						</li>
						<li class="nav-item ml-3 btn-group-vertical">
							<a class="nav-link" href="login.html">登录</a>
						</li>
						<li class="nav-item ml-3 btn-group-vertical dropdown">
							<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
								<img src="http://images.nowcoder.com/head/1t.png" class="rounded-circle" style="width:30px;"/>
							</a>
							<div class="dropdown-menu" aria-labelledby="navbarDropdown">
								<a class="dropdown-item text-center" href="profile.html">个人主页</a>
								<a class="dropdown-item text-center" href="setting.html">账号设置</a>
								<a class="dropdown-item text-center" href="login.html">退出登录</a>
								<div class="dropdown-divider"></div>
								<span class="dropdown-item text-center text-secondary">nowcoder</span>
							</div>
						</li>
					</ul>
					<!-- 搜索 -->
					<form class="form-inline my-2 my-lg-0" action="search.html">
						<input class="form-control mr-sm-2" type="search" aria-label="Search" />
						<button class="btn btn-outline-light my-2 my-sm-0" type="submit">搜索</button>
					</form>
				</div>
			</nav>
		</div>
	</header>

	<!-- 内容 -->
	<div class="main">
		<!-- 帖子详情 -->
		<div class="container">
			<!-- 标题 -->
			<h6 class="mb-4">
				<img src="http://static.nowcoder.com/images/img/icons/ico-discuss.png"/>
				<span th:utext="${post.title}">备战春招,面试刷题跟他复习,一个月全搞定!</span>
				<div class="float-right">
					<button type="button" class="btn btn-danger btn-sm">置顶</button>
					<button type="button" class="btn btn-danger btn-sm">加精</button>
					<button type="button" class="btn btn-danger btn-sm">删除</button>
				</div>
			</h6>
			<!-- 作者 -->
			<div class="media pb-3 border-bottom">
				<a href="profile.html">
					<img th:src="${user.headerUrl}" class="align-self-start mr-4 rounded-circle user-header" alt="用户头像" >
				</a>
				<div class="media-body">
					<div class="mt-0 text-warning" th:utext="${user.username}">寒江雪</div>
					<div class="text-muted mt-3">
						发布于 <b th:text="${#dates.format(post.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</b>
						<ul class="d-inline float-right">
							<li class="d-inline ml-2"><a href="#" class="text-primary">赞 11</a></li>
							<li class="d-inline ml-2">|</li>
							<li class="d-inline ml-2"><a href="#replyform" class="text-primary">回帖 <i th:text="${post.commentCount}">7</i></a></li>
						</ul>
					</div>
				</div>
			</div>
			<!-- 正文 -->
			<div class="mt-4 mb-3 content" th:utext="${post.content}">
				金三银四的金三已经到了,你还沉浸在过年的喜悦中吗?
				如果是,那我要让你清醒一下了:目前大部分公司已经开启了内推,正式网申也将在3月份陆续开始,金三银四,春招的求职黄金时期已经来啦!!!
				再不准备,作为19应届生的你可能就找不到工作了。。。作为20届实习生的你可能就找不到实习了。。。
				现阶段时间紧,任务重,能做到短时间内快速提升的也就只有算法了,
				那么算法要怎么复习?重点在哪里?常见笔试面试算法题型和解题思路以及最优代码是怎样的?
				跟左程云老师学算法,不仅能解决以上所有问题,还能在短时间内得到最大程度的提升!!!
			</div>
		</div>
		<!-- 回帖 -->
		<div class="container mt-3">
			<!-- 回帖数量 -->
			<div class="row">
				<div class="col-8">
					<h6><b class="square"></b> <i th:text="${post.commentCount}">30</i>条回帖</h6>
				</div>
				<div class="col-4 text-right">
					<a href="#replyform" class="btn btn-primary btn-sm">&nbsp;&nbsp;回&nbsp;&nbsp;帖&nbsp;&nbsp;</a>
				</div>
			</div>
			<!-- 回帖列表 -->
			<ul class="list-unstyled mt-4">
				<li class="media pb-3 pt-3 mb-3 border-bottom" th:each="cvo:${comments}">
					<a href="profile.html">
						<img th:src="${cvo.user.headerUrl}" class="align-self-start mr-4 rounded-circle user-header" alt="用户头像" >
					</a>
					<div class="media-body">
						<div class="mt-0">
							<span class="font-size-12 text-success" th:utext="${cvo.user.username}">掉脑袋切切</span>
							<span class="badge badge-secondary float-right floor">
									<i th:text="${page.offset + cvoStat.count}">1</i>#
								</span>
						</div>
						<div class="mt-2" th:utext="${cvo.comment.content}">
							这开课时间是不是有点晚啊。。。
						</div>
						<div class="mt-4 text-muted font-size-12">
							<span>发布于 <b th:text="${#dates.format(cvo.comment.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</b></span>
							<ul class="d-inline float-right">
								<li class="d-inline ml-2"><a href="#" class="text-primary">赞(1)</a></li>
								<li class="d-inline ml-2">|</li>
								<li class="d-inline ml-2"><a href="#" class="text-primary">回复(<i th:text="${cvo.replyCount}">2</i>)</a></li>
							</ul>
						</div>
						<!-- 回复列表 -->
						<ul class="list-unstyled mt-4 bg-gray p-3 font-size-12 text-muted">

							<li class="pb-3 pt-3 mb-3 border-bottom" th:each="rvo:${cvo.replys}">
								<div>
										<span th:if="${rvo.target==null}">
											<b class="text-info" th:text="${rvo.user.username}">寒江雪</b>:&nbsp;&nbsp;
										</span>
									<span th:if="${rvo.target!=null}">
											<i class="text-info" th:text="${rvo.user.username}">Sissi</i> 回复
											<b class="text-info" th:text="${rvo.target.username}">寒江雪</b>:&nbsp;&nbsp;
										</span>
									<span th:utext="${rvo.reply.content}">这个是直播时间哈,觉得晚的话可以直接看之前的完整录播的~</span>
								</div>
								<div class="mt-3">
									<span th:text="${#dates.format(rvo.reply.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</span>
									<ul class="d-inline float-right">
										<li class="d-inline ml-2"><a href="#" class="text-primary">赞(1)</a></li>
										<li class="d-inline ml-2">|</li>
										<li class="d-inline ml-2"><a th:href="|#huifu-${rvoStat.count}|" data-toggle="collapse" class="text-primary">回复</a></li>
									</ul>
									<div th:id="|huifu-${rvoStat.count}|" class="mt-4 collapse">
										<form method="post" th:action="@{|/comment/add/${post.id}|}">
											<div>
												<input type="text" class="input-size" name="content" th:placeholder="|回复${rvo.user.username}|"/>
												<input type="hidden" name="entityType" value="2">
												<input type="hidden" name="entityId" th:value="${cvo.comment.id}">
												<input type="hidden" name="targetId" th:value="${rvo.user.id}">
											</div>
											<div class="text-right mt-2">
												<button type="submit" class="btn btn-primary btn-sm" onclick="#">&nbsp;&nbsp;回&nbsp;&nbsp;复&nbsp;&nbsp;</button>
											</div>
										</form>
									</div>
								</div>
							</li>

							<!-- 回复输入框 -->
							<li class="pb-3 pt-3">
								<form method="post" th:action="@{|/comment/add/${post.id}|}">
									<div>
										<input type="text" class="input-size" name="content" placeholder="请输入你的观点"/>
										<input type="hidden" name="entityType" value="2">
										<input type="hidden" name="entityId" th:value="${cvo.comment.id}">
									</div>
									<div class="text-right mt-2">
										<button type="submit" class="btn btn-primary btn-sm" onclick="#">&nbsp;&nbsp;回&nbsp;&nbsp;复&nbsp;&nbsp;</button>
									</div>
								</form>
							</li>
						</ul>
					</div>
				</li>
			</ul>
			<!-- 分页 -->
			<nav class="mt-5" th:replace="index::pagination">
				<ul class="pagination justify-content-center">
					<li class="page-item"><a class="page-link" href="#">首页</a></li>
					<li class="page-item disabled"><a class="page-link" href="#">上一页</a></li>
					<li class="page-item active"><a class="page-link" href="#">1</a></li>
					<li class="page-item"><a class="page-link" href="#">2</a></li>
					<li class="page-item"><a class="page-link" href="#">3</a></li>
					<li class="page-item"><a class="page-link" href="#">4</a></li>
					<li class="page-item"><a class="page-link" href="#">5</a></li>
					<li class="page-item"><a class="page-link" href="#">下一页</a></li>
					<li class="page-item"><a class="page-link" href="#">末页</a></li>
				</ul>
			</nav>
		</div>
		<!-- 回帖输入 -->
		<div class="container mt-3">
			<form class="replyform" method="post" th:action="@{|/comment/add/${post.id}|}">
				<p class="mt-3">
					<a name="replyform"></a>
					<textarea placeholder="在这里畅所欲言你的看法吧!" name="content"></textarea>
					<input type="hidden" name="entityType" value="1">
					<input type="hidden" name="entityId" th:value="${post.id}">
				</p>
				<p class="text-right">
					<button type="submit" class="btn btn-primary btn-sm">&nbsp;&nbsp;回&nbsp;&nbsp;帖&nbsp;&nbsp;</button>
				</p>
			</form>
		</div>
	</div>

	<!-- 尾部 -->
	<footer class="bg-dark">
		<div class="container">
			<div class="row">
				<!-- 二维码 -->
				<div class="col-4 qrcode">
					<img src="https://uploadfiles.nowcoder.com/app/app_download.png" class="img-thumbnail" style="width:136px;" />
				</div>
				<!-- 公司信息 -->
				<div class="col-8 detail-info">
					<div class="row">
						<div class="col">
							<ul class="nav">
								<li class="nav-item">
									<a class="nav-link text-light" href="#">关于我们</a>
								</li>
								<li class="nav-item">
									<a class="nav-link text-light" href="#">加入我们</a>
								</li>
								<li class="nav-item">
									<a class="nav-link text-light" href="#">意见反馈</a>
								</li>
								<li class="nav-item">
									<a class="nav-link text-light" href="#">企业服务</a>
								</li>
								<li class="nav-item">
									<a class="nav-link text-light" href="#">联系我们</a>
								</li>
								<li class="nav-item">
									<a class="nav-link text-light" href="#">免责声明</a>
								</li>
								<li class="nav-item">
									<a class="nav-link text-light" href="#">友情链接</a>
								</li>
							</ul>
						</div>
					</div>
					<div class="row">
						<div class="col">
							<ul class="nav btn-group-vertical company-info">
								<li class="nav-item text-white-50">
									公司地址:北京市朝阳区大屯路东金泉时代3-2708北京牛客科技有限公司
								</li>
								<li class="nav-item text-white-50">
									联系方式:010-60728802(电话)&nbsp;&nbsp;&nbsp;&nbsp;admin@nowcoder.com
								</li>
								<li class="nav-item text-white-50">
									牛客科技©2018 All rights reserved
								</li>
								<li class="nav-item text-white-50">
									京ICP备14055008号-4 &nbsp;&nbsp;&nbsp;&nbsp;
									<img src="http://static.nowcoder.com/company/images/res/ghs.png" style="width:18px;" />
									京公网安备 11010502036488号
								</li>
							</ul>
						</div>
					</div>
				</div>
			</div>
		</div>
	</footer>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" crossorigin="anonymous"></script>
<script th:src="@{/js/global.js}"></script>
</body>
</html>
相关推荐
wearegogog1231 小时前
基于 MATLAB 的卡尔曼滤波器实现,用于消除噪声并估算信号
前端·算法·matlab
molaifeng1 小时前
Go 语言如何实现高性能网络 I/O:Netpoller 模型揭秘
开发语言·网络·golang
韩师学子--小倪1 小时前
fastjson与gson的toString差异
java·json
Drawing stars1 小时前
JAVA后端 前端 大模型应用 学习路线
java·前端·学习
崇山峻岭之间1 小时前
Matlab学习记录33
开发语言·学习·matlab
品克缤1 小时前
Element UI MessageBox 增加第三个按钮(DOM Hack 方案)
前端·javascript·vue.js
Evand J1 小时前
【2026课题推荐】DOA定位——MUSIC算法进行多传感器协同目标定位。附MATLAB例程运行结果
开发语言·算法·matlab
nbsaas-boot1 小时前
SQL Server 存储过程开发规范(公司内部模板)
java·服务器·数据库
小二·1 小时前
Python Web 开发进阶实战:性能压测与调优 —— Locust + Prometheus + Grafana 构建高并发可观测系统
前端·python·prometheus
小沐°1 小时前
vue-设置不同环境的打包和运行
前端·javascript·vue.js