课程发布-课程大纲和课程发布

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 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 {
    List nestedList(String courseId);
}

实现

package com.guli.edu.service.impl;

@Service
public class ChapterServiceImpl extends ServiceImpl implements ChapterService {

    @Autowired
    private VideoService videoService;

    @Override
    public List nestedList(String courseId) {

        //最终要的到的数据列表
        ArrayList chapterVoArrayList = new ArrayList<>();

        //获取章节信息
        QueryWrapper queryWrapper1 = new QueryWrapper<>();
        queryWrapper1.eq("course_id", courseId);
        queryWrapper1.orderByAsc("sort", "id");
        List chapters = baseMapper.selectList(queryWrapper1);

        //获取课时信息
        QueryWrapper

3、web层

package com.guli.edu.controller.admin;

@Api(description="课程章节管理")
@CrossOrigin //跨域
@RestController
@RequestMapping("/admin/edu/chapter")
public class ChapterAdminController {

    @Autowired
    private ChapterService chapterService;
    
    @ApiOperation(value = "嵌套章节数据列表")
    @GetMapping("nested-list/{courseId}")
    public R nestedListByCourseId(
            @ApiParam(name = "courseId", value = "课程ID", required = true)
            @PathVariable String courseId){

        List 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、定义组件模板

添加章节

  • {{ chapter.title }} 添加课时 编辑 删除

    • {{ video.title }} 编辑 删除

上一步 下一步

4、定义样式

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

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


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

五、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

五、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

五、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、添加章节按钮

添加章节

3、章节表单dialog



    
        
            
        
        
            
        
    
    

4、添加章节methods

saveOrUpdate() {
  this.saveBtnDisabled = true
  if (!this.chapter.id) {
    this.saveData()
  } else {
    this.updateData()
  }
},

saveData() {
  this.chapter.courseId = this.courseId
  chapter.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、编辑章节按钮

编辑

2、定义编辑方法

editChapter(chapterId) {
    this.dialogChapterFormVisible = true
    chapter.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、按钮

删除

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 {

    @Autowired
    private 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) {

    //删除视频资源 TODO

    Integer 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、添加课时按钮

添加课时

3、课时表单dialog



  
    
      
    
    
      
    
    
      
        免费
        默认
      
    
    
      
    
  
  

4、添加课时methods

引入video模块

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

方法的定义

saveOrUpdateVideo() {
  this.saveVideoBtnDisabled = true
  if (!this.video.id) {
    this.saveDataVideo()
  } else {
    this.updateDataVideo()
  }
},

saveDataVideo() {
  this.video.courseId = this.courseId
  this.video.chapterId = this.chapterId
  video.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 = ''// 重置视频资源id
  this.saveVideoBtnDisabled = false
},

三、修改课时信息

1、编辑课时按钮

编辑

2、定义编辑方法

editVideo(videoId) {
  this.dialogVideoFormVisible = true
  video.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、按钮

删除

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

返回修改 发布课程
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、组件模板


6、css样式


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 {
    CoursePublishVo selectCoursePublishVoById(String id);
}

实现:CourseMapper.xml


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张图片

解决方案:

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



    
        
            src/main/java
            
                **/*.xml
            
            false
        
    

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

课程发布-课程大纲和课程发布_第2张图片

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;
}

你可能感兴趣的:(课程发布-课程大纲和课程发布)