续 开发显示回答列表
重构updateDuration方法
步骤1:
在utils.js文件中添加通用的计算持续时间的方法
function addDuration(item){
//判断参数状态
if(item==null || item.createtime==null){
return;
}
//获得问题中的创建时间属性(毫秒数)
let createtime=new Date(item.createtime).getTime();
//获得当前时间的毫秒数
let now=new Date().getTime();
//计算时间差(秒)
let durtaion=(now-createtime)/1000;
if(durtaion<60){
// 显示刚刚
//duration这个名字可以随便起,只要保证和页面上取的一样就行
item.duration="刚刚";
}else if(durtaion<60*60){
// 显示XX分钟
item.duration=
(durtaion/60).toFixed(0)+"分钟前";
}else if (durtaion<60*60*24){
//显示XX小时
item.duration=
(durtaion/60/60).toFixed(0)+"小时前";
}else{
//显示XX天
item.duration=
(durtaion/60/60/24).toFixed(0)+"天前";
}
}
步骤2:
在question_detail.js文件中
修改代码如下
let questionApp = new Vue({
el:"#questionApp",
data:{
question:{}
},
methods:{
loadQuestion:function(){
//获取浏览器地址栏中当前url中?之后的内容
let questionId=location.search;
console.log("questionId:"+questionId);
//判断是不是获得了?之后的内容
if(!questionId){
//如果没有?则终止
alert("必须指定问题id");
return;
}
//如果存在?之后的内容,则去掉? ?354
questionId=questionId.substring(1);
//发送异步请求
$.ajax({
url:"/v1/questions/"+questionId,//v1/questions/15
method:"get",
success:function(r){
console.log(r);
if(r.code == OK){
questionApp.question=r.data;
addDuration(questionApp.question);//修改了这个里!!!!
}else{
alert(r.message);
}
}
})
}
},
created:function(){
this.loadQuestion();
}
});
步骤3:
修改index.js和index_teacher.js中updateDuration方法的的内容
代码如下
updateDuration:function () {
let questions=this.questions;
for(let i=0;i<questions.length;i++){
addDuration(questions[i]);
}
}
步骤4:
编写回答列表中老师回答问题的持续时间
代码如下
let answersApp=new Vue({
el:"#answersApp",
data:{
message:"",
hasError:false,
answers:[]
},
methods:{
loadAnswers:function(){
let questionId=location.search;
if(!questionId){
this.message="必须有问题ID";
this.hasError=true;
return;
}
questionId=questionId.substring(1);
$.ajax({
url:"/v1/answers/question/"+questionId,
method:"get",
success:function(r){
if(r.code==OK){
answersApp.answers=r.data;
answersApp.updateDuration();
}else{
answersApp.message=r.message;
answersApp.hasError=true;
}
}
})
},
updateDuration:function(){
for(let i=0;i<this.answers.length;i++){
addDuration(this.answers[i]);
}
}
},
created:function(){
this.loadAnswers();
}
})
将新回复的答案插入答案列表
步骤1:
在R类中补全一个方法
这个方法是在执行新增操作时,不但返回新增结果信息,还返回新增对象的方法
R类中添加方法如下
/**
* 新增成功,并且需要获得新增成功对象时使用这个方法
*/
public static R created(Object data){
return new R().setCode(CREATED).setMessage("创建成功")
.setData(data);
}
步骤2:
重构AnswerController中的postAnswer方法
代码如下
//新增回复的控制方法
@PostMapping("")
@PreAuthorize("hasRole('ROLE_TEACHER')")
public R postAnswer(
@Validated AnswerVo answerVo,
BindingResult result,
@AuthenticationPrincipal User user){
log.debug("收到回复信息{}",answerVo);
if(result.hasErrors()){
String message=result.getFieldError().getDefaultMessage();
log.warn(message);
return R.unproecsableEntity(message);
}
//这里调用业务逻辑层方法
Answer answer=
answerService.saveAnswer(answerVo,user.getUsername());
return R.created(answer);
}
步骤3:
重构question_detail.js文件中的postAnswerApp区域的方法
代码如下
let postAnswerApp=new Vue({
el:"#postAnswerApp",
data:{
message:"",
hasError:false
},
methods:{
postAnswer:function(){
postAnswerApp.hasError=false;
let questionId=location.search;
if(!questionId){
this.message="没有问题ID";
this.hasError=true;
return;
}
//去掉?
questionId=questionId.substring(1);
let content=$("#summernote").val();
if(!content){
this.message="请填写回复内容";
this.hasError=true;
return;
}
let data={
questionId:questionId,
content:content
}
$.ajax({
url:"/v1/answers",
method:"post",
data:data,
success:function(r){
if(r.code==CREATED){
let answer=r.data;//这个r.data就是新增的回答
//将这个问题的持续时间计算出来
addDuration(answer);
//将新增的方法插入到anwsers数组的后面
answersApp.answers.push(answer);
//回答已经显示,清空富文本编辑器中的内容
$("#summernote").summernote("reset");
postAnswerApp.message=r.message;
postAnswerApp.hasError=true;
//2秒中之后信息消失
setTimeout(function(){
postAnswerApp.hasError=false;
},2000);
}else{
postAnswerApp.message=r.message;
postAnswerApp.hasError=true;
}
}
})
}
}
})
开发评论答案的功能
再次强调<问题>、<回答>、<评论>的关系
question对answer是一对多
answer对comment又是一对多
要想查询一个问题的所有评论,是需要连表查询的
开发显示评论的功能
显示评论信息需要昵称和评论内容
但是数据表中没有昵称列,这样我们就需要连表查询才能得到
为了简化这样的操作,我们可以添加一个冗余列信息:user_nick_name
保存用户昵称,避免过多的连表操作
怎么样给当前的表添加一个列呢?
步骤1:
为comment表添加user_nick_name列,简化查询
代码如下
ALTER TABLE `comment` ADD COLUMN user_nick_name
VARCHAR(255) AFTER user_id
UPDATE `comment` c SET user_nick_name =
(SELECT nickname FROM `user` u WHERE u.id=c.user_id )
步骤2:
我们在数据库中添加了昵称
那么也许要在实体类中添加对应的属性
找到model.Comment类,添加新增的列的属性
/**
- 用户昵称
*/
@TableField(“user_nick_name”)
private String userNickName;
步骤3:
开始执行查询操作的准备工作
每个回答包含多个评论,所以我们先要在Answer实体类中添加一个Comment类型的集合
model.Answer实体类添加代码如下
/**
- 当前回答的所有评论
*/
@TableField(exist = false)
private List comments=new ArrayList<>();
步骤4:
开始数据访问层的编写
这个查询是复杂查询,需要在xml文件中编写代码
所以我们要按照Mybatis复杂映射学习的内容完成这个查询
首先在AnswerMapper接口中编写下面的方法
@Repository
public interface AnswerMapper extends BaseMapper<Answer> {
//复杂映射查询按问题id获得所有回答以及每个回答包含的评论
List<Answer> findAnswersByQuestionId(Integer questionId);
}
步骤5:
编写执行复杂映射查询的xml文件
首先SpringBoot约定这些xml文件的位置必须在resources/mapper下
如果不在复制过去
步骤6:
编写AnswerMapper.xml文件,在文件中编写代码如下
<?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="cn.tedu.straw.portal.mapper.AnswerMapper">
<!-- 通用查询映射结果 -->
<resultMap id="answerCommentMap" type="cn.tedu.straw.portal.model.Answer">
<id column="id" property="id"/>
<result column="content" property="content"/>
<result column="like_count" property="likeCount"/>
<result column="user_id" property="userId"/>
<result column="user_nick_name" property="userNickName"/>
<result column="quest_id" property="questId"/>
<result column="createtime" property="createtime"/>
<result column="accept_status" property="acceptStatus"/>
<collection property="comments" ofType="cn.tedu.straw.portal.model.Comment">
<id column="comment_id" property="id" />
<result column="comment_user_id" property="userId" />
<result column="comment_answer_id" property="answerId" />
<result column="comment_user_nick_name" property="userNickName" />
<result column="comment_content" property="content" />
<result column="comment_createtime" property="createtime" />
</collection>
</resultMap>
<select id="findAnswersByQuestionId" resultMap="answerCommentMap" >
SELECT
a.id,
a.content,
a.user_id,
a.user_nick_name,
a.quest_id,
a.createtime,
a.accept_status,
a.like_count,
c.id AS comment_id,
c.user_id AS comment_user_id,
c.user_nick_name AS comment_user_nick_name,
c.content AS comment_content,
c.createtime AS comment_createtime,
c.answer_id AS comment_answer_id
FROM answer a
LEFT JOIN `comment` c
ON c.answer_id=a.id
WHERE a.quest_id=#{question_id}
ORDER BY a.createtime,c.createtime
</select>
</mapper>
步骤7:
编写业务逻辑层接口
IAnswerService接口中是有一个按问题id查询问题的所有会的的方法的
所以我们没有必要再添加新的方法
所以直接去重构这个方法的实现即可
AnswerServiceImpl类中getAnswersByQuestionId方法代码如下
@Override
public List<Answer> getAnswersByQuestionId(Integer questionId) {
if(questionId==null){
throw ServiceException.invalidRequest("问题id不能为空");
}
List<Answer> answers=answerMapper.findAnswersByQuestionId(questionId);
return answers;
}
步骤8:
之前已经完成过控制器调用按id查询回答的方法
无需再次编写
现在直接到页面去修改vue的绑定即可
<p class="text-success">
<i class="fa fa-comment"></i>
<span v-text="answer.comments.length">1</span>条评论
</p>
<ul class="list-unstyled mt-3">
<li class="media my-2" v-for="comment in answer.comments">
<img style="width: 50px;height: 50px;border-radius: 50%;"
src="../img/user.jpg" class="mr-3"
alt="...">
<div class="media-body">
<h6 class="mt-0 mb-1">
<span v-text="comment.userNickName">李四</span>:
</h6>
<p class="text-dark">
<span class="text-monospace" v-text="comment.content">
明白了,谢谢老师!
</span>
<span class="font-weight-light text-info"
style="font-size: small"></span>
<a class="text-primary ml-2"
style="font-size: small" data-toggle="collapse" href="#editCommemt1"
role="button"
v-bind:href="'#editComment'+comment.id"
aria-expanded="false" aria-controls="collapseExample">
<i class="fa fa-edit"></i>编辑
</a>
<!--老师角色或者属于本用户的评论可以删除该评论-->
<a class="ml-2 fa fa-close " style="font-size: small"
data-toggle="collapse" role="button"
aria-expanded="false" aria-controls="collapseExample">
删除
</a>
</p>
<div class="collapse" id="editCommemt1"
v-bind:id="'editComment'+comment.id">
<div class="card card-body border-light">
<form action="" method="post" class="needs-validation" novalidate>
<div class="form-group">
<textarea class="form-control"
id="textareaComment1" name="content" rows="4"
required></textarea>
<div class="invalid-feedback">
内容不能为空!
</div>
</div>
<button type="submit" class="btn btn-primary my-1 float-right">提交修改</button>
</form>
</div>
</div>
</div>
</li>
</ul>
开发添加评论的功能
开发思路
1.创建CommentVo
2.编写控制器代码
3.页面表单的绑定
4.编写js文件代码
5.业务逻辑层和添加操作
步骤1:
编写CommentVo类来准备新增评论
@Data
@Accessors(chain=true)
public class CommentVo implements Serializable {
@NotNull(message = "问题ID不能为空")
private Integer answerId;
@NotBlank(message = "评论内容不能为空")
private String content;
}
步骤2:
编写控制器代码
CommentController类中添加方法
代码如下
@RestController
@RequestMapping("/v1/comments")
@Slf4j
public class CommentController {
@Resource
ICommentService commentService;
@PostMapping
public R<Comment> postComment(
@Validated CommentVo commentVo,
BindingResult result){
if(result.hasErrors()){
String message=result.getFieldError().getDefaultMessage();
return R.unproecsableEntity(message);
}
log.debug("收到评论信息{}:",commentVo);
//这里调用业务逻辑层方法执行评论的新增即可
return R.created("OK");
}
}