微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”九、整合阿里云视频播放器、课程评论功能、讲师详情页、课程详情页、检索功能、课程和讲师列表功能

一、讲师

所需功能确认
分页显示数据,每页8名讲师,点击讲师进入详情页面
进入到详情页面后,显示对应讲师详细信息,和关于他的课程

在这里插入图片描述
在这里插入图片描述

1、分页查询接口(后端)

1、controller

package com.yzpnb.eduservice.controller.api;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yzpnb.common_utils.Result;
import com.yzpnb.eduservice.entity.EduTeacher;
import com.yzpnb.eduservice.service.EduTeacherService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("/eduservice/api/teacher")
@CrossOrigin
public class TeacherApiController {


    @Autowired
    private EduTeacherService eduTeacherService;

    @ApiOperation("分页查询讲师")
    @GetMapping("limitSelectTeacher/{page}/{limit}")
    public Result limitSelectTeacher(@ApiParam(name="page",value = "当前页")
                                     @PathVariable Long page,
                                     @ApiParam(name = "limit",value = "每页记录数")
                                     @PathVariable Long limit){

        Page<EduTeacher> pageTeacher=new Page<>(page,limit);

        Map<String,Object> map=eduTeacherService.limitSelectTeacher(pageTeacher);

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

在这里插入图片描述

2、service

package com.yzpnb.eduservice.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yzpnb.eduservice.entity.EduTeacher;
import com.yzpnb.eduservice.mapper.EduTeacherMapper;
import com.yzpnb.eduservice.service.EduTeacherService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 讲师 服务实现类
 * </p>
 *
 * @author testjava
 * @since 2020-05-07
 */
@Service
public class EduTeacherServiceImpl extends ServiceImpl<EduTeacherMapper, EduTeacher> implements EduTeacherService {

    /**
     * 分页查询讲师,返回所有数据
     * @param pageTeacher
     * @return
     */
    @Override
    public Map<String, Object> limitSelectTeacher(Page<EduTeacher> pageTeacher) {
        QueryWrapper<EduTeacher> wrapper=new QueryWrapper<>();
        wrapper.orderByDesc("id");//根据id降序排列

        baseMapper.selectPage(pageTeacher, wrapper);

        Map<String,Object> map=new HashMap<>();
        map.put("allLimitTeacher", pageTeacher.getRecords());   //获取所有分页讲师
        map.put("current", pageTeacher.getCurrent());           //获取当前页
        map.put("pages", pageTeacher.getPages());               //页码
        map.put("size", pageTeacher.getSize());                 //获取每页记录数
        map.put("total", pageTeacher.getTotal());               //获取总记录
        map.put("hasNext", pageTeacher.hasNext());              //是否有下一页
        map.put("hasPrevious", pageTeacher.hasPrevious());      //是否有上一页

        return map;
    }
}

在这里插入图片描述

3、测试

在这里插入图片描述

2、分页显示讲师(前端)

编写api接口,调用方法得到数据,然后v-for遍历,都是换汤不换药,参考GitHub源码即可
就是将静态页面换成动态页面,将数据与数据库联系起来而已

在这里插入图片描述

3、讲师详情页(后端)

根据讲师id查询讲师详细信息,和对应课程信息

在这里插入图片描述

1、controller

 @Autowired
    EduCourseService eduCourseService;
    @ApiOperation("根据id查询讲师信息和他讲的课程")
    @GetMapping("selectTeacherAndCourse/{id}")
    public Result selectTeacherAndCourse(@ApiParam(name = "id",value = "讲师id")
                                         @PathVariable String id){

        //查询讲师信息
        EduTeacher eduTeacher = eduTeacherService.getById(id);

        //查询课程
        QueryWrapper<EduCourse> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("teacher_id",id);
        List<EduCourse> eduCourseList = eduCourseService.list(queryWrapper);

        //将数据存入map集合
        Map<String,Object> map=new HashMap<>();
        map.put("teacher",eduTeacher);
        map.put("courseList",eduCourseList);

        //返回数据
        return Result.ok().data(map);
    }

2、测试

在这里插入图片描述

4、讲师详情页(前端)

老步骤,源码在GitHub

在这里插入图片描述

二、课程

功能确认
根据一级二级分类筛选课程,根据不同规则排序
分页查询课程,每页8个课程,点击课程进入详情页
进入详情页后,显示课程信息,课程简介,课程大纲,主讲教师等信息

在这里插入图片描述
在这里插入图片描述

1、分页条件查询排序接口(后端)

1、创建vo对象

package com.yzpnb.eduservice.entity.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@ApiModel(value = "课程查询对象", description = "课程查询对象封装")
@Data
public class CourseApiVo {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "课程名称")
    private String title;

    @ApiModelProperty(value = "讲师id")
    private String teacherId;

    @ApiModelProperty(value = "一级类别id")
    private String subjectParentId;

    @ApiModelProperty(value = "二级类别id")
    private String subjectId;

    @ApiModelProperty(value = "销量排序")
    private String buyCountSort;

    @ApiModelProperty(value = "最新时间排序")
    private String gmtCreateSort;

    @ApiModelProperty(value = "价格排序")
    private String priceSort;
}

在这里插入图片描述

2、controller

package com.yzpnb.eduservice.controller.api;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yzpnb.common_utils.Result;
import com.yzpnb.eduservice.entity.EduCourse;
import com.yzpnb.eduservice.entity.vo.CourseApiVo;
import com.yzpnb.eduservice.service.EduCourseService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/eduservice/api/course")
@CrossOrigin
public class CourseApiController {

