微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql

文章目录

  • 一、课程发布完善
    • 1、(前端)集成富文本插件
      • 1、下载依赖
      • 2、配置环境
      • 3、测试
      • 4、添加插件
    • 2、(后端)课程大纲列表(多级联动和课程分类相同,有章节和小节)
      • 1、创建两个实体类分别表示章节和小节,每个章节包含多个小节
      • 2、controller层
      • 3、service层
      • 4、测试
    • 3、(前端)课程大纲列表的显示
      • 1、API接口
      • 2、页面实现
    • 4、(后端)数据回显(点击上一步时,显示出原内容,之后根据请求类型执行不同操作put修改)
      • 1、controller
      • 2、service
    • 5、(前端)数据回显
      • 1、api接口
      • 2、回显实现
      • 3、修改实现(点击下一步保存的时候判断是添加还是修改操作)
    • 6、(后端)章节的增删改查(记住写完代码,给实体类设置自动填充,然后重启服务器)
      • 1、controller
      • 2、service
    • 7、(前端)章节的增删改
      • 1、Api接口
      • 2、代码实现(添加操作)
      • 3、测试
    • 8、(后端)小节的增删改查(只编写了controller层)
    • 9、(前端)小节的增删改查
      • 1、api接口
      • 2、添加组件
      • 3、代码实现,和章节的代码实现大同小异
    • 10、(后端)整体数据的回显(手写sql,除了需要自己写Mapper以外,其它和以前的内容一模一样)
      • 1、编写sql语句
      • 2、创建实体类
      • 3、编写mapper映射文件
      • 4、service
      • 5、controller
      • 6、解决百分百会遇到的错误
        • 1、配置pom.xml让其编译指定包下的xml文件
        • 2、配置启动类
      • 7、测试
    • 11、(前端)整体数据回显
      • 1、api接口
      • 2、代码实现
      • 3、测试
    • 12、完善最终发布
      • 1、后端
      • 2、前端
  • 二、课程列表增删改查
    • 1、(后端)实现分页展示课程信息(手写sql)
      • 1、代码
      • 2、测试
    • 2、(后端)实现删除课程
      • 1、controller(执需要编写controller即可)
    • 2、(前端)实现分页展示课程信息
      • 1、api接口
      • 2、组件
      • 3、代码实现
      • 4、测试
  • 三、完善小节视频功能(阿里云视频点播)
    • 1、阿里云视频点播
      • 1、申请服务(请先登录,推荐使用支付宝扫码登录)
      • 2、获取密钥(和OSS头像存储相同,上次已经有的就用上次的)
      • 3、文档(不推荐使用API,使用SDK更方便,因为是已经封装好的)
      • 4、开发分析
    • 2、阿里云视频点播环境搭建(此小节仅用于讲解)
      • 1、引入依赖
      • 2、阿里云视频点播基本使用方法介绍(先了解即可,之后会进入到实战)
        • 1、初始化
        • 2、获取视频播放地址
        • 3、获取视频凭证
        • 4、删除视频
        • 5、上传视频到阿里云(这里你需要学会如何引入jar包依赖)
          • 上传需要的依赖
          • 代码测试
        • 6、已上传视频转码(注意是给已经上传的视频,而我们上传的时候,可以通过设置参数直接上传转码视频)
    • 3、(后端)小节视频上传(实现转码上传,并返回视频id)
      • 1、搭建微服务环境(根据图片创建目录结构)
      • 2、视频点播工具类
      • 3、application.yml
      • 4、启动类
      • 5、service(这是实现类(需要使用注解注入),需要你自己在接口中补上定义)
      • 6、controller
      • 7、测试
    • 4、(前端)小节视频上传
      • 1、组件
      • 2、代码实现
      • 3、测试报错
    • 5、删除视频以及删除小节时删除对应视频
      • 1、后端
      • 2、前端

一、课程发布完善

1、(前端)集成富文本插件

1、下载依赖

1、安装tinymce,不安装这个没法使用tinymce vue(你可以直接下载离线包)
npm install tinymce 
2、安装tinymce vue
npm install --save @tinymce/tinymce-vue

2、配置环境

1、安装之后,在 node_modules 中找到 tinymce目录,然后将目录拷贝到 static 目录下 如果是使用 vue-cli 3.x 构建的,就放到 public 目录下

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第1张图片
2、下载语言包,默认英文,我们可以使用中文,放到我们刚复制的文件夹中,不过你可以先看看文件夹里面有没有,因为你可能下载的就是中文的语言包
3、主页面引入js文件,第二个是语言包,如果不需要就不用引入
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第2张图片
4、需要使用富文本编辑组件的页面引入

<el-form-item label="课程简介">
        <Editor id="tinymce" v-model="courseInfo.description" :init="editorInit"></Editor>
      </el-form-item>

import Editor from '@tinymce/tinymce-vue'

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第3张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第4张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第5张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第6张图片

3、测试

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第7张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第8张图片

4、添加插件

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第9张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第10张图片

2、(后端)课程大纲列表(多级联动和课程分类相同,有章节和小节)

1、创建两个实体类分别表示章节和小节,每个章节包含多个小节

package com.yzpnb.eduservice.entity.chapter;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;

/**
 * 章节实体类
 */
@Data
public class ChapterVo {

    @ApiModelProperty("章节id")
    private String id;

    @ApiModelProperty("章节标题")
    private String title;

    @ApiModelProperty("每章节包含的小节,名称为children,前端需要对应")
    private List<VideoVo> children=new ArrayList<>();
}

package com.yzpnb.eduservice.entity.chapter;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * 小节实体类
 */
@Data
public class VideoVo {

    @ApiModelProperty("小节id")
    private String id;

    @ApiModelProperty("小节标题")
    private String title;
}

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第11张图片

2、controller层

package com.yzpnb.eduservice.controller;


import com.yzpnb.common_utils.Result;
import com.yzpnb.eduservice.entity.chapter.ChapterVo;
import com.yzpnb.eduservice.service.EduChapterService;
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.List;


/**
 * 

* 课程 前端控制器 *

* * @author testjava * @since 2020-05-24 */
@RestController @RequestMapping("/eduservice/edu-chapter") @CrossOrigin public class EduChapterController { @Autowired private EduChapterService eduChapterService; /** * * 查询 */ @ApiOperation("查询指定的课程大纲列表") @GetMapping("selectTree/{courseId}") public Result selectTree( @ApiParam(name = "courseId",value = "课程id") @PathVariable String courseId){ List<ChapterVo> list= eduChapterService.selectChapterVideoByCourseId(courseId);//根据课程id查询章节和小节 return Result.ok().data("chapterAndVideo",list); } }

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第12张图片

3、service层

package com.yzpnb.eduservice.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yzpnb.eduservice.entity.EduChapter;
import com.yzpnb.eduservice.entity.EduVideo;
import com.yzpnb.eduservice.entity.chapter.ChapterVo;
import com.yzpnb.eduservice.entity.chapter.VideoVo;
import com.yzpnb.eduservice.mapper.EduChapterMapper;
import com.yzpnb.eduservice.service.EduChapterService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yzpnb.eduservice.service.EduVideoService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * 

* 课程 服务实现类 *

* * @author testjava * @since 2020-05-24 */
@Service public class EduChapterServiceImpl extends ServiceImpl<EduChapterMapper, EduChapter> implements EduChapterService { @Autowired private EduVideoService eduVideoService; @Override public List<ChapterVo> selectChapterVideoByCourseId(String courseId) { /**1、根据课程id查询对应所有章节**/ QueryWrapper<EduChapter> queryWrapper=new QueryWrapper<>(); queryWrapper.eq("course_id",courseId); List<EduChapter> eduChapters = baseMapper.selectList(queryWrapper); /**2、根据课程id和当前查询的章节id查出对应小节,然后封装到章节中**/ //2.1先查询出当前课程id对应的所有小节 QueryWrapper<EduVideo> queryWrapper2=new QueryWrapper<>(); queryWrapper2.eq("course_id",courseId); List<EduVideo> eduVideos=eduVideoService.list(queryWrapper2); //2.2遍历集合,判断小节的章节id(chapter_id)是否等于查询到的章节id进行封装 List<ChapterVo> list=new ArrayList<>();//保存最终封装号的对象 for(EduChapter eduChapter:eduChapters)//遍历所有章节(总) { ChapterVo chapterVo=new ChapterVo();//创建我们封装用的章节对象 BeanUtils.copyProperties(eduChapter,chapterVo);//使用工具类将章节(总)对象中我们需要的信息封装到章节对象中 for (EduVideo eduVideo :eduVideos){//遍历小节(总)对象 if(eduVideo.getChapterId().equals(chapterVo.getId())){//判断当前小节(总)对象的章节id是否等于章节对象的id(一定用equals) VideoVo videoVo =new VideoVo();//创建小节对象 BeanUtils.copyProperties(eduVideo,videoVo);//封装信息 chapterVo.getChildren().add(videoVo);//将此小节封装当此章节中 } //if结束 } //内循环结束,将对象封装 list.add(chapterVo); } return list; } }

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第13张图片

4、测试

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第14张图片

3、(前端)课程大纲列表的显示

1、API接口

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第15张图片

2、页面实现

<template>
  <div class="app-container">
    <!-- 导航-->
    <h2 style="text-align: center;">发布新课程</h2>
    <el-steps :active="2" process-status="wait" align-center style="margin-bottom: 40px;">
      <el-step title="填写课程基本信息"/>
      <el-step title="创建课程大纲"/>
      <el-step title="提交审核"/>
    </el-steps>

