文章目录
一、讲师
| 所需功能确认 |
|---|
| 分页显示数据,每页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、编写详情页

在线教育平台开发实战
3225

被折叠的 条评论
为什么被折叠?