    @Autowired
    private EduCourseService eduCourseService;

    @ApiOperation("条件分页查询")
    @PostMapping("selectIfLimitCourse/{page}/{limit}")
    public Result selectIfLimitCourse(@ApiParam(name = "page",value = "当前页")
                                      @PathVariable Long page,
                                      @ApiParam(name = "limit",value="每页记录数")
                                      @PathVariable Long limit,
                                      @ApiParam(name = "courseApiVo",value = "课程和课程条件对象,没有值也可以,不报错",required = false)
                                      @RequestBody(required = false) CourseApiVo courseApiVo){

        Page<EduCourse> pageCourse=new Page<>(page,limit);

        Map<String,Object> map=eduCourseService.selectIfLimitCourse(pageCourse,courseApiVo);

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

}

在这里插入图片描述

3、service

 /**
     * 条件分页查询
     * @param pageCourse
     * @param courseApiVo
     * @return
     */
    @Override
    public Map<String, Object> selectIfLimitCourse(Page<EduCourse> pageCourse, CourseApiVo courseApiVo) {
        QueryWrapper<EduCourse> queryWrapper=new QueryWrapper<>();

        if (!StringUtils.isEmpty(courseApiVo.getSubjectParentId())) {//如果选择了一级分类,添加查询条件一级分类id=用户选择分类id
            queryWrapper.eq("subject_parent_id", courseApiVo.getSubjectParentId());
        }

        if (!StringUtils.isEmpty(courseApiVo.getSubjectId())) {//二级分类
            queryWrapper.eq("subject_id", courseApiVo.getSubjectId());
        }

        if (!StringUtils.isEmpty(courseApiVo.getBuyCountSort())) {//选择按关注度排序
            queryWrapper.orderByDesc("buy_count");
        }

        if (!StringUtils.isEmpty(courseApiVo.getGmtCreateSort())) {//按更新时间排序
            queryWrapper.orderByDesc("gmt_create");
        }

        if (!StringUtils.isEmpty(courseApiVo.getPriceSort())) {//按价格排序
            queryWrapper.orderByDesc("price");
        }


        pageCourse=baseMapper.selectPage(pageCourse,queryWrapper);

        Map<String,Object> map=new HashMap<>();
        map.put("allLimitTeacher", pageCourse.getRecords());   //获取所有分页课程
        map.put("current", pageCourse.getCurrent());           //获取当前页
        map.put("pages", pageCourse.getPages());               //页码
        map.put("size", pageCourse.getSize());                 //获取每页记录数
        map.put("total", pageCourse.getTotal());               //获取总记录
        map.put("hasNext", pageCourse.hasNext());              //是否有下一页
        map.put("hasPrevious", pageCourse.hasPrevious());      //是否有上一页

        return map;
    }

在这里插入图片描述

4、测试

在这里插入图片描述
在这里插入图片描述

2、条件分页显示讲师(前端)

老规矩,GitHub

在这里插入图片描述
在这里插入图片描述

3、课程详情页(后端)

需要根据信息获取页面中信息,涉及多表查询,

在这里插入图片描述

1、创建Vo对象

package com.yzpnb.eduservice.entity.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;

@ApiModel(value="课程信息", description="网站课程详情页需要的相关字段")
@Data
public class CourseApiInfoVo implements Serializable {
    private static final long serialVersionUID = 1L;

