【项目】小帽学堂(九)

小帽学堂

20. 课程最终发布

@RestController
@RequestMapping("/eduservice/course")
@CrossOrigin
public class EduCourseController {

    @Autowired
    private EduCourseService courseService;

    // 课程最终发布
    // 修改课程状态
    @PostMapping("publishCourse/{id}")
    public R publishCourse(@PathVariable String id) {
        EduCourse eduCourse = new EduCourse();
        eduCourse.setId(id);
        eduCourse.setStatus("Normal");    // 设置课程发布状态
        courseService.updateById(eduCourse);
        return R.ok();
    }
}


// src\api\edu\course.js
import request from '@/utils/request'

export default {
    // 课程最终发布
    publishCourse(id) {
      return request({
        url: '/eduservice/course/publishCourse/' + id,
        method: 'post',
      })
    }
}
// src\views\edu\course\publish.vue
<script>
import course from '@/api/edu/course'
export default {
  methods: {
    publish() {
      course.publishCourse(this.courseId)
        .then(response => {
          // 提示
          this.$message({
            type: 'success',
            message: '课程发布成功!'
          });
          // 跳转课程列表页面
          this.$router.push({ path: '/course/list' })
        })
    }
  }
}
script>

21. 课程列表

【项目】小帽学堂(九)_第1张图片

@RestController
@RequestMapping("/eduservice/course")
@CrossOrigin
public class EduCourseController {

    @Autowired
    private EduCourseService courseService;

    // 课程列表 基本实现
    // TODO 完善条件查询带分页
    @GetMapping
    public R getCourseList() {
        List<EduCourse> list = courseService.list(null);
        return R.ok().data("list",list);
    }
}
// src\api\edu\course.js
import request from '@/utils/request'

export default {
    // TODO 课程列表
    getListCourse() {
      return request({
        url: '/eduservice/course',
        method: 'get'
      })
    }
}
// src\views\edu\course\list.vue
<template>
  <div class="app-container">
    课程列表
    
    <el-form :inline="true" class="demo-form-inline">
      <el-form-item>
        <el-input v-model="courseQuery.title" placeholder="课程名称"/>
      el-form-item>
      <el-form-item>
        <el-select v-model="courseQuery.status" clearable placeholder="课程状态">
          <el-option :value="Normal" label="已发布"/>
          <el-option :value="Draft" label="未发布"/>
        el-select>
      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>
    
    <el-table
      :data="list"
      border
      fit
      highlight-current-row>
      <el-table-column
        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="80" />
      <el-table-column label="课程状态" width="80">
        <template slot-scope="scope">
          {{ scope.row.status==='Normal'?'已发布':'未发布' }}
        template>
      el-table-column>
      <el-table-column prop="lessonNum" label="课时数" />
      <el-table-column prop="gmtCreate" label="添加时间" width="160"/>
      <el-table-column prop="viewCount" label="浏览数量" width="60" />
      <el-table-column label="操作" width="200" align="center">
        <template slot-scope="scope">
          <router-link :to="'/teacher/edit/'+scope.row.id">
            <el-button type="primary" size="mini" icon="el-icon-edit">编辑课程基本信息el-button>
          router-link>
          <router-link :to="'/teacher/edit/'+scope.row.id">
            <el-button type="primary" size="mini" icon="el-icon-edit">编辑课程大纲信息el-button>
          router-link>
          <el-button type="danger" size="mini" icon="el-icon-delete" @click="removeDataById(scope.row.id)">删除课程信息el-button>
        template>
      el-table-column>
    el-table>

   
    <el-pagination
      :current-page="page"
      :page-size="limit"
      :total="total"
      style="padding: 30px 0; text-align: center;"
      layout="total, prev, pager, next, jumper"
      @current-change="getList"
    />
  div>
template>
<script>
// 引入调用course.js文件
import course from '@/api/edu/course'
export default {
  // 写核心代码位置
  data() {  // 定义变量和初始值
    return {
      list: null, // 查询之后接口返回的集合
      page: 1,  // 当前页
      limit: 10,  // 每页记录数
      total: 0, // 总记录数
      courseQuery: {}  // 条件封装对象
    }
  },
  created() { // 页面渲染之前执行,一般调用methods定义的方法
    this.getList()
  },
  methods: {  // 创建具体的方法,调用teacher.js定义的方法
    // 讲师列表的方法
    getList() {
      course.getListCourse()
      .then(response => { // 请求成功
        // response 接口返回的数据
        // console.log(response);
        this.list= response.data.list
      })
    },
    resetData() { // 清空的方法
      // 表单输入项数据清空
      this.courseQuery = {}
      // 查询所有讲师数据
      this.getList()
    },
    
  }
}
script>