    <!-- 课程大纲 -->
    <ul class="chanpterList">
        <li
            v-for="chapter in list"
            :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">
                <li
                    v-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>
    <!-- 按钮-->
    <el-form label-width="120px">
      <el-form-item>
        <el-button @click="previous">上一步</el-button>
        <el-button :disabled="saveBtnDisabled" type="primary" @click="next">下一步</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
  import eduCourse from '@/api/course/eduCourse.js'
export default {
  data() {
    return {
      saveBtnDisabled: false ,// 保存按钮是否禁用
      list:[],//数据
    }
  },
  created() {
    console.log('chapter created')
    if(this.$route.params.id && this.$route.params){//若路由中有参数,并且参数名为id就执行
      // this.getChapterAndVideoById(this.$route.params.id)
      this.getChapterAndVideoById('18')//这里先用假数据18作为参数,之后会完善
    }
  },
  methods: {
    /* 根据课程id查询章节和小节*/
    getChapterAndVideoById(id){
      eduCourse.getChapterAndVideoById(id)
      .then(response=>{
        this.list=response.data.chapterAndVideo
      })
    },
    previous() {
      console.log('previous')
      this.$router.push({name:"课程添加",params:{id:1}})
    },
    next() {
      console.log('next')
      this.$router.push({name:"课程添加3",params:{id:1}})
    }
  }
}
</script>
<style>
.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>

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第16张图片

4、(后端)数据回显(点击上一步时,显示出原内容,之后根据请求类型执行不同操作put修改)

1、controller

/**
     * 查询
     */
    @ApiOperation("根据id查询课程基本信息和简介")
    @GetMapping("selectById/{id}")
    public Result selectById(@ApiParam(name = "id",value = "课程id")
                             @PathVariable String id){
        CourseInfoVo courseInfoVo=eduCourseService.selectCourseInfo(id);
        return Result.ok().data("courseInfoVo",courseInfoVo);
    }
    /**
     * 修改
     */
    @ApiOperation("根据id修改课程信息")
    @PostMapping("updateCourseInfoVo")
    public Result updateCourseInfoVo(@RequestBody CourseInfoVo courseInfoVo){

        eduCourseService.updateCourseInfoVo(courseInfoVo);
        return Result.ok();
    }

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第17张图片

2、service

/**
     * 根据课程id查询课程信息
     */
    @Override
    public CourseInfoVo selectCourseInfo(String id) {
        /**1、根据id查询课程基本信息*/
        EduCourse eduCourse=baseMapper.selectById(id);
        /**2、根据id查询课程简介*/
        EduCourseDescription eduCourseDescription =eduCourseDescriptionService.getById(id);
        /**3、将信息封装到信息汇总对象中*/
        CourseInfoVo courseInfoVo=new CourseInfoVo();
        BeanUtils.copyProperties(eduCourse,courseInfoVo);
        courseInfoVo.setDescription(eduCourseDescription.getDescription());

        return courseInfoVo;
    }

    /**
     * 根据id修改课程信息
     */
    @Override
    public void updateCourseInfoVo(CourseInfoVo courseInfoVo) {

        /**1、修改课程基本信息*/
        EduCourse eduCourse=new EduCourse();
        BeanUtils.copyProperties(courseInfoVo,eduCourse);
        int i=baseMapper.updateById(eduCourse);
        if(i==0){
            throw new CustomExceptionHandler(20001,"修改课程基本信息失败");
        }
        /**2、修改简介*/
        EduCourseDescription eduCourseDescription=new EduCourseDescription();
        eduCourseDescription.setId(courseInfoVo.getId());
        eduCourseDescription.setDescription(courseInfoVo.getDescription());
        boolean b=eduCourseDescriptionService.updateById(eduCourseDescription);
        if(!b){
            throw new CustomExceptionHandler(20001,"修改课程简介信息失败");
        }
    }

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第18张图片

5、(前端)数据回显

1、api接口

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第19张图片

2、回显实现

设置save2页面中,单击上一步按钮时的参数
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第20张图片
save1中实现回显
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第21张图片

//有值就说明是回显的,因为我们第一次添加课程时,是没有id参数的
        eduCourse.getCourseInfo(id)
        .then(response=>{
          this.courseInfo=response.data.courseInfoVo;

          //解决回显数据不显示内容只显示id值(只需要让二级分类有值就可以)
          eduSubject.getList()
          .then(response=>{
            this.oneSubject=response.data.allSubject;//获取一级分类
            for (let i = 0; i < this.oneSubject.length; i++) {//遍历一级分类
              if (this.oneSubject[i].id === this.courseInfo.subjectParentId) {
                  this.twoSubject = this.oneSubject[i].children//找到与回显的id对应的二级分类,赋值
              }
            }
          })
        })

3、修改实现(点击下一步保存的时候判断是添加还是修改操作)

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第22张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第23张图片

6、(后端)章节的增删改查(记住写完代码,给实体类设置自动填充,然后重启服务器)

1、controller

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第24张图片

@ApiOperation("根据Id查询章节信息")
    @GetMapping("selectById/{id}")
    public Result selectById(@ApiParam(name = "id",value = "章节id")
                             @PathVariable String id){

        EduChapter eduChapter = eduChapterService.getById(id);
        return Result.ok().data("chapter",eduChapter);
    }
    /**
     * 添加章节
     */
    @ApiOperation("添加章节")
    @PostMapping("insertChapter")
    public Result insertChapter(@ApiParam(name = "eduChapter",value = "章节信息数据")
                                @RequestBody EduChapter eduChapter){
        eduChapterService.save(eduChapter);
        return Result.ok();
    }
    /**
     * 修改
     */
    @ApiOperation("修改章节")
    @PostMapping("updateChapter")
    public Result updateChapter(@ApiParam(name = "eduChapter",value = "章节信息数据")
                                @RequestBody EduChapter eduChapter){
        eduChapterService.updateById(eduChapter);
        return Result.ok();
    }
    /**
     * 删除
     */
    @ApiOperation("删除章节")
    @DeleteMapping("{id}")
    public Result deleteById(@ApiParam(name = "id",value = "章节id")
                             @PathVariable String id) {
        eduChapterService.deleteChapterVideo(id);//删除章节后,需要对小节处理
        return Result.ok();
    }

2、service

/**
     * 删除章节,如果有小节,则不能删除章节
     */
    @Override
    public void deleteChapterVideo(String id) {
        /**1、根据章节id查小节*/
        QueryWrapper<EduVideo> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("chapter_id",id);
        int count = eduVideoService.count(queryWrapper);//返回当前查询结果的记录个数
        if(count>0){//如果不是0表示有小节
            throw new CustomExceptionHandler(20001,"章节中包含小节,请先删除小节");
        }else{
            baseMapper.deleteById(id);
        }

    }

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第25张图片

7、(前端)章节的增删改

1、Api接口

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第26张图片

2、代码实现(添加操作)

<template>
  <div class="app-container">
    <!-- 导航-->
    <h2 style="text-align: center;">发布新课程</h2>
    <el-steps :active="2" process-status="wait" align-center style="margin-bottom: 40px;">
      <el-step title="填写课程基本信息"/>
      <el-step title="创建课程大纲"/>
      <el-step title="提交审核"/>
    </el-steps>

    <!-- 课程大纲 -->
    <el-button type="text" @click="openDialog(0)">添加章节</el-button>

    <ul class="chanpterList">
        <li
            v-for="chapter in list"
            :key="chapter.id">
            <p>
                {{ chapter.title }}
                <span class="acts">
                    <el-button type="text">添加课时</el-button>
                    <el-button style="disabled=true" type="text" @click="openDialog(chapter.id)">编辑</el-button>
                    <el-button type="text" @click="deleteChapter(chapter.id)">删除</el-button>
                </span>
            </p>
            <!-- 视频 -->
            <ul class="chanpterList videoList">
                <li
                    v-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>

