SpringBoot+Vue+Mybatis-plus 博客:个人博客介绍及效果展示
SpringBoot+Vue+Mybatis-plus 博客(一):完成博客后台前端登录页面、后端登录接口
SpringBoot+Vue+Mybatis-plus 博客(二):完成登录的前后端对接、完善左侧菜单栏
SpringBoot+Vue+Mybatis-plus 博客(三):完成搜索及博客列表展示功能前后端
SpringBoot+Vue+Mybatis-plus 博客(四):完成发布文章、编辑文章、删除文章及查询文章功能
SpringBoot+Vue+Mybatis-plus 博客(五):完成分类管理和标签管理前后端对接
SpringBoot+Vue+Mybatis-plus 博客(六):完成评论管理前后端交互
SpringBoot+Vue+Mybatis-plus 博客(七):完成友链管理前后端对接
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="TComment对象", description="")
public class TComment implements Serializable {
private static final long serialVersionUID = 1L;
@JsonSerialize(using = ToStringSerializer.class) //系统序列化时,保留相关精度
@ApiModelProperty(value = "评论id")
private Long id;
@ApiModelProperty(value = "是否为管理员评论")
private Boolean adminComment;
@ApiModelProperty(value = "头像")
private String avatar;
@ApiModelProperty(value = "评论内容")
private String content;
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "Asia/ShangHai")
@ApiModelProperty(value = "创建时间")
private LocalDateTime createTime;
@ApiModelProperty(value = "邮箱")
private String email;
@ApiModelProperty(value = "ip地址")
private String ip;
@ApiModelProperty(value = "昵称")
private String nickname;
@JsonSerialize(using = ToStringSerializer.class) //系统序列化时,保留相关精度
@ApiModelProperty(value = "博客id")
private Long blogId;
@JsonSerialize(using = ToStringSerializer.class) //系统序列化时,保留相关精度
@ApiModelProperty(value = "父评论id")
private Long parentCommentId;
//回复评论
@TableField(exist = false) //查询时过滤非数据库字段
private List<TComment> replyComments = new ArrayList<>();
@TableField(exist = false) //查询时过滤非数据库字段
private TComment parentComment;
@TableField(exist = false) //查询时过滤非数据库字段
private String parentNickname;
}
public interface TCommentMapper extends BaseMapper<TComment> {
//查询父级评论
List<TComment> findByBlogIdParentIdNull(@Param("blogId") Long blogId, @Param("blogParentId") Long blogParentId);
//查询一级回复
List<TComment> findByBlogIdParentIdNotNull(@Param("blogId") Long blogId, @Param("id") Long id);
//查询二级回复
List<TComment> findByBlogIdAndReplayId(@Param("blogId") Long blogId,@Param("childId") Long childId);
//添加一个评论
int saveComment(TComment comment);
//删除评论
void deleteComment(Long id);
}
<?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.xlblog.blog.mapper.TCommentMapper">
<!--添加评论-->
<insert id="saveComment" parameterType="com.xlblog.blog.entity.TComment">
insert into t_comment (nickname,email,content,avatar,create_time,blog_id,parent_comment_id,admin_comment,ip)
values (#{nickname},#{email},#{content},#{avatar},#{createTime},#{blogId},#{parentCommentId},#{adminComment},#{ip});
</insert>
<!--查询父级评论-->
<select id="findByBlogIdParentIdNull" resultType="com.xlblog.blog.entity.TComment">
select *
from t_comment c
where c.blog_id = #{blogId} and c.parent_comment_id = #{blogParentId}
order by c.create_time desc
</select>
<!--查询一级子评论-->
<select id="findByBlogIdParentIdNotNull" resultType="com.xlblog.blog.entity.TComment">
select *
from t_comment c
where c.blog_id = #{blogId} and c.parent_comment_id = #{id}
order by c.create_time desc
</select>
<!--查询二级子评论-->
<select id="findByBlogIdAndReplayId" resultType="com.xlblog.blog.entity.TComment">
select *
from t_comment c
where c.blog_id = #{blogId} and c.parent_comment_id = #{childId}
order by c.create_time desc
</select>
<!--删除评论-->
<delete id="deleteComment" >
delete from t_comment where id = #{id}
</delete>
</mapper>
public interface TCommentService extends IService<TComment> {
//根据博客id查询评论信息
List<TComment> listCommentByBlogId(Long blogId);
//添加保存评论
int saveComment(TComment comment);
//删除评论
void deleteComment(Long id);
List<TComment> findCommentByNickname(String nickname);
Page<TComment> getCommentByPage(Long current, Long size);
}
@Service
public class TCommentServiceImpl extends ServiceImpl<TCommentMapper, TComment> implements TCommentService {
@Autowired
private TCommentMapper commentMapper;
@Autowired
private TBlogMapper blogMapper;
//存放迭代找出的所有子代的集合
private List<TComment> tempReplys = new ArrayList<>();
/**
* @Description: 查询评论
* @Param: blogId:博客id
* @Return: 评论消息
*/
@Override
public List<TComment> listCommentByBlogId(Long blogId) {
//查询出父节点
List<TComment> comments = commentMapper.findByBlogIdParentIdNull(blogId, Long.parseLong("-1"));
for(TComment comment : comments){
Long id = comment.getId();
String parentNickname1 = comment.getNickname();
List<TComment> childComments = commentMapper.findByBlogIdParentIdNotNull(blogId,id);
//查询出子评论
combineChildren(blogId, childComments, parentNickname1);
comment.setReplyComments(tempReplys);
tempReplys = new ArrayList<>();
}
return comments;
}
/**
* @Description: 查询出子评论
* @Auther: ONESTAR
* @Date: 10:43 2020/6/23
* @Param: childComments:所有子评论
* @Param: parentNickname1:父评论姓名
* @Return:
*/
private void combineChildren(Long blogId, List<TComment> childComments, String parentNickname1) {
//判断是否有一级子评论
if(childComments.size() > 0){
//循环找出子评论的id
for(TComment childComment : childComments){
String parentNickname = childComment.getNickname();
childComment.setParentNickname(parentNickname1);
tempReplys.add(childComment);
Long childId = childComment.getId();
//查询出子二级评论
recursively(blogId, childId, parentNickname);
}
}
}
/**
* @Description: 循环迭代找出子集回复
* @Auther: ONESTAR
* @Date: 10:44 2020/6/23
* @Param: chileId:子评论id
* @Param: parentNickname1:子评论姓名
* @Return:
*/
private void recursively(Long blogId, Long childId, String parentNickname1) {
//根据子一级评论的id找到子二级评论
List<TComment> replayComments = commentMapper.findByBlogIdAndReplayId(blogId,childId);
if(replayComments.size() > 0){
for(TComment replayComment : replayComments){
String parentNickname = replayComment.getNickname();
replayComment.setParentNickname(parentNickname1);
Long replayId = replayComment.getId();
tempReplys.add(replayComment);
recursively(blogId,replayId,parentNickname);
}
}
}
//新增评论
@Override
public int saveComment(TComment comment) {
comment.setCreateTime(LocalDateTime.now());
int comments = commentMapper.saveComment(comment);
//文章评论计数
// blogMapper.getCommentCountById(comment.getBlogId());
return comments;
}
//删除评论
@Override
public void deleteComment(Long id) {
TComment comment = commentMapper.selectById(id);
commentMapper.deleteComment(id);
TBlog tBlog = blogMapper.selectById(comment.getBlogId());
tBlog.setCommentCount(tBlog.getCommentCount() - 1);
blogMapper.updateById(tBlog);
// blogMapper.getCommentCountById(comment.getBlogId());
}
//根据昵称查询评论
@Override
public List<TComment> findCommentByNickname(String nickname) {
QueryWrapper<TComment> queryWrapper = new QueryWrapper<TComment>();
queryWrapper.like("nickname",nickname);
//以创建时间排序(降序)
queryWrapper.orderByDesc("create_time");
List<TComment> tBlogList = commentMapper.selectList(queryWrapper);
return tBlogList;
}
@Override
public Page<TComment> getCommentByPage(Long current, Long size) {
//创建Page对象
Page<TComment> commentPage = new Page<>(current,size);
//构建条件
QueryWrapper<TComment> wrapper = new QueryWrapper<>();
//排序
wrapper.orderByDesc("create_time");
//调用mybatis plus分页方法进行查询
commentMapper.selectPage(commentPage,wrapper);
//通过Page对象获取分页信息
List<TComment> typeList = commentPage.getRecords(); //每页的数据 list集合
long pagesize = commentPage.getSize(); //每页显示的条数
long total = commentPage.getTotal(); //总记录数
long pages = commentPage.getPages(); //总页数
return commentPage;
}
}
@RestController
@RequestMapping("/comment")
public class TCommentController {
@Autowired
private TCommentService commentService;
@Autowired
private TBlogService tBlogService;
//查询评论列表
@GetMapping("/comments/{blogId}")
public List<TComment> comments(@PathVariable Long blogId) {
return commentService.listCommentByBlogId(blogId);
}
//查询所有评论
@GetMapping("/getCommentByPage")
public Page<TComment> getCommentByPage(Long current, Long size){
return commentService.getCommentByPage(current, size);
}
//根据评论人姓名查询评论
@GetMapping("/comments")
public List<TComment> findCommentByNickname(String nickname){
return commentService.findCommentByNickname(nickname);
}
//新增评论
@PostMapping("/comments")
public List<TComment> post(@RequestBody TComment tComment,Authentication authentication,HttpServletRequest request) {
//获取评论人的ip
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
Long blogId = tComment.getBlogId();
if (authentication != null){
TUser user = (TUser)authentication.getPrincipal();
tComment.setAvatar(user.getAvatar());
tComment.setAdminComment(true);
tComment.setIp(ip);
}
else {
//设置头像
tComment.setAvatar("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2070453827,1163403148&fm=26&gp=0.jpg");
tComment.setIp(ip);
}
if (tComment.getParentComment() != null) {
tComment.setParentCommentId(tComment.getParentComment().getId());
}
commentService.saveComment(tComment);
TBlog tBlog = tBlogService.getById(blogId);
tBlog.setCommentCount(tBlog.getCommentCount() + 1);
tBlogService.updateByComment(tBlog);
return commentService.listCommentByBlogId(blogId);
}
//删除评论
@DeleteMapping("/deleteComment")
public String delete(Long id){
commentService.deleteComment(id);
return "OK";
}
}
<template>
<div>
<div style="margin-top: 20px;">
<!-- 搜索 -->
<el-input size="small" v-model="input_name" placeholder="请输入评论人名称,可回车搜索..." prefix-icon="el-icon-search"
style="width: 400px;margin-right: 10px;" @keydown.enter.native="search_name"></el-input>
<el-button size="small" type="primary" @click="search_name" icon="el-icon-search">搜索</el-button>
<!-- <el-button size="small" type="success" @click="dialog_add = true" icon="el-icon-plus">新增</el-button> -->
</div>
<div>
<el-table
:data="commentData"
style="width: 100%">
<el-table-column
label="编号"
width="70">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.id }}</span>
</template>
</el-table-column>
<el-table-column
label="昵称"
width="150">
<template slot-scope="scope">
<el-tag size="medium">{{ scope.row.nickname }}</el-tag>
</template>
</el-table-column>
<el-table-column
label="身份"
width="100">
<template slot-scope="scope">
<el-tag v-if="scope.row.adminComment" type="danger" size="medium">管理员</el-tag>
<el-tag v-if="!scope.row.adminComment" size="medium">游客</el-tag>
</template>
</el-table-column>
<el-table-column
label="评论内容"
width="300">
<template slot-scope="scope">
<el-tag size="medium">{{ scope.row.content }}</el-tag>
</template>
</el-table-column>
<el-table-column
label="发表时间"
width="230">
<template slot-scope="scope">
<el-tag size="medium">{{ scope.row.createTime }}</el-tag>
</template>
</el-table-column>
<el-table-column
label="ip地址"
width="180">
<template slot-scope="scope">
<el-tag size="medium">{{ scope.row.ip }}</el-tag>
</template>
</el-table-column>
<el-table-column
label="头像"
width="230">
<template slot-scope="scope">
<el-tag size="medium">{{ scope.row.avatar }}</el-tag>
</template>
</el-table-column>
<el-table-column
label="博客id"
width="200">
<template slot-scope="scope">
<el-tag size="medium">{{ scope.row.blogId }}</el-tag>
</template>
</el-table-column>
<el-table-column
label="父评论id"
width="100">
<template slot-scope="scope">
<el-tag size="medium">{{ scope.row.parentCommentId }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="180">
<template slot-scope="scope">
<el-button
size="mini"
type="primary"
@click="handleLook(scope.$index, scope.row)">回复</el-button>
<!-- <el-button
size="mini"
@click="handleEdit(scope.$index, scope.row)">编辑</el-button> -->
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div style="margin-top: 20px;" v-if="showPage">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-size="pagesize"
layout="total, prev, pager, next"
:total="total">
</el-pagination>
</div>
<!-- 对话框 回复评论 -->
<el-dialog
title="回复评论"
:visible.sync="dialog_look"
width="30%"
>
<el-form status-icon label-width="80px" :model="subComment" ref="subComment">
<el-form-item label="文章:">
{{BackComment.blogTitle}}
</el-form-item>
<el-form-item label="昵称:">
{{BackComment.nickname}}
</el-form-item>
<el-form-item label="内容:">
{{BackComment.content}}
</el-form-item>
<el-form-item label="日期:">
{{BackComment.time}}
</el-form-item>
<el-form-item label="回复:" prop="content"
:rules=" { required: true, message: '请输入评论内容', trigger: 'blur' }">
<el-input type="textarea" v-model="subComment.content"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialog_look = false">取 消</el-button>
<el-button type="primary" @click="submitBackComment('subComment')">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'CommentBlog',
data () {
return {
commentData:[], //评论数据
currentPage: 1, //当前页
total:0, //总记录数
pagesize:5, //页面大小
input_name:'', //搜索框值
dialog_add: false, //添加评论的对话框
dialog_edit: false, //编辑评论的对话框
type_edit:{
id:'',
name:''
},
showPage: true, //是否显示分页
dialog_look: false, //显示回复的对话框
BackComment: {
blogTitle: '',
nickname: '',
content: '',
time: '',
},
subComment:{
blogId:'',
parentCommentId:'',
adminComment:'',
nickname:'小L-Admin',
content:'', //回复评论的内容
email:'[email protected]'
}
}
},
mounted() {
this.initComment();
},
methods:{
//初始化评论数据
initComment(){
const _this = this
this.getRequest('/comment/getCommentByPage?current=' + this.currentPage + '&size=' + this.pagesize).then(resp=>{
console.log(resp)
_this.commentData = resp.records
_this.total = resp.total
})
},
//查看评论
handleLook(index, row){
console.log(row)
const _this = this
_this.dialog_look = true
this.getRequest('/blog/getByBlogId?id=' + row.blogId).then(resp=>{
console.log(resp)
_this.BackComment.blogTitle = resp.obj.title
})
_this.BackComment.nickname = row.nickname
_this.BackComment.content = row.content
_this.BackComment.time = row.createTime
_this.subComment.blogId = row.blogId
_this.subComment.parentCommentId = row.parentCommentId
_this.subComment.adminComment = true
},
//提交回复
submitBackComment(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
const _this = this
this.postRequest('/comment/comments',this.subComment).then(resp=>{
console.log(resp)
_this.initComment()
_this.BackComment.backComment = ''
_this.dialog_look = false
this.$message({ message: '回复评论成功!',type: 'success' });
})
} else {
console.log('error submit!!');
return false;
}
});
},
//编辑评论
handleEdit(index, row) {
const _this = this
console.log(index, row);
this.dialog_edit = true
this.getRequest('/type/getTypeById?id=' + row.id).then(resp=>{
console.log(resp)
_this.type_edit = resp.obj
})
},
//更新评论
updateType(){
const _this = this
this.putRequest('/type/updateType',this.type_edit).then(resp=>{
if(resp){
this.initComment()
_this.dialog_edit = false
}
})
},
//删除评论
handleDelete(index, row) {
console.log(index, row);
const _this = this
this.$confirm('此操作将永久删除该评论, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.deleteRequest('/comment/deleteComment?id=' + row.id).then(resp=>{
if(resp){
this.initComment()
this.$message({
type: 'success',
message: '已删除该评论'
});
}
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
//搜索评论
search_name(){
const _this = this
this.getRequest('/comment/comments?nickname=' + this.input_name).then(resp=>{
console.log(resp)
if(_this.input_name == ''){
_this.initComment();
_this.showPage = true
}else{
_this.commentData = resp
_this.showPage = false
_this.input_name = ''
}
})
},
handleSizeChange(val) {
console.log(`每页 ${val} 条`);
},
handleCurrentChange(val) {
console.log(`当前页: ${val}`);
this.currentPage = val
this.initComment()
}
}
}
</script>
<style scoped>
</style>