22. 删除课程(后端)

【项目】小帽学堂(九)_第2张图片

@RestController
@RequestMapping("/eduservice/course")
@CrossOrigin
public class EduCourseController {

    @Autowired
    private EduCourseService courseService;
    
    // 删除课程
    @DeleteMapping("{courseId}")
    public R deleteCourse(@PathVariable String courseId) {
        courseService.removeCourse(courseId);
        return R.ok();
    }
}
@Service
public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {
    // 课程描述注入
    @Autowired
    private EduCourseDescriptionService courseDescriptionService;

    // 注入小节和章节service
    @Autowired
    private EduVideoService eduVideoService;

    @Autowired
    private EduChapterService eduChapterService;

    // 删除课程
    @Override
    public void removeCourse(String courseId) {
        // 1.根据课程id删除小节
        eduVideoService.removeVideoByCourseId(courseId);
        // 2.根据课程id删除章节
        eduChapterService.removeChapterByCourseId(courseId);
        // 3.根据课程id删除描述
        courseDescriptionService.removeById(courseId);
        // 4.根据课程id删除课程本身
        int result = baseMapper.deleteById(courseId);
        if(result == 0) {   // 失败返回
            throw new LemonException(20001, "删除失败");
        }
    }
}
public interface EduVideoService extends IService<EduVideo> {
    void removeVideoByCourseId(String courseId);
}
@Service
public class EduVideoServiceImpl extends ServiceImpl<EduVideoMapper, EduVideo> implements EduVideoService {
    // 根据课程id删除小节
    // TODO 删除小节,删除对应视频文件
    @Override
    public void removeVideoByCourseId(String courseId) {
        QueryWrapper<EduVideo> wrapper = new QueryWrapper<>();
        wrapper.eq("course_id", courseId);
        baseMapper.delete(wrapper);
    }
}
public interface EduChapterService extends IService<EduChapter> {
    // 根据课程id删除章节
    void removeChapterByCourseId(String courseId);
}
@Service
public class EduChapterServiceImpl extends ServiceImpl<EduChapterMapper, EduChapter> implements EduChapterService {

    @Autowired
    private EduVideoService videoService;   // 注入小节service

    // 根据课程id删除章节
    @Override
    public void removeChapterByCourseId(String courseId) {
        QueryWrapper<EduChapter> wrapper = new QueryWrapper<>();
        wrapper.eq("course_id", courseId);
        baseMapper.delete(wrapper);
    }
}

23. 阿里云视频点播介绍

【项目】小帽学堂(九)_第3张图片

  • 视频点播(ApsaraVideo for VoD)是集音视频采集、编辑、上传、自动化转码处理、媒体资源管理、分发加速于一体的一站式音视频点播解决方案。
  • 应用场景
    • 音视频网站:无论是初创视频服务企业,还是已拥有海量视频资源,可定制化的点播服务帮助客户快速搭建拥有极致观看体验、安全可靠的视频点播应用。
    • 短视频:集音视频拍摄、特效编辑、本地转码、高速上传、自动化云端转码、媒体资源管理、分发加速、播放于一体的完整短视频解决方案。目前已帮助1000+APP快速实现手机短视频功能。
    • 直播转点播:将直播流同步录制为点播视频,用于回看。并支持媒资管理、媒体处理(转码及内容审核/智能首图等AI处理)、内容制作(云剪辑)、CDN分发加速等一系列操作。
    • 在线教育:为在线教育客户提供简单易用、安全可靠的视频点播服务。可通过控制台/API等多种方式上传教学视频,强大的转码能力保证视频可以快速发布,覆盖全网的加速节点保证学生观看的流畅度。防盗链、视频加密等版权保护方案保护教学内容不被窃取。
    • 视频生产制作:提供在线可视化剪辑平台及丰富的OpenAPI,帮助客户高效处理、制作视频内容。除基础的剪切拼接、混音、遮标、特效、合成等一系列功能外,依托云剪辑及点播一体化服务还可实现标准化、智能化剪辑生产,大大降低视频制作的槛,缩短制作时间,提升内容生产效率。
    • 内容审核:应用于短视频平台、传媒行业审核等场景,帮助客户从从语音、文字、视觉等多维度精准识别视频、封面、标题或评论的违禁内容进行AI智能审核与人工审核。
  • 功能介绍
    【项目】小帽学堂(九)_第4张图片

24. 阿里云视频点播SDK(获取视频地址)

【项目】小帽学堂(九)_第5张图片

  • 创建视频点播微服务

    • 创建微服务模块 Artifact:service-vod
    • pom
      • service-vod中引入依赖
    <dependencies>
            <dependency>
                <groupId>com.aliyungroupId>
                <artifactId>aliyun-java-sdk-coreartifactId>
            dependency>
            <dependency>
                <groupId>com.aliyun.ossgroupId>
                <artifactId>aliyun-sdk-ossartifactId>
            dependency>
            <dependency>
                <groupId>com.aliyungroupId>
                <artifactId>aliyun-java-sdk-vodartifactId>
            dependency>
            <dependency>
                <groupId>com.aliyungroupId>
                <artifactId>aliyun-sdk-vod-uploadartifactId>
            dependency>
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>fastjsonartifactId>
            dependency>
            <dependency>
                <groupId>org.jsongroupId>
                <artifactId>jsonartifactId>
            dependency>
            <dependency>
                <groupId>com.google.code.gsongroupId>
                <artifactId>gsonartifactId>
            dependency>
            <dependency>
                <groupId>joda-timegroupId>
                <artifactId>joda-timeartifactId>
            dependency>
    dependencies>
    
  • 测试

    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 class TestVod {
        public static void main(String[] args) throws Exception{
            // 1.根据视频id获取视频播放地址
            // 创建初始化对象
            DefaultAcsClient client = InitObject.initVodClient("秘钥", "密码");
    
            // 创建获取视频地址request和response
            GetPlayInfoRequest request = new GetPlayInfoRequest();
            GetPlayInfoResponse response = new GetPlayInfoResponse();
    
            // 向request对象里面设置视频id
            request.setVideoId("7272bc8cc64344c2a3209c15881712fe");
            // 调用初始化对象里面的方法,传递request,获取数据
            response = client.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");
        }
    }
    

25. 阿里云视频点播SDK(获取视频凭证)

public class TestVod {
    public static void main(String[] args) throws Exception{
        // 2.根据视频id获取视频播放凭证
        // 创建初始化对象
        // 创建初始化对象
        DefaultAcsClient client = InitObject.initVodClient("秘钥", "密码");

        // 创建获取视频凭证request和response
        GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();
        GetVideoPlayAuthResponse response = new GetVideoPlayAuthResponse();

        // 向request对象里面设置视频id
        request.setVideoId("7272bc8cc64344c2a3209c15881712fe");
        // 调用初始化对象里面的方法,传递request,获取数据
        response = client.getAcsResponse(request);
        System.out.println("platauth: " + response.getPlayAuth());
    }
}

26. 阿里云视频点播(上传视频)

【项目】小帽学堂(九)_第6张图片

public class TestVod {
    public static void main(String[] args){
        String accessKeyId = "秘钥";
        String accessKeySecret = "密码";

        String title = "3 - How Does Project Submission Work - upload by sdk";  // 上传之后文件的名称
        String fileName = "D:/abc/3 - How Does Project Submission Work.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);
        System.out.print("RequestId=" + response.getRequestId() + "\n");  //请求视频点播服务的请求ID
        if (response.isSuccess()) {
            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");
        }
    }
}

27. 添加小节上传视频(后端)

【项目】小帽学堂(九)_第7张图片

  • 创建微服务模块(同上)

    • pom 引入依赖(同上)
    • application.properties
    # 服务端口
    server.port=8003
    # 服务名
    spring.application.name=service-vod
    # 环境设置:dev、test、prod
    spring.profiles.active=dev
    #阿里云 vod
    #不同的服务器,地址不同
    aliyun.vod.file.keyid=your accessKeyId
    aliyun.vod.file.keysecret=your accessKeySecret
    # 最大上传单个文件大小:默认1M
    spring.servlet.multipart.max-file-size=1024MB
    # 最大置总上传的数据大小 :默认10M
    spring.servlet.multipart.max-request-size=1024MB
    
    • 启动类
    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
    @ComponentScan(basePackages = {"com.alex"})
    public class VodApplication {
        public static void main(String[] args) {
            SpringApplication.run(VodApplication.class, args);
        }
    }
    
  • 整合阿里云vod实现视频上传

// 创建常量类
@Component
public class ConstantVodUtils implements InitializingBean {

    @Value("${aliyun.vod.file.keyid}")
    private String keyid;

    @Value("${aliyun.vod.file.keysecret}")
    private String keysecret;

    public static String ACCESS_KEY_ID;
    public static String ACCESS_KEY_SECRET;

    @Override
    public void afterPropertiesSet() throws Exception {
        ACCESS_KEY_ID = keyid;
        ACCESS_KEY_SECRET = keysecret;
    }
}
@RestController
@RequestMapping("/eduvod/video")
@CrossOrigin
public class VodController {

    @Autowired
    private VodService vodService;

    // 上传视频到阿里云
    @PostMapping("uploadAlyVideo")
    public R uploadAlyVideo(MultipartFile file) {
        // 返回上传视频id
        String videoId = vodService.uploadVideoAly(file);
        return R.ok().data("videoId", videoId);
    }
}
public interface VodService {
    String uploadVideoAly(MultipartFile file);
}
@Service
public class VodServiceImpl implements VodService {
    @Override
    public String uploadVideoAly(MultipartFile file) {
        try {
            // accessKeyId,accessKeySecret
            // fileName:上传文件原始名称
            // 01.mp4
            String fileName = file.getOriginalFilename();
            // title:上传之后显示名称
            String title = fileName.substring(0, fileName.lastIndexOf("."));
            // inputStream:上传文件输入流
            InputStream inputStream = file.getInputStream();
            UploadStreamRequest request = new UploadStreamRequest(ConstantVodUtils.ACCESS_KEY_ID, ConstantVodUtils.ACCESS_KEY_SECRET, title, fileName, inputStream);
            UploadVideoImpl uploader = new UploadVideoImpl();
            UploadStreamResponse response = uploader.uploadStream(request);
            String videoId = null;
            if (response.isSuccess()) {
                videoId = response.getVideoId();
            } else {
                videoId = response.getVideoId();
            }
            return videoId;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@ComponentScan(basePackages = {"com.alex"})
public class VodApplication {
    public static void main(String[] args) {
        SpringApplication.run(VodApplication.class, args);
    }
}

28. 添加小节上传视频(前端)

【项目】小帽学堂(九)_第8张图片

  • 配置nginx反向代理

    location ~ /vod/ {           
        proxy_pass http://localhost:8003;
    }
    
    • 配置nginx上传文件大小,否则上传时会有 413 (Request Entity Too Large) 异常
    • 打开nginx主配置文件nginx.conf,找到http{},添加
      client_max_body_size 1024m;
      
    • 重启nginx
      nginx -s reload
      # PS:停止nginx命令
      nginx -s stop
      
  • 前端实现(src\views\edu\course\chapter.vue)

    • 数据定义
    fileList: [],//上传文件列表
    BASE_API: process.env.BASE_API // 接口API地址
    
    • 整合上传组件
    <el-form-item label="上传视频">
        <el-upload
               :on-success="handleVodUploadSuccess"
               :on-remove="handleVodRemove"
               :before-remove="beforeVodRemove"
               :on-exceed="handleUploadExceed"
               :file-list="fileList"
               :action="BASE_API+'/eduvod/video/uploadAlyVideo'"
               :limit="1"
               class="upload-demo">
        <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>
    
    • 方法定义
    // 上传视频成功调用的方法
    handleVodUploadSuccess(response, file, fileList) {
      // 上传视频id赋值
      this.video.videoSourceId = response.data.videoId
      // 上传视频名称赋值
      this.video.videoOriginalName = file.name
    },
    handleUploadExceed() {
      this.$message.warning('想要重新上传视频,请先删除已上传的视频')
    },
    

你可能感兴趣的:(项目,java,开发语言,后端)