一、讲师
所需功能确认 |
分页显示数据,每页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;
@Service
public class EduTeacherServiceImpl extends ServiceImpl<EduTeacherMapper, EduTeacher> implements EduTeacherService {
@Override
public Map<String, Object> limitSelectTeacher(Page<EduTeacher> pageTeacher) {
QueryWrapper<EduTeacher> wrapper=new QueryWrapper<>();
wrapper.orderByDesc("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、讲师详情页(后端)

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<String,Object> map=new HashMap<>();
map.put("teacher",eduTeacher);
map.put("courseList",eduCourseList);
return Result.ok().data(map);
}
2、测试

4、讲师详情页(前端)

二、课程
功能确认 |
根据一级二级分类筛选课程,根据不同规则排序 |
分页查询课程,每页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
@Override
public Map<String, Object> selectIfLimitCourse(Page<EduCourse> pageCourse, CourseApiVo courseApiVo) {
QueryWrapper<EduCourse> queryWrapper=new QueryWrapper<>();
if (!StringUtils.isEmpty(courseApiVo.getSubjectParentId())) {
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、条件分页显示讲师(前端)


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
@Override
public CourseApiInfoVo selectCourserApiInfoVoById(String id) {
EduCourse eduCourse = baseMapper.selectById(id);
eduCourse.setViewCount(eduCourse.getViewCount() + 1);
baseMapper.updateById(eduCourse);
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',
vid : '视频id',
playauth : '视频授权码',
},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");
}
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、更多功能
四、课程评论功能
功能需求 |
当用户进入详情页面后,分页查询课程评论 |
评论中要显示评论人的头像,昵称 |
输入框中输入评论并提交后,提交评论内容,并将课程id,讲师id,以及当前登录账号的id,昵称和头像存入数据库 |
评论时,需要先判断用户是否登录,没登录先登录,登录了才能评论 |
登陆以后我们就可以从header中获取token字符串,然后获取用户id,然后查询出昵称和头像 |


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.*;
@RestController
@RequestMapping("/eduservice/edu-comment")
@CrossOrigin
public class EduCommentController {
@Autowired
private EduCommentService eduCommentService;
@Autowired
private FeignToUcenterClient feignToUcenterClient;
@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);
if(StringUtils.isEmpty(memberId)) {
return Result.error().code(28004).message("请登录");
}
comment.setMemberId(memberId);
UcenterMember ucenterMember = feignToUcenterClient.selectById(memberId);
comment.setNickname(ucenterMember.getNickname());
comment.setAvatar(ucenterMember.getAvatar());
eduCommentService.save(comment);
return Result.ok();
}
}

2、前端
1、api接口

2、编写详情页

所有评论(0)