参考课程分类列表
1 创建实体类
创建章节、小节两个实体类,在章节实体类中使用list表示小节
@Data
public class VideoVo {
private String id;
private String title;
}
@Data
public class ChapterVo {
private String id;
private String title;
// 章节包含很多小节
private List<VideoVo> children = new ArrayList<>();
}
2 controller和service
controller
@RestController
@RequestMapping("/eduservice/chapter")
@CrossOrigin
public class EduChapterController {
@Autowired
private EduChapterService chapterService;
// 课程大纲列表,根据课程id查章节、小节
@GetMapping("getChapterVideo/{courseId}")
public R getChapterVideo(@PathVariable String courseId) {
List<ChapterVo> list = chapterService.getChapterVideoByCourseId(courseId);
return R.ok().data("allChapterVideo", list);
}
}
service
@Service
public class EduChapterServiceImpl extends ServiceImpl<EduChapterMapper, EduChapter> implements EduChapterService {
@Autowired
private EduVideoService videoService; // 注入小节的service
// 课程大纲列表,根据课程id查章节、小节
@Override
public List<ChapterVo> getChapterVideoByCourseId(String courseId) {
// 1.根据课程id查询课程里面所有的章节
QueryWrapper<EduChapter> wrapperChapter = new QueryWrapper<>();
wrapperChapter.eq("course_id", courseId);
List<EduChapter> eduChapterList = baseMapper.selectList(wrapperChapter);
// 2.根据课程id查询课程里面所有的小节
QueryWrapper<EduVideo> wrapperVideo = new QueryWrapper<>();
wrapperVideo.eq("course_id", courseId);
List<EduVideo> eduVideoList = videoService.list(wrapperVideo);
// 创建list集合,用于最终的数据封装 ChapterVo
List<ChapterVo> finalList = new ArrayList<>();
// 3.遍历查询章节list集合,进行封装
for (EduChapter eduChapter: eduChapterList) {
ChapterVo chapterVo = new ChapterVo();
BeanUtils.copyProperties(eduChapter, chapterVo);
finalList.add(chapterVo);
// 创建list集合,用于封装章节中的小节VideoVo
List<VideoVo> videoList = new ArrayList<>();
// 4.遍历查询小节list集合,进行封装
for (EduVideo eduVideo : eduVideoList) {
// 判断小节的chapter_id与章节的id是否一样
if (eduVideo.getChapterId().equals(eduChapter.getId())) {
VideoVo videoVo = new VideoVo();
BeanUtils.copyProperties(eduVideo, videoVo);
videoList.add(videoVo);
}
}
// 小节list放到章节中
chapterVo.setChildren(videoList);
}
return finalList;
}
}
1 api/edu/chapter.js
import request from '@/utils/request'
export default {
// 根据课程id获取章节、小节列表
getAllChapterVideo(courseId) {
return request({
url: `/eduservice/chapter/getChapterVideo/${courseId}`,
method: 'get'
})
}
}
2 chapter.vue
-
{{ chapter.title }}
-
{{ video.title }}
created() {
// 获取路由的id值
if (this.$route.params && this.$route.params.id) {
this.courseId = this.$route.params.id
}
// 根据课程id查询章节和小节
this.getChapterVideo()
},
methods: {
// 根据课程id查询章节、小节列表
getChapterVideo() {
chapter.getAllChapterVideo(this.courseId)
.then(response => {
this.chapterVideoList = response.data.allChapterVideo
})
},
点击上一步,回到上一步,并做课程基本信息数据回显
数据回显页面,修改数据库内容
1 后端接口
controller
// 根据课程id查询课程基本信息
@GetMapping("getCourseInfo/{courseId}")
public R getCourseInfo(@PathVariable String courseId) {
CourseInfoVo courseInfoVo = courseService.getCourseInfo(courseId);
return R.ok().data("courseInfoVo", courseInfoVo);
}
// 修改课程信息
@PostMapping("updateCourseInfo")
public R updateCourseInfo(@RequestBody CourseInfoVo courseInfoVo) {
courseService.updateCourseInfo(courseInfoVo);
return R.ok();
}
service
// 根据课程id查询课程基本信息
@Override
public CourseInfoVo getCourseInfo(String courseId) {
// 1 查询课程表
EduCourse eduCourse = baseMapper.selectById(courseId);
CourseInfoVo courseInfoVo = new CourseInfoVo();
BeanUtils.copyProperties(eduCourse, courseInfoVo);
// 2 查询描述表
EduCourseDescription courseDescription = courseDescriptionService.getById(courseId);
courseInfoVo.setDescription(courseDescription.getDescription());
return courseInfoVo;
}
// 修改课程信息
@Override
public void updateCourseInfo(CourseInfoVo courseInfoVo) {
// 1 修改课程表
EduCourse eduCourse = new EduCourse();
BeanUtils.copyProperties(courseInfoVo, eduCourse);
int update = baseMapper.updateById(eduCourse);
if (update == 0) {
throw new GuliException(20001, "修改课程信息失败");
}
// 2 修改描述表
EduCourseDescription description = new EduCourseDescription();
BeanUtils.copyProperties(courseInfoVo, description);
courseDescriptionService.updateById(description);
}
2 前端实现
定义接口的两个方法:api/edu/course.js
// 3 根据课程id查询课程基本信息
getCourseInfo(id) {
return request({
url: `/eduservice/course/getCourseInfo/${id}`,
method: 'get'
})
},
// 4 修改课程信息
updateCourseInfo(courseInfoVo) {
return request({
url: '/eduservice/course/updateCourseInfo',
method: 'post',
data: courseInfoVo
})
}
修改chapter.vue,跳转页面
// 上一步
previous() {
this.$router.push({ path: '/course/info/' + this.courseId })
},
// 下一步
next() {
this.$router.push({ path: '/course/publish/' + this.courseId })
}
在info页面实现数据回显
// 获取路由id值
if (this.$route.params && this.$route.params.id) {
this.courseId = this.$route.params.id
// 调用根据课程id查询
this.getInfo()
}
// 7 根据课程id查询
getInfo() {
course.getCourseInfo(this.courseId)
.then(response => {
this.courseInfo = response.data.courseInfoVo
})
}
下拉列表数据回显
根据存储id和分类所有id进行比较,如果比较相同,让相同值进行数据回显
<select>
<option selected>前端开发option>
<select>
created() {
// 修改
if (this.$route.params && this.$route.params.id) { // 获取路由id值
this.courseId = this.$route.params.id
// 调用根据课程id查询
this.getInfo()
} else { // 添加
// 初始化所有讲师
this.getListTeacher()
// 初始化一级分类
this.getOneSubject()
}
},
// 7 根据课程id查询
getInfo() {
course.getCourseInfo(this.courseId)
.then(response => {
// courseInfo包含一级分类id和二级分类id
this.courseInfo = response.data.courseInfoVo
// 1)查询所有的分类,包含一级和二级
subject.getSubjectList()
.then(response => {
// 2)获取所有一级分类
this.subjectOneList = response.data.list
// 3)遍历一级分类,获取二级分类
for (const oneSubject of this.subjectOneList) {
if (this.courseInfo.subjectParentId === oneSubject.id) { // 当前一级分类id和所有一级分类id比较
this.subjectTwoList = oneSubject.children
}
}
})
// 初始化所有讲师
this.getListTeacher()
})
}
添加、修改并保存
// 添加课程
addCourse() {
course.addCourseInfo(this.courseInfo)
.then(response => {
// 提示信息
this.$message({
type: 'success',
message: '添加课程信息成功'
})
// 跳转到下一步
this.$router.push({ path: '/course/chapter/' + response.data.courseId })
})
},
// 修改课程
updateCourse() {
course.updateCourseInfo(this.courseInfo)
.then(response => {
// 提示信息
this.$message({
type: 'success',
message: '修改课程信息成功'
})
// 跳转到下一步
this.$router.push({ path: '/course/chapter/' + this.courseId })
})
},
// 6 保存
saveOrUpdate() {
// 判断添加还是修改
// 没有id值,添加
if (!this.courseInfo.id) {
this.addCourse()
} else {
// 有id值,修改
this.updateCourse()
}
},
添加、修改、删除
1 添加按钮:添加章节
添加章节
2 点击 添加章节 按钮,弹出添加框,输入章节信息,点击保存添加
3 开发章节后端接口
修改章节
添加章节
删除章节
controller
// 添加章节
@PostMapping("addChapter")
public R addChapter(@RequestBody EduChapter eduChapter) {
chapterService.save(eduChapter);
return R.ok();
}
// 根据章节id查询
@GetMapping("getChapterInfo/{chapterId}")
public R getChapterInfo(@PathVariable String chapterId) {
EduChapter eduChapter = chapterService.getById(chapterId);
return R.ok().data("chapter", eduChapter);
}
// 修改章节
@PostMapping("updateChapter")
public R updateChapter(@RequestBody EduChapter eduChapter) {
chapterService.updateById(eduChapter);
return R.ok();
}
// 删除章节
@DeleteMapping("{chapterId}")
public R deleteChapter(@PathVariable String chapterId) {
boolean flag = chapterService.deleteChapter(chapterId);
return flag ? R.ok() : R.error();
}
service
// 删除章节
@Override
public boolean deleteChapter(String chapterId) {
// 根据章节id查询小节表,如果能查到数据,不删除
QueryWrapper<EduVideo> wrapper = new QueryWrapper<>();
wrapper.eq("chapter_id", chapterId);
int count = videoService.count(wrapper);
if (count > 0) { // 有小节,不删除
throw new GuliException(20001, "不能删除");
} else { // 没有小节,删除
int result = baseMapper.deleteById(chapterId);
return result > 0;
}
}
4 前端调用接口
chapter.js
// 添加章节
addChapter(chapter) {
return request({
url: `/eduservice/chapter/addChapter`,
method: 'post',
data: chapter
})
},
// 根据id查询章节
getChapter(chapterId) {
return request({
url: `/eduservice/chapter/getChapterInfo/${chapterId}`,
method: 'get'
})
},
// 修改章节
updateChapter(chapter) {
return request({
url: `/eduservice/chapter/updateChapter`,
method: 'post',
data: chapter
})
},
// 删除章节
deleteChapter(chapterId) {
return request({
url: `/eduservice/chapter/${chapterId}`,
method: 'delete'
})
}
chapter.vue
-
{{ chapter.title }}
编辑
删除
// 添加章节
addChapter() {
// 设置课程id到chapter对象中
this.chapter.courseId = this.courseId
chapter.addChapter(this.chapter)
.then(response => {
// 1) 关闭弹窗
this.dialogChapterFormVisible = false
// 2) 提示信息
this.$message({
type: 'success',
message: '添加章节成功'
})
// 3) 刷新页面
this.getChapterVideo()
})
},
// 修改章节
updateChapter() {
chapter.updateChapter(this.chapter)
.then(response => {
// 1) 关闭弹窗
this.dialogChapterFormVisible = false
// 2) 提示信息
this.$message({
type: 'success',
message: '修改章节成功'
})
// 3) 刷新页面
this.getChapterVideo()
})
},
// 保存
saveOrUpdate() {
if (!this.chapter.id) {
this.addChapter()
} else {
this.updateChapter()
}
},
// 删除章节
removeChapter(chapterId) {
// 1)弹出确认框
this.$confirm('此操作将删除章节, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => { // 点击确认,执行then方法
chapter.deleteChapter(chapterId)
.then((response) => {
// 提示信息
this.$message({
type: 'success',
message: '删除成功!'
})
// 2) 刷新页面
this.getChapterVideo()
})
})
},
// 修改章节弹框数据回显
openEditChapter(chapterId) {
// 弹框
this.dialogChapterFormVisible = true
// 调用接口
chapter.getChapter(chapterId)
.then(response => {
this.chapter = response.data.chapter
})
}
1 概述
封面、课程名称、课程价格、课程分类、课程简介、课程讲师…
这些数据需要查询多张表,一般通过手写sql实现
多表连接查询:内连接、左外连接、右外连接
select ec.id,ec.title,ec.price,ec.lesson_num,
ecd.description,
et.`name`,
es1.title as oneSubject,
es2.title AS twoSubject
from edu_course ec LEFT OUTER JOIN edu_course_description ecd ON ec.id=ecd.id
LEFT OUTER JOIN edu_teacher et ON ec.teacher_id=et.id
LEFT OUTER JOIN edu_subject es1 ON ec.subject_parent_id=es1.id
LEFT OUTER JOIN edu_subject es2 ON ec.subject_id=es2.id
WHERE ec.id='1555079243711434754'
2 controller、service、mapper
controller
// 根据课程id查询课程确认信息
@GetMapping("getPublishCourseInfo/{id}")
public R getPublishCourseInfo(@PathVariable String id) {
CoursePublishVo coursePublishVo = courseService.publishCourseInfo(id);
return R.ok().data("publishCourse", coursePublishVo);
}
service
@Override
public CoursePublishVo publishCourseInfo(String id) {
CoursePublishVo publishCourseInfo = baseMapper.getPublishCourseInfo(id);
return publishCourseInfo;
}
mapper
public interface EduCourseMapper extends BaseMapper<EduCourse> {
public CoursePublishVo getPublishCourseInfo(String courseId);
}
<select id="getPublishCourseInfo" resultType="com.mys.eduservice.entity.vo.CoursePublishVo">
select ec.id,ec.title,ec.price,ec.lesson_num AS lessonNum,ec.cover,
ecd.description,
et.`name` as teacherName,
es1.title as subjectLevelOne,
es2.title AS subjectLevelTwo
from edu_course ec LEFT OUTER JOIN edu_course_description ecd ON ec.id=ecd.id
LEFT OUTER JOIN edu_teacher et ON ec.teacher_id=et.id
LEFT OUTER JOIN edu_subject es1 ON ec.subject_parent_id=es1.id
LEFT OUTER JOIN edu_subject es2 ON ec.subject_id=es2.id
WHERE ec.id=#{courseId}
select>
3 测试报错与解决
问题:项目中创建mapper接口,编写xml文件写sql语句,执行出错:
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.mys.eduservice.mapper.EduCourseMapper.getPublishCourseInfo
错误原因:由maven默认加载机制造成的,maven加载时,把java文件夹里面.java文件进行编译,其他类型文件不会加载
解决办法:
复制xml到target目录
把xml文件放到resources目录
推荐:通过配置实现
pom.xml
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
application.properties
# 配置mapper.xml文件的路径
mybatis-plus.mapper-locations=classpath:com/mys/eduservice/mapper/xml/*.xml
4 前端实现:显示查询的课程确认信息
api/edu/course.js
// 5 课程确认信息的显示
getPublishCourseInfo(id) {
return request({
url: `/eduservice/course/getPublishCourseInfo/${id}`,
method: 'get'
})
}
publish.vue
created() {
// 1 获取路由课程id
if (this.$route.params && this.$route.params.id) {
this.courseId = this.$route.params.id
// 2 调用接口方法,根据课程id查询
this.getPublishCourseId()
}
},
// 根据课程id查询确认发布信息
getPublishCourseId() {
course.getPublishCourseInfo(this.courseId)
.then(response => {
this.publishCourse = response.data.publishCourse
// 提示信息
this.$message({
type: 'success',
message: '查询课程发布确认信息成功'
})
})
}
修改课程的status状态是Normal-已发布
controller
// 课程最终发布:修改课程状态
@PostMapping("publishCourse/{id}")
public R publishCourse(@PathVariable String id) {
EduCourse eduCourse = new EduCourse();
eduCourse.setId(id);
eduCourse.setStatus("Normal");
courseService.updateById(eduCourse);
return R.ok();
}
publish.vue
// 发布
publish() {
// 调用发布方法
course.publishCourse(this.courseId)
.then(response => {
// 提示信息
this.$message({
type: 'success',
message: '发布成功'
})
// 跳转到课程列表
this.$router.push({ path: '/course/list' })
})
},