当前位置:   article > 正文

day08-硅谷课堂-腾讯云点播管理模块(二)

day08-硅谷课堂-腾讯云点播管理模块(二)

硅谷课堂第八天-点播管理模块(二)

一、发布课程-创建课程大纲

在这里插入图片描述


在这里插入图片描述
重点就放在章节和小节的接口上
看看大纲的数据JSON结构:
数组结构的JSON,明显是返回List集合
在这里插入图片描述

1、课程章节接口

实现课程章节的列表、添加、修改和删除功能

1.1、编写章节Controller
@RestController
@RequestMapping("/admin/vod/chapter")
@CrossOrigin
@Api(tags = "章节管理接口")
public class ChapterController {

    @Autowired
    private ChapterService chapterService;

    //1大纲列表(章节和小节列表也叫课时)树形结构显示  截图json数据格式(是个数组。对应java中list集合)children是小节的集合
    /**
     *   第一章
     *       第一节
     *       第二节
     *   第二章
     *       第一节
     *       第二节
     **/
    @ApiOperation("大纲列表")
    @GetMapping("getNestedTreeList/{courseId}")
    public Result getTreeList(@PathVariable Long courseId){
        //看看chapterVo有什么属性,他里面还用到了videoVO这是小节(课时)里面的内容  看看course表跟chapter表字段,还有那张图的映射关系
        List<ChapterVo> list=chapterService.getTreeList(courseId);
        return Result.ok(list);
    }

    //2添加章节
    @ApiOperation("添加章节")
    @PostMapping("save")
    public Result save(@RequestBody Chapter chapter){
        chapterService.save(chapter);
        return Result.ok(null);

    }

    //3修改-根据id查询
    @ApiOperation("根据id查询章节信息")
    @GetMapping("get/{id}")
    public Result get(@PathVariable Long id){
        Chapter chapter=chapterService.getById(id);
        return Result.ok(chapter);
    }

    //4修改实现(@putMapping也行,resultufl风格推荐用putMapping)
    @PostMapping("update")
    public Result update(@RequestBody Chapter chapter){
        chapterService.updateById(chapter);
        return Result.ok(null);
    }

    //5删除章节(这里好像只删除了章节没有删除里面的小节,后边腾讯云点播管理模块三完善了该功能)
    @DeleteMapping("remove/{id}")
    public Result remove(@PathVariable Long id){
        chapterService.removeById(id);
        return Result.ok(null);
    }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);而最常用的使用请求体传参的无疑是POST请求了,所以使用@RequestBody接收数据时,一般都用POST方式进行提交。在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。

mybatisplus实现逻辑删除:mybatisplus使用

json格式:
在这里插入图片描述
ChapterVo(章节对象)

@ApiModel("课程章节对象")
@Data
public class ChapterVo {

    @ApiModelProperty(value = "章节ID")
    private Long id;
    @ApiModelProperty(value = "章节标题")
    private String title;
    @ApiModelProperty(value = "排序")
    private Integer sort;
    @ApiModelProperty(value = "章节下的课时列表")
    //小节(课时)用VideoVo进行封装
    private List<VideoVo> children = new ArrayList<>();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

VideoVo(小节也是课时对象)

@ApiModel("课时信息")
@Data
public class VideoVo {
    @ApiModelProperty(value = "课时ID")
    private Long id;
    @ApiModelProperty(value = "课时标题")
    private String title;
    @ApiModelProperty(value = "是否可以试听")
    private Integer isFree;
    @ApiModelProperty(value = "排序")
    private Integer sort;
    @ApiModelProperty(value = "腾讯云视频ID")
    private String videoSourceId;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

course表:
在这里插入图片描述
chapter表:
在这里插入图片描述
表关系:
在这里插入图片描述

效果图:
在这里插入图片描述

1.2、编写章节Service

(1)ChapterService

public interface ChapterService extends IService<Chapter> {
   1大纲列表(章节和小节列表也叫课时)树形结构显示
    List<ChapterVo> getTreeList(Long courseId);
}
  • 1
  • 2
  • 3
  • 4

(2)ChapterServiceImpl

@Service
public class ChapterServiceImpl extends ServiceImpl<ChapterMapper, Chapter> implements ChapterService {

    @Autowired
    private VideoService videoService;

    //章节小结列表封装
     @Override
    public List<ChapterVo> getTreeList(Long courseId) {
        //定义最终数据List集合
        List<ChapterVo>finalChapterList=new ArrayList<>();
        //1根据courseId获取课程所有章节
        QueryWrapper<Chapter>wrapperChapter=new QueryWrapper<>();
        wrapperChapter.eq("course_id",courseId);
        //如果是自己的实现类就用baseMapper去查询,不是自己的就调用
        List<Chapter>chapterList=baseMapper.selectList(wrapperChapter);

        //2根据courseId获取课程里面所有小节(课时) video是小节内容
        // 利用LambdaQueryWrapper写方便复习一下之前的知识
        //其实等同于上边获取课程章节的方法,这里使用java8实现   查询一下::的意义
        LambdaQueryWrapper<Video> wrapperVideo=new LambdaQueryWrapper<>();
        wrapperVideo.eq(Video::getCourseId,courseId);
        List<Video> videoList=videoService.list(wrapperVideo);

        //封装章节(记住一个原则遇到集合要么遍历要么return)
        //遍历所有章节  自己写的话定义一个sql语句也能实现这是一个嵌套循环,多多理解去
        for (int i = 0; i <chapterList.size() ; i++) {
            //得到课程每个章节
            Chapter chapter=chapterList.get(i);
            // (该集合的泛型为ChapterVo而我们得到课程每个章节为chapter所以要它转换为chapervo中)
            ChapterVo chapterVo=new ChapterVo();
            BeanUtils.copyProperties(chapter,chapterVo);
            //得到每个章节对象放到finalChaperList集合
            finalChapterList.add(chapterVo);

            //封装章节里的小节
            //创建List集合用户封装章节里的所有小节
            List<VideoVo>videoVoList=new ArrayList<>();
            //遍历小节List
            for (Video video:videoList) {
                //判断小节是哪个章节下面的
                //章节id 和小节chapter_id相同则为一个章节
                if(chapter.getId().equals(video.getChapterId())){
                    //video--videoVo跟上边同理
                    VideoVo videoVo=new VideoVo();
                    BeanUtils.copyProperties(video,videoVo);
                    //放到videoVoList
                    videoVoList.add(videoVo);
                }
            }
            //把章节里面所有小节集合放到每个章节里面
            chapterVo.setChildren(videoVoList);
        }
        return finalChapterList;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

知识补充:
java8中可以通过 “::” 关键字来访问类的构造方法,对象方法,静态方法

ChapterVo:

@ApiModel("课程章节对象")
@Data
public class ChapterVo {

    @ApiModelProperty(value = "章节ID")
    private Long id;
    @ApiModelProperty(value = "章节标题")
    private String title;
    @ApiModelProperty(value = "排序")
    private Integer sort;
    @ApiModelProperty(value = "章节下的课时列表")
    //小节(课时)用VideoVo进行封装
    private List<VideoVo> children = new ArrayList<>();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
2、课程小节接口
2.1、编写VideoController
@RestController
@RequestMapping("/admin/vod/video")
@CrossOrigin
@Api(tags = "小节管理接口")
public class VideoController {
    @Autowired
    private VideoService videoService;
    //根据id查询小节
    @ApiOperation(value = "获取")
    @GetMapping("get/{id}")
    public Result get(@PathVariable Long id) {
        Video video = videoService.getById(id);
        return Result.ok(video);
    }
    //小节新增
    @ApiOperation(value = "新增")
    @PostMapping("save")
    public Result save(@RequestBody Video video) {
        videoService.save(video);
        return Result.ok(null);
    }
    //小节修改
    @ApiOperation(value = "修改")
    @PutMapping("update")
    public Result updateById(@RequestBody Video video) {
        videoService.updateById(video);
        return Result.ok(null);
    }
    //小节删除
    @ApiOperation(value = "删除")
    @DeleteMapping("remove/{id}")
    public Result remove(@PathVariable Long id) {
        videoService.removeById(id);
        return Result.ok(null);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
3、课程大纲前端
3.1、定义接口

(1)chapter.js

import request from '@/utils/request'

const api_name = '/admin/vod/chapter'

export default {

  getNestedTreeList(courseId) {
    return request({
      url: `${api_name}/getNestedTreeList/${courseId}`,
      method: 'get'
    })
  },

  removeById(id) {
    return request({
      url: `${api_name}/remove/${id}`,
      method: 'delete'
    })
  },

  save(chapter) {
    return request({
      url: `${api_name}/save`,
      method: 'post',
      data: chapter
    })
  },

  getById(id) {
    return request({
      url: `${api_name}/get/${id}`,
      method: 'get'
    })
  },

  updateById(chapter) {
    return request({
      url: `${api_name}/update`,
      method: 'post',
      data: chapter
    })
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

(2)创建video.js

import request from '@/utils/request'

const api_name = '/admin/vod/video'

export default {

  save(video) {
    return request({
      url: `${api_name}/save`,
      method: 'post',
      data: video
    })
  },

  getById(id) {
    return request({
      url: `${api_name}/get/${id}`,
      method: 'get'
    })
  },

  updateById(video) {
    return request({
      url: `${api_name}/update`,
      method: 'put',
      data: video
    })
  },

  removeById(id) {
    return request({
      url: `${api_name}/remove/${id}`,
      method: 'delete'
    })
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
3.2、编写章节页面

(1)Chapter -> index.vue

<template>
  <div class="app-container">

    <!-- 添加章节按钮 -->
    <div>
      <el-button type="primary" @click="addChapter()">添加章节</el-button>
    </div>

    <!-- 章节列表 -->
    <ul class="chapterList">
      <li
        v-for="chapter in chapterList"
        :key="chapter.id">
        <p>
          {{ chapter.title }}
          <span class="acts">
            <el-button type="text" @click="addVideo(chapter.id)">添加课时</el-button>
            <el-button type="text" @click="editChapter(chapter.id)">编辑</el-button>
            <el-button type="text" @click="removeChapterById(chapter.id)">删除</el-button>
          </span>
        </p>
        <!-- 视频 -->
        <ul class="chapterList videoList">
          <li
            v-for="video in chapter.children"
            :key="video.id">
            <p>
              {{ video.title }}
              <el-tag v-if="!video.videoSourceId" size="mini" type="danger">
                {{ '尚未上传视频' }}
              </el-tag>
              <span class="acts">
                <el-tag v-if="video.isFree" size="mini" type="success">{{ '免费观看' }}</el-tag>
                <el-button type="text" @click="editVideo(chapter.id, video.id)">编辑</el-button>
                <el-button type="text" @click="removeVideoById(video.id)">删除</el-button>
              </span>
            </p>
          </li>
        </ul>
      </li>
    </ul>
    <!-- 章节表单对话框 -->
    <chapter-form ref="chapterForm" />
    <!-- 课时表单对话框 -->
    <video-form ref="videoForm" />
    <div style="text-align:center">
      <el-button type="primary" @click="prev()">上一步</el-button>
      <el-button type="primary" @click="next()">下一步</el-button>
    </div>
  </div>
</template>
<script>
import chapterApi from '@/api/vod/chapter'
import videoApi from '@/api/vod/video'

// 引入组件
import ChapterForm from '@/views/vod/course/components/Chapter/Form'
import VideoForm from '@/views/vod/course/components/Video/Form'

export default {
  // 注册组件
  components: { ChapterForm, VideoForm },
  data() {
    return {
      chapterList: [] // 章节嵌套列表
    }
  },
  created() {
    this.fetchNodeList()
  },
  methods: {
    // 获取章节小节数据
    fetchNodeList() {
      chapterApi.getNestedTreeList(this.$parent.courseId).then(response => {
        this.chapterList = response.data
      })
    },
    //删除章节
    removeChapterById(chapterId) {
      this.$confirm('此操作将永久删除该章节,是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        return chapterApi.removeById(chapterId)
      }).then(response => {
        this.fetchNodeList()
        this.$message.success(response.message)
      }).catch((response) => {
        if (response === 'cancel') {
          this.$message.info('取消删除')
        }
      })
    },
    // 添加章节
    addChapter() {
      this.$refs.chapterForm.open()
    },
    // 编辑章节
    editChapter(chapterId) {
      this.$refs.chapterForm.open(chapterId)
    },
    // 添加课时
    addVideo(chapterId) {
      this.$refs.videoForm.open(chapterId)
    },
    // 编辑课时
    editVideo(chapterId, videoId) {
      this.$refs.videoForm.open(chapterId, videoId)
    },
    // 删除课时
    removeVideoById(videoId) {
      this.$confirm('此操作将永久删除该课时, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        return videoApi.removeById(videoId)
      }).then(response => {
        this.fetchNodeList()
        this.$message.success(response.message)
      }).catch((response) => {
        if (response === 'cancel') {
          this.$message.info('取消删除')
        }
      })
    },
    // 上一步
    prev() {
      this.$parent.active = 0
    },
    // 下一步
    next() {
      this.$parent.active = 2
    }
  }
}
</script>
<style scoped>
.chapterList{
    position: relative;
    list-style: none;
    margin: 0;
    padding: 0;
}
.chapterList li{
  position: relative;
}
.chapterList p{
  float: left;
  font-size: 20px;
  margin: 10px 0;
  padding: 10px;
  height: 70px;
  line-height: 50px;
  width: 100%;
  border: 1px solid #DDD;
}
.chapterList .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 dashed #DDD;
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177

(2)Chapter -> Form.vue

<template>
    <!-- 添加和修改章节表单 -->
    <el-dialog :visible="dialogVisible" title="添加章节" @close="close()">
      <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"/>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="close()">取 消</el-button>
        <el-button type="primary" @click="saveOrUpdate()">确 定</el-button>
      </div>
    </el-dialog>
  </template>
  
  <script>
  import chapterApi from '@/api/vod/chapter'
  export default {
  
    data() {
      return {
        dialogVisible: false,
        chapter: {
          sort: 0
        }
      }
    },
    methods: {
      open(chapterId) {
        this.dialogVisible = true
        if (chapterId) {
          chapterApi.getById(chapterId).then(response => {
            this.chapter = response.data
          })
        }
      },
  
      close() {
        this.dialogVisible = false
        // 重置表单
        this.resetForm()
      },
  
      resetForm() {
        this.chapter = {
          sort: 0
        }
      },
  
      saveOrUpdate() {
        if (!this.chapter.id) {
          this.save()
        } else {
          this.update()
        }
      },
  
      save() {
        this.chapter.courseId = this.$parent.$parent.courseId
        chapterApi.save(this.chapter).then(response => {
          this.$message.success(response.message)
          // 关闭组件
          this.close()
          // 刷新列表
          this.$parent.fetchNodeList()
        })
      },
  
      update() {
        chapterApi.updateById(this.chapter).then(response => {
          this.$message.success(response.message)
          // 关闭组件
          this.close()
          // 刷新列表
          this.$parent.fetchNodeList()
        })
      }
    }
  }
  </script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
3.3、编写小节(课时)页面

(1)Video -> Form.vue

<template>
    <!-- 添加和修改课时表单 -->
    <el-dialog :visible="dialogVisible" title="添加课时" @close="close()">
      <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" />
        </el-form-item>
        <el-form-item label="是否免费">
          <el-radio-group v-model="video.isFree">
            <el-radio :label="0">免费</el-radio>
            <el-radio :label="1">默认</el-radio>
          </el-radio-group>
        </el-form-item>
  
        <!-- 上传视频 -->
        <el-form-item label="上传视频">
          <el-upload
            ref="upload"
            :auto-upload="false"
            :on-success="handleUploadSuccess"
            :on-error="handleUploadError"
            :on-exceed="handleUploadExceed"
            :file-list="fileList"
            :limit="1"
            :before-remove="handleBeforeRemove"
            :on-remove="handleOnRemove"
            :action="BASE_API+'/admin/vod/upload'">
            <el-button slot="trigger" size="small" type="primary">选择视频</el-button>
            <el-button
              :disabled="uploadBtnDisabled"
              style="margin-left: 10px;"
              size="small"
              type="success"
              @click="submitUpload()">上传</el-button>
          </el-upload>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="close()">取 消</el-button>
        <el-button type="primary" @click="saveOrUpdate()">确 定</el-button>
      </div>
    </el-dialog>
  </template>
  
  <script>
  import videoApi from '@/api/vod/video'
  //import vodApi from '@/api/vod/vod'
  export default {
  
    data() {
      return {
        BASE_API: 'http://localhost:8301',
        dialogVisible: false,
        video: {
          sort: 0,
          free: false
        },
        fileList: [], // 上传文件列表
        uploadBtnDisabled: false
      }
    },
  
    methods: {
      open(chapterId, videoId) {
        this.dialogVisible = true
        this.video.chapterId = chapterId
        if (videoId) {
          videoApi.getById(videoId).then(response => {
            this.video = response.data
            // 回显
            if (this.video.videoOriginalName) {
              this.fileList = [{ 'name': this.video.videoOriginalName }]
            }
          })
        }
      },
  
      close() {
        this.dialogVisible = false
        // 重置表单
        this.resetForm()
      },
  
      resetForm() {
        this.video = {
          sort: 0,
          free: false
        }
  
        this.fileList = [] // 重置视频上传列表
      },
  
      saveOrUpdate() {
        if (!this.video.id) {
          this.save()
        } else {
          this.update()
        }
      },
  
      save() {
        this.video.courseId = this.$parent.$parent.courseId
        videoApi.save(this.video).then(response => {
          this.$message.success(response.message)
          // 关闭组件
          this.close()
          // 刷新列表
          this.$parent.fetchNodeList()
        })
      },
  
      update() {
        videoApi.updateById(this.video).then(response => {
          this.$message.success(response.message)
          // 关闭组件
          this.close()
          // 刷新列表
          this.$parent.fetchNodeList()
        })
      },
  
      // 上传多于一个视频
      handleUploadExceed(files, fileList) {
        this.$message.warning('想要重新上传视频,请先删除已上传的视频')
      },
  
      // 上传
      submitUpload() {
        this.uploadBtnDisabled = true
        this.$refs.upload.submit() // 提交上传请求
      },
  
      // 视频上传成功的回调
      handleUploadSuccess(response, file, fileList) {
        this.uploadBtnDisabled = false
        this.video.videoSourceId = response.data
        this.video.videoOriginalName = file.name
      },
  
      // 失败回调
      handleUploadError() {
        this.uploadBtnDisabled = false
        this.$message.error('上传失败2')
      },
  
      // 删除视频文件确认
      handleBeforeRemove(file, fileList) {
        return this.$confirm(`确定移除 ${file.name}`)
      },
  
      // 执行视频文件的删除
      handleOnRemove(file, fileList) {
        if (!this.video.videoSourceId) {
          return
        }
      }
    }
  }
  </script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162

二、发布课程-课程最终发布

1、课程最终发布接口

在这里插入图片描述

1.1、编写CourseController

添加方法

    /**
     * 根据课程id获取课程发布信息
     * @param id
     * @return
     */
    @ApiOperation("根据id获取课程发布信息")
    @GetMapping("getCoursePublishVo/{id}")
    public Result getCoursePublishVoById(
            @ApiParam(value = "课程ID", required = true)
            @PathVariable Long id){

        CoursePublishVo coursePublishVo = courseService.getCoursePublishVo(id);
        return Result.ok(coursePublishVo);
    }

    /**
     * 根据课程id发布课程
     * @param id
     * @return
     */
    @ApiOperation("课程最终发布")
    @PutMapping("publishCourseById/{id}")
    public Result publishCourseById(
            @ApiParam(value = "课程ID", required = true)
            @PathVariable Long id){
        courseService.publishCourseById(id);
        return Result.ok(null);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

CoursePublishVo:

@ApiModel("课程发布对象")
@Data
public class CoursePublishVo {
    @ApiModelProperty(value = "课程ID")
    private String id;
    @ApiModelProperty(value = "课程标题")
    private String title;
    @ApiModelProperty(value = "课程封面图片路径")
    private String cover;
    @ApiModelProperty(value = "总课时")
    private Integer lessonNum;
    @ApiModelProperty(value = "一级分类标题")
    private String subjectParentTitle;
    @ApiModelProperty(value = "二级分类标题")
    private String subjectTitle;
    @ApiModelProperty(value = "讲师姓名")
    private String teacherName;
    @ApiModelProperty(value = "课程销售价格")
    private String price;//只用于显示
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
1.2、编写CourseService
	//根据id获取课程发布信息
    CoursePublishVo getCoursePublishVo(Long id);
    //根据id发布课程
    void publishCourseById(Long id);
  • 1
  • 2
  • 3
  • 4
1.3、编写CourseServiceImpl
 /***
     * 根据课程id获取课程发布信息(涉及三张表课程基本信息表,讲师表,分类表所以要自己写一个sql语句)
     * @param id
     * @return
     */
    @Override
    public CoursePublishVo getCoursePublishVo(Long id) {
        return baseMapper.selectCoursePublishVoById(id);
    }

    /**
     * 课程最终发布(修改发布状态)
     * @param id
     * @return
     */
    @Override
    public void publishCourseById(Long id) {
        //根据id获取课程信息
        Course course=baseMapper.selectById(id);
        course.setStatus(1);//1为已经发布
        course.setPublishTime(new Date());
        baseMapper.updateById(course);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
1.4、编写CourseMapper
public interface CourseMapper extends BaseMapper<Course> {
	 /**
     * 根据课程id获取课程发布信息
     * @param id
     * @return
     */
    CoursePublishVo selectCoursePublishVoById(Long id);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

1.5、编写CourseMapper.xml

<?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.atguigu.ggkt.vod.mapper.CourseMapper">
    <select id="selectCoursePublishVoById" resultType="com.atguigu.ggkt.vo.vod.CoursePublishVo">
        SELECT
            c.id,
            c.title,
            c.cover,
            c.lesson_num AS lessonNum,
            c.price,
            t.name AS teacherName,
            s1.title AS subjectParentTitle,
            s2.title AS subjectTitle
        FROM course c
                 LEFT OUTER JOIN teacher t ON c.teacher_id=t.id
                 LEFT OUTER JOIN `subject` s1 ON c.subject_parent_id=s1.id
                 LEFT OUTER JOIN `subject` s2 ON c.subject_id=s2.id
        WHERE c.id=#{id}
    </select>
</mapper>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

left outer join简写left join
sql中subject是关键字,表中的字段名是MySql的关键字时,我们可以在表名上面加``
容易混淆的关键字有subject,describe等等

mysql多表连接

1.6、添加配置(为什么)

为什么添加配置:
在这里插入图片描述
不添加会报这样的错误,因为扫描不到
在这里插入图片描述

1)application.properties添加

mybatis-plus.mapper-locations=classpath:com/atguigu/ggkt/vod/mapper/xml/*.xml
  • 1

(2)service模块pom.xml添加

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.yml</include>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes> <include>**/*.yml</include>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
2、课程最终发布前端
2.1、course.js定义接口
//获取发布课程信息  
getCoursePublishById(id) {
  return request({
    url: `${api_name}/getCoursePublishVo/${id}`,
    method: 'get'
  })
},
//发布课程  
publishCourseById(id) {
  return request({
    url: `${api_name}/publishCourseById/${id}`,
    method: 'put'
  })
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
2.2、编写Publish.vue

<template>
    <div class="app-container">
      <!--课程预览-->
      <div class="ccInfo">
        <img :src="coursePublish.cover">
        <div class="main">
          <h2>{{ coursePublish.title }}</h2>
          <p class="gray"><span>{{ coursePublish.lessonNum }}课时</span></p>
          <p><span>所属分类:{{ coursePublish.subjectParentTitle }}{{ coursePublish.subjectTitle }}</span></p>
          <p>课程讲师:{{ coursePublish.teacherName }}</p>
          <h3 class="red">{{ coursePublish.price }}</h3>
        </div>
      </div>
      <div style="text-align:center">
        <el-button type="primary" @click="prev()">上一步</el-button>
        <el-button :disabled="publishBtnDisabled" type="primary" @click="publish()">发布课程</el-button>
      </div>
    </div>
  </template>
  
  <script>
  import courseApi from '@/api/vod/course'
  
  export default {
    data() {
      return {
        publishBtnDisabled: false, // 按钮是否禁用
        coursePublish: {}
      }
    },
    created() {
      if (this.$parent.courseId) {
        this.fetchCoursePublishById(this.$parent.courseId)
      }
    },
    methods: {
      // 获取课程发布信息
      fetchCoursePublishById(id) {
        courseApi.getCoursePublishById(id).then(response => {
          this.coursePublish = response.data
        })
      },
      // 上一步
      prev() {
        this.$parent.active = 1
      },
      // 下一步
      publish() {
        this.publishBtnDisabled = true
        courseApi.publishCourseById(this.$parent.courseId).then(response => {
          this.$parent.active = 3
          this.$message.success(response.message)
          this.$router.push({ path: '/vodcourse/course/list' })//路由跳转
        })
      }
    }
  }
  </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;
  }
  </style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108

知识补充:
{{}}作为html模板,用于输出对象属性和函数返回值,其中内容可以是:变量,三元表达式,函数
绑定简单的键值
fetchCoursePublishById(id) 这里通过这个方法获取对象然后得到相应的值

三、功能实现-课程删除

1、课程删除接口

一个课程下包含多个内容
在这里插入图片描述

(1)编写课程Controller
	/**
     * 删除课程
     * @param id
     * @return
     */
    @ApiOperation(value = "删除课程")
    @DeleteMapping("remove/{id}")
    public Result remove(@PathVariable Long id) {
        courseService.removeCourseById(id);
        return Result.ok(null);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
(2)编写课程Service
   //根据id删除课程
    void removeCourseById(Long id);
  • 1
  • 2
(3)编写课程ServiceImpl
	/**
     * 根据课程id删除课程信息
     * @param id
     */
    @Override
    public void removeCourseById(Long id) {
        //根据课程id删除小节
        videoService.removeVideoByCourseId(id);
        //根据课程id删除章节
        chapterService.removeChapterByCourseId(id);
        //根据课程id删除描述
        courseDescriptionService.removeById(id);
        //根据课程id删除课程
        baseMapper.deleteById(id);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
(4)编写VideoService
//根据课程id删除小节
    void removeVideoByCourseId(Long id);
  • 1
  • 2
(5)编写VideoServiceImpl
 	//根据课程id删除小节
    @Override
    public void removeVideoByCourseId(Long id) {
        QueryWrapper<Video> wrapper = new QueryWrapper<>();
        wrapper.eq("course_id",id);
        baseMapper.delete(wrapper);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
(6)编写ChapterService
   //根据课程id删除章节
    void removeChapterByCourseId(Long id);
  • 1
  • 2
(7)编写ChapterServiceImpl
 //根据课程id删除章节
    @Override
    public void removeChapterByCourseId(Long id) {
        QueryWrapper<Chapter> wrapper = new QueryWrapper<>();
        wrapper.eq("course_id",id);
        baseMapper.delete(wrapper);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
2、课程删除前端
2.1、course.js定义接口
 //删除课程
removeById(id) {
  return request({
    url: `${api_name}/remove/${id}`,
    method: 'delete'
  })
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
2.2、course -> list.vue添加方法
methods: {
	......
     // 根据id删除数据
    removeById(id) {
      this.$confirm('此操作将永久删除该课程,以及该课程下的章节和视频,是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        return courseApi.removeById(id)
      }).then(response => {
        this.fetchData()
        this.$message.success(response.message)
      }).catch((response) => { // 失败
        if (response === 'cancel') {
          this.$message.info('取消删除')
        }
      })
    },
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/1014163
推荐阅读
相关标签
  

闽ICP备14008679号