01-课程大纲列表显示

一、后端实现

1、定义vo

ChapterVo

package com.guli.edu.vo;
@ApiModel(value = "章节信息")
@Data
public class ChapterVo implements Serializable {private static final long serialVersionUID = 1L;private String id;private String title;private List<VideoVo> children = new ArrayList<>();
}

VideoVo

package com.guli.edu.vo;
@ApiModel(value = "课时信息")
@Data
public class VideoVo implements Serializable {private static final long serialVersionUID = 1L;private String id;private String title;private Boolean free;
}

2、服务层

接口

package com.guli.edu.service;
public interface ChapterService extends IService<Chapter> {List<ChapterVo> nestedList(String courseId);
}

实现

package com.guli.edu.service.impl;@Service
public class ChapterServiceImpl extends ServiceImpl<ChapterMapper, Chapter> implements ChapterService {@Autowiredprivate VideoService videoService;@Overridepublic List<ChapterVo> nestedList(String courseId) {//最终要的到的数据列表ArrayList<ChapterVo> chapterVoArrayList = new ArrayList<>();//获取章节信息QueryWrapper<Chapter> queryWrapper1 = new QueryWrapper<>();queryWrapper1.eq("course_id", courseId);queryWrapper1.orderByAsc("sort", "id");List<Chapter> chapters = baseMapper.selectList(queryWrapper1);//获取课时信息QueryWrapper<Video> queryWrapper2 = new QueryWrapper<>();queryWrapper2.eq("course_id", courseId);queryWrapper2.orderByAsc("sort", "id");List<Video> videos = videoService.list(queryWrapper2);//填充章节vo数据int count1 = chapters.size();for (int i = 0; i < count1; i++) {Chapter chapter = chapters.get(i);//创建章节vo对象ChapterVo chapterVo = new ChapterVo();BeanUtils.copyProperties(chapter, chapterVo);chapterVoArrayList.add(chapterVo);//填充课时vo数据ArrayList<VideoVo> videoVoArrayList = new ArrayList<>();int count2 = videos.size();for (int j = 0; j < count2; j++) {Video video = videos.get(j);if(chapter.getId().equals(video.getChapterId())){//创建课时vo对象VideoVo videoVo = new VideoVo();BeanUtils.copyProperties(video, videoVo);videoVoArrayList.add(videoVo);}}chapterVo.setChildren(videoVoArrayList);}return chapterVoArrayList;}
}

3、web层

package com.guli.edu.controller.admin;@Api(description="课程章节管理")
@CrossOrigin //跨域
@RestController
@RequestMapping("/admin/edu/chapter")
public class ChapterAdminController {@Autowiredprivate ChapterService chapterService;@ApiOperation(value = "嵌套章节数据列表")@GetMapping("nested-list/{courseId}")public R nestedListByCourseId(@ApiParam(name = "courseId", value = "课程ID", required = true)@PathVariable String courseId){List<ChapterVo> chapterVoList = chapterService.nestedList(courseId);return R.ok().data("items", chapterVoList);}
}

4、Swagger测试

二、前端实现

1、定义api

chapter.js

import request from '@/utils/request'const api_name = '/admin/edu/chapter'export default {getNestedTreeList(courseId) {return request({url: `${api_name}/nested-list/${courseId}`,method: 'get'})}
}

2、定义组件脚本

定义data

courseId: '', // 所属课程
chapterNestedList: [] // 章节嵌套课时列表

created中调用init方法

created() {console.log('chapter created')this.init()
},

定义相关methods获取章节和课时列表

init() {if (this.$route.params && this.$route.params.id) {this.courseId = this.$route.params.id// 根据id获取课程基本信息this.fetchChapterNestedListByCourseId()}
},fetchChapterNestedListByCourseId() {chapter.getNestedTreeList(this.courseId).then(response => {this.chapterNestedList = response.data.items})
},

3、定义组件模板

<el-button type="text">添加章节</el-button>
<!-- 章节 -->
<ul class="chanpterList"><liv-for="chapter in chapterNestedList":key="chapter.id"><p>{{ chapter.title }}<span class="acts"><el-button type="text">添加课时</el-button><el-button style="" type="text">编辑</el-button><el-button type="text">删除</el-button></span></p><!-- 视频 --><ul class="chanpterList videoList"><liv-for="video in chapter.children":key="video.id"><p>{{ video.title }}<span class="acts"><el-button type="text">编辑</el-button><el-button type="text">删除</el-button></span></p></li></ul></li>
</ul>
<div><el-button @click="previous">上一步</el-button><el-button :disabled="saveBtnDisabled" type="primary" @click="next">下一步</el-button>
</div>

4、定义样式

将样式的定义放在页面的最后

scope表示这里定义的样式只在当前页面范围内生效,不会污染到其他的页面

<style scoped>
.chanpterList{position: relative;list-style: none;margin: 0;padding: 0;
}
.chanpterList li{position: relative;
}
.chanpterList p{float: left;font-size: 20px;margin: 10px 0;padding: 10px;height: 70px;line-height: 50px;width: 100%;border: 1px solid #DDD;
}
.chanpterList .acts {float: right;font-size: 14px;
}.videoList{padding-left: 50px;
}
.videoList p{float: left;font-size: 14px;margin: 10px 0;padding: 10px;height: 50px;line-height: 30px;width: 100%;border: 1px dotted #DDD;
}</style>

02-章节管理后端接口开发

一、新增章节

web层

@ApiOperation(value = "新增章节")
@PostMapping
public R save(@ApiParam(name = "chapterVo", value = "章节对象", required = true)@RequestBody Chapter chapter){chapterService.save(chapter);return R.ok();
}

二、根据id查询

web层

@ApiOperation(value = "根据ID查询章节")
@GetMapping("{id}")
public R getById(@ApiParam(name = "id", value = "章节ID", required = true)@PathVariable String id){Chapter chapter = chapterService.getById(id);return R.ok().data("item", chapter);
}

三、更新

web层

@ApiOperation(value = "根据ID修改章节")
@PutMapping("{id}")
public R updateById(@ApiParam(name = "id", value = "章节ID", required = true)@PathVariable String id,@ApiParam(name = "chapter", value = "章节对象", required = true)@RequestBody Chapter chapter){chapter.setId(id);chapterService.updateById(chapter);return R.ok();
}

四、删除

1、web层

@ApiOperation(value = "根据ID删除章节")
@DeleteMapping("{id}")
public R removeById(@ApiParam(name = "id", value = "章节ID", required = true)@PathVariable String id){boolean result = chapterService.removeChapterById(id);if(result){return R.ok();}else{return R.error().message("删除失败");}
}

2、Service

ChapterService层:接口

boolean removeChapterById(String id);

ChapterService层:实现

@Override
public boolean removeChapterById(String id) {//根据id查询是否存在视频,如果有则提示用户尚有子节点if(videoService.getCountByChapterId(id)){throw new GuliException(20001,"该分章节下存在视频课程,请先删除视频课程");}Integer result = baseMapper.deleteById(id);return null != result && result > 0;
}

VideoService:接口

boolean getCountByChapterId(String chapterId);

VideoService:实现

@Override
public boolean getCountByChapterId(String chapterId) {QueryWrapper<Video> queryWrapper = new QueryWrapper<>();queryWrapper.eq("chapter_id", chapterId);Integer count = baseMapper.selectCount(queryWrapper);return null != count && count > 0;
}

五、Swagger测试

一、新增章节

web层

@ApiOperation(value = "新增章节")
@PostMapping
public R save(@ApiParam(name = "chapterVo", value = "章节对象", required = true)@RequestBody Chapter chapter){chapterService.save(chapter);return R.ok();
}

二、根据id查询

web层

@ApiOperation(value = "根据ID查询章节")
@GetMapping("{id}")
public R getById(@ApiParam(name = "id", value = "章节ID", required = true)@PathVariable String id){Chapter chapter = chapterService.getById(id);return R.ok().data("item", chapter);
}

三、更新

web层

@ApiOperation(value = "根据ID修改章节")
@PutMapping("{id}")
public R updateById(@ApiParam(name = "id", value = "章节ID", required = true)@PathVariable String id,@ApiParam(name = "chapter", value = "章节对象", required = true)@RequestBody Chapter chapter){chapter.setId(id);chapterService.updateById(chapter);return R.ok();
}

四、删除

1、web层

@ApiOperation(value = "根据ID删除章节")
@DeleteMapping("{id}")
public R removeById(@ApiParam(name = "id", value = "章节ID", required = true)@PathVariable String id){boolean result = chapterService.removeChapterById(id);if(result){return R.ok();}else{return R.error().message("删除失败");}
}

2、Service

ChapterService层:接口

boolean removeChapterById(String id);

ChapterService层:实现

@Override
public boolean removeChapterById(String id) {//根据id查询是否存在视频,如果有则提示用户尚有子节点if(videoService.getCountByChapterId(id)){throw new GuliException(20001,"该分章节下存在视频课程,请先删除视频课程");}Integer result = baseMapper.deleteById(id);return null != result && result > 0;
}

VideoService:接口

boolean getCountByChapterId(String chapterId);

VideoService:实现

@Override
public boolean getCountByChapterId(String chapterId) {QueryWrapper<Video> queryWrapper = new QueryWrapper<>();queryWrapper.eq("chapter_id", chapterId);Integer count = baseMapper.selectCount(queryWrapper);return null != count && count > 0;
}

五、Swagger测试# 一、新增章节

web层

@ApiOperation(value = "新增章节")
@PostMapping
public R save(@ApiParam(name = "chapterVo", value = "章节对象", required = true)@RequestBody Chapter chapter){chapterService.save(chapter);return R.ok();
}

二、根据id查询

web层

@ApiOperation(value = "根据ID查询章节")
@GetMapping("{id}")
public R getById(@ApiParam(name = "id", value = "章节ID", required = true)@PathVariable String id){Chapter chapter = chapterService.getById(id);return R.ok().data("item", chapter);
}

三、更新

web层

@ApiOperation(value = "根据ID修改章节")
@PutMapping("{id}")
public R updateById(@ApiParam(name = "id", value = "章节ID", required = true)@PathVariable String id,@ApiParam(name = "chapter", value = "章节对象", required = true)@RequestBody Chapter chapter){chapter.setId(id);chapterService.updateById(chapter);return R.ok();
}

四、删除

1、web层

@ApiOperation(value = "根据ID删除章节")
@DeleteMapping("{id}")
public R removeById(@ApiParam(name = "id", value = "章节ID", required = true)@PathVariable String id){boolean result = chapterService.removeChapterById(id);if(result){return R.ok();}else{return R.error().message("删除失败");}
}

2、Service

ChapterService层:接口

boolean removeChapterById(String id);

ChapterService层:实现

@Override
public boolean removeChapterById(String id) {//根据id查询是否存在视频,如果有则提示用户尚有子节点if(videoService.getCountByChapterId(id)){throw new GuliException(20001,"该分章节下存在视频课程,请先删除视频课程");}Integer result = baseMapper.deleteById(id);return null != result && result > 0;
}

VideoService:接口

boolean getCountByChapterId(String chapterId);

VideoService:实现

@Override
public boolean getCountByChapterId(String chapterId) {QueryWrapper<Video> queryWrapper = new QueryWrapper<>();queryWrapper.eq("chapter_id", chapterId);Integer count = baseMapper.selectCount(queryWrapper);return null != count && count > 0;
}

五、Swagger测试

03-章节管理前端页面实现

一、定义api

  removeById(id) {return request({url: `${api_name}/${id}`,method: 'delete'})},save(chapter) {return request({url: api_name,method: 'post',data: chapter})},getById(id) {return request({url: `${api_name}/${id}`,method: 'get'})},updateById(chapter) {return request({url: `${api_name}/${chapter.id}`,method: 'put',data: chapter})}

二、新增章节页面功能

1、定义data数据

dialogChapterFormVisible: false, //是否显示章节表单
chapter: {// 章节对象title: '',sort: 0
}

2、添加章节按钮

<el-button type="text" @click="dialogChapterFormVisible = true">添加章节</el-button>

3、章节表单dialog

<!-- 添加和修改章节表单 -->
<el-dialog :visible.sync="dialogChapterFormVisible" title="添加章节"><el-form :model="chapter" label-width="120px"><el-form-item label="章节标题"><el-input v-model="chapter.title"/></el-form-item><el-form-item label="章节排序"><el-input-number v-model="chapter.sort" :min="0" controls-position="right"/></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="dialogChapterFormVisible = false">取 消</el-button><el-button type="primary" @click="saveOrUpdate">确 定</el-button></div>
</el-dialog>

4、添加章节methods

saveOrUpdate() {this.saveBtnDisabled = trueif (!this.chapter.id) {this.saveData()} else {this.updateData()}
},saveData() {this.chapter.courseId = this.courseIdchapter.save(this.chapter).then(response => {this.$message({type: 'success',message: '保存成功!'})this.helpSave()}).catch((response) => {this.$message({type: 'error',message: response.message})})
},updateData() {},helpSave(){this.dialogChapterFormVisible = false// 如果保存成功则关闭对话框this.fetchChapterNestedListByCourseId()// 刷新列表this.chapter.title = ''// 重置章节标题this.chapter.sort = 0// 重置章节标题this.saveBtnDisabled = false
},

三、修改章节信息

1、编辑章节按钮

<el-button type="text" @click="editChapter(chapter.id)">编辑</el-button>

2、定义编辑方法

editChapter(chapterId) {this.dialogChapterFormVisible = truechapter.getById(chapterId).then(response => {this.chapter = response.data.item})
},

3、定义更新方法

updateData() {chapter.updateById(this.chapter).then(response => {this.$message({type: 'success',message: '修改成功!'})this.helpSave()}).catch((response) => {// console.log(response)this.$message({type: 'error',message: response.message})})
},

四、删除章节

1、按钮

<el-button type="text" @click="removeChapter(chapter.id)">删除</el-button>

2、定义删除方法

removeChapter(chapterId) {this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {return chapter.removeById(chapterId)}).then(() => {this.fetchChapterNestedListByCourseId()// 刷新列表this.$message({type: 'success',message: '删除成功!'})}).catch((response) => { // 失败if (response === 'cancel') {this.$message({type: 'info',message: '已取消删除'})} else {this.$message({type: 'error',message: response.message})}})
},

04-课时管理后端开发

一、定义Form表单对象

VideoInfoForm.java
package com.guli.edu.form;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;/*** @author helen* @since 2019/3/5*/
@ApiModel(value = "课时基本信息", description = "编辑课时基本信息的表单对象")
@Data
public class VideoInfoForm {@ApiModelProperty(value = "视频ID")private String id;@ApiModelProperty(value = "节点名称")private String title;@ApiModelProperty(value = "课程ID")private String courseId;@ApiModelProperty(value = "章节ID")private String chapterId;@ApiModelProperty(value = "视频资源")private String videoSourceId;@ApiModelProperty(value = "显示排序")private Integer sort;@ApiModelProperty(value = "是否可以试听:0默认 1免费")private Boolean free;
}

二、课时保存

1、web层接口的定义

VideoAdminController.java

package com.guli.edu.controller.admin;@Api(description="课时管理")
@CrossOrigin //跨域
@RestController
@RequestMapping("/admin/edu/video")
public class VideoAdminController {@Autowiredprivate VideoService videoService;@ApiOperation(value = "新增课时")@PostMapping("save-video-info")public R save(@ApiParam(name = "videoForm", value = "课时对象", required = true)@RequestBody VideoInfoForm videoInfoForm){videoService.saveVideoInfo(videoInfoForm);return R.ok();}
}

2、业务层

VideoService.java

void saveVideoInfo(VideoInfoForm videoInfoForm);

VideoServiceImpl.java

@Override
public void saveVideoInfo(VideoInfoForm videoInfoForm) {Video video = new Video();BeanUtils.copyProperties(videoInfoForm, video);boolean result = this.save(video);if(!result){throw new GuliException(20001, "课时信息保存失败");}
}

三、课时的修改

1、web层接口的定义

VideoAdminController.java

@ApiOperation(value = "根据ID查询课时")
@GetMapping("video-info/{id}")
public R getVideInfoById(@ApiParam(name = "id", value = "课时ID", required = true)@PathVariable String id){VideoInfoForm videoInfoForm = videoService.getVideoInfoFormById(id);return R.ok().data("item", videoInfoForm);
}@ApiOperation(value = "更新课时")
@PutMapping("update-video-info/{id}")
public R updateCourseInfoById(@ApiParam(name = "VideoInfoForm", value = "课时基本信息", required = true)@RequestBody VideoInfoForm videoInfoForm,@ApiParam(name = "id", value = "课时ID", required = true)@PathVariable String id){videoService.updateVideoInfoById(videoInfoForm);return R.ok();
}

2、业务层

VideoService.java

VideoInfoForm getVideoInfoFormById(String id);void updateVideoInfoById(VideoInfoForm videoInfoForm);

VideoServiceImpl.java

@Override
public VideoInfoForm getVideoInfoFormById(String id) {//从video表中取数据Video video = this.getById(id);if(video == null){throw new GuliException(20001, "数据不存在");}//创建videoInfoForm对象VideoInfoForm videoInfoForm = new VideoInfoForm();BeanUtils.copyProperties(video, videoInfoForm);return videoInfoForm;
}@Override
public void updateVideoInfoById(VideoInfoForm videoInfoForm) {//保存课时基本信息Video video = new Video();BeanUtils.copyProperties(videoInfoForm, video);boolean result = this.updateById(video);if(!result){throw new GuliException(20001, "课时信息保存失败");}
}

四、课时的删除

1、web层接口的定义

VideoAdminController.java

@ApiOperation(value = "根据ID删除课时")
@DeleteMapping("{id}")
public R removeById(@ApiParam(name = "id", value = "课时ID", required = true)@PathVariable String id){boolean result = videoService.removeVideoById(id);if(result){return R.ok();}else{return R.error().message("删除失败");}
}

2、业务层

VideoService.java

boolean removeVideoById(String id);

VideoServiceImpl.java

@Override
public boolean removeVideoById(String id) {//删除视频资源 TODOInteger result = baseMapper.deleteById(id);return null != result && result > 0;
}

05-课时管理前端开发

一、定义api

创建video.js

参考course.js

import request from '@/utils/request'const api_name = '/admin/edu/video'export default {saveVideoInfo(videoInfo) {return request({url: `${api_name}/save-video-info`,method: 'post',data: videoInfo})},getVideoInfoById(id) {return request({url: `${api_name}/video-info/${id}`,method: 'get'})},updateVideoInfoById(videoInfo) {return request({url: `${api_name}/update-video-info/${videoInfo.id}`,method: 'put',data: videoInfo})},removeById(id) {return request({url: `${api_name}/${id}`,method: 'delete'})}
}

二、新增课时页面功能

1、定义data数据

saveVideoBtnDisabled: false, // 课时按钮是否禁用
dialogVideoFormVisible: false, // 是否显示课时表单
chapterId: '', // 课时所在的章节id
video: {// 课时对象title: '',sort: 0,free: 0,videoSourceId: ''
},

2、添加课时按钮

<el-button type="text" @click="dialogVideoFormVisible = true; chapterId = chapter.id">添加课时</el-button>

3、课时表单dialog

<!-- 添加和修改课时表单 -->
<el-dialog :visible.sync="dialogVideoFormVisible" title="添加课时"><el-form :model="video" label-width="120px"><el-form-item label="课时标题"><el-input v-model="video.title"/></el-form-item><el-form-item label="课时排序"><el-input-number v-model="video.sort" :min="0" controls-position="right"/></el-form-item><el-form-item label="是否免费"><el-radio-group v-model="video.free"><el-radio :label="true">免费</el-radio><el-radio :label="false">默认</el-radio></el-radio-group></el-form-item><el-form-item label="上传视频"><!-- TODO --></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="dialogVideoFormVisible = false">取 消</el-button><el-button :disabled="saveVideoBtnDisabled" type="primary" @click="saveOrUpdateVideo">确 定</el-button></div>
</el-dialog>

4、添加课时methods

引入video模块

import video from '@/api/edu/video'

方法的定义

saveOrUpdateVideo() {this.saveVideoBtnDisabled = trueif (!this.video.id) {this.saveDataVideo()} else {this.updateDataVideo()}
},saveDataVideo() {this.video.courseId = this.courseIdthis.video.chapterId = this.chapterIdvideo.saveVideoInfo(this.video).then(response => {this.$message({type: 'success',message: '保存成功!'})this.helpSaveVideo()})
},updateDataVideo() {},helpSaveVideo() {this.dialogVideoFormVisible = false// 如果保存成功则关闭对话框this.fetchChapterNestedListByCourseId()// 刷新列表this.video.title = ''// 重置章节标题this.video.sort = 0// 重置章节标题this.video.videoSourceId = ''// 重置视频资源idthis.saveVideoBtnDisabled = false
},

三、修改课时信息

1、编辑课时按钮

<el-button type="text" @click="editVideo(video.id)">编辑</el-button>

2、定义编辑方法

editVideo(videoId) {this.dialogVideoFormVisible = truevideo.getVideoInfoById(videoId).then(response => {this.video = response.data.item})
},

3、定义更新方法

updateDataVideo() {video.updateVideoInfoById(this.video).then(response => {this.$message({type: 'success',message: '修改成功!'})this.helpSaveVideo()})
},

四、删除课时

1、按钮

<el-button type="text" @click="removeVideo(video.id)">删除</el-button>

2、定义删除方法

removeVideo(videoId) {this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {return video.removeById(videoId)}).then(() => {this.fetchChapterNestedListByCourseId()// 刷新列表this.$message({type: 'success',message: '删除成功!'})}).catch((response) => { // 失败if (response === 'cancel') {this.$message({type: 'info',message: '已取消删除'})}})
}

06-课程最终发布前端

一、前端代码

1、定义api

分析这个页面一共有两个远程方法:一个是根基课程id获取课程基本预览信息,第二个是发布课程

getCoursePublishInfoById(id) {return request({url: `${api_name}/course-publish-info/${id}`,method: 'get'})
},publishCourse(id) {return request({url: `${api_name}/publish-course/${id}`,method: 'put'})
}

2、定义数据模型

data() {return {saveBtnDisabled: false, // 保存按钮是否禁用courseId: '', // 所属课程coursePublish: {}}
},

3、完善步骤导航

edu/course/chapter.js

previous() {console.log('previous')this.$router.push({ path: '/edu/course/info/' + this.courseId })
},next() {console.log('next')this.$router.push({ path: '/edu/course/publish/' + this.courseId })
}

edu/course/pubish.js

<div><el-button @click="previous">返回修改</el-button><el-button :disabled="saveBtnDisabled" type="primary" @click="publish">发布课程</el-button>
</div>
previous() {console.log('previous')this.$router.push({ path: '/edu/course/chapter/' + this.courseId  })
},publish() {console.log('publish')course.publishCourse(this.courseId).then(response => {this.$router.push({ path: '/edu/course/list' })})
}

4、组件方法定义

import

import course from '@/api/edu/course'

created

created() {console.log('chapter created')this.init()
},

获取数据的方法

init() {if (this.$route.params && this.$route.params.id) {this.courseId = this.$route.params.id// 根据id获取课程基本信息this.fetchCoursePublishInfoById()}
},fetchCoursePublishInfoById() {course.getCoursePublishInfoById(this.courseId).then(response => {this.coursePublish = response.data.item})
},

5、组件模板

<template><div class="app-container"><h2 style="text-align: center;">发布新课程</h2><el-steps :active="3" process-status="wait" align-center style="margin-bottom: 40px;"><el-step title="填写课程基本信息"/><el-step title="创建课程大纲"/><el-step title="发布课程"/></el-steps><div class="ccInfo"><img :src="coursePublish.cover"><div class="main"><h2>{{ coursePublish.title }}</h2><p class="gray"><span>共{{ coursePublish.lessonNum }}课时</span></p><p><span>所属分类:{{ coursePublish.subjectLevelOne }} — {{ coursePublish.subjectLevelTwo }}</span></p><p>课程讲师:{{ coursePublish.teacherName }}</p><h3 class="red">¥{{ coursePublish.price }}</h3></div></div><div><el-button @click="previous">返回修改</el-button><el-button :disabled="saveBtnDisabled" type="primary" @click="publish">发布课程</el-button></div></div>
</template>

6、css样式

<style scoped>
.ccInfo {background: #f5f5f5;padding: 20px;overflow: hidden;border: 1px dashed #DDD;margin-bottom: 40px;position: relative;
}
.ccInfo img {background: #d6d6d6;width: 500px;height: 278px;display: block;float: left;border: none;
}
.ccInfo .main {margin-left: 520px;
}.ccInfo .main h2 {font-size: 28px;margin-bottom: 30px;line-height: 1;font-weight: normal;
}
.ccInfo .main p {margin-bottom: 10px;word-wrap: break-word;line-height: 24px;max-height: 48px;overflow: hidden;
}.ccInfo .main p {margin-bottom: 10px;word-wrap: break-word;line-height: 24px;max-height: 48px;overflow: hidden;
}
.ccInfo .main h3 {left: 540px;bottom: 20px;line-height: 1;font-size: 28px;color: #d32f24;font-weight: normal;position: absolute;
}
</style>

07-课程最终发布后端

一、根据id查询课程发布信息

方式一:业务层组装多个表多次的查询结果

方式二:数据访问层进行关联查询

我们使用第二种方式实现

1、定义vo

package com.guli.edu.vo;@ApiModel(value = "课程发布信息")
@Data
public class CoursePublishVo  implements Serializable {private static final long serialVersionUID = 1L;private String title;private String cover;private Integer lessonNum;private String subjectLevelOne;private String subjectLevelTwo;private String teacherName;private String price;//只用于显示
}

2、数据访问层

接口:CourseMapper.java

package com.guli.edu.mapper;
public interface CourseMapper extends BaseMapper<Course> {CoursePublishVo selectCoursePublishVoById(String id);
}

实现:CourseMapper.xml

<select id="getCoursePublishVoById" resultType="com.guli.edu.vo.CoursePublishVo">SELECTc.title,c.cover,c.lesson_num AS lessonNum,CONVERT(c.price, DECIMAL(8,2)) AS price,s1.title AS subjectLevelOne,s2.title AS subjectLevelTwo,t.name AS teacherNameFROMedu_course cLEFT JOIN edu_teacher t ON c.teacher_id = t.idLEFT JOIN edu_subject s1 ON c.subject_parent_id = s1.idLEFT JOIN edu_subject s2 ON c.subject_id = s2.idWHEREc.id = #{id}
</select>

3、业务层

接口:CourseService.java

CoursePublishVo getCoursePublishVoById(String id);

实现:CourseServiceImpl.java

@Override
public CoursePublishVo getCoursePublishVoById(String id) {return baseMapper.getCoursePublishVoById(id);
}

4、web层

@ApiOperation(value = "根据ID获取课程发布信息")
@GetMapping("course-publish-info/{id}")
public R getCoursePublishVoById(@ApiParam(name = "id", value = "课程ID", required = true)@PathVariable String id){CoursePublishVo courseInfoForm = courseService.getCoursePublishVoById(id);return R.ok().data("item", courseInfoForm);
}

测试:报告异常

AbstractHandlerExceptionResolver.java:194 |org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver |Resolved exception caused by handler execution: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.guli.edu.mapper.CourseMapper.getCoursePublishVoById

问题分析:

dao层编译后只有class文件,没有mapper.xml,因为maven工程在默认情况下src/main/java目录下的所有资源文件是不发布到target目录下的,

解决方案:

1、在guli_edu的pom中配置如下节点

<!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
<build><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes><filtering>false</filtering></resource></resources>
</build>

重新打包项目会发现target目录下出现了xml文件夹

2、在Spring Boot配置文件中添加配置

#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/guli/edu/mapper/xml/*.xml

二、根据id发布课程

1、web层

@ApiOperation(value = "根据id发布课程")
@PutMapping("publish-course/{id}")
public R publishCourseById(@ApiParam(name = "id", value = "课程ID", required = true)@PathVariable String id){courseService.publishCourseById(id);return R.ok();
}

2、service层

接口

void publishCourseById(String id);

实现

@Override
public boolean publishCourseById(String id) {Course course = new Course();course.setId(id);course.setStatus(Course.COURSE_NORMAL);Integer count = baseMapper.updateById(course);return null != count && count > 0;
}

课程发布-课程大纲和课程发布相关推荐

  1. 谷粒学院——Day08【课程发布-课程大纲和课程发布】

    富文本编辑器Tinymce 一.Tinymce可视化编辑器 参考 https://panjiachen.gitee.io/vue-element-admin/#/components/tinymce ...

  2. 谷粒学苑 —— 6、课程管理:课程发布页面1 —— 添加课程信息

    目录 1.使用代码生成器生成代码 2.添加课程信息 2.1.后端代码 2.1.1.创建 VO 类用于封装表单信息 2.1.2.修改课程简介实体类的主键生成策略 2.1.3.Service 方法 2.1 ...

  3. 资源 | 阿里发布免费深度学习课程:感知机梳理(附链接)

    来源: 机器之心 本文约1000字,建议阅读5分钟. 本文带你简要了解阿里发布的深度学习课程中的感知机部分. 在2018 云栖大会·上海峰会上,阿里巴巴发布了免费的天池深度学习课程,涵盖了神经网络基础 ...

  4. 价值1.4万元的课程讲义开源,fast.ai发布新书源代码,登GitHub趋势榜第一

    关注上方"深度学习技术前沿",选择"星标公众号", 资源干货,第一时间送达! 转载自量子位 价值2000美元的AI线上课程讲义,现在已经开源了. 一直致力于AI ...

  5. 新课程盘古人工智能框架开发专题发布,智华欢迎读者学习!

    新课程盘古人工智能框架开发专题发布,智华欢迎读者学习! 盘古人工智能框架开发专题系列新课程https://edu.csdn.net/course/detail/31166 通过图文并茂.深入浅出的讲解 ...

  6. 西湖大学自然语言处理(一)—— 课程简介及大纲

    课程简介及大纲 自然语言处理概述 课程特点 课程内容概述 基础 结构 神经网络 自然语言处理概述 Natural Language Processing (NLP) A sub field of Al ...

  7. 潍坊学院c语言试卷,潍坊学院《C语言程序设计》课程(0212002)实验大纲.

    潍坊学院<C语言程序设计>课程(0212002)实验大纲. 潍坊学院<C语言程序设计>课程(0212002)实验大纲适用专业:计算机科学与技术专业:实验学时:16 学时 一.实 ...

  8. 电子商务实战课程-张晨光-专题视频课程

    电子商务实战课程-201人已学习 课程介绍         学习电子商务实战的知识 课程收益     达成淘宝大学的目标,可以开店和销售自己的产品. 讲师介绍     张晨光 更多讲师课程     主 ...

  9. 莫言作品确定将收录入高中语文选修课程-莫言-高中语文-选修课程

    莫言作品确定将收录入高中语文选修课程|莫言|高中语文|选修课程 获得诺贝尔文学奖后,有关莫言作品是否该入选中学生教材的话题引起热议.昨天,记者从语文出版社中学语文教研组了解到,目前该社已经确定将莫言作 ...

最新文章

  1. 开始接触QM(Quality Management)
  2. go语言for的三种形式
  3. Zabbix安装 Grafana安装
  4. flask高级编程 LocalStack 线程隔离
  5. 深入理解JavaScript this
  6. 在使用 ADO.NET 导出 Excel 文件时,设置 IMEX=1 仍不能导出所有数据的解决办法
  7. 四川大学金融转计算机,[请教]川大和西财哪个金融系好?!
  8. k8s实战之从私有仓库拉取镜像 - kubernetes
  9. Centos 8 RHEL 8 破解root密码
  10. 忘记mysql数据库名称_忘记MySQL数据库密码的解决办法
  11. Java笔记-ReadWriteLock与Condition的使用
  12. VS Code 1.38 发布,官方图标存储库发布
  13. AAAI'22 | 多模态摘要任务中的知识蒸馏和分层语义关联
  14. android js模板下载地址,template.js
  15. B 1002 写出这个数(简单字符串处理)
  16. HDU 6514 2019中山大学程序设计竞赛(二维前缀和)
  17. html特使数字符号,特殊符号大全
  18. 计算机维修.pdf,计算机维修(中).pdf
  19. 定位点击WebBrowser、点击Flash、偷菜外挂
  20. D435i+vins-Fusion+ego-planner+yolo无人机避障实测

热门文章

  1. 【Exception】Navicat连接Oracle闪退 Navicat连接Oracle 报错:connection to server failed,probable Oracle Net admi
  2. win10鼠标右键文件夹一直转圈圈
  3. oracle周中的日无效,【填坑】ORA-01846 周中的日无效
  4. mysql ibata文件_重装系统后,怎么调用之前mysql的数据_mysql
  5. Django 中CSRF中间件 'django.middleware.csrf.CsrfViewMiddleware',
  6. 安卓网apk安装器_短视频无水印下载,apk提取,带壳截图 iOS Android极客必备的三款软件,你全都用上了吗?...
  7. css设置半透明背景颜色并且内容不会受影响的解决办法
  8. 面向Java开发者的ChatGPT提示词工程(5)
  9. linux 看硬盘运行时间长,Linux/CentOS下怎么查看硬盘使用时间
  10. 基于jeecgboot的flowable流程支持定时捕获事件