    private String id;

    @ApiModelProperty(value = "课程标题")
    private String title;

    @ApiModelProperty(value = "课程销售价格,设置为0则可免费观看")
    private BigDecimal price;

    @ApiModelProperty(value = "总课时")
    private Integer lessonNum;

    @ApiModelProperty(value = "课程封面图片路径")
    private String cover;

    @ApiModelProperty(value = "销售数量")
    private Long buyCount;

    @ApiModelProperty(value = "浏览数量")
    private Long viewCount;

    @ApiModelProperty(value = "课程简介")
    private String description;

    @ApiModelProperty(value = "讲师ID")
    private String teacherId;

    @ApiModelProperty(value = "讲师姓名")
    private String teacherName;

    @ApiModelProperty(value = "讲师资历,一句话说明讲师")
    private String intro;

    @ApiModelProperty(value = "讲师头像")
    private String avatar;

    @ApiModelProperty(value = "课程类别ID")
    private String subjectLevelOneId;

    @ApiModelProperty(value = "类别名称")
    private String subjectLevelOne;

    @ApiModelProperty(value = "课程类别ID")
    private String subjectLevelTwoId;

    @ApiModelProperty(value = "类别名称")
    private String subjectLevelTwo;
}

在这里插入图片描述

2、controller

@Autowired
    private EduChapterService eduChapterService;
    @ApiOperation("根据id获取课程详细信息,包括讲师和课程大纲")
    @GetMapping(value = "selectCourserApiInfoVoById/{id}")
    public Result selectCourserApiInfoVoById(@ApiParam(name = "id", value = "课程ID", required = true)
                                             @PathVariable String id){

        //查询课程信息和讲师信息
        CourseApiInfoVo courseApiInfoVo = eduCourseService.selectCourserApiInfoVoById(id);

        //查询当前课程的章节信息
        List<ChapterVo> chapterVoList = eduChapterService.selectChapterVideoByCourseId(id);

       Map<String,Object> map=new HashMap<>();
       map.put("course", courseApiInfoVo);
       map.put("chapterVoList", chapterVoList);

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

在这里插入图片描述

3、service

/**
     * 根据id获取课程详细信息,包括讲师,并更新浏览量
     * @param id
     * @return
     */
    @Override
    public CourseApiInfoVo selectCourserApiInfoVoById(String id) {
        //更新浏览量
        EduCourse eduCourse = baseMapper.selectById(id);
        eduCourse.setViewCount(eduCourse.getViewCount() + 1);
        baseMapper.updateById(eduCourse);

        //调用mapper接口并返回数据
        return eduCourseMapper.selectCourserApiInfoVoById(id);
    }

在这里插入图片描述

4、Mapper和sql

在这里插入图片描述

<!--根据id获取课程详细信息,包括讲师-->
    <select id="selectCourserApiInfoVoById" resultType="com.yzpnb.eduservice.entity.vo.CourseApiInfoVo">
        SELECT
            c.id,
            c.title,
            c.cover,
            CONVERT(c.price, DECIMAL(8,2)) AS price,
            c.lesson_num AS lessonNum,
            c.cover,
            c.buy_count AS buyCount,
            c.view_count AS viewCount,
            cd.description,

            t.id AS teacherId,
            t.name AS teacherName,
            t.intro,
            t.avatar,

            s1.id AS subjectLevelOneId,
            s1.title AS subjectLevelOne,
            s2.id AS subjectLevelTwoId,
            s2.title AS subjectLevelTwo

          FROM
            edu_course c
            LEFT JOIN edu_course_description cd ON c.id = cd.id
            LEFT JOIN edu_teacher t ON c.teacher_id = t.id
            LEFT JOIN edu_subject s1 ON c.subject_parent_id = s1.id
            LEFT JOIN edu_subject s2 ON c.subject_id = s2.id
          WHERE
            c.id = #{id}

    </select>

在这里插入图片描述

4、测试

在这里插入图片描述

4、课程详情页(前端)

在这里插入图片描述
在这里插入图片描述

三、阿里云播放器在线播放视频

方式
通过视频地址播放(只能播放不加密视频)
通过凭证播放(可以播放加密试视频)
集成阿里云视频播放器
需要引入:
<link rel="stylesheet" href="https://g.alicdn.com/de/prismplayer/2.8.1/skins/default/aliplayer-min.css" />
<script charset="utf-8" type="text/javascript" src="https://g.alicdn.com/de/prismplayer/2.8.1/aliplayer-min.js"></script>

初始化视频播放器
<body>
    <div  class="prism-player" id="J_prismPlayer"></div>
    <script>
        var player = new Aliplayer({
            id: 'J_prismPlayer',
            width: '100%',//播放器大小
            autoplay: false,//是否自动播放
            cover: 'http://liveroom-img.oss-cn-qingdao.aliyuncs.com/logo.png',  
            //播放配置(两种方式只能选一个)
			//播放方式一:支持播放地址播放,此播放优先级最高,此种方式不能播放加密视频
			source : '你的视频播放地址',
			//播放方式二:通过凭证播放(推荐)
			encryptType:'1',//如果播放加密视频,则需设置encryptType=1,非加密视频无需设置此项
			vid : '视频id',//你的视频id
			playauth : '视频授权码',//视频授权码(播放凭证)可以通过视频id获取
        },function(player){
            console.log('播放器创建好了。')
        });
    </script>
</body>
步骤分析
通过我们点击小节视频,获取小节对象中的视频id
通过视频id获取播放凭证,视频地址等内容
在播放器中播放

1、后端

在这里插入图片描述

1、修改工具类

/*获取播放地址函数*/
    public String getPlayInfo(String id) throws Exception {
        client=initVodClient(accessKeyId,accessKeySecret);
        GetPlayInfoRequest request = new GetPlayInfoRequest();
        GetPlayInfoResponse response = new GetPlayInfoResponse();
        request.setVideoId(id);
        response=client.getAcsResponse(request);
        try {
            List<GetPlayInfoResponse.PlayInfo> playInfoList = response.getPlayInfoList();
            //播放地址
            for (GetPlayInfoResponse.PlayInfo playInfo : playInfoList) {
                System.out.print("PlayInfo.PlayURL = " + playInfo.getPlayURL() + "\n");
            }
            //Base信息
            System.out.print("VideoBase.Title = " + response.getVideoBase().getTitle() + "\n");

        } catch (Exception e) {
            System.out.print("ErrorMessage = " + e.getLocalizedMessage());
        }
        System.out.print("RequestId = " + response.getRequestId() + "\n");
        return response.getRequestId();
    }
    /*获取播放凭证函数*/
    public String getVideoPlayAuth(String id) throws Exception {
        client=initVodClient(accessKeyId,accessKeySecret);
        /***获取播放凭证***/
        GetVideoPlayAuthRequest requestAuth = new GetVideoPlayAuthRequest();
        GetVideoPlayAuthResponse responseAuth = new GetVideoPlayAuthResponse();

        requestAuth.setVideoId(id);

        responseAuth=client.getAcsResponse(requestAuth);
        //播放凭证
        System.out.println("PlayAuth = " + responseAuth.getPlayAuth() + "\n");
        return responseAuth.getPlayAuth();
    }

在这里插入图片描述

2、编写controller

@ApiOperation("根据视频id获取视频凭证")
    @GetMapping("getVideoPlayAuth/{id}")
    public Result getVideoPlayAuth(@ApiParam(name = "id",value = "视频id")
                                   @PathVariable String id){

        try {
            String playAuth=new AliyunVideoUtil().getVideoPlayAuth(id);
            return Result.ok().message("视频凭证获取成功").data("playAuth",playAuth);
        } catch (Exception e) {
            return Result.error().message("视频凭证获取失败");
        }
    }

在这里插入图片描述

3、测试

在这里插入图片描述

2、前端

1、播放页面的布局(源码GitHub)

在这里插入图片描述

2、api接口

在这里插入图片描述

3、播放页面中引入文件

在这里插入图片描述

4、获取播放凭证

在这里插入图片描述

5、初始化播放器

在这里插入图片描述
在这里插入图片描述

6、测试

在这里插入图片描述

3、更多功能

功能展示:https://player.alicdn.com/aliplayer/presentation/index.html
到官网可以根据需要获取更多功能

四、课程评论功能

功能需求
当用户进入详情页面后,分页查询课程评论
评论中要显示评论人的头像,昵称
输入框中输入评论并提交后,提交评论内容,并将课程id,讲师id,以及当前登录账号的id,昵称和头像存入数据库
评论时,需要先判断用户是否登录,没登录先登录,登录了才能评论
登陆以后我们就可以从header中获取token字符串,然后获取用户id,然后查询出昵称和头像

在这里插入图片描述

需要两个微服务互联,Feign远程调用
需要用到的数据库

在这里插入图片描述

1、后端

1、使用代码生成器生成相关代码(注意是在我们处理讲师,课程,章节小节的微服务中)

在这里插入图片描述
在这里插入图片描述

2、编写根据会员id查询会员信息接口

在这里插入图片描述

3、评论微服务,编写Feign调用接口

package com.yzpnb.eduservice.feign;

import com.yzpnb.eduservice.entity.ucenter_member.UcenterMember;
import com.yzpnb.eduservice.feign.impl.FeignToUcenterClientImpl;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

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

    @ApiOperation("根据id获取用户信息")
    @GetMapping("/ucenter_service/ucenter-member/selectById/{id}")
    public UcenterMember selectById(@ApiParam(name = "id",value = "用户id")
                             @PathVariable(value = "id") String id);
}



package com.yzpnb.eduservice.feign.impl;

import com.yzpnb.eduservice.entity.ucenter_member.UcenterMember;
import com.yzpnb.eduservice.feign.FeignToUcenterClient;
import org.springframework.stereotype.Component;

@Component
public class FeignToUcenterClientImpl implements FeignToUcenterClient {
    @Override
    public UcenterMember selectById(String id) {
        return null;
    }
}


在这里插入图片描述
在这里插入图片描述

4、复制ucenter中的用户实体类

在这里插入图片描述

5、分页查询评论,添加评论接口

package com.yzpnb.eduservice.controller;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yzpnb.common_utils.JwtUtils;
import com.yzpnb.common_utils.Result;
import com.yzpnb.eduservice.entity.EduComment;
import com.yzpnb.eduservice.entity.ucenter_member.UcenterMember;
import com.yzpnb.eduservice.feign.FeignToUcenterClient;
import com.yzpnb.eduservice.service.EduCommentService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
/**
 * <p>
 * 评论 前端控制器
 * </p>
 *
 * @author testjava
 * @since 2020-06-06
 */
@RestController
@RequestMapping("/eduservice/edu-comment")
@CrossOrigin
public class EduCommentController {

    @Autowired
    private EduCommentService eduCommentService;

    @Autowired
    private FeignToUcenterClient feignToUcenterClient;

    //根据课程id查询评论列表
    @ApiOperation(value = "根据课程id分页查询评论")
    @GetMapping("limitSelectComment/{page}/{limit}")
    public Result limitSelectComment(@ApiParam(name = "page", value = "当前页码", required = true)
                                     @PathVariable Long page,
                                     @ApiParam(name = "limit", value = "每页记录数", required = true)
                                     @PathVariable Long limit,
                                     @ApiParam(name = "courseQuery", value = "课程id", required = false) String courseId) {
        Page<EduComment> pageParam = new Page<>(page, limit);

        QueryWrapper<EduComment> wrapper = new QueryWrapper<>();
        wrapper.eq("course_id",courseId);

        eduCommentService.page(pageParam,wrapper);

        List<EduComment> 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 Result.ok().data(map);
    }

    @ApiOperation(value = "添加评论")
    @PostMapping("insertComment")
    public Result save(@RequestBody EduComment comment, HttpServletRequest request) {

        String memberId = JwtUtils.getMemberIdByJwtToken(request);//解析token字符串中的信息,获取id

        if(StringUtils.isEmpty(memberId)) {//如果没有值,让用户先登录
            return Result.error().code(28004).message("请登录");
        }

        comment.setMemberId(memberId);//将用户id添加

        UcenterMember ucenterMember = feignToUcenterClient.selectById(memberId);//调用feign接口,远程调用根据id获取用户信息

        //将用户昵称和头像添加
        comment.setNickname(ucenterMember.getNickname());
        comment.setAvatar(ucenterMember.getAvatar());

        eduCommentService.save(comment);//执行添加操作

        return Result.ok();
        }
}


在这里插入图片描述

2、前端

1、api接口

在这里插入图片描述

2、编写详情页

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/grd_java/article/details/106555439