1. 回复功能
1.1 建表
CREATE TABLE comment
(
id BIGINT AUTO_INCREMENT PRIMARY KEY,
parent_id BIGINT NOT NULL,
type INT NOT NULL,
commentator INT NOT NULL,
gmt_create BIGINT NOT NULL,
gmt_modified BIGINT NOT NULL,
like_count BIGINT DEFAULT 0
);

逆向工程创建实体类和mapper

1.2 创建CommentDTO
parentId:问题id
content:回复内容
type:一级回复或二级回复


1.3 ResultDTO
用来封装返回的JSON数据

package life.guohui.community.dto;
@Data
public class ResultDTO {
private Integer code;
private String message;
public static ResultDTO errorOf(Integer code,String message){
ResultDTO resultDTO = new ResultDTO();
resultDTO.setCode(code);
resultDTO.setMessage(message);
return resultDTO;
}
public static ResultDTO errorOf(CustomizeErrorCode errorCode) {
return errorOf(errorCode.getCode(),errorCode.getMessage());
}
public static ResultDTO okOf(){
ResultDTO resultDTO = new ResultDTO();
resultDTO.setCode(200);
resultDTO.setMessage("请求成功");
return resultDTO;
}
public static ResultDTO errorOf(CustomizeException e) {
return errorOf(e.getCode(),e.getMessage());
}
}
1.4 CommentController
用CustomizeErrorCode枚举表示错误信息


package life.guohui.community.controller;
@Controller
public class CommentController {
@Autowired
private CommentService commentService;
@ResponseBody
@RequestMapping(value = "/comment",method = RequestMethod.POST)
public Object post(@RequestBody CommentDTO commentDTO,
HttpServletRequest request){
User user = (User) request.getSession().getAttribute("user");
if(user == null){
return ResultDTO.errorOf(CustomizeErrorCode.NO_LOGIN);
}
Comment comment = new Comment();
comment.setParentId(commentDTO.getParentId());
comment.setContent(commentDTO.getContent());
comment.setType(commentDTO.getType());
comment.setGmtCreate(System.currentTimeMillis());
comment.setGmtModified(comment.getGmtCreate());
comment.setCommentator(user.getId());
comment.setLikeCount(0l);
commentService.insert(comment);
return ResultDTO.okOf();
}
}
1.5 ICustomizeErrorCode定义code

1.6 CustomizeErrorCode修改

package life.guohui.community.exception;
public enum CustomizeErrorCode implements ICustomizeErrorCode{
QUESTION_NOT_FOUND(2001,"你找的问题都不在了,要不要换一个试试?"),
TARGET_PARAM_NOT_FOUND(2002,"未选中任何问题或评论进行回复"),
NO_LOGIN(2003,"当前操作需要登陆,请登录后重试"),
SYS_ERROR(2004,"服务器冒烟了,要不你稍后再试试!!!"),
TYPE_PARAM_WRONG(2005,"评论类型错误或不存在"),
COMMENT_NOT_FOUND(2006,"回复的评论不存在了,要不要换个试试?" );
private String message;
private Integer code;
CustomizeErrorCode(Integer code, String message) {
this.message = message;
this.code = code;
}
@Override
public String getMessage() {
return message;
}
@Override
public Integer getCode() {
return code;
}
}
CommentTypeEnum中编写isExist方法
判断是否存在该类型

1.7 CommentService中使用
- 不符合校验要求抛出异常信息
- 同时判断是一级回复还是二级回复
- 回复问题属于一级回复,除了添加到Comment数据库还要给Question对象加回复数
- 回复评论属于二级回复,直接添加到Comment数据库

自定义异常

QuestionMapper.xml
中的incCommentCount
方法
<update id="incCommentCount" parameterType="life.guohui.community.model.Question">
update QUESTION set COMMENT_COUNT = COMMENT_COUNT + #{commentCount,jdbcType=INTEGER} WHERE id = #{id}
</update>
1.8 CustomizeExceptionHandler验证返回json
- 有异常后判断返回的数据类型是否是JSON格式
- 异常类型是否是我们自定义的类型
- 是的话异常信息保持不变
- 否则异常信息有我们自定义“服务器过热!!!”
- 需要用response返回JSON

