谷粒学院—课程评论功能的实现
在学习谷粒学院中,课程的分页查看的增加评论模块老师提供了思路解决方案,并没有带领大家去实现。
由于自己太笨,花费了近四个小时终于完成,总结出来,供大家参考和自己复习时候使用;
一…创建课程评论表
CREATE TABLE `edu_comment` (
`id` char(19) NOT NULL COMMENT '讲师ID',
`course_id` varchar(19) NOT NULL DEFAULT '' COMMENT '课程id',
`teacher_id` char(19) NOT NULL DEFAULT '' COMMENT '讲师id',
`member_id` varchar(19) NOT NULL DEFAULT '' COMMENT '会员id',
`nickname` varchar(50) DEFAULT NULL COMMENT '会员昵称',
`avatar` varchar(255) DEFAULT NULL COMMENT '会员头像',
`content` varchar(500) DEFAULT NULL COMMENT '评论内容',
`is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_course_id` (`course_id`),
KEY `idx_teacher_id` (`teacher_id`),
KEY `idx_member_id` (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评论';
#
# Data for table "edu_comment"
#
INSERT INTO `edu_comment` VALUES ('1194499162790211585','1192252213659774977','1189389726308478977','1','小三123','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','课程很好',0,'2019-11-13 14:16:08','2019-11-13 14:16:08'),('1194898406466420738','1192252213659774977','1189389726308478977','1','小三123','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','11',0,'2019-11-14 16:42:35','2019-11-14 16:42:35'),('1194898484388200450','1192252213659774977','1189389726308478977','1','小三123','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','111',0,'2019-11-14 16:42:53','2019-11-14 16:42:53'),('1195251020861317122','1192252213659774977','1189389726308478977','1',NULL,NULL,'2233',0,'2019-11-15 16:03:45','2019-11-15 16:03:45'),('1195251382720700418','1192252213659774977','1189389726308478977','1',NULL,NULL,'4455',0,'2019-11-15 16:05:11','2019-11-15 16:05:11'),('1195252819177570306','1192252213659774977','1189389726308478977','1','小三1231','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','55',0,'2019-11-15 16:10:53','2019-11-15 16:10:53'),('1195252899448160258','1192252213659774977','1189389726308478977','1','小三1231','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','55',0,'2019-11-15 16:11:13','2019-11-15 16:11:13'),('1195252920587452417','1192252213659774977','1189389726308478977','1','小三1231','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','223344',0,'2019-11-15 16:11:18','2019-11-15 16:11:18'),('1195262128095559681','14','1189389726308478977','1','小三1231','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','11',0,'2019-11-15 16:47:53','2019-11-15 16:47:53'),('1196264505170767874','1192252213659774977','1189389726308478977','1','小三1231','http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','666666',0,'2019-11-18 11:10:58','2019-11-18 11:10:58');
使用MP逆向工程生成对应的Mapper,Service,pojo,Controller等
2.分页查询课程评论
分页可以使用两种解决方案
一:使用element-ui提供封装好的插件,我们只需要将总记录数和当前页的记录传入即到Map中。
1.后端接口
//分页查询评论
@GetMapping("getCommentInfo/{page}/{limit}")
public R getCommentInfo(@PathVariable long page,@PathVariable long limit,String courseId){
Page<EduComment> commentPage = new Page<>(page,limit);
QueryWrapper<EduComment> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("gmt_create");
wrapper.eq("course_id",courseId);
IPage<EduComment> iPage = commentService.page(commentPage, wrapper);
List<EduComment> records = iPage.getRecords();//当前页的记录
long total = iPage.getTotal();//总记录数字
Map map = new HashMap();
map.put("items",records);
map.put("total",total);
return R.ok().data(map);
}
2.前端页面
<!-- 分页 使用element-ui疯转好的插件 -->
<el-pagination
:current-page="page"
:page-size="limit"
:total="total"
style="padding: 30px 0; text-align: center;"
layout="total, prev, pager, next, jumper"
@current-change="getCommon"
/>
<!-- 分页结束 -->
3.api调用接口
import request from '@/utils/request'
export default{
getCommontList(page,limit,courseId){
return request({
url:'/eduservice/comment/getCommentInfo/'+page+"/"+limit,
method:'get',
params: {courseId}
})
}
}
4.页面中的methods
需要导入api中的commont.js import commonApi from ‘@/api/common’
<script>
import couseApi from '@/api/course'
import cookie from 'js-cookie'
import commonApi from '@/api/common'
export default {
data(){
return{
courseWebVo:{},
chapterVideoList:{},
data:{},
comment:{
content:'',
courseId:''
},
courseId:'',
page:1,
limit:4,
total:0
}
},
created(){
this.courseId = this.$route.params.id
this.getCommon()
},
methods:{
getCommon(page=1){
this.page = page
console.log(page);
commonApi.getCommontList(this.page,this.limit,this.courseId)
.then(responce=>{
this.data = responce.data.data
this.total = responce.data.data.total
})
}
}
};
</script>
5.将返回数据data使用v-for遍历即可
二:使用较为底层的方法进行实现,此时我们需要将当前页,总记录数,一页记录数,总页数,上否有上一页,是否有下一页传入,最后将信息放入到Map中
和上述方法类似,只有在接口和分页条上有所差异
1.接口
//根据课程id查询评论列表
@ApiOperation(value = "评论分页列表")
@GetMapping("{page}/{limit}")
public R index(
@ApiParam(name = "page", value = "当前页码", required = true)
@PathVariable Long page,
@ApiParam(name = "limit", value = "每页记录数", required = true)
@PathVariable Long limit,
@ApiParam(name = "courseQuery", value = "查询对象", required = false)
String courseId) {
Page<Comment> pageParam = new Page<>(page, limit);
QueryWrapper<Comment> wrapper = new QueryWrapper<>();
wrapper.eq("course_id",courseId);
commentService.page(pageParam,wrapper);
List<Comment> commentList = pageParam.getRecords();
Map<String, Object> map = new HashMap<>();
map.put("items", commentList);
map.put("current", pageParam.getCurrent());
map.put("pages", pageParam.getPages());
map.put("size", pageParam.getSize());
map.put("total", pageParam.getTotal());
map.put("hasNext", pageParam.hasNext());
map.put("hasPrevious", pageParam.hasPrevious());
return R.ok().data(map);
}
2.分页条
<!-- 公共分页 开始 -->
<div class="paging">
<!-- undisable这个class是否存在,取决于数据属性hasPrevious -->
<a
:class="{undisable: !data.hasPrevious}"
href="#"
title="首页"
@click.prevent="gotoPage(1)">首</a>
<a
:class="{undisable: !data.hasPrevious}"
href="#"
title="前一页"
@click.prevent="gotoPage(data.current-1)"><</a>
<a
v-for="page in data.pages"
:key="page"
:class="{current: data.current == page, undisable: data.current == page}"
:title="'第'+page+'页'"
href="#"
@click.prevent="gotoPage(page)">{{ page }}</a>
<a
:class="{undisable: !data.hasNext}"
href="#"
title="后一页"
@click.prevent="gotoPage(data.current+1)">></a>
<a
:class="{undisable: !data.hasNext}"
href="#"
title="末页"
@click.prevent="gotoPage(data.pages)">末</a>
<div class="clear"/>
</div>
<!-- 公共分页 结束 -->
3.添加课程评论
实现思路
添加课程评论的两种解决方案
3.1.使用远程调用
1.将生产者和消费者进行Nacos的注册
#nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
2.增加启动类注解
在生产者也就是接口提供者的启动类上增加注解 @EnableDiscoveryClient 服务发现
在消费中也就是接口的使用者的启动类上增加注解 @EnableFeignClients 服务调用
3.服务调用Feign
创建接口
@Component
@FeignClient(name = "service-ucenter",fallback = CommentFileDegradeFeignClient.class)
public interface CommentClient {
//定义到调用方法的路径
@PostMapping("/educenter/member/getMemberInfoById/{memberId}")
public Map getUserId(@PathVariable String memberId);
}
创建熔断器
@Component
public class CommentFileDegradeFeignClient implements CommentClient{
@Override
public Map getUserId(String memberId) {
throw new GuliException(20001,"获取用户信息失败");
}
}
注意:
1.需要在接口和实现类上添加注解@Component,当项目启动后,加载到我们的容器中
2.在接口中要声明出服务调用的模块名称和指定熔断器的类
3.服务调用的路径应与服务发现的路径,提交方式应一致
4.服务发现的接口
@CrossOrigin
@RestController
@RequestMapping("/educenter/member")
public class UcenterMemberController {
@Autowired
private UcenterMemberService memberService;
//根据用户id查询用户信息
@PostMapping("getMemberInfoById/{memberId}")
public Map getMemberInfoById(@PathVariable String memberId){
//根据用户id查询用户信息
UcenterMember ucenterMember = memberService.getById(memberId);
Map<String, Object> map = new HashMap<>();
map.put("userInfo",ucenterMember);
return map;
}
}
5.发布评论接口实现
@CrossOrigin
@RestController
@RequestMapping("/eduservice/comment")
public class EduCommentController {
@Autowired
private EduCommentService commentService;
@Autowired
private CommentClient commentClient;
//添加评论
@PostMapping("addComment")
public R addComment(@RequestBody EduComment comment,HttpServletRequest request){
String memberId = JwtUtils.getMemberIdByJwtToken(request);
if (StringUtils.isEmpty(memberId)){
throw new GuliException(20001,"请先登录");
}
//如果不为空,证明已经登录过
comment.setMemberId(memberId);
//远程调用ucenter根据用户id获取用户信息
Map map = commentClient.getUserId(memberId);
Object object = map.get("userInfo");
// 将list中的数据转成json字符串
String jsonObject= JSON.toJSONString(object);
//将json转成需要的对象
EduComment userInfo = JSONObject.parseObject(jsonObject,EduComment.class);
//设置名字和头像
comment.setNickname(userInfo.getNickname());
comment.setAvatar(userInfo.getAvatar());
//保存评论
commentService.save(comment);
return R.ok();
}
}
这里会遇到一个异常:
java.util.LinkedHashMap cannot be cast to [Object]
原因:我们调用ucenter模块中从控制器,使用的@restController修饰,返回的是一JSON数据,我们需要将这个Json数据转换为Object
解决方案:
1.使用阿里巴巴的JSON转换工具 FastJSON
// 将list中的数据转成json字符串
String jsonObject= JSON.toJSONString(object);
//将json转成需要的对象
EduComment userInfo = JSONObject.parseObject(jsonObject,EduComment.class);
2.使用Google的Gson
Gson gson = new Gson();
EduComment userInfo = gson.fromJson(object,EduComment.class)
6.前端的实现
methods:{
addComment(){
//1.先判断是否登录
var isLogin = cookie.get("guli_token")
if(!isLogin){
this.$message({
type:"info",
message:"请先登录"
})
this.$router.push({path:"/login"})
}
this.comment.courseId = this.$route.params.id
this.comment.teacherId = this.courseWebVo.teacherId
commonApi.addComment(this.comment)
.then(responce=>{
if(responce.data.success){
this.$message({
message: "评论成功",
type: "success",
})
//清空
this.comment={}
//查询评论
this.getCommon()
}
})
}
}
3.2.不使用远程调用
- 点击发布评论时,通过cookie获取到当前的token,判断是否登录,如果没有登录,我们跳转到登录页面进行登录
- 当用户登录后,获取到用户的token,请求后端ucenter模块的接口,调用jwt的请求头信息,获取用户的id,根据id返回用户的信息
- 将用户信息的id,名称,头像获取并赋值给前端中定义的data,调用edu模块的接口,实现评论的增加