谷粒学院—课程评论功能的实现


在学习谷粒学院中,课程的分页查看的增加评论模块老师提供了思路解决方案,并没有带领大家去实现。

由于自己太笨,花费了近四个小时终于完成,总结出来,供大家参考和自己复习时候使用;

一…创建课程评论表

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)">&lt;</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)">&gt;</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模块的接口,实现评论的增加
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