    <!-- 添加和修改章节表单-->
    <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>

    <!-- 按钮-->
    <el-form label-width="120px">
      <el-form-item>
        <el-button @click="previous">上一步</el-button>
        <el-button :disabled="saveBtnDisabled" type="primary" @click="next">下一步</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
  import eduCourse from '@/api/course/eduCourse.js'
  const defaultChapter={//封装章节数据
        title:'', //章节名称
        sort:1,   //排序,默认为1
        courseId:0//课程id
      }
export default {
  data() {
    return {
      saveBtnDisabled: false ,// 保存按钮是否禁用
      list:[],//数据
      dialogChapterFormVisible:false,//弹窗初始不显示
      chapter:defaultChapter,//封装章节数据
      flag:0,//用于判断是添加还是修改操作,0为添加,其它值为修改(因为我会把需要修改的章节id传过来)
    }
  },
  created() {
    console.log('chapter created')
    if(this.$route.params.id && this.$route.params){//若路由中有参数,并且参数名为id就执行
      this.getChapterAndVideoById(this.$route.params.id)//现在可以使用完善的喽
    }
  },
  methods: {
    /* 根据课程id查询章节和小节*/
    getChapterAndVideoById(id){
      eduCourse.getChapterAndVideoById(id)
      .then(response=>{
        this.list=response.data.chapterAndVideo
       })
    },
    /* 开启弹窗*/
    openDialog(flag){
      this.dialogChapterFormVisible = true;
      //将chapter重新赋值
      this.chapter=defaultChapter;
      //判断是添加还是修改
      this.flag=flag;
      if(!(flag===0)){//如果是修改操作
        //乐观锁,先查数据
        eduCourse.selectById(flag)
        .then(response=>{
          this.chapter=response.data.chapter;//将查出的数据保存起来
        })
      }
    },
    /* 点击确定按钮后*/
    saveOrUpdate(){
      if(this.flag===0){//id
        this.insertChapter();
      }else{//其它值表示修改操作(传的就是章节id
        this.updateChapter(this.flag);
      }
      //3、刷新页面(为了保险,多刷一次)
      this.getChapterAndVideoById(this.$route.params.id)
    },
    /*添加操作 */
    insertChapter(){
      this.chapter.courseId=this.$route.params.id//将课程id给章节对象中
      eduCourse.insertChapter(this.chapter)
      .then(response=>{
        //1、关闭弹窗
        this.dialogChapterFormVisible = false;
        //2、提示信息
        this.$message({//提示
          type: 'success',
          message: '添加成功!'
        })
      })
      //刷新页面(为了保险,多刷一次)
      this.getChapterAndVideoById(this.$route.params.id)
    },
    /* 修改操作*/
    updateChapter(){
      eduCourse.updateChapter(this.chapter)
      .then(response=>{
        //1、关闭弹窗
        this.dialogChapterFormVisible = false;
        //2、提示信息
        this.$message({//提示
          type: 'success',
          message: '修改成功!'
        })
      })
      //刷新页面(为了保险,多刷一次)
      this.getChapterAndVideoById(this.$route.params.id)
    },
    /* 删除*/
    deleteChapter(id){
      this.$confirm('此操作将永久删除数据, 是否继续?', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
              }).then(() => {
                eduCourse.deleteChapter(id)
                .then(response=>{
                  this.$message({//提示
                    type: 'success',
                    message: '删除成功!'
                  })
                })
              }).catch(() => {
                this.$message({
                  type: 'info',
                  message: '已取消删除'
                });
              });
    },
    previous() {
      console.log('previous')
      this.$router.push({name:"课程添加",params:{id:this.$route.params.id}})
    },
    next() {
      console.log('next')
      this.$router.push({name:"课程添加3",params:{id:1}})
    }
  }
}
</script>
<style>
.chanpterList{
    position: relative;
    list-style: none;
    margin: 0;
    padding: 0;
    border: 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>

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第27张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第28张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第29张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第30张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第31张图片

3、测试

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第32张图片

8、(后端)小节的增删改查(只编写了controller层)

package com.yzpnb.eduservice.controller;


import com.yzpnb.common_utils.Result;
import com.yzpnb.eduservice.entity.EduVideo;
import com.yzpnb.eduservice.service.EduVideoService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * 

* 课程视频 前端控制器 *

* * @author testjava * @since 2020-05-24 */
@RestController @RequestMapping("/eduservice/edu-video") public class EduVideoController { @Autowired private EduVideoService eduVideoService; /** * 查询 */ @ApiOperation("根据id查询小节") @GetMapping("selectById/{id}") public Result selectById(@ApiParam(name = "id",value = "小节id") @PathVariable String id){ EduVideo eduVideo = eduVideoService.getById(id); return Result.ok().data("video",eduVideo); } /** * 添加 */ @ApiOperation("添加小节") @PostMapping("insertVideo") public Result insertVideo(@ApiParam(name="eduVideo",value="小节对象json") @RequestBody EduVideo eduVideo){ eduVideoService.save(eduVideo); return Result.ok(); } /** * 修改 */ @ApiOperation("根据id修改小节内容") @PostMapping("updateVideo") public Result updateVideo(@ApiParam(name = "eduVideo",value = "小节对象json") @RequestBody EduVideo eduVideo){ eduVideoService.updateById(eduVideo); return Result.ok(); } /** * 删除 * TODO 之后会需要添加视频,删除小节时,需要将视频也删除掉 */ @ApiOperation("根据id删除小节") @DeleteMapping("{id}") public Result deleteVideoById(@ApiParam(name="id",value="小节id") @PathVariable String id){ eduVideoService.removeById(id); return Result.ok(); } }

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第33张图片

9、(前端)小节的增删改查

1、api接口

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第34张图片

2、添加组件

<!-- 添加和修改课时表单 -->
    <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.isFree">
            <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>

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第35张图片

3、代码实现,和章节的代码实现大同小异

const defaultVideo={  //封装小节默认数据
        title:'1',       //小节名称
        sort:1,
        courseId:0,     //课程id
        chapterId:0,    //章节id
        play_count:0,   //播放次数
        isFree:1,       //是否可以试听,0收费,1免费
        duration:0,     //视频时长
        status:'',      //视频转码
        size:0,         //视频源文件字节
  };

data() {
    return {
      saveBtnDisabled: false ,        // 保存按钮是否禁用
      list:[],                        //数据
      dialogChapterFormVisible:false, //章节弹窗初始不显示
      dialogVideoFormVisible:false,   //小节弹窗初始不显示
      saveVideoBtnDisabled:false,     //小节弹窗中确定按钮是否可用
      chapter:defaultChapter,         //封装章节数据
      video:defaultVideo,             //封装小节数据
      flag:0,                         //用于判断是添加还是修改操作,0为添加,其它值为修改(因为我会把需要修改的章节id传过来)
      videoFlag:0,                    //用于判断小节是添加还是修改操作
      dialogTitle:"添加章节",
      videoDialogTitle:"添加小节",
    }
  },

/**===================================小节操作函数==============================================**/
  /* 开启弹窗 */
  openVideoDialog(videoFlag,chapterId){
    this.dialogVideoFormVisible=true;//开启弹窗

    //将video重新赋值
    this.video={...defaultVideo};
    //获取当前章节id
    this.video.chapterId=chapterId;

    this.VideoDialogTitle="添加小节";
    //判断是添加还是修改
    this.videoFlag=videoFlag
    if(!(videoFlag===0)){//不等于0表示修改,根据小节id查出数据保存,实现回显
      this.VideoDialogTitle="修改小节";
      eduCourse.selectVideoById(videoFlag)
      .then(response=>{
        this.video=response.data.video;//将查出的数据保存
      })
    }
  },
  /* 点击弹窗确认按钮后*/
  saveOrUpdateVideo(){
    if(this.videoFlag===0){//为0表示添加
      this.insertVideo();
    }else{//表示为修改操作
      this.updateVideo();
    }
    //1、关闭弹窗(多关一次)
    this.dialogVideoFormVisible = false;
    //刷新页面(为了保险,多刷一次)
    this.getChapterAndVideoById(this.$route.params.id)
  },
  /* 添加小节*/
  insertVideo(){
    eduCourse.insertVideo(this.video)
    .then(response=>{
      //获取课程id
      this.video.courseId=this.$route.params.id;
      //执行添加操作
      eduCourse.insertVideo(this.video)
      .then(response=>{
        //1、关闭弹窗
        this.dialogVideoFormVisible = false;
        //2、提示信息
        this.$message({//提示
          type: 'success',
          message: '添加成功!'
        })
      })
      //刷新页面(为了保险,多刷一次)
      this.getChapterAndVideoById(this.$route.params.id)
    })
  },
  /* 修改小节*/
  updateVideo(){
    eduCourse.updateVideo(this.video)
    .then(response=>{
      //1、关闭弹窗
      this.dialogChapterFormVisible = false;
      //2、提示信息
      this.$message({//提示
        type: 'success',
        message: '修改成功!'
      })
    })
    //刷新页面(为了保险,多刷一次)
    this.getChapterAndVideoById(this.$route.params.id)
  },
  /* 删除小节*/
  deleteVideo(id){
    this.$confirm('此操作将永久删除数据, 是否继续?', '提示', {
              confirmButtonText: '确定',
              cancelButtonText: '取消',
              type: 'warning'
            }).then(() => {
              eduCourse.deleteVideo(id)
              .then(response=>{
                this.$message({//提示
                  type: 'success',
                  message: '删除成功!'
                })
              })
              //刷新页面(为了保险,多刷一次)
              this.getChapterAndVideoById(this.$route.params.id)
            }).catch(() => {
              this.$message({
                type: 'info',
                message: '已取消删除'
              })
              //刷新页面(为了保险,多刷一次)
              this.getChapterAndVideoById(this.$route.params.id)
            });
    //刷新页面(为了保险,多刷一次)
    this.getChapterAndVideoById(this.$route.params.id)
  },

10、(后端)整体数据的回显(手写sql,除了需要自己写Mapper以外,其它和以前的内容一模一样)

1、编写sql语句

#查询出指定课程id的课程id,课程标题,课程价格,总课时,课程封面,课程讲师,课程简介,课程类别一级和二级

select
	课程基本信息.id as 课程id,
	课程基本信息.title as 课程标题,
	课程基本信息.price as 课程价格,
	课程基本信息.lesson_num as 总课时,
	课程基本信息.cover as 课程封面,
	课程基本信息.status as 课程发布状态,
	讲师.`name` as 讲师名,
	课程简介.description as 课程简介,
	一级课程分类.title as 一级分类,
	二级课程分类.title as 二级分类
from
	edu_course as 课程基本信息
left join
	edu_teacher as 讲师
on
	课程基本信息.teacher_id=讲师.id
left join
	edu_course_description as 课程简介
on
	课程简介.id=课程基本信息.id
left join
	edu_subject as 一级课程分类
on
	课程基本信息.subject_id=一级课程分类.id 
left join
	edu_subject as 二级课程分类
on
	课程基本信息.subject_parent_id=二级课程分类.id
where
	课程基本信息.id=18
		

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第36张图片

2、创建实体类

package com.yzpnb.eduservice.entity.vo;


import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.math.BigDecimal;

@Data
public class CourseAllInfoVo {

    @ApiModelProperty(value = "课程id")
    private String id;

    @ApiModelProperty(value = "课程标题")
    private String title;

    @ApiModelProperty(value = "课程价格")
    private BigDecimal price;

    @ApiModelProperty(value = "总课时")
    private Integer lessonNum;

    @ApiModelProperty(value = "课程封面")
    private String cover;

	@ApiModelProperty(value = "课程发布状态")
    private String status;
    
    @ApiModelProperty(value = "讲师名")
    private String teacherName;

    @ApiModelProperty(value = "课程简介")
    private String description;

    @ApiModelProperty("一级分类")
    private String oneTitle;

    @ApiModelProperty("二级分类")
    private String twoTitle;
}

在这里插入图片描述

3、编写mapper映射文件

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第37张图片

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yzpnb.eduservice.mapper.EduCourseMapper"><!--mapper映射的接口-->
    <!--
        #查询出指定课程id的课程id,课程标题,课程价格,总课时,课程封面,课程讲师,课程简介,课程类别一级和二级

        select
            课程基本信息.id as 课程id,
            课程基本信息.title as 课程标题,
            课程基本信息.price as 课程价格,
            课程基本信息.lesson_num as 总课时,
            课程基本信息.cover as 课程封面,
            课程基本信息.status as 课程发布状态,
            讲师.`name` as 讲师名,
            课程简介.description as 课程简介,
            一级课程分类.title as 一级分类,
            二级课程分类.title as 二级分类
        from edu_course as 课程基本信息
        left join edu_teacher as 讲师 on 课程基本信息.teacher_id=讲师.id
        left join edu_course_description as 课程简介 on 课程简介.id=课程基本信息.id
        left join edu_subject as 一级课程分类 on 课程基本信息.subject_id=一级课程分类.id
        left join  edu_subject as 二级课程分类 on 课程基本信息.subject_parent_id=二级课程分类.id
        where 课程基本信息.id=18

    -->
    <select id="selectCourseAllInfoVo" parameterType="java.lang.String" resultType="com.yzpnb.eduservice.entity.vo.CourseAllInfoVo">
        select
            ec.id as id,
            ec.title as title,
            ec.price as price,
            ec.lesson_num as lessonNum,
            ec.cover as cover,
            ec.status as status,
            et.`name` as teacherName,
            ecd.description as description,
            es1.title as oneTitle,
            es2.title as twoTitle
        from
            edu_course as ec
        left join
            edu_teacher as et
        on
            ec.teacher_id=et.id
        left join
            edu_course_description as ecd
        on
            ecd.id=ec.id
        left join
            edu_subject as es1
        on
            ec.subject_id=es1.id
        left join
            edu_subject as es2
        on
            ec.subject_parent_id=es2.id
        where
            ec.id=#{id}
    </select>
</mapper>

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第38张图片

4、service

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第39张图片微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第40张图片

5、controller

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第41张图片

6、解决百分百会遇到的错误

Invalid bound statement (not found):com.yzpnb.eduservice.mapper.EduCourseMapper.selectCourseAllInfoVo

这个错误很简单,就是找不到mapper.xml文件
我们可以将xml文件复制到能找到的地方,或者在编程时就按照官方规定将xml文件放在resource文件夹中
或者通过配置
1、配置pom.xml文件
2、配置application.yml文件

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第42张图片

1、配置pom.xml让其编译指定包下的xml文件

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

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第43张图片

2、配置启动类

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

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第44张图片

7、测试

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第45张图片

11、(前端)整体数据回显

1、api接口

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第46张图片

2、代码实现

<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="courseAllInfoVo.cover">
        <div class="main">
          <h2>{{ courseAllInfoVo.title }}</h2>
          <p class="gray"><span>{{ courseAllInfoVo.lessonNum }}课时</span></p>
          <p><span>所属分类:{{ courseAllInfoVo.oneTitle }}{{ courseAllInfoVo.twoTitle }}</span></p>
          <p>课程讲师:{{ courseAllInfoVo.teacherName }}</p>
          <h3 class="red">{{ courseAllInfoVo.price }}</h3>
        </div>
      </div>
      <ul class="chanpterList">
          <li
              v-for="chapter in list"
              :key="chapter.id">
              <p>{{ chapter.title }}</p>
              <!-- 视频 -->
              <ul class="chanpterList videoList">
                  <li
                      v-for="video in chapter.children"
                      :key="video.id">
                      <p>{{ video.title }}</p>
                  </li>
              </ul>
          </li>
      </ul>
    <div>
      <el-button @click="previous">返回修改</el-button>
      <el-button :disabled="saveBtnDisabled" type="primary" @click="publish">发布课程</el-button>
    </div>
  </div>
</template>
<script>
  import eduCourse from '@/api/course/eduCourse.js'
export default {
  data() {
    return {
      saveBtnDisabled: false ,// 保存按钮是否禁用
      courseAllInfoVo:{},             //初始化数据
      list:[],                        //数据
    }
  },
  created() {
    console.log('publish created')
    this.init()
  },
  methods: {
    /* 初始化*/
    init(){
      this.selectCourseAllInfoVoById(this.$route.params.id)
      this.getChapterAndVideoById(this.$route.params.id)
    },
    /* 获取所有数据回显*/
    selectCourseAllInfoVoById(id){
      eduCourse.selectCourseAllInfoVoById(id)
      .then(response=>{
        this.courseAllInfoVo=response.data.courseAllInfoVo
      })
    },
    /* 根据课程id查询章节和小节*/
    getChapterAndVideoById(id){
      eduCourse.getChapterAndVideoById(id)
      .then(response=>{
        this.list=response.data.chapterAndVideo
       })
    },
    previous() {
      console.log('previous')
      this.$router.push({name:"课程添加2",params:{id:this.$route.params.id}})
    },
    publish() {
      console.log('publish')
      this.$router.push({name:"课程展示"})
    }
  }
}
</script>
<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;
}
.chanpterList{
    position: relative;
    list-style: none;
    margin: 0;
    padding: 0;
    border: 0;
}
.chanpterList li{
  position: relative;
}
.videoList{
  padding-left: 50px;
}
.chanpterList p{
  float: left;
  font-size: 20px;
  margin: 10px 0;
  padding: 10px;
  height: 70px;
  line-height: 50px;
  width: 100%;
  border: 1px solid #DDD;
}
.videoList p{
  float: left;
  font-size: 14px;
  margin: 10px 0;
  padding: 10px;
  height: 50px;
  line-height: 30px;
  width: 100%;
  border: 1px dotted #DDD;
}
</style>

3、测试

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第47张图片

12、完善最终发布

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第48张图片

# 修改指定id的发布状态,如果是未发布Draft就改为Normal已发布,如果是已发布Normal,就改为Draft未发布
update 
	edu_course
set
	status=if(status='Draft',
		'Normal',
		'Draft'
	)
where
	id=18

1、后端

mapper

 <!--修改指定id的发布状态,如果是未发布Draft就改为Normal已发布,如果是已发布Normal,就改为Draft未发布-->
    <update id="updateStatus" >
        update
            edu_course
        set
            status=if(status='Draft',
                'Normal',
                'Draft'
            )
        where
            id=#{id}
    </update>

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第49张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第50张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第51张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第52张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第53张图片

2、前端

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第54张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第55张图片
在这里插入图片描述
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第56张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第57张图片
在这里插入图片描述

二、课程列表增删改查

1、(后端)实现分页展示课程信息(手写sql)

1、代码

一、controller
@ApiOperation("分页查询课程信息")
    @GetMapping("limitSelectCourseAllInfoVo/{current}/{size}")
    public Result limitSelect(@ApiParam(name = "current",value = "当前页")
                              @PathVariable Long current,
                              @ApiParam(name = "size",value = "每页记录数")
                              @PathVariable Long size){
        List<CourseAllInfoVo> courseAllInfoVoList= eduCourseService.limitSelectCourseAllInfoVo(current,size);
        Map<String,Object> map=new HashMap<>();
        map.put("courseAllInfoVoList",courseAllInfoVoList);
        map.put("tatol",eduCourseService.count());//添加数据总量
        return Result.ok().data("courseAllInfoVoMap",map);
    }
二、service
	/**
     * 分页查询课程信息
     * @param current 当前页
     * @param size  每页记录
     * @return  每页数据
     */
    @Override
    public List<CourseAllInfoVo> limitSelectCourseAllInfoVo(Long current, Long size) {
        //获取每页起始索引
        Long index=(current-1)*size;
        return eduCourseMapper.limitSelectCourseAllInfoVo(index,size);
    }
三、mapper
/**
     * 分页查询课程信息
     * @param index 起始索引
     * @param size  每页记录数
     * @return
     */
    public List<CourseAllInfoVo> limitSelectCourseAllInfoVo(Long index, Long size);
四、xml
<!--分页查询课程信息-->
    <select id="limitSelectCourseAllInfoVo" resultType="com.yzpnb.eduservice.entity.vo.CourseAllInfoVo">
        select
            ec.id as id,
            ec.title as title,
            ec.price as price,
            ec.lesson_num as lessonNum,
            ec.cover as cover,
            ec.status as status,
            et.`name` as teacherName,
            ecd.description as description,
            es1.title as oneTitle,
            es2.title as twoTitle
        from
            edu_course as ec
        left join
            edu_teacher as et
        on
            ec.teacher_id=et.id
        left join
            edu_course_description as ecd
        on
            ecd.id=ec.id
        left join
            edu_subject as es1
        on
            ec.subject_id=es1.id
        left join
            edu_subject as es2
        on
            ec.subject_parent_id=es2.id
        limit #{index},#{size}
    </select>

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第58张图片

2、测试

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第59张图片

2、(后端)实现删除课程

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第60张图片

1、controller(执需要编写controller即可)

/**
     * 删除课程,需要删除课程信息表,简介表,章节表,小节表
     */
    @Autowired
    EduCourseDescriptionService eduCourseDescriptionService;
    @Autowired
    EduChapterService eduChapterService;
    @Autowired
    EduVideoService eduVideoService;
    @ApiOperation("删除课程")
    @DeleteMapping("deleteCourseById/{id}")
    public Result deleteCourse(@ApiParam(name = "id",value = "课程id")
                               @PathVariable String id ){
        QueryWrapper queryWrapper=new QueryWrapper();
        queryWrapper.eq("course_id",id);
        
        //删除小节
        eduVideoService.remove(queryWrapper);
        //删除章节
        eduChapterService.remove(queryWrapper);
        //删除简介
        eduCourseDescriptionService.removeById(id);
        //删除课程信息
        eduCourseService.removeById(id);
        
        return Result.ok();
    }

2、(前端)实现分页展示课程信息

1、api接口

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第61张图片

2、组件

<template>
  <div>
    <!-- 折叠栏-->
    <div class="app-container">
     <el-collapse @change="handleChange" accordion>
       <el-collapse-item v-for="courseAllInfoVo in courseAllInfoVoList" :title="courseAllInfoVo.title" :name="courseAllInfoVo.id">
         <!-- 折叠内容-->
         <div class="ccInfo">
             <img :src="courseAllInfoVo.cover">
             <div class="main">
               <h2>{{ courseAllInfoVo.title}}&nbsp;&nbsp;&nbsp;&nbsp;
                    <el-switch
                      v-model="courseAllInfoVo.status==='Normal'?true:false"
                      active-color="#13ce66"
                      inactive-color="#ff4949"
                      @change="switchChange(courseAllInfoVo.id)">
                    </el-switch>{{courseAllInfoVo.status==='Normal'?"已发布":"未发布"}}
               </h2>
               <p class="gray"><span>{{ courseAllInfoVo.lessonNum }}课时</span></p>
               <p><span>所属分类:{{ courseAllInfoVo.oneTitle }}{{ courseAllInfoVo.twoTitle }}</span></p>
               <p>课程讲师:{{ courseAllInfoVo.teacherName }}</p>
               <h3 class="red">{{ courseAllInfoVo.price }}</h3>
             </div>

           </div>
          <!-- 操作按钮-->
          <el-button type="primary" @click="updateCourse(courseAllInfoVo.id)">修改信息</el-button>
          <el-button type="success" @click="updateChapterVideo(courseAllInfoVo.id)">编辑课程大纲</el-button>
          <el-button type="danger" @click="deleteCourse(courseAllInfoVo.id)">删除课程</el-button>
       </el-collapse-item>
     </el-collapse>
    </div>
      <!-- 分页-->
    <div class="block">
        <el-pagination
          @current-change="limitSelectCourseAllInfoVo"
          :current-page="current"
          :page-size="size"
          layout=" total ,prev, pager, next, jumper"
          :total="total">
        </el-pagination>
      </div>
  </div>
</template>

3、代码实现

<script>
import eduCourse from '@/api/course/eduCourse.js'
export default {
  data() {
    return {
      courseAllInfoVoList:null,    //数据
      /* 初始化变量*/
      current:1,//表示当前页
      size:5,   //每页记录数
      total:0,
      search:"",//搜索内容
    }
  },
  watch: {
  },
  created() {
    console.log('info created')
    this.init()
  },
  methods: {
    init() {
      this.limitSelectCourseAllInfoVo();
    },
    /* 分页查询课程信息*/
    limitSelectCourseAllInfoVo(current =1){
      this.current=current;
      eduCourse.limitSelectCourseAllInfoVo(this.current,this.size)
      .then(response=>{
          this.courseAllInfoVoList=response.data.courseAllInfoVoMap.courseAllInfoVoList;
          this.total=response.data.courseAllInfoVoMap.total;
      })
    },
    /* 点击折叠面板触发改变事件*/
    handleChange(){
      console.log("11");
    },
    /* 当改变课程发布状态是*/
    switchChange(id){
      this.updateStatus(id)
    },
    /* 根据id修改发布状态*/
    updateStatus(id){
      eduCourse.updateStatus(id)
      .then(response=>{
        //修改成功重新获取信息
        this.limitSelectCourseAllInfoVo();

        this.$message({//提示
          type: 'success',
          message: '修改发布状态!'
        })
      })
      //为了保险,多获取一次
      this.limitSelectCourseAllInfoVo();
    },
    /**=========================按钮==========================================**/
    /* 单击修改信息按钮后*/
    updateCourse(cid){
      this.$router.push({name:"课程添加",params:{id:cid}})
    },
    /* 单击编辑课程大纲按钮后*/
    updateChapterVideo(cid){
      this.$router.push({name:"课程添加2",params:{id:cid}})
    },
    /* 删除课程接口 and 单击删除课程按钮后*/
    deleteCourse(cid){
      this.$confirm('此操作将永久删除数据, 是否继续?', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
              }).then(() => {
                eduCourse.deleteCourseById(cid)
                .then(response=>{
                  this.$message({//提示
                    type: 'success',
                    message: '删除成功!'
                  })
                })
                //刷新页面(为了保险,多刷一次)
                this.limitSelectCourseAllInfoVo();
              }).catch(() => {
                this.$message({
                  type: 'info',
                  message: '已取消删除'
                })
              });
      //刷新页面(为了保险,多刷一次)
      this.limitSelectCourseAllInfoVo();
    }
},
}
</script>
<style>

  .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>

4、测试

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第62张图片

三、完善小节视频功能(阿里云视频点播)

1、阿里云视频点播

1、申请服务(请先登录,推荐使用支付宝扫码登录)

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第63张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第64张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第65张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第66张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第67张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第68张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第69张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第70张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第71张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第72张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第73张图片
在这里插入图片描述
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第74张图片

2、获取密钥(和OSS头像存储相同,上次已经有的就用上次的)

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第75张图片

3、文档(不推荐使用API,使用SDK更方便,因为是已经封装好的)

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第76张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第77张图片

4、开发分析

1、获取视频播放地址
	用于测试,可通过视频id获取
2、获取视频播放凭证
	如何不花钱申请域名也能播放加密视频?
	拥有凭证即可,可通过视频id获取
3、上传视频到阿里云点播
	我们上传的视频,如果你设置转码,它是会加密的。
	加密和未加密的区别在于,加密的视频url地址,是不能直接播放的,而未加密的视频地址是可以播放的
	而存储在阿里云中的每个视频都有唯一的一个id值
	所以我们在数据库中存这个id值,不存储视频地址,因为我们的视频不可以随便让别人看

2、阿里云视频点播环境搭建(此小节仅用于讲解)

1、引入依赖

<!--阿里云视频点播-->
 <dependency>
     <groupId>com.aliyun</groupId>
     <artifactId>aliyun-java-sdk-core</artifactId>
     <version>4.5.1</version>
 </dependency>
 <dependency>
     <groupId>com.aliyun</groupId>
     <artifactId>aliyun-java-sdk-vod</artifactId>
     <version>2.15.8</version>
 </dependency>
 <dependency>
     <groupId>com.google.code.gson</groupId>
     <artifactId>gson</artifactId>
     <version>2.8.6</version>
 </dependency>
 <!--阿里云OSS依赖 这个还非得3.1.0版本的-->
 <dependency>
     <groupId>com.aliyun.oss</groupId>
     <artifactId>aliyun-sdk-oss</artifactId>
     <version>3.1.0</version>
 </dependency>
 <!--阿里云视频上传-->
 <dependency>
     <groupId>com.aliyun</groupId>
     <artifactId>aliyun-java-vod-upload</artifactId>
     <version>1.4.12</version>
 </dependency>
 <!--阿里巴巴fastjson-->
 <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>fastjson</artifactId>
     <version>1.2.68</version>
 </dependency>
 <dependency>
     <groupId>org.json</groupId>
     <artifactId>json</artifactId>
     <version>20200518</version>
 </dependency>

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第78张图片

2、阿里云视频点播基本使用方法介绍(先了解即可,之后会进入到实战)

1、初始化

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第79张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第80张图片

2、获取视频播放地址

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第81张图片

package com.test;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.vod.model.v20170321.GetPlayInfoRequest;
import com.aliyuncs.vod.model.v20170321.GetPlayInfoResponse;

import java.util.*;
public class Test {
/**我这里不使用这个方法**/
//    /*获取播放地址函数*/
//    public static GetPlayInfoResponse getPlayInfo(DefaultAcsClient client) throws Exception {
//        GetPlayInfoRequest request = new GetPlayInfoRequest();
//        request.setVideoId("视频ID");
//        return client.getAcsResponse(request);
//    }

    public static void main(String[] args) throws ClientException {
        /**1、根据视频id获取视频播放地址**/
        //1、创建初始化对象
        DefaultAcsClient client = InitVodClient.initVodClient("你的id", "你的密钥");

        //2、创建获取视频地址request请求对象和response响应对象
        GetPlayInfoRequest request=new GetPlayInfoRequest();
        GetPlayInfoResponse response = new GetPlayInfoResponse();

        //3、设置请求的视频id(从你的阿里云中找)
        request.setVideoId("你视频的id");

        //4、 根据初始化对象,获取数据,返回一个响应体对象
        response = client.getAcsResponse(request);//返回GetPlayInfoResponse对象response,里面封装了视频的所有信息

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

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第82张图片

3、获取视频凭证

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第83张图片

/***获取播放凭证***/
        GetVideoPlayAuthRequest requestAuth = new GetVideoPlayAuthRequest();
        GetVideoPlayAuthResponse responseAuth = new GetVideoPlayAuthResponse();

        requestAuth.setVideoId("你的视频id");

        responseAuth=client.getAcsResponse(requestAuth);
        //播放凭证
        System.out.println("PlayAuth = " + responseAuth.getPlayAuth() + "\n");

4、删除视频

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第84张图片

/**
     * 删除视频
     * @param client 发送请求客户端
     * @param idList 用户要删除的视频id,可以传多个
     * @throws Exception
     */
    public static void deleteVideo(DefaultAcsClient client,String ...idList) throws Exception {
        DeleteVideoRequest request = new DeleteVideoRequest();
        DeleteVideoResponse response = new DeleteVideoResponse();
        StringBuffer stringBuffer=new StringBuffer();

        for (String id:idList) {
            stringBuffer.append(id + ",");//将所有视频id用逗号拼接
        }
        stringBuffer.deleteCharAt(stringBuffer.length()-1);//删除最后多余的逗号

        //支持传入多个视频ID,多个用逗号分隔 request.setVideoIds("VideoId1,VideoId2");
        request.setVideoIds(stringBuffer.toString());

        try {
            response=client.getAcsResponse(request);
        } catch (Exception e) {
            System.out.print("ErrorMessage = " + e.getLocalizedMessage());
        }
        System.out.print("RequestId = " + response.getRequestId() + "\n");

    }

5、上传视频到阿里云(这里你需要学会如何引入jar包依赖)

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第85张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第86张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第87张图片

上传需要的依赖
<!--阿里云OSS依赖 这个还非得3.1.0版本的-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.1.0</version>
        </dependency>
        <!--阿里云视频上传-->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-vod-upload</artifactId>
            <version>1.4.12</version>
        </dependency>
        <!--阿里巴巴fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.68</version>
        </dependency>
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20200518</version>
        </dependency>

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第88张图片

代码测试
package com.test;

import com.aliyun.vod.upload.impl.UploadVideoImpl;
import com.aliyun.vod.upload.req.UploadVideoRequest;
import com.aliyun.vod.upload.resp.UploadVideoResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.vod.model.v20170321.*;

import java.util.*;
public class Test {



    public static void main(String[] args) throws Exception {
        String accessKeyId="你的id";//id
        String accessKeySecret="你的密钥";//密钥


        /**1、根据视频id获取视频播放地址**/
        //1、创建初始化对象
        DefaultAcsClient client = InitVodClient.initVodClient(accessKeyId, accessKeySecret);
        //2、调用方法获取播放地址
//        Test.getPlayInfo(client,"11e7ce8051b948ddba24bd39d73a41ae");
        //3、调用方法获取播放凭证
//        Test.getVideoPlayAuth(client,"11e7ce8051b948ddba24bd39d73a41ae");
        //4、调用方法上传视频
        Test.testUploadVideo(accessKeyId, accessKeySecret,"testVideo","E:/flash/课程资料/1-阿里云上传测试视频/6 - What If I Want to Move Faster.mp4");
        //5、调用删除视频方法
        // Test.deleteVideo(client,"f296d3de53904917b5656db7320872f2");
    }
    /**
     * 本地文件上传接口
     *
     * @param accessKeyId
     * @param accessKeySecret
     * @param title 上传之后文件名
     * @param fileName  本地文件的路径和名称,就是你要上传的文件路径
     */
    private static void testUploadVideo(String accessKeyId, String accessKeySecret, String title, String fileName) {
        UploadVideoRequest request = new UploadVideoRequest(accessKeyId, accessKeySecret, title, fileName);
        /* 可指定分片上传时每个分片的大小,默认为1M字节 */
        request.setPartSize(1 * 1024 * 1024L);
        /* 可指定分片上传时的并发线程数,默认为1,(注:该配置会占用服务器CPU资源,需根据服务器情况指定)*/
        request.setTaskNum(1);
        /* 是否开启断点续传, 默认断点续传功能关闭。当网络不稳定或者程序崩溃时,再次发起相同上传请求,可以继续未完成的上传任务,适用于超时3000秒仍不能上传完成的大文件。
        注意: 断点续传开启后,会在上传过程中将上传位置写入本地磁盘文件,影响文件上传速度,请您根据实际情况选择是否开启*/
        request.setEnableCheckpoint(false);


        UploadVideoImpl uploader = new UploadVideoImpl();
        UploadVideoResponse response = uploader.uploadVideo(request);
        System.out.print("RequestId=" + response.getRequestId() + "\n");  //请求视频点播服务的请求ID

        if (response.isSuccess()) {
            /****如果上传成功就将视频id返回****/
            System.out.print("VideoId=" + response.getVideoId() + "\n");
        } else {
            /* 如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因 */
            System.out.print("VideoId=" + response.getVideoId() + "\n");
            System.out.print("ErrorCode=" + response.getCode() + "\n");
            System.out.print("ErrorMessage=" + response.getMessage() + "\n");
        }
    }

    /*获取播放地址函数*/
    public static void getPlayInfo(DefaultAcsClient client,String id) throws Exception {
        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");
    }
    /*获取播放凭证函数*/
    public static void getVideoPlayAuth(DefaultAcsClient client,String id) throws Exception {
        /***获取播放凭证***/
        GetVideoPlayAuthRequest requestAuth = new GetVideoPlayAuthRequest();
        GetVideoPlayAuthResponse responseAuth = new GetVideoPlayAuthResponse();

        requestAuth.setVideoId(id);

        responseAuth=client.getAcsResponse(requestAuth);
        //播放凭证
        System.out.println("PlayAuth = " + responseAuth.getPlayAuth() + "\n");
    }
    /**
     * 删除视频
     * @param client 发送请求客户端
     * @return DeleteVideoResponse 删除视频响应数据
     * @throws Exception
     */
    public static void deleteVideo(DefaultAcsClient client,String ...idList) throws Exception {
        DeleteVideoRequest request = new DeleteVideoRequest();
        DeleteVideoResponse response = new DeleteVideoResponse();
        StringBuffer stringBuffer=new StringBuffer();

        for (String id:idList) {
            stringBuffer.append(id + ",");//将所有视频id用逗号拼接
        }
        stringBuffer.deleteCharAt(stringBuffer.length()-1);//删除最后多余的逗号

        //支持传入多个视频ID,多个用逗号分隔 request.setVideoIds("VideoId1,VideoId2");
        request.setVideoIds(stringBuffer.toString());

        try {
            response=client.getAcsResponse(request);
        } catch (Exception e) {
            System.out.print("ErrorMessage = " + e.getLocalizedMessage());
        }
        System.out.print("RequestId = " + response.getRequestId() + "\n");

    }
}

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第89张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第90张图片

6、已上传视频转码(注意是给已经上传的视频,而我们上传的时候,可以通过设置参数直接上传转码视频)

/**
     * 提交媒体处理作业
     */
    public static SubmitTranscodeJobsResponse submitTranscodeJobs(DefaultAcsClient client,String id) throws Exception {
        SubmitTranscodeJobsRequest request = new SubmitTranscodeJobsRequest();
        //需要转码的视频ID
        request.setVideoId(id);
        //转码模板ID
        request.setTemplateGroupId("fc4c9920e7332c6c8d9518d4c00aaf54");
        return client.getAcsResponse(request);
    }

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第91张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第92张图片

3、(后端)小节视频上传(实现转码上传,并返回视频id)

1、搭建微服务环境(根据图片创建目录结构)

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第93张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第94张图片

2、视频点播工具类

package com.yzpnb.video.util;


import com.aliyun.vod.upload.impl.UploadVideoImpl;
import com.aliyun.vod.upload.req.UploadStreamRequest;
import com.aliyun.vod.upload.resp.UploadStreamResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.vod.model.v20170321.*;

import java.io.InputStream;
import java.util.List;

public class AliyunVideoUtil {
    private String accessKeyId="LTAI4GGf4cjh4zZdNeKqUUGw";//id
    private String accessKeySecret="oQpda38y0cCUql5TOmaYiSJYDz32OP";//密钥
    private DefaultAcsClient client;
    /**
     * 初始化方法
     * @param accessKeyId 你的证书id
     * @param accessKeySecret   你的密钥
     * @return
     * @throws ClientException
     */
    public DefaultAcsClient initVodClient(String accessKeyId, String accessKeySecret) throws ClientException {
        String regionId = "cn-shanghai";  // 点播服务接入区域
        DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
        DefaultAcsClient client = new DefaultAcsClient(profile);
        return client;
    }

    /**
     * 本地文件上传接口,并使用模板组转码(流形式)
     *
     * @paramT accessKeyId 已经封装到工具类,无需声明
     * @paramT accessKeySecret 已经封装到工具类,无需声明
     * @param title 上传之后文件名
     * @param fileName  本地文件的路径和名称,就是你要上传的文件路径
     * @param inputStream 上传文件流
     */
    public String uploadVideo(String title, String fileName, InputStream inputStream) {
        /**设置上传请求头,包含转码*/
        UploadStreamRequest request = new UploadStreamRequest(accessKeyId, accessKeySecret, title, fileName,inputStream);
        /* 视频分类ID(可选) */
        //request.setCateId(0);
        /* 模板组ID(可选) */
        request.setTemplateGroupId("fc4c9920e7332c6c8d9518d4c00aaf54");

        /**执行流上传,获取响应体*/
        UploadVideoImpl uploader = new UploadVideoImpl();
        UploadStreamResponse response = uploader.uploadStream(request);


        if (response.isSuccess()) {
            /* 如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因 */
            if(response.getVideoId()!=null) return response.getVideoId();
        }
            /****如果上传成功就将视频id返回****/
            return response.getVideoId();
    }

    /**
     * 提交媒体处理作业,视频转码
     */
    public SubmitTranscodeJobsResponse submitTranscodeJobs(String id) throws Exception {
        client=initVodClient(accessKeyId,accessKeySecret);

        SubmitTranscodeJobsRequest request = new SubmitTranscodeJobsRequest();
        //需要转码的视频ID
        request.setVideoId(id);
        //转码模板ID
        request.setTemplateGroupId("fc4c9920e7332c6c8d9518d4c00aaf54");
        return client.getAcsResponse(request);
    }

    /*获取播放地址函数*/
    public static void getPlayInfo(DefaultAcsClient client,String id) throws Exception {
        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");
    }
    /*获取播放凭证函数*/
    public static void getVideoPlayAuth(DefaultAcsClient client,String id) throws Exception {
        /***获取播放凭证***/
        GetVideoPlayAuthRequest requestAuth = new GetVideoPlayAuthRequest();
        GetVideoPlayAuthResponse responseAuth = new GetVideoPlayAuthResponse();

        requestAuth.setVideoId(id);

        responseAuth=client.getAcsResponse(requestAuth);
        //播放凭证
        System.out.println("PlayAuth = " + responseAuth.getPlayAuth() + "\n");
    }
    /**
     * 删除视频
     * @param client 发送请求客户端
     * @return DeleteVideoResponse 删除视频响应数据
     * @throws Exception
     */
    public static void deleteVideo(DefaultAcsClient client,String ...idList) throws Exception {
        DeleteVideoRequest request = new DeleteVideoRequest();
        DeleteVideoResponse response = new DeleteVideoResponse();
        StringBuffer stringBuffer=new StringBuffer();

        for (String id:idList) {
            stringBuffer.append(id + ",");//将所有视频id用逗号拼接
        }
        stringBuffer.deleteCharAt(stringBuffer.length()-1);//删除最后多余的逗号

        //支持传入多个视频ID,多个用逗号分隔 request.setVideoIds("VideoId1,VideoId2");
        request.setVideoIds(stringBuffer.toString());

        try {
            response=client.getAcsResponse(request);
        } catch (Exception e) {
            System.out.print("ErrorMessage = " + e.getLocalizedMessage());
        }
        System.out.print("RequestId = " + response.getRequestId() + "\n");

    }

}

3、application.yml

server:
  port: 8003 #微服务端口号
spring:
  application:
    name: service-video #服务名
  profiles:
    active: dev #环境
  servlet:
    multipart:
      max-file-size: 1024MB #设置单个文件最大上传大小限制
      max-request-size: 1024MB #设置总体文件最大上传大小限制

4、启动类

package com.yzpnb.video;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//这里不使用数据库,所以需要取消自动扫描数据源
@ComponentScan(basePackages = {"com.yzpnb"})//自动扫描,扫描的包下必须有子目录
public class VideoApplication {
    public static void main(String[] args) {
        SpringApplication.run(VideoApplication.class);
    }
}

5、service(这是实现类(需要使用注解注入),需要你自己在接口中补上定义)

package com.yzpnb.video.service.impl;

import com.yzpnb.video.service.VideoService;
import com.yzpnb.video.util.AliyunVideoUtil;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;

@Service
public class VideoServiceImpl implements VideoService {
    /**
     *
     * 上传本地视屏到阿里云
     * @param videoFile
     * @return
     */
    @Override
    public String uploadVideo(MultipartFile videoFile) {
        /**1、上传**/
        //1、创建封装阿里云视频操作的对象
        AliyunVideoUtil aliyunVideoUtil=new AliyunVideoUtil();
        
        String videoId = null;
        //2、获取输入流,文件名等等
        try {
            InputStream inputStream = videoFile.getInputStream();
            String fileName=videoFile.getOriginalFilename();//获取文件名
            /**
             * 设置上传文件名,获取文件名中第一个字符到“.”的前一个字符
             * substring:字符串截取函数,截取起始索引到末尾索引的前一个字符
             * lastIndexOf:从字符串最后面往前找,找我们指定的字符,找到的第一匹配的字符,返回字符的下标
             */
            String title=fileName.substring(0,fileName.lastIndexOf("."));
            videoId=aliyunVideoUtil.uploadVideo(title,fileName,inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return videoId;
    }
}

6、controller

package com.yzpnb.video.controller;

import com.yzpnb.common_utils.Result;
import com.yzpnb.video.service.VideoService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/videoservice/")
@CrossOrigin
public class VideoController {

    @Autowired
    VideoService videoService;

    @ApiOperation("上传视频(以流形式上传,并且直接转码)")
    @PostMapping("uploadVideo")
    public Result uploadVideo(@ApiParam(name = "videoFile",value = "用户上传的文件") MultipartFile videoFile){
        String videoId=videoService.uploadVideo(videoFile);
        return  Result.ok().data("videoId",videoId);
    }
}

7、测试

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第95张图片

4、(前端)小节视频上传

如果你配置了nginx,需要先配置代理
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第96张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第97张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第98张图片

1、组件

<el-form-item label="上传视频">
            <el-upload
                   :on-success="handleVodUploadSuccess"
                   :on-remove="handleVodRemove"
                   :before-remove="beforeVodRemove"
                   :on-exceed="handleUploadExceed"
                   :file-list="fileList"
                   :action="BASE_API+'/videoservice/uploadVideo'"
                   :limit="1"
                   class="upload-demo">
            <el-button size="small" type="primary">上传视频</el-button>
            <el-tooltip placement="right-end">
                <div slot="content">最大支持1G,<br>
                    支持3GP、ASFAVIDATDVFLVF4V<br>
                    GIFM2TM4VMJ2MJPEGMKVMOVMP4<br>
                    MPEMPGMPEGMTSOGGQTRMRMVB<br>
                    SWFTSVOBWMVWEBM 等视频格式上传</div>
                <i class="el-icon-question"/>
            </el-tooltip>
            </el-upload>
        </el-form-item>

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第99张图片

2、代码实现

const defaultVideo={  //封装小节默认数据
        title:'1',       //小节名称
        sort:1,
        courseId:0,     //课程id
        chapterId:0,    //章节id
        play_count:0,   //播放次数
        isFree:1,       //是否可以试听,0收费,1免费
        duration:0,     //视频时长
        status:'',      //视频转码 Transcoding转码中  Normal正常
        size:0,         //视频源文件字节
        videoSourceId:'',//阿里云视频id
        videoOriginalName:'',//源文件名
  };
  data() {
    return {
      saveBtnDisabled: false ,        // 保存按钮是否禁用
      list:[],                        //数据
      dialogChapterFormVisible:false, //章节弹窗初始不显示
      dialogVideoFormVisible:false,   //小节弹窗初始不显示
      saveVideoBtnDisabled:false,     //小节弹窗中确定按钮是否可用
      chapter:defaultChapter,         //封装章节数据
      video:defaultVideo,             //封装小节数据
      flag:0,                         //用于判断是添加还是修改操作,0为添加,其它值为修改(因为我会把需要修改的章节id传过来)
      videoFlag:0,                    //用于判断小节是添加还是修改操作
      dialogTitle:"添加章节",
      videoDialogTitle:"添加小节",
      BASE_API:process.env.VUE_APP_BASE_API,//将http://localhost:9001赋值给BASE_API
      fileList:[],
    }

  },
  /**===================================视频上传操作==============================================**/
  //上传成功回调
  handleVodUploadSuccess(response, file, fileList) {
    this.video.videoSourceId = response.data.videoId//为数据库的阿里云视频id赋值
    this.video.videoOriginalName=file.name;//将组件中上传文件的名称给video中赋值
  },
  //上传开始之前调用,视图上传多于一个视频
  handleUploadExceed(files, fileList) {
    this.$message.warning('想要重新上传视频,请先删除已上传的视频')
  },
  /* 删除视频时调用*/
  handleVodRemove(){},
  /* 删除之前调用*/
  beforeVodRemove(){},

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第100张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第101张图片

3、测试报错

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第102张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第103张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第104张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第105张图片微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第106张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第107张图片

5、删除视频以及删除小节时删除对应视频

1、后端

一、工具类:


/**
     * 删除视频
     * @paramT client 发送请求客户端
     * @return DeleteVideoResponse 删除视频响应数据
     * @throws Exception
     */
    public void deleteVideo(String ...idList) throws Exception {
        client=initVodClient(accessKeyId,accessKeySecret);
        DeleteVideoRequest request = new DeleteVideoRequest();
        DeleteVideoResponse response = new DeleteVideoResponse();
        StringBuffer stringBuffer=new StringBuffer();

        for (String id:idList) {
            stringBuffer.append(id + ",");//将所有视频id用逗号拼接
        }
        stringBuffer.deleteCharAt(stringBuffer.length()-1);//删除最后多余的逗号

        //支持传入多个视频ID,多个用逗号分隔 request.setVideoIds("VideoId1,VideoId2");
        request.setVideoIds(stringBuffer.toString());

        try {
            response=client.getAcsResponse(request);
        } catch (Exception e) {
            System.out.print("ErrorMessage = " + e.getLocalizedMessage());
        }
        System.out.print("RequestId = " + response.getRequestId() + "\n");

    }

二、controller


@DeleteMapping("{videoId}")
    public Result removeVideo(@ApiParam(name = "videoId", value = "云端视频id", required = true)
                              @PathVariable String videoId){
        videoService.removeVideo(videoId);
        return Result.ok().message("视频删除成功");
    }

三、service

 /**
     * 根据id删除视频
     * @param videoId
     */
    @Override
    public void removeVideo(String videoId) {
        //1、创建封装阿里云视频操作的对象
        AliyunVideoUtil aliyunVideoUtil=new AliyunVideoUtil();
        //2、删除视频
        try {
            aliyunVideoUtil.deleteVideo(videoId);
        } catch (Exception e) {
            throw new CustomExceptionHandler(20001,"删除失败");
        }
    }

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第108张图片

2、前端

一、api接口
/* 删除小节视频*/
  removeById(id) {
      return request({
        url: `/videoservice/${id}`,
        method: 'delete'
      })
    },
二、写方法
/* 删除之前调用,也就是点击了删除按钮,这时弹窗提示用户是否确定删除*/
  beforeVodRemove(file, fileList) {
    return this.$confirm(`确定移除 ${file.name}?`)
  },
  /* 删除视频时调用,当用户点击了删除按钮后,又点击了弹窗中的确定按钮,则调用此方法*/
  handleVodRemove(file, fileList) {
    console.log(file)
    eduCourse.removeById(this.video.videoSourceId).then(response=>{
      this.$message({
        type: 'success',
        message: response.message
      })
      this.video.videoSourceId = ''//为数据库的阿里云视频id赋值
      this.video.videoOriginalName=''//将组件中上传文件的名称给video中赋值
    })
  },

微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第109张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第110张图片
微服务项目实战技术点汇总:“尚硅谷的谷粒在线教育”五、阿里云视频点播,完善课程管理模块,(集成富文本插件,大纲管理,信息确认回显,发布完成),手写sql_第111张图片

你可能感兴趣的:(微服务实战)