package life.guohui.community.advice;
@ControllerAdvice
public class CustomizeExceptionHandler {
Object handle(HttpServletRequest request, Throwable e, Model model, HttpServletResponse response){
String contentType = request.getContentType();
if("application/json".equals(contentType)){
ResultDTO resultDTO;
if(e instanceof CustomizeException){
resultDTO = ResultDTO.errorOf((CustomizeException)e);
}else{
resultDTO = ResultDTO.errorOf(CustomizeErrorCode.SYS_ERROR);
}
//返回JSON
try{
response.setContentType("application/json");
response.setStatus(200);
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
writer.write(JSON.toJSONString(resultDTO));
}catch (IOException ioe){
}
return null;
}else{
//错误页面跳转
if(e instanceof CustomizeException){
model.addAttribute("message",e.getMessage());
}else{
model.addAttribute("message",CustomizeErrorCode.SYS_ERROR.getMessage());
}
}
return new ModelAndView("error");
}
}
1.9 改变表中的数据类型
ALTER TABLE QUESTION ALTER column id BIGINT DEFAULT not null;
ALTER TABLE "USER" ALTER column id BIGINT DEFAULT not null;
ALTER TABLE QUESTION ALTER column creator BIGINT DEFAULT not null;
ALTER TABLE COMMENT ALTER column commentator BIGINT DEFAULT not null;
修改完后用逆向工程重新生成实体类和mapper
1.10 增加事务

2. 页面提交回复
2.1 修改类型

2.2 页面设计

<div class="container-fluid main profile">
<div class="row">
<div class="col-lg-9 col-md-12 col-sm-12 col-xs-12">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<h4><span th:text="${question.title}"></span></h4>
<span class="text-desc">
作者:[[${question.user.name}]] |
发布时间:[[${#dates.format(question.gmtCreate,'yyyy-MM-dd HH:mm')}]] |
阅读数:[[${question.viewCount}]]
</span>
<div></div>
<hr class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" th:text="${question.description}"></div>
<hr class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<a th:href="@{'/publish/'+${question.id}}" class="community-menu"
th:if="${session.user != null && session.user.id == question.creator}">
<span class="glyphicon glyphicon-pencil">编辑</span>
</a>
</div>
<hr class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="media">
<div class="media-left">
<a href="">
<img th:src="${question.user.getAvatarUrl()}" class="media-object img-rounded">
</a>
</div>
<div class="media-body">
<h5 class="media-heading">
<span th:text="${question.user.name}"></span>
</h5>
</div>
</div>
<input type="hidden" id="question_id" th:value="${question.id}">
<textarea class="form-control comment" rows="6" id="comment_content"></textarea>
<button type="button" class="btn btn-success btn-comment" onclick="post()">回复</button>
</div>
</div>
<div class="col-lg-3 col-md-12 col-sm-12 col-xs-12">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<h4>发起人</h4>
<div class="media">
<div class="media-left">
<a href="">
<img th:src="${question.user.getAvatarUrl()}" class="media-object img-rounded">
</a>
</div>
<div class="media-body">
<h5 class="media-heading">
<span th:text="${question.user.name}"></span>
</h5>
</div>
</div>
</div>
<hr class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<h4>相关问题</h4>
</div>
</div>
</div>
</div>
2.3 样式的修改
让名字往下移点


头像改为方形

让头像离评论框有点距离


按钮移到右侧


2.4 Ajax请求方法
a. 将questionId放到隐藏域中
绑定id=“question_id”
<input type="hidden" id="question_id" th:text="${question.id}">
b. 给回复按钮绑定函数post()
c. 给评论内容绑定id

d. 编写community.js文件

function post() {
var questionId = $("#question_id").val();
var content = $("#comment_content").val();
$.ajax({
type:"post",
url:"/comment",
contentType:"application/json",
data:JSON.stringify({
"parentId":questionId,
"content":content,
"type":1
}),
success:function (response) {
console.log(response);
},
dataType:"json"
});
}
e. 点击回复按钮成功后评论框消失


function post() {
var questionId = $("#question_id").val();
var content = $("#comment_content").val();
$.ajax({
type:"post",
url:"/comment",
contentType:"application/json",
data:JSON.stringify({
"parentId":questionId,
"content":content,
"type":1
}),
success:function (response) {
if(response.code == 200){
$("#comment_section").hide();
}else {
alert(response.message);
}
console.log(response);
},
dataType:"json"
});
}
记得在question.html中引入js文件
2.5 未登陆的回复处理
a. 未登陆单击回复按钮后,弹出提问框
b. 点击确定后自动去登陆
c. 同时用localStorage存入值
d. 在index页面判断是否有值,有的话直接关闭index页面

function post() {
var questionId = $("#question_id").val();
var content = $("#comment_content").val();
$.ajax({
type:"post",
url:"/comment",
contentType:"application/json",
data:JSON.stringify({
"parentId":questionId,
"content":content,
"type":1
}),
success:function (response) {
if(response.code == 200){
$("#comment_section").hide();
}else {
if(response.code == 2003){
var isAccepted = confirm(response.message);
if(isAccepted){
window.open("https://github.com/login/oauth/authorize?client_id=Iv1.bf5154208e60707f&redirect_uri=http://localhost:8887/callback&scope=user&state=1");
window.localStorage.setItem("closable",true);
}
}
}
console.log(response);
},
dataType:"json"
});
}

<script type="text/javascript">
window.onload = function () {
var closable = window.localStorage.getItem("closable");
if(closable){
window.close();
window.localStorage.removeItem("closable");
}
}
</script>
网友评论