谷粒学院Day-15,评论

评论功能

一、数据库设计

- 数据库

edu_comment

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='评论';

二、后端设计接口

1、在service-edu模块,生成课程评论代码

(1)使用mp代码生成器生成

public class CodeGenerator {

    @Test
    public void run() {

        // 1、创建代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 2、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        //C:\Users\86185\Desktop\guli_parent
        gc.setOutputDir("C:\\Users\\86185\\Desktop\\guli_parent\\service\\service-edu" + "/src/main/java"); //输出目录

        gc.setAuthor("czy"); //作者名
        gc.setOpen(false); //生成后是否打开资源管理器
        gc.setFileOverride(false); //重新生成时文件是否覆盖

        gc.setServiceName("%sService");	//去掉Service接口的首字母I
        gc.setIdType(IdType.ID_WORKER_STR); //主键策略
        gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
        gc.setSwagger2(true);//开启Swagger2模式

        mpg.setGlobalConfig(gc);

        // 3、数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 4、包配置
        PackageConfig pc = new PackageConfig();

        //生成包:com.czy.eduservice
        pc.setModuleName("eduservice"); //模块名
        pc.setParent("com.czy");

        //生成包:com.czy.controller
        pc.setController("controller");
        pc.setEntity("entity");
        pc.setService("service");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);

        // 5、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("edu_comment");//根据数据库哪张表生成,有多张表就加逗号继续填写

        strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
        strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀

        strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
        strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作

        strategy.setRestControllerStyle(true); //restful api风格控制器
        strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符

        mpg.setStrategy(strategy);


        // 6、执行
        mpg.execute();
    }
}

2、在service-ucenter模块,创建接口

image-20210308115011622

com.czy.educenter.controller.UcenterMemberController

评论前先登录,查询用户信息
@RestController
@RequestMapping("/educenter/member")
@CrossOrigin
public class UcenterMemberController {

    @Autowired
    private UcenterMemberService memberService;


    //2.评论前先登录,查询用户信息
    @PostMapping("/getMemberInfoById/{memberId}")
    public UcenterMember getMemberInfoById(@PathVariable String memberId){
        UcenterMember member = memberService.getById(memberId);
        UcenterMember memberVo = new UcenterMember();
        BeanUtils.copyProperties(member,memberVo);

        return memberVo;
    }
}

3、封装返回给评论服务的对象

com.czy.educenter.entity.UcenterMember

用于返回给调用的edu评论entity

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="UcenterMember对象", description="会员表")
public class UcenterMember implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "会员id")
    @TableId(value = "id", type = IdType.ID_WORKER_STR)
    private String id;

    @ApiModelProperty(value = "微信openid")
    private String openid;

    @ApiModelProperty(value = "手机号")
    private String mobile;

    @ApiModelProperty(value = "密码")
    private String password;

    @ApiModelProperty(value = "昵称")
    private String nickname;

    @ApiModelProperty(value = "性别 1 女,2 男")
    private Integer sex;

    @ApiModelProperty(value = "年龄")
    private Integer age;

    @ApiModelProperty(value = "用户头像")
    private String avatar;

    @ApiModelProperty(value = "用户签名")
    private String sign;

    @ApiModelProperty(value = "是否禁用 1(true)已禁用,  0(false)未禁用")
    private Boolean isDisabled;

    @ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除")
    private Boolean isDeleted;

    @ApiModelProperty(value = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private Date gmtCreate;

    @ApiModelProperty(value = "更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date gmtModified;


}

4、创建课程评论controller

com.czy.eduservice.controller.EduCommentController

@RestController
@RequestMapping("/eduservice/comment")
@CrossOrigin
public class EduCommentController {

    @Autowired
    private EduCommentService eduCommentService;

    @Autowired
    private UcenterClient ucenterClient;

    //根据课程id_分页查询课程评论的方法
    @GetMapping("/getCommentPage/{page}/{limit}")
    public R getCommentPage(@PathVariable Long page,@PathVariable Long limit,String courseId){
        Page<EduComment> commentPage = new Page<>(page, limit);

        QueryWrapper<EduComment> wrapper = new QueryWrapper<>();

        //判断课程id是否为空
        if (!StringUtils.isEmpty(courseId)){
            wrapper.eq("course_id",courseId);
        }

        //按最新排序
        wrapper.orderByDesc("gmt_create");

        //数据会被封装到commentPage中
        eduCommentService.page(commentPage,wrapper);

        List<EduComment> commentList = commentPage.getRecords();
        long current = commentPage.getCurrent();//当前页
        long size = commentPage.getSize();//一页记录数
        long total = commentPage.getTotal();//总记录数
        long pages = commentPage.getPages();//总页数
        boolean hasPrevious = commentPage.hasPrevious();//是否有上页
        boolean hasNext = commentPage.hasNext();//是否有下页

        HashMap<String, Object> map = new HashMap<>();
        map.put("current",current);
        map.put("size",size);
        map.put("total",total);
        map.put("pages",pages);
        map.put("hasPrevious",hasPrevious);
        map.put("hasNext",hasNext);
        map.put("list",commentList);

        return R.ok().data(map);
    }

    //添加评论
    @PostMapping("/auth/addComment")
    public R addComment(HttpServletRequest request,@RequestBody EduComment eduComment){
        String memberId = JwtUtils.getMemberIdByJwtToken(request);
        //判断用户是否登录
        if (StringUtils.isEmpty(memberId)){
            throw new GuliException(20001,"请先登录");
        }
        eduComment.setMemberId(memberId);

        //远程调用ucenter根据用户id获取用户信息
        UcenterMember memberVo = ucenterClient.getMemberInfoById(memberId);

        eduComment.setAvatar(memberVo.getAvatar());
        eduComment.setNickname(memberVo.getNickname());
        eduComment.setGmtCreate(memberVo.getGmtCreate());
        eduComment.setGmtModified(memberVo.getGmtModified());

        //保存评论
        eduCommentService.save(eduComment);

        return R.ok();
    }


}

 5、Open Feign远程调用类

  • UcenterClient接口

com.czy.eduservice.client.UcenterClient

@Component
@FeignClient(name = "service-ucenter",fallback = UcenterClientImpl.class)
public interface UcenterClient {

    @PostMapping("/educenter/member/getMemberInfoById/{memberId}")
    public UcenterMember getMemberInfoById(@PathVariable String memberId);

}
  • UcenterClientImpl

com.czy.eduservice.client.impl.UcenterClientImpl

@Component
public class UcenterClientImpl implements UcenterClient {
    @Override
    public UcenterMember getMemberInfoById(String memberId) {
        return null;
    }
}

三、前端部分

  • api方法_commonedu.js

guli-front\api\commonedu.js

import request from '@/utils/request'
export default {
    getPageList(page, limit, courseId) {
        return request({
            url: `/eduservice/comment/getCommentPage/${page}/${limit}`,
            method: 'get',
            params: courseId
        })
    },
    addComment(comment) {
        return request({
            url: `/eduservice/comment/auth/addComment`,
            method: 'post',
            data: comment
        })
    }
}

guli-front\pages/course/_id.vue

把刚刚定义的接口引入,然后编写guli-front\pages/course/_id.vue的js脚本

<script>
import courseApi from '@/api/course'
import comment from "@/api/commonedu"
export default {
  data() {
    return {
      chapterList: [],
      course: {
        courseId: "",
      },
      data: {},
      page: 1,
      limit: 4,
      total: 10,
      comment: {
        content: "",
        courseId: "",
        teacherId: "",
      },
      isbuyCourse: false,
    };
  },
  methods:{
    initComment() {
      comment
        .getPageList(this.page, this.limit, this.course.courseId)
        .then((response) => {
          this.data = response.data.data;
          console.log(response.data.data);
        });
    },
    addComment() {
                this.comment.courseId = this.course.courseId;
                this.comment.teacherId = this.course.teacherId;
                comment.addComment(this.comment).then((response) => {
                    if (response.data.success) {
                        this.$message({
                            message: "评论成功",
                            type: "success",
                        });
                        this.comment.content = "";
                        this.initComment();
                    }
                });
            },
            gotoPage(page) {
                comment.getPageList(page, this.limit, this.courseId).then((response) => {
                    this.data = response.data.data;
                });
            },
  }
  ,
   asyncData({ params, error }) {
     return courseApi.getCourseInfo(params.id)
        .then(response => {
          return {
            courseWebVo: response.data.data.courseWebVo,
            chapterVideoList: response.data.data.chapterVideoList
          }
        })
   },
   
            created() {
            this.course.courseId = this.$route.params.id;
            //获取课程详细信息
            //this.getCourseInfo();
            this.initComment();
        },

};
</script>

前端页面

<template>
  <div id="aCoursesList" class="bg-fa of">
    <!-- /课程详情 开始 -->
    <section class="container">
      <section class="path-wrap txtOf hLh30">
        <a href="#" title class="c-999 fsize14">首页</a>
        \
        <a href="#" title class="c-999 fsize14">{
   
   {courseWebVo.subjectLevelOne}}</a>
        \
        <span class="c-333 fsize14">{
   
   {courseWebVo.subjectLevelTwo}}</span>
      </section>
      <div>
        <article class="c-v-pic-wrap" style="height: 357px;">
          <section class="p-h-video-box" id="videoPlay">
            <img :src="courseWebVo.cover" :alt="courseWebVo.title" class="dis c-v-pic">
          </section>
        </article>
        <aside class="c-attr-wrap">
          <section class="ml20 mr15">
            <h2 class="hLh30 txtOf mt15">
              <span class="c-fff fsize24">{
   
   {courseWebVo.title}}</span>
            </h2>
            <section class="c-attr-jg">
              <span class="c-fff">价格:</span>
              <b class="c-yellow" style="font-size:24px;">¥{
   
   {courseWebVo.price}}</b>
            </section>
            <section class="c-attr-mt c-attr-undis">
              <span class="c-fff fsize14">主讲: {
   
   {courseWebVo.teacherName}}&nbsp;&nbsp;&nbsp;</span>
            </section>
            <section class="c-attr-mt of">
              <span class="ml10 vam">
                <em class="icon18 scIcon"></em>
                <a class="c-fff vam" title="收藏" href="#" >收藏</a>
              </span>
            </section>
            <section class="c-attr-mt">
              <a href="#" title="立即观看" class="comm-btn c-btn-3">立即观看</a>
            </section>
          </section>
        </aside>
        <aside class="thr-attr-box">
          <ol class="thr-attr-ol clearfix">
            <li>
              <p>&nbsp;</p>
              <aside>
                <span class="c-fff f-fM">购买数</span>
                <br>
                <h6 class="c-fff f-fM mt10">{
   
   {courseWebVo.buyCount}}</h6>
              </aside>
            </li>
            <li>
              <p>&nbsp;</p>
              <aside>
                <span class="c-fff f-fM">课时数</span>
                <br>
                <h6 class="c-fff f-fM mt10">20</h6>
              </aside>
            </li>
            <li>
              <p>&nbsp;</p>
              <aside>
                <span class="c-fff f-fM">浏览数</span>
                <br>
                <h6 class="c-fff f-fM mt10">501</h6>
              </aside>
            </li>
          </ol>
        </aside>
        <div class="clear"></div>
      </div>
      <!-- /课程封面介绍 -->
      <div class="mt20 c-infor-box">
        <article class="fl col-7">
          <section class="mr30">
            <div class="i-box">
              <div>
                <section id="c-i-tabTitle" class="c-infor-tabTitle c-tab-title">
                  <a name="c-i" class="current" title="课程详情">课程详情</a>
                </section>
              </div>
              <article class="ml10 mr10 pt20">
                <div>
                  <h6 class="c-i-content c-infor-title">
                    <span>课程介绍</span>
                  </h6>
                  <div class="course-txt-body-wrap">
                    <section class="course-txt-body">
                      <p v-html="courseWebVo.description">{
   
   {courseWebVo.description}}</p>
                    </section>
                  </div>
                </div>
                <!-- /课程介绍 -->
                <div class="mt50">
                  <h6 class="c-g-content c-infor-title">
                    <span>课程大纲</span>
                  </h6>
                  <section class="mt20">
                    <div class="lh-menu-wrap">
                      <menu id="lh-menu" class="lh-menu mt10 mr10">
                        <ul>
                          <!-- 文件目录 -->
                          <li class="lh-menu-stair" v-for="chapter in chapterVideoList" :key="chapter.id">
                            <a href="javascript: void(0)" :title="chapter.title" class="current-1">
                              <em class="lh-menu-i-1 icon18 mr10"></em>{
   
   {chapter.title}}
                            </a>
 
                            <ol class="lh-menu-ol" style="display: block;">
                              <li class="lh-menu-second ml30" v-for="video in chapter.children" :key="video.id">
                                <a :href="'/player/'+video.videoSourceId" target="_blank">
                                  <span class="fr">
                                    <i class="free-icon vam mr10">免费试听</i>
                                  </span>
                                  <em class="lh-menu-i-2 icon16 mr5">&nbsp;</em>{
   
   {video.title}}
                                </a>
                              </li>
                              
                            </ol>
 
                          </li>
                        </ul>
                      </menu>
                    </div>
                  </section>
                </div>
                <!-- /课程大纲 -->
              </article>
            </div>
          </section>
        </article>
        <aside class="fl col-3">
          <div class="i-box">
            <div>
              <section class="c-infor-tabTitle c-tab-title">
                <a title href="javascript:void(0)">主讲讲师</a>
              </section>
              <section class="stud-act-list">
                <ul style="height: auto;">
                  <li>
                    <div class="u-face">
                      <a href="#">
                        <img :src="courseWebVo.avatar" width="50" height="50" alt>
                      </a>
                    </div>
                    <section class="hLh30 txtOf">
                      <a class="c-333 fsize16 fl" href="#">{
   
   {courseWebVo.teacherName}}</a>
                    </section>
                    <section class="hLh20 txtOf">
                      <span class="c-999">{
   
   {courseWebVo.intro}}</span>
                    </section>
                  </li>
                </ul>
              </section>
            </div>
          </div>
        </aside>
        <div class="clear"></div>
        <!-- 评论模板开始 -->
          <div class="mt50 commentHtml">
    <div>
        <h6 class="c-c-content c-infor-title" id="i-art-comment">
            <span class="commentTitle">课程评论</span>
        </h6>
        <section class="lh-bj-list pr mt20 replyhtml">
            <ul>
                <li class="unBr">
                    <aside class="noter-pic">
                        <img
                             width="50"
                             height="50"
                             class="picImg"
                             src="~/assets/img/avatar-boy.gif"
                             />
                    </aside>
                    <div class="of">
                        <section class="n-reply-wrap">
                            <fieldset>
                                <textarea
                                          name=""
                                          v-model="comment.content"
                                          placeholder="输入您要评论的文字"
                                          id="commentContent"
                                          ></textarea>
                            </fieldset>
                            <p class="of mt5 tar pl10 pr10">
                                <span class="fl"
                                      ><tt
                                           class="c-red commentContentmeg"
                                           style="display: none"
                                           ></tt
                                    ></span>
                                <input
                                       type="button"
                                       @click="addComment()"
                                       value="回复"
                                       class="lh-reply-btn"
                                       />
                            </p>
                        </section>
                    </div>
                </li>
            </ul>
        </section>
        <section class="">
            <section class="question-list lh-bj-list pr">
                <ul class="pr10">
                    <li v-for="comment in data.list" :key="comment.id">
                        <aside class="noter-pic">
                            <img
                                 width="50"
                                 height="50"
                                 class="picImg"
                                 :src="comment.avatar"
                                 />
                        </aside>
                        <div class="of">
                            <span class="fl">
                                <font class="fsize12 c-blue">{
   
   { comment.nickname }}</font>
                                <font class="fsize12 c-999 ml5">评论:</font></span
                                >
                        </div>
                        <div class="noter-txt mt5">
                            <p>{
   
   { comment.content }}</p>
                        </div>
                        <div class="of mt5">
                            <span class="fr"
                                  ><font class="fsize12 c-999ml5">{
   
   {
                                comment.gmtCreate
                                }}</font></span
                                >
                        </div>
                    </li>
                </ul>
            </section>
        </section>
        <!-- 公共分页 开始 -->
        <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>
        <!-- 公共分页 结束 -->
        <!-- 评论模板结束 -->
      </div>
     </div>
   </div>
</section>
    <!-- /课程详情 结束 -->
  </div>
</template>

运行,随便发点评论

猜你喜欢

转载自blog.csdn.net/weixin_46511995/article/details/124640696