目录
数据库设计:
CREATE TABLE `comment` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'id',
`content` varchar(255) DEFAULT NULL COMMENT '评论内容',
`username` varchar(255) DEFAULT NULL COMMENT '用户名称',
`user_id` int DEFAULT NULL COMMENT '用户id',
`rate` decimal(10,1) DEFAULT NULL COMMENT '评分',
`foreign_id` int DEFAULT NULL COMMENT '业务模块的id',
`pid` int DEFAULT NULL COMMENT '父级评论id',
`target` varchar(255) DEFAULT NULL COMMENT '回复对象',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
实体类:entity
package com.chen.comments.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import javax.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author Y·C
* @version 1.0.0
* @ClassName Comment.java
* @Description TODO
* @createTime 2023年04月21日 21:52:00
*/
@Data
@Table(name = "comment")
public class Comment {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String content;
private String username;
private Integer userId;
private BigDecimal rate;
private Integer foreignId;
private Integer pid;
private String target;
@JsonFormat(pattern = "yyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@TableField(exist = false)
private List<Comment> children;
}
mapper:
package com.chen.comments.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chen.comments.entity.Comment;
import org.apache.ibatis.annotations.Mapper;
/**
* @author Y·C
* @version 1.0.0
* @ClassName CommentDao.java
* @Description TODO
* @createTime 2023年04月21日 22:06:00
*/
@Mapper
public interface CommentMapper extends BaseMapper<Comment> {
}
service:
package com.chen.comments.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.chen.comments.entity.Comment;
import java.util.List;
/**
* @author Y·C
* @version 1.0.0
* @ClassName CommentService.java
* @Description TODO
* @createTime 2023年04月22日 10:20:00
*/
public interface CommentService extends IService<Comment> {
List<Comment> findAllByForeignId(Integer foreignId);
}
serviceImpl:
package com.chen.comments.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chen.comments.entity.Comment;
import com.chen.comments.mapper.CommentMapper;
import com.chen.comments.service.CommentService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* @author Y·C
* @version 1.0.0
* @ClassName CommentServiceImpl.java
* @Description TODO
* @createTime 2023年04月22日 10:21:00
*/
@Service
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService {
@Resource
private CommentMapper commentMapper;
@Override
public List<Comment> findAllByForeignId(Integer foreignId) {
QueryWrapper<Comment> wrapper = new QueryWrapper<>();
wrapper.eq("foreign_id",foreignId);
List<Comment> comments = commentMapper.selectList(wrapper);
return comments;
}
}
controller:
package com.chen.comments.controller;
import com.chen.comments.entity.Comment;
import com.chen.comments.service.CommentService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author Y·C
* @version 1.0.0
* @ClassName CommentController.java
* @Description TODO
* @createTime 2023年04月21日 22:09:00
*/
@CrossOrigin
@RestController
@RequestMapping("/comment")
public class CommentController {
@Resource
private CommentService commentService;
@PostMapping
public void save(@RequestBody Comment comment){
commentService.save(comment);
}
@GetMapping
public Map<String,Object> list(@RequestParam Integer foreignId){
Map<String,Object> map = new HashMap<>();
map.put("rate",BigDecimal.ZERO);
List<Comment> comments = commentService.findAllByForeignId(foreignId);
//获得所有不为空的评分
List<Comment> commentList = comments.stream().filter(comment -> comment.getRate() != null).collect(Collectors.toList());
//将查询出来的评分进行累加
commentList.stream().map(Comment::getRate).reduce(BigDecimal::add).ifPresent(res -> {
//算出平均分
map.put("rate",res.divide(BigDecimal.valueOf(commentList.size()),1,RoundingMode.HALF_UP));
});
List<Comment> rootComments = comments.stream().filter(comment -> comment.getPid() == null).collect(Collectors.toList());
for (Comment rootComment : rootComments) {
rootComment.setChildren(comments.stream().filter(comment -> rootComment.getId().equals(comment.getPid())).collect(Collectors.toList()));
}
map.put("comments",rootComments);
return map;
}
}
application.yml:
server:
port: 9000
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/rate-demo?serverTimezone=GMT%2b8
username: root
password: password
Vue代码:
<template>
<div style="width: 1000px; margin: 0 auto">
<div style="width: 500px;margin: 10px auto; border: 1px solid #ccc; padding: 20px">
<div>商品名称:++++</div>
<div style="margin: 10px 0">商品价格 :¥100</div>
<div style="margin: 10px 0">商品评分:<el-rate style="display: inline-block" :colors="['#99A9BF','#F7BA2A','#FF9900']"
v-model="value" disabled allow-half show-text></el-rate></div>
</div>
<div style="margin: 20px 0">
<el-rate v-model="comment.rate" :colors="['#99A9BF','#F7BA2A','#FF9900']"
:max="5"
allow-half
show-text
:texts="['非常差','失望至极','一般般','非常满意','surprise']"></el-rate>
<div style="margin: 10px 0">
<el-input type="textarea" placeholder="请输入评论" v-model="comment.content"></el-input>
<div style="text-align: right; margin: 10px 0" >
<el-button type="primary" @click="submit" >提交</el-button>
</div>
</div>
</div>
<div style="margin: 20px 0">
<div style="margin: 10px 0; font-size: 24px; padding: 10px 0; border-bottom: 1px solid #ccc; text-align: left">
评论列表
</div>
<div style="margin: 20px 0; text-align: left">
<div style="padding: 10px 0;border-bottom: 1px solid #ccc;" v-for="item in comments" :key = "item.id">
<div style=" display: flex">
<div style="width: 80px"><el-avatar :size="50" :src="'https://edu-yc.oss-cn-beijing.aliyuncs.com/avatar/b70223e8672e10f69f0ca36e652507ca.png'"></el-avatar></div>
<div style="flex: 1">
<div style="color: black "> <b>{{ item.username }}:</b><span>{{ item.content }}</span></div>
<div style="width: 200px;margin-top: 15px">
<i class="el-icon-time"></i><span style="margin-left: 5px">{{ item.createTime}}</span>
</div>
<!--多级回复-->
<div>
<el-button type="text" @click="reply(item.id,item.username)">回复</el-button>
</div>
<!--回复列表-->
<div v-if="item.children.length" style="margin-left: 100px;background-color:aliceblue;padding: 10px;border-radius: 10px">
<div v-for="sub in item.children" :key="sub.id">
<div style="padding: 5px 0">
<b style="cursor: pointer" @click="reply(sub.pid,sub.username)">{{ sub.username }}</b>
<span>
回复
<span style="color: cornflowerblue">@{{ sub.target }}</span>
<span style="color: #666; margin-left: 10px">{{ sub.content }}</span>
</span>
<div style="width: 200px;margin-top: 15px">
<i class="el-icon-time"></i><span style="margin-left: 5px">{{ item.createTime}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<el-dialog title="回复" :visible.sync="dialogFormVisible" width="40%">
<el-form :model="replyComment">
<el-form-item label="内容" :label-width="100">
<el-input v-model="replyComment.content" autocomplete="off" style="width: 80%"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="saveReply">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'Home',
data(){
return{
value:0,
comments:[],
pid: 0,
comment:{
rate:0,
content:''
},
replyComment:{
},
dialogFormVisible:false
}
},
created() {
this.load()
},
methods:{
reply(pid,target){
this.replyComment ={pid: pid, userId:2,username:'李四',foreignId:1,target:target}
this.dialogFormVisible =true
},
load(){
fetch("http://localhost:9000/comment?foreignId=1").then(res =>{
if(res.status ===500){
this.$notify.success("系统错误")
return {}
}
return res.json()
}).then(res => {
this.value = res.rate
this.comments = res.comments
})
},
saveReply(){
fetch("http://localhost:9000/comment",{
method:'post',
headers:{
'Content-Type':'application/json;charset=utf-8'
},
body: JSON.stringify(this.replyComment)
}).then(res =>{
if (res.status ===200){
this.$notify.success("评论成功")
this.load();
this.replyComment ={}
this.dialogFormVisible=false
}
})
},
submit(){
this.comment.userId = 1
this.comment.username = '张三'
this.comment.foreignId = 1
fetch("http://localhost:9000/comment",{
method:'post',
headers:{
'Content-Type':'application/json;charset=utf-8'
},
body: JSON.stringify(this.comment)
}).then(res =>{
if (res.status ===200){
this.$notify.success("评论成功")
this.load();
this.comment ={}
}
})
}
}
}
</script>