2022年最新《谷粒学院开发教程》:5 - 章节管理
| 資料地址 |
| 1 - 構建工程篇 | 7 - 渲染前臺篇 |
| 2 - 前后交互篇 | 8 - 前臺登錄篇 |
| 3 - 文件上傳篇 | 9 - 前臺課程篇 |
| 4 - 課程管理篇 | 10 - 前臺支付篇 |
| 5 - 章節管理篇 | 11 - 統計分析篇 |
| 6 - 微服務治理 | 12 - 項目完結篇 |
目錄
- 一、課程章節增刪改查
- 1.1、后端接口
- 1.2、前端實現
- 1.3、添加章節
- 1.4、修改章節
- 1.5、刪除章節
- 二、課程章節小節功能
- 2.1、后端接口
- 2.2、前端實現
- 2.3、添加小節
- 2.4、刪除小節
- 2.5、修改小節
- 三、課程發布信息預覽
- 3.1、后端接口
- 3.3、前端實現
- 四、課程發布
- 4.1、后端接口
- 4.2、前端頁面
- 五、課程列表
- 5.1、后端接口
- 5.2、前端實現
- 六、課程刪除
- 6.1、后端接口
- 6.2、前端實現
- 七、編輯基本信息及大綱
- 八、阿里云視頻點播
- 8.1、簡介
- 8.2、使用
- 九、使用服務端SDK
- 9.1、簡介
- 9.2、獲取視頻
- 9.3、上傳視頻
- 十、視頻點播微服務
- 10.1、后端接口
- 10.2、前端實現
- 10.3、視頻刪除
一、課程章節增刪改查
1.1、后端接口
@Api(tags = "章節模塊") @CrossOrigin @RestController @RequestMapping("/eduservice/chapter") public class EduChapterController {@Autowiredprivate EduChapterService eduChapterService;@AutowiredEduCourseService eduCourseService;// 添加章節@PostMapping("addChapter")public R addChapter(@RequestBody EduChapter eduChapter) {eduChapterService.save(eduChapter);return R.ok();}// 根據章節id查詢@GetMapping("getChapter/{chapterId}")public R getChapter(@PathVariable String chapterId) {EduChapter eduChapter = eduChapterService.getById(chapterId);return R.ok().data("chapter", eduChapter);}// 修改章節@PostMapping("updateChapter")public R updateChapter(@RequestBody EduChapter eduChapter) {eduChapterService.updateById(eduChapter);return R.ok();}// 刪除章節 若存在小節則不可刪除@DeleteMapping("deleteById/{chapterId}")public R deleteById(@PathVariable String chapterId) {boolean flag = eduChapterService.deleteChapter(chapterId);if (flag) {return R.ok();} else {return R.error();}}} @Override public boolean deleteChapter(String chapterId) {QueryWrapper<EduVideo> wrapper = new QueryWrapper<>();wrapper.eq("chapter_id", chapterId);int count = eduVideoService.count(wrapper);if (count > 0) {throw new LaptoyException(20001, "還有小節數據,不能刪除");} else {return this.removeById(chapterId);} }1.2、前端實現
1、頁面代碼
<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-collapse accordion v-for="chapter in chapterVideoList" :key="chapter.id"><!-- 按鈕組 --><el-button-group><el-button type="primary" size="mini" icon="el-icon-circle-plus">添加小節</el-button><el-button type="primary" size="mini" icon="el-icon-edit" @click="openEditChapter(chapter.id)"></el-button><el-button type="danger" size="mini" icon="el-icon-delete" @click="removeById(chapter.id)"></el-button></el-button-group><!-- 小節數據 --><el-collapse-item :title=chapter.title><div v-for="video in chapter.children" :key="video.id">{{ video.title }}<span class="acts"><el-button type="text">編輯</el-button><el-button type="text">刪除</el-button></span></div></el-collapse-item></el-collapse><!-- 底部按鈕 --><el-form label-width="120px"><el-form-item><el-button @click="dialogChapterFormVisible=true">添加章節</el-button><el-button @click="previous">上一步</el-button><el-button :disabled="saveBtnDisabled" type="primary" @click="next">下 一步</el-button></el-form-item></el-form><!-- 添加或修改章節彈框 --><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></div> </template> data() {return {dialogChapterFormVisible: false,chapter: {title: "",sort: 0},}; },稍微優化了頁面
2、創建api - chapter.js
//添加章節 addChapter(chapter) {return request({url: `/eduservice/chapter/addChapter`,method: `post`,data: chapter}) }, //根據id查詢章節 updateChapterById(chapterId) {return request({url: `/eduservice/chapter/getChapter/${chapterId}`,method: `get`,}) }, //修改章節 updateChapter(chapter) {return request({url: `/eduservice/chapter/updateChapter`,method: `post`,data: chapter}) }, //刪除章節 deleteById(chapterId) {return request({url: `/eduservice/chapter/deleteById/${chapterId}`,method: `delete`,}) }1.3、添加章節
saveChapter() {//設置課程id到chapter對象中,不指定課程id無法正常保存,因為數據庫字段為非空this.chapter.courseId = this.courseIdchapter.addChapter(this.chapter).then((resp) => {this.dialogChapterFormVisible = false;this.$message({message: "添加章節成功",type: "success",});this.chapter = {}this.getChapterVideoByCourseId()}); }, saveOrUpdate() {this.saveChapter() },1.4、修改章節
1、點擊修改章節按鈕根據章節id回顯數據
<el-button type="primary" size="mini" icon="el-icon-edit" @click="openEditChapter(chapter.id)"></el-button>2、修改方法
// 修改章節彈窗回顯數據 openEditChapter(id) {this.dialogChapterFormVisible = true;chapter.updateChapterById(id).then((resp) => {this.chapter = resp.data.data}) }, // 修改章節 updateChapter(id) {//設置課程id到chapter對象中,不指定課程id無法正常保存,因為數據庫字段為非空this.chapter.courseId = this.courseIdchapter.updateChapter(this.chapter).then((resp) => {this.dialogChapterFormVisible = false;this.$message({message: "修改章節成功",type: "success",});this.getChapterVideoByCourseId();}); },1.5、刪除章節
<el-button type="danger" size="mini" icon="el-icon-delete" @click="removeById(chapter.id)"></el-button> // 刪除章節 removeById(chapterId) {this.$confirm("此操作將永久刪除章節信息, 是否繼續?", "提示", {confirmButtonText: "確定",cancelButtonText: "取消",type: "warning",}).then(() => {chapter.deleteById(chapterId).then((resp) => {this.$message({type: "success",message: "刪除成功!",});this.getChapterVideoByCourseId();});}); },二、課程章節小節功能
2.1、后端接口
@Api(tags = "小節模塊") @RestController @RequestMapping("/eduservice/video") @CrossOrigin //解決跨域問題 public class EduVideoController {@Autowiredprivate EduVideoService eduVideoService;//添加小節@PostMapping("/addVideo")public R addVideo(@RequestBody EduVideo eduVideo) {eduVideoService.save(eduVideo);return R.ok();}//刪除小節// TODO 后面這個方法需要完善,刪除小節的時候,同時也要把視頻刪除@DeleteMapping("/deleteVideo/{id}")public R deleteVideo(@PathVariable String id) {eduVideoService.removeById(id);return R.ok();}//修改小節@PostMapping("/updateVideo")public R updateVideo(@RequestBody EduVideo eduVideo) {eduVideoService.updateById(eduVideo);return R.ok();}//根據小節id查詢@GetMapping("/getVideoById/{videoId}")public R getVideoById(@PathVariable String videoId) {EduVideo eduVideo = eduVideoService.getById(videoId);return R.ok().data("data", eduVideo);} }2.2、前端實現
1、頁面
<!-- 章節數據折疊面板 --> <el-collapse accordion v-for="chapter in chapterVideoList" :key="chapter.id"><!-- 按鈕組 --><el-button-group><el-button type="primary" size="mini" icon="el-icon-circle-plus" @click="openSaveVideoForm(chapter.id)">添加小節</el-button></el-button-group> </el-collapse><!--添加小節表單--> <el-dialog :visible.sync="dialogVideoFormVisible" title="添加小節"><el-form :model="video" label-width="120px"><el-form-item label="小節標題"><el-input v-model="video.title" /></el-form-item><el-form-item label="小節排序"><el-input-number v-model="video.sort" :min="0" controls-position="right" /></el-form-item><el-form-item label="是否免費"><el-radio-group v-model="video.free"><el-radio :label="true">免費</el-radio><el-radio :label="false">默認</el-radio></el-radio-group></el-form-item><el-form-item label="上傳視頻"><!-- TODO --></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="dialogVideoFormVisible = false">取 消</el-button><el-button :disabled="saveVideoBtnDisabled" type="primary" @click="saveOrUpdateVideo">確 定</el-button></div> </el-dialog> data() {return {saveVideoBtnDisabled: false,dialogVideoFormVisible: false,video: {sort: 0,title: "",free: ""}}; },2、API - src\api\teacher\video.js
import request from '@/utils/request' //引入已經封裝好的axios 和 攔截器export default {//添加小節addVideo(video) {return request({url: `/eduservice/video/addVideo`,method: `post`,data: video})},//根據id查詢小節getVideoById(videoId) {return request({url: `/eduservice/video/getVideoById/${videoId}`,method: `get`,})},//修改小節updateVideo(video) {return request({url: `/eduservice/video/updateVideo`,method: `post`,data: video})},//刪除小節deleteById(videoId) {return request({url: `/eduservice/video/deleteVideo/${videoId}`,method: `delete`,})}, }2.3、添加小節
openSaveVideoForm(id) {this.dialogVideoFormVisible = truethis.video.courseId = this.courseIdthis.video.chapterId = id }, saveOrUpdateVideo(){if (this.video.id) {//修改小節} else {//新增小節this.saveVideo();} }, // 添加小節 saveVideo() {video.addVideo(this.video).then((resp) => {this.dialogVideoFormVisible = false;this.$message({message: "添加小節成功",type: "success",});this.video = {}this.getChapterVideoByCourseId();}); },2.4、刪除小節
<!-- 章節數據折疊面板 --> <el-collapse accordion v-for="chapter in chapterVideoList" :key="chapter.id"><!-- 按鈕組 --><!-- 小節數據 --><el-collapse-item :title=chapter.title><div v-for="video in chapter.children" :key="video.id">{{ video.title }}<span class="acts"><el-button type="text">編輯</el-button><el-button type="text" @click="removeVideo(video.id)">刪除</el-button></span></div></el-collapse-item> </el-collapse> // 刪除小節 removeVideo(id) {this.$confirm("此操作將永久刪除小節信息, 是否繼續?", "提示", {confirmButtonText: "確定",cancelButtonText: "取消",type: "warning",}).then(() => {video.deleteById(id).then((resp) => {this.$message({type: "success",message: "刪除成功!",});this.getChapterVideoByCourseId();});}); },2.5、修改小節
<!-- 章節數據折疊面板 --> <el-collapse accordion v-for="chapter in chapterVideoList" :key="chapter.id"><!-- 按鈕組 --><!-- 小節數據 --><el-collapse-item :title=chapter.title><div v-for="video in chapter.children" :key="video.id">{{ video.title }}<span class="acts"><el-button type="text" @click="openEditVideoForm(video.id)">編輯</el-button><el-button type="text" @click="removeVideo(video.id)">刪除</el-button></span></div></el-collapse-item> </el-collapse> // 展開編輯表單 openEditVideoForm(id) {this.dialogVideoFormVisible = true;video.getVideoById(id).then((resp) => {this.video = resp.data.data;}); },// 修改小節 updateVideo() {video.updateVideo(this.video).then((resp) => {this.dialogVideoFormVisible = false;this.$message({message: "修改小節成功",type: "success",});this.getChapterVideoByCourseId();}); },// 添加或修改小節 saveOrUpdateVideo() {if (this.video.id) {//修改小節this.updateVideo();} else {//新增小節this.saveVideo();} },三、課程發布信息預覽
3.1、后端接口
1、創建 VO
@Data public class CoursePublishVo implements Serializable {private static final long serialVersionUID = 1L;private String id;//課程idprivate String title; //課程名稱private String cover; //封面private Integer lessonNum;//課時數private String subjectLevelOne;//一級分類private String subjectLevelTwo;//二級分類private String teacherName;//講師名稱private String price;//價格 ,只用于顯示}2、控制層
// 根據課程id查詢課程確認信息 @GetMapping("/getPublishCourseInfo/{id}") public R getPublishCourseInfo(@PathVariable String id) {CoursePublishVo publishCourseInfo = eduCourseService.getPublishCourseInfo(id);return R.ok().data("data", publishCourseInfo); }3、業務層
@Override public CoursePublishVo getPublishCourseInfo(String id) {return eduCourseMapper.getPublishCourseInfo(id); }4、數據層
CoursePublishVo getPublishCourseInfo(String id); <select id="getPublishCourseInfo" resultType="com.laptoy.eduservice.entity.vo.CoursePublishVo">SELECT ec.id,ec.title,ec.cover,ec.lesson_num AS lessonNum,ec.price,s1.title AS subjectLevelOne,s2.title AS subjectLevelTwo,t.name AS teacherNameFROM edu_course ecLEFT JOIN edu_teacher t ON ec.teacher_id = t.idLEFT JOIN edu_subject s1 ON ec.subject_parent_id = s1.idLEFT JOIN edu_subject s2 ON ec.subject_id = s2.idWHERE ec.id = #{id} </select>5、將xml文件放到 resources/xml 目錄下,并再YML文件指定
mybatis-plus:mapper-locations: /mapper/*.xml3.3、前端實現
1、定義API - course.js
//課程確認信息顯示 getPublishCourseInfo(courseId){return request({url:"/eduservice/course/getpublishCourseInfo/"+courseId,method: 'get',}) }2、頁面
<div class="ccInfo"><img :src="publishCourseInfo.cover" /><div class="main"><h2>{{ publishCourseInfo.title }}</h2><p class="gray"><span>共{{ publishCourseInfo.lessonNum }}課時</span></p><p><span>所屬分類:{{ publishCourseInfo.subjectLevelOne }} : {{ publishCourseInfo.subjectLevelTwo }}</span></p><p>課程講師:{{ publishCourseInfo.teacherName }}</p><h3 class="red">¥{{ publishCourseInfo.price }}</h3></div> </div>3、方法
export default {data() {return {saveBtnDisabled: false,courseId: '',publishCourseInfo: {},};},methods: {//根據課程id查詢getPublishCourseInfo() {course.getPublishCourseInfo(this.courseId).then(resp => {this.publishCourseInfo = resp.data.dataconsole.log(this.publishCourseInfo)})},// 跳轉到上一步previous() {this.$router.push("/course/chapter/" + this.courseId);},publish() {this.$router.push("/course/list");}},created() {//獲取路由中的id值if (this.$route.params && this.$route.params.id) {this.courseId = this.$route.params.id//調用接口方法根據課程id查詢課程信息this.getPublishCourseInfo()}}, };4、css
<style scoped> .ccInfo {background: #f5f5f5;padding: 20px;overflow: hidden;border: 1px dashed #ddd;margin-bottom: 40px;position: relative; } .ccInfo img {background: #d6d6d6;width: 500px;height: 278px;display: block;float: left;border: none; } .ccInfo .main {margin-left: 520px; } .ccInfo .main h2 {font-size: 28px;margin-bottom: 30px;line-height: 1;font-weight: normal; } .ccInfo .main p {margin-bottom: 10px;word-wrap: break-word;line-height: 24px;max-height: 48px;overflow: hidden; } .ccInfo .main p {margin-bottom: 10px;word-wrap: break-word;line-height: 24px;max-height: 48px;overflow: hidden; } .ccInfo .main h3 {left: 540px;bottom: 20px;line-height: 1;font-size: 28px;color: #d32f24;font-weight: normal;position: absolute; } </style>5、測試
四、課程發布
4.1、后端接口
1、控制層
//課程最終發布 //修改課程狀態 @PostMapping("publishCourse/{id}") public R publishCourse(@PathVariable String id){EduCourse eduCourse = new EduCourse();eduCourse.setStatus("Normal"); //設置課程發布狀態eduCourse.setId(id);boolean flag = eduCourseService.updateById(eduCourse);if (flag){return R.ok();}else {return R.error();} }4.2、前端頁面
1、API
//課程最終發布 publishCourse(courseId) {return request({url: "/eduservice/course/publishCourse/" + courseId,method: 'post',}) }2、方法
//發布課程 publish() {this.$confirm("你確定要發布此課程, 是否繼續?", "提示", {confirmButtonText: "確定",cancelButtonText: "取消",type: "warning",}).then(() => {course.publishCourse(this.courseId).then((resp) => {this.$message({message: "課程發布成功",type: "success",});//跳轉課程列表頁面this.$router.push({ path: "/course/list" });});}); }五、課程列表
5.1、后端接口
1、實體類作為模糊查詢條件
@ApiModel(value = "Course查詢對象", description = "課程查詢對象封裝") @Data public class CourseQuery implements Serializable {private static final long serialVersionUID = 1L;@ApiModelProperty(value = "課程名稱,模糊查詢")private String title;@ApiModelProperty(value = "發布狀態 Normal已發布 Draft未發布")private String status; }2、控制層
//多條件查詢課程帶分頁 @ApiOperation(value = "多條件查詢課程帶分頁") @PostMapping("/pageCourseCondition/{page}/{limit}") public R pageCourseCondition(@ApiParam(name = "page", value = "當前頁碼", required = true) @PathVariable Long page,@ApiParam(name = "limit", value = "每頁記錄數", required = true) @PathVariable Long limit,@RequestBody(required = false) CourseQuery courseQuery) {//通過封裝courseQuery對象來直接傳遞查詢條件//創建分頁page對象Page<EduCourse> pageParam = new Page<>(page, limit);//調用方法實現多條件分頁查詢eduCourseService.pageQuery(pageParam, courseQuery);//獲取查詢到的數據List<EduCourse> records = pageParam.getRecords();//獲取總記錄數long total = pageParam.getTotal();return R.ok().data("total", total).data("data", records); }3、業務層
@Override public void pageQuery(Page<EduCourse> pageParam, CourseQuery courseQuery) {QueryWrapper<EduCourse> wrapper = new QueryWrapper<>();String title = courseQuery.getTitle();String status = courseQuery.getStatus();if (!StringUtils.isEmpty(title)) {wrapper.like("title", title);//參數1:數據庫字段名; 參數2:模糊查詢的值}if (!StringUtils.isEmpty(status)) {wrapper.eq("status", status);}wrapper.orderByDesc("gmt_create");baseMapper.selectPage(pageParam, wrapper); }5.2、前端實現
1、頁面
<template><div><!--多條件查詢表單--><el-form :inline="true" class="demo-form-inline" style="margin-left: 20px; margin-top: 12px"><el-form-item label="課程名稱"><el-input v-model="courseQuery.title" placeholder="請輸入名稱"></el-input></el-form-item><el-form-item label="發布狀態"><el-select v-model="courseQuery.status" placeholder="課程狀態"><el-option label="已發布" :value="'Normal'"></el-option><el-option label="未發布" :value="'Draft'"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" icon="el-icon-search" @click="getList()">查詢</el-button><el-button type="default" @click="resetData()">清空</el-button></el-form-item></el-form><!-- 展示表格 --><el-table :data="list" style="width: 100%" height="620" border fit highlight-current-row element-loading-text="數據加載中"><el-table-column prop="date" label="序號" width="70" align="center"><template slot-scope="scope">{{ (page - 1) * limit + scope.$index + 1 }}</template></el-table-column><el-table-column prop="title" label="課程名稱" width="400"></el-table-column><el-table-column label="發布狀態" width="80"><template slot-scope="scope">{{ scope.row.status === "Normal" ? "已發布" : "未發布" }}</template></el-table-column><el-table-column prop="lessonNum" label="課時數" width="100" /><el-table-column prop="gmtCreate" label="添加時間" width="300" /><el-table-column prop="viewCount" label="瀏覽數量" width="200" /><el-table-column label="操作" align="center"><template slot-scope="scope"><router-link :to="'/teacher/edit/' + scope.row.id"><el-button type="primary" size="mini" icon="el-icon-edit" plain>編輯基本信息</el-button></router-link><router-link :to="'/teacher/edit/' + scope.row.id"><el-button type="info" size="mini" icon="el-icon-edit" plain>編輯課程大綱</el-button></router-link><el-button type="danger" size="mini" icon="el-icon-delete" @click="removeById(scope.row.id)" plain>點擊刪除課程</el-button></template></el-table-column></el-table><!--分頁組件--><el-pagination background layout="prev, pager, next,total,jumper" :total="total" :page-size="limit" style="padding: 30px 0; text-align: center" :current-page="page" @current-change="getList"></el-pagination></div> </template>2、API - course.js
//課程列表多條件分頁查詢 //page:當前頁,limit:每頁記錄數,teacherQuery:條件對象 getCourseListPage(page, limit, courseQuery) {return request({url: `/eduservice/course/pageCourseCondition/${page}/${limit}`,method: 'post',data: courseQuery}) },3、js
import course from "@/api/teacher/course.js";export default {data() {return {list: null, //查詢之后給接口返回的數據裝的集合page: 1, //當前頁limit: 10, //每頁顯示記錄數courseQuery: {}, //條件封裝對象total: 0, //總記錄數};},created() {this.getList();},methods: {getList(page = 1) {this.page = page;course.getCourseListPage(this.page, this.limit, this.courseQuery).then((resp) => {this.list = resp.data.data;this.total = resp.data.total;}) //請求成功.catch((err) => {console.log(err);}); //請求失敗},//清空方法resetData() {//表單輸入項數據清空this.courseQuery = {};//查詢所有課程數據this.getList();},}, };六、課程刪除
6.1、后端接口
1、控制層
//課程列表中刪除課程方法 @DeleteMapping("/removeCourseById/{id}") public R removeCourseById(@PathVariable String id) {boolean flag = eduCourseService.removeCourse(id);if (flag) {return R.ok();} else {return R.error();} }2、業務層
//刪除課程 @Transactional @Override public boolean removeCourse(String id) {//1、根據課程id刪除小節eduVideoService.removeVideoByCourseId(id);//2、根據課程id刪除章節部分eduChapterService.removeChapterByCourseId(id);//3、根據課程id刪除課程描述descriptionService.removeById(id);//4、根據課程id刪除課程本身boolean flag = this.removeById(id);if (flag) {return true;} else {throw new LaptoyException(20001, "刪除失敗");} }3、刪除小節
//根據課程id刪除小節 // TODO 刪除小節,要刪除對應的視頻文件 @Override public void removeVideoByCourseId(String id) {QueryWrapper<EduVideo> wrapper = new QueryWrapper<>();wrapper.eq("course_id", id);baseMapper.delete(wrapper); }4、刪除章節
@Override public void removeChapterByCourseId(String id) {QueryWrapper<EduChapter> wrapper = new QueryWrapper<>();wrapper.eq("course_id",id);baseMapper.delete(wrapper); }6.2、前端實現
1、API - course.js
//刪除課程 removeCourseById(courseId) {return request({url: "/eduservice/course/removeCourseById/" + courseId,method: 'delete',}) }2、方法
<el-button type="danger" size="mini" icon="el-icon-delete" @click="removeById(scope.row.id)" plain>點擊刪除課程</el-button><script> // 刪除課程 removeById(id) {this.$confirm("此操作將永久刪除該課程記錄, 是否繼續?", "提示", {confirmButtonText: "確定",cancelButtonText: "取消",type: "warning",}).then(() => {course.removeCourseById(id).then((resp) => {this.$message({type: "success",message: "刪除成功!",});this.getList();});}); }, </script>七、編輯基本信息及大綱
1、頁面
<el-table-column label="操作" align="center"><template slot-scope="scope"><router-link :to="'/course/info/' + scope.row.id"><el-button type="primary" size="mini" icon="el-icon-edit" plain>編輯基本信息</el-button></router-link><router-link :to="'/course/chapter/' + scope.row.id"><el-button type="info" size="mini" icon="el-icon-edit" plain>編輯課程大綱</el-button></router-link><el-button type="danger" size="mini" icon="el-icon-delete" @click="removeById(scope.row.id)" plain>點擊刪除課程</el-button></template> </el-table-column>2、添加路由
{path: 'info/:id',name: 'EduCourseInfoEdit',component: () => import('@/views/edu/course/info.vue'),meta: { title: '編輯課程基本信息', noCache: true },hidden: true }, {path: 'chapter/:id',name: 'EduCourseChapterEdit',component: () => import('@/views/edu/course/chapter.vue'),meta: { title: '編輯課程大綱', noCache: true },hidden: true },八、阿里云視頻點播
8.1、簡介
視頻點播( ApsaraVideo for VoD )是集音視頻采集、編輯、上傳、自動化轉碼處理、媒體資源管理、分發加速于一體的一站式音視頻點播解決方案。
開通服務-按流量計費
8.2、使用
1、開啟 存儲管理
2、添加 轉碼模板組
3、上傳視頻
九、使用服務端SDK
9.1、簡介
1、簡介
- sdk的方式將api進行了進一步的封裝,不用自己創建工具類。
- 我們可以基于服務端SDK編寫代碼來調用點播API,實現對點播產品和服務的快速操作
2、功能介紹
- SDK封裝了對API的調用請求和響應,避免自行計算較為繁瑣的 API簽名。
- 支持所有點播服務的API,并提供了相應的示例代碼。
- 支持7種開發語言,包括:Java、Python、PHP、.NET、Node.js、Go、C/C++。
- 通常在發布新的API后,我們會及時同步更新SDK,所以即便您沒有找到對應API的示例代碼,也可以參考舊的示例自行實現調用。
9.2、獲取視頻
1、在 service模塊 新建微服務模塊 service_vod
2、POM
<dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId> </dependency> <dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId> </dependency> <dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-vod</artifactId> </dependency> <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId> </dependency> <dependency><groupId>org.json</groupId><artifactId>json</artifactId> </dependency> <dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId> </dependency>3、測試獲取視頻播放地
//初始化類 public class InitObject {public static 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;}public static void main(String[] args) throws ClientException {//1、根據視頻id獲取視頻播放地址//創建初始化對象DefaultAcsClient cl = InitObject.initVodClient("LTAI5tL5FrVJBuQadij4KRvJ", "Xs7dHUvxCdHLd0K5iFK7NWEbdUN7GG");//創建獲取視頻地址request對象和response對象GetPlayInfoResponse response = new GetPlayInfoResponse();GetPlayInfoRequest request = new GetPlayInfoRequest();//向request對象設置視頻id值request.setVideoId("1a383ac7aa7f4b8197714ca6f886e5be");//調用初始化對象里面的方法傳遞request,獲取數據response = cl.getAcsResponse(request);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");//VideoBase.Title = 6 - What If I Want to Move Faster.mp4} } PlayInfo.PlayURL = https://outin-327a9fa3dcae11ecbc2400163e1c8dba.oss-cn-shanghai.aliyuncs.com/sv/60fc3bbf-180ff24e597/60fc3bbf-180ff24e597.mp4?Expires=1653551691&OSSAccessKeyId=LTAIrkwb21KyGjJl&Signature=h0s4mbCbwFd8797gs1KXWxJAUpU%3D VideoBase.Title = 視頻點播控制臺 - Google Chrome 2022-05-25 21-26-51.mp43、測試獲取視頻播放憑證(加密視頻)
public static void main(String[] args) throws ClientException {//創建初始化對象DefaultAcsClient cl = InitObject.initVodClient("LTAI5tL5FrVJBuQadij4KRvJ", "Xs7dHUvxCdHLd0K5iFK7NWEbdUN7GG");//創建獲取視頻地址request對象和response對象GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();//向request對象設置視頻id值request.setVideoId("1a383ac7aa7f4b8197714ca6f886e5be");GetVideoPlayAuthResponse response = cl.getAcsResponse(request);//播放憑證System.out.print("PlayAuth = " + response.getPlayAuth() + "\n");//VideoMeta信息System.out.print("VideoMeta.Title = " + response.getVideoMeta().getTitle() + "\n"); } PlayAuth = eyJTZWN1cml0eVRva2VuIjoiQ0FJU2h3TjFxNkZ0NUIyeWZTaklyNWJESGZqVG40eHA0cUM1ZWw3WTFuay9kZUZHdWJmOW1qejJJSDlJZEhWb0FPOGZ2dlUwbTJ0WTdQc1psck1xR3NFZUhoZWJONUlwdDg0T29GMzlKcExGc3QySjZyOEpqc1VZcDVBQTdFYXBzdlhKYXNEVkVmbDJFNVhFTWlJUi8wMGU2TC8rY2lyWXBUWEhWYlNDbFo5Z2FQa09Rd0M4ZGtBb0xkeEtKd3hrMnQxNFVtWFdPYVNDUHdMU2htUEJMVXhtdldnR2wyUnp1NHV5M3ZPZDVoZlpwMXI4eE80YXhlTDBQb1AyVjgxbExacGxlc3FwM0k0U2M3YmFnaFpVNGdscjhxbHg3c3BCNVN5Vmt0eVdHVWhKL3phTElvaXQ3TnBqZmlCMGVvUUFQb3BGcC9YNmp2QWF3UExVbTliWXhncGhCOFIrWGo3RFpZYXV4N0d6ZW9XVE84MCthS3p3TmxuVXo5bUxMZU9WaVE0L1ptOEJQdzQ0RUxoSWFGMElVRTF5R21DQ2QvWDRvZ3VSUDF6N0VwTG9pdjltamNCSHFIeno1c2VQS2xTMVJMR1U3RDBWSUpkVWJUbHphRUpHZ1RTNExmWldJbGNUS0FNOVd1MlBNYXgzYlFGRHI1M3ZzVGJiWHpaYjBtcHR1UG56ZDE0Sk9CS2cxMUtVR29BQlUrY0Q3SXpmeDIvSHJmNXA2cWdSa2UvdDNaMjFPUXF1NkRsSkV3VnpPL2poWngva3JNNitGNWlEK25kT1JHQitZbDJtcTN6SlBnclQxNVdveE5QdXJndy9xbWNib1BWR3dtV01qa3RKOXo0YS90TnA0SWNLRGxKQStvOWFvOWdLbUpPZWJFMnZXT1FicE1LbGFmOER6Q1JCMTl5NmZDUi9qWDFObndub3RTQT0iLCJBdXRoSW5mbyI6IntcIkNJXCI6XCJLenVKNUlCVmRIc0E2eTNlTXFDbS9vYXVOMFZwVFBmYkU0L1VaUUt5c0dhVzc3MjRTWEZQejVyUHlyOGRSN0VaZXVFenpEdndDcDRIY2NoZFlkMjdMVy8yK0R2S1lySXRsVGxlTCtUa0lsZz1cIixcIkNhbGxlclwiOlwiNldQZVY4MkttMDlUbGNGUzVWeDB0SjUxY2pCWEVYb2FuZEk4dEU4OENjWT1cIixcIkV4cGlyZVRpbWVcIjpcIjIwMjItMDUtMjZUMDc6MDQ6NTZaXCIsXCJNZWRpYUlkXCI6XCIxYTM4M2FjN2FhN2Y0YjgxOTc3MTRjYTZmODg2ZTViZVwiLFwiU2lnbmF0dXJlXCI6XCI3Vjhza3RPTnRIK1NBZGV0bVFWRk9CU29vRmc9XCJ9IiwiVmlkZW9NZXRhIjp7IlN0YXR1cyI6Ik5vcm1hbCIsIlZpZGVvSWQiOiIxYTM4M2FjN2FhN2Y0YjgxOTc3MTRjYTZmODg2ZTViZSIsIlRpdGxlIjoi6KeG6aKR54K55pKt5o6n5Yi25Y+wIC0gR29vZ2xlIENocm9tZSAyMDIyLTA1LTI1IDIxLTI2LTUxLm1wNCIsIkNvdmVyVVJMIjoiaHR0cDovL291dGluLTMyN2E5ZmEzZGNhZTExZWNiYzI0MDAxNjNlMWM4ZGJhLm9zcy1jbi1zaGFuZ2hhaS5hbGl5dW5jcy5jb20vMWEzODNhYzdhYTdmNGI4MTk3NzE0Y2E2Zjg4NmU1YmUvc25hcHNob3RzLzNmOTg0ZTE0YjU5OTQxNTM5ODhmNDdiMGI1MDQxMDZhLTAwMDAxLmpwZz9FeHBpcmVzPTE2NTM1NTIxOTYmT1NTQWNjZXNzS2V5SWQ9TFRBSXJrd2IyMUt5R2pKbCZTaWduYXR1cmU9c040UEljem9UbUFPSFJzaHhRTXhMd3pxc09vJTNEIiwiRHVyYXRpb24iOjUuMzE3N30sIkFjY2Vzc0tleUlkIjoiU1RTLk5VdlZCZ3JTSFViUnh4aTJ5anlualZyVngiLCJBY2Nlc3NLZXlTZWNyZXQiOiI3dzRnaWtuSEFlbndCTVYxN2hqbXNqekw4cUZIdlRmZ0tHNjFtWDJ1cVdKcCIsIlJlZ2lvbiI6ImNuLXNoYW5naGFpIiwiQ3VzdG9tZXJJZCI6MTk2MzMzNzIxMTEyMTk2NH0= VideoMeta.Title = 視頻點播控制臺 - Google Chrome 2022-05-25 21-26-51.mp49.3、上傳視頻
1、POM - 默認找不到該依賴
<dependency><groupId>com.aliyun</groupId><artifactId>aliyun-sdk-vod-upload</artifactId> </dependency>2、將資料的 5-lib/aliyun-java-vod-upload-1.4.11.jar 放到maven倉庫bin目錄下并執行如下命令安裝上述依賴
mvn install:install-file -DgroupId=com.aliyun -DartifactId=aliyun-sdk-vod-upload -Dversion=1.4.11 -Dpackaging=jar -Dfile=aliyun-java-vod-upload-1.4.11.jar3、測試
@Test public void testFileUpload() {String accessKeyId = "LTAI5tL5FrVJBuQadij4KRvJ";String accessKeySecret = "Xs7dHUvxCdHLd0K5iFK7NWEbdUN7GG";String title = "My Upload Video"; //上傳之后文件的名稱String fileName = "C:\\Users\\lapto\\Desktop\\myVideo.mp4"; //本地文件的路徑和名稱UploadVideoRequest request = new UploadVideoRequest(accessKeyId, accessKeySecret, title, fileName);/* 可指定分片上傳時每個分片的大小,默認為2M字節 */request.setPartSize(2 * 1024 * 1024L);/* 可指定分片上傳時的并發線程數,默認為1,(注:該配置會占用服務器CPU資源,需根據服務器情況指定)*/request.setTaskNum(1);UploadVideoImpl uploader = new UploadVideoImpl();UploadVideoResponse response = uploader.uploadVideo(request);if (response.isSuccess()) {System.out.print("VideoId=" + response.getVideoId() + "\n"); //獲取到上傳視頻的id} 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");} }十、視頻點播微服務
10.1、后端接口
1、service_vod微服務 主啟動類
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @ComponentScan(basePackages = {"com.laptoy"}) //用于掃描swagger public class Service_vod_Main8003 {public static void main(String[] args) {SpringApplication.run(Service_vod_Main8003.class, args);} }2、YML
# 服務端口號 server.port=8003 # 服務名 spring.application.name=service-vod # nacos注冊中心 spring.cloud.nacos.discovery.server-addr=120.76.55.55:8848 # 環境設置 dev test prod spring.profiles.active=dev # 最大上傳單個文件大小:默認1M spring.servlet.multipart.max-file-size=1024MB # 最大置總上傳的數據大小 :默認10M spring.servlet.multipart.max-request-size=1024MB # 阿里云 vod aliyun.vod.file.keyid=LTAI5tL5FrVJBuQadij4KRvJ aliyun.vod.file.keysecret=Xs7dHUvxCdHLd0K5iFK7NWEbdUN7GG3、控制層
@RestController @CrossOrigin @RequestMapping("/eduvod/video") public class VodController {@Autowiredprivate VodService vodService;//上傳視頻到阿里云@PostMapping("/uploadAliyunVideo")public R uploadAliyunVideo(MultipartFile file) {//返回上傳視頻的idString videoId = vodService.uploadVideoAliyun(file);return R.ok().data("data", videoId);}}4、業務層
@Service public class VodServiceImpl implements VodService {@Value("${aliyun.vod.file.keyid}")private String accessKeyId;@Value("${aliyun.vod.file.keysecret}")private String accessKeySecret;@Overridepublic String uploadVideoAliyun(MultipartFile file) {try {//fileName:上傳文件原始名稱String fileName = file.getOriginalFilename();//title:上傳之后顯示名稱String title = fileName.substring(0, fileName.lastIndexOf("."));//inputStream:上傳文件的輸入流InputStream inputStream = file.getInputStream();UploadStreamRequest request = new UploadStreamRequest(accessKeyId, accessKeySecret, title, fileName, inputStream);UploadVideoImpl uploader = new UploadVideoImpl();UploadStreamResponse response = uploader.uploadStream(request);System.out.print("RequestId=" + response.getRequestId() + "\n"); //請求視頻點播服務的請求IDString videoId = null;if (response.isSuccess()) {videoId = response.getVideoId();} else { //如果設置回調URL無效,不影響視頻上傳,可以返回VideoId同時會返回錯誤碼。其他情況上傳失敗時,VideoId為空,此時需要根據返回錯誤碼分析具體錯誤原因videoId = response.getVideoId();}return videoId;} catch (Exception e) {e.printStackTrace();return null;}}}5、測試
10.2、前端實現
1、nginx.conf 添加配置并重啟 nginx
http { <--文件上傳最大大小 --> client_max_body_size 1024m;server {listen 9001;server_name localhost;...location ~ /eduvod/ {proxy_pass http://localhost:8003;}} }2、頁面
<el-form-item label="上傳視頻"><el-upload class="upload-demo" :on-success="handleVodUploadSuccess" :before-remove="beforeVodRemove" :file-list="fileList" :action="BASE_API + '/eduvod/video/uploadAliyunVideo'" :limit="1" ><el-button size="small" type="primary">上傳視頻</el-button><el-tooltip placement="right-end"><!-- 隱藏提示信息 --><div slot="content">最大支持1G,<br />支持3GP、ASF、AVI、DAT、DV、FLV、F4V、<br />GIF、M2T、M4V、MJ2、MJPEG、MKV、MOV、MP4、<br />MPE、MPG、MPEG、MTS、OGG、QT、RM、RMVB、<br />SWF、TS、VOB、WMV、WEBM 等視頻格式上傳</div><i class="el-icon-question" /></el-tooltip></el-upload> </el-form-item>3、js
data() {return {video: {sort: 0,title: "",free: "",videoSourceId: ""},fileList: [], //上傳文件列表BASE_API: process.env.BASE_API, // 接口API地址}; }, methods: {// 上傳成功執行方法handleVodUploadSuccess(response, file, fileList) {this.video.videoSourceId = response.data.data},beforeVodRemove(file, fileList) {return this.$confirm(`確定移除 ${file.name}?`);}, }10.3、視頻刪除
1、控制層
// 根據視頻id刪除阿里云視頻 @DeleteMapping("/removeAliyunVideoById/{id}") public R removeAliyunVideoById(@PathVariable String id) {vodService.removeAliyunVideoById(id);return R.ok(); }2、業務層
// 根據id刪除阿里云視頻 @Override public void removeAliyunVideoById(String id) {try {DefaultAcsClient client = initVodClient(accessKeyId, accessKeySecret);DeleteVideoRequest request = new DeleteVideoRequest();request.setVideoIds(id);DeleteVideoResponse response = client.getAcsResponse(request);System.out.println("RequestId = " + response.getRequestId() + "\n");} catch (ClientException e) {throw new LaptoyException(20001, "視頻刪除失敗");} }public static 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; }總結
以上是生活随笔為你收集整理的2022年最新《谷粒学院开发教程》:5 - 章节管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最简单的基于FFmpeg的解码器
- 下一篇: 编译error: no acceptab