XC-14媒资管理

视频处理

需求分析

原始视频通常需要经过编码处理,生成m3u8和ts文件放可以基于HLS协议播放视频,通常用户上传原始视频,系统自动处理成标准格式,系统对用户上传的视频自动编码,转换,最总生成m3u8文件和ts文件,处理流程如下:
1.用户上传视频成功
2.系统对上传成功的视频自动开始编码处理
3.用户查看视频处理结果,没有处理成功的视频用户可以再管理界面再次促发处理
4.视频处理完成将视频地址及处理结果保存到数据库

视频处理流程如下:

视频处理开发

视频处理工程创建

单独处理视频进程处理,不在媒资上传项目中处理.导入项目
在这里插入图片描述
2.RabbitMQ配置
使用RabbitMQ的routing交换机模式,视频处理程序监听视频处理队列,如下图:
在这里插入图片描述
RabbitMQ配置

package com.xuecheng.manage_media_process.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author Administrator
 * @version 1.0
 * @create 2018-07-12 9:04
 **/
@Configuration
public class RabbitMQConfig {

    public static final String EX_MEDIA_PROCESSTASK = "ex_media_processor";

    //视频处理队列
    @Value("${xc-service-manage-media.mq.queue-media-video-processor}")
    public  String queue_media_video_processtask;

    //视频处理路由
    @Value("${xc-service-manage-media.mq.routingkey-media-video}")
    public  String routingkey_media_video;

    //消费者并发数量
    public static final int DEFAULT_CONCURRENT = 10;


    /**
     * 交换机配置
     * @return the exchange
     */
    @Bean(EX_MEDIA_PROCESSTASK)
    public Exchange EX_MEDIA_VIDEOTASK() {
        return ExchangeBuilder.directExchange(EX_MEDIA_PROCESSTASK).durable(true).build();
    }
    //声明队列
    @Bean("queue_media_video_processtask")
    public Queue QUEUE_PROCESSTASK() {
        Queue queue = new Queue(queue_media_video_processtask,true,false,true);
        return queue;
    }
    /**
     * 绑定队列到交换机 .
     * @param queue    the queue
     * @param exchange the exchange
     * @return the binding
     */
    @Bean
    public Binding binding_queue_media_processtask(@Qualifier("queue_media_video_processtask") Queue queue, @Qualifier(EX_MEDIA_PROCESSTASK) Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(routingkey_media_video).noargs();
    }
}

视频处理技术方案

如何通过程序进行视频处理?
ffmpeg是一个可行的视频处理程序,可以通过java调用ffmpeg.exe完成视频处理.
在java中可以使用Runtime类和ProcessBuilder类两种方式来执行外部程序,工作中至少掌握一种,本项目使用ProcessBuilder的方式来调用ffmpeg完成视频处理.
Runtime可以线下查询一些资料进行自学
这里使用ProcessBuilder
关于ProcessBuilder测试如下:

package com.xuecheng.manage_media_process;

import com.xuecheng.framework.utils.Mp4VideoUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

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

/**
 * @author Administrator
 * @version 1.0
 * @create 2018-07-12 9:11
 **/
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestProcessBuilder {

    //使用ProcessBuilder来调用第三饭程序
    @Test
    public void testProcessBuilder() throws IOException {
//       首先,创建一个processBuilder对象
        ProcessBuilder processBuilder = new ProcessBuilder();
//      设置第三方程序的命令
        processBuilder.command("ping", "127.0.0.1");


//        将标输入流和错误流合并
        processBuilder.redirectErrorStream(true);

//        启动进程,相当于在命令行输入了这样一个命令
        Process process = processBuilder.start();

//        通过标准输入流,拿到正常和错误的信息了
        InputStream inputStream = process.getInputStream();

//        装成字符流
//        相当于包装了一个字符流
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"gbk");

//        缓冲
        char[] chars = new char[1024];
        int len = -1;
        while ((len = inputStreamReader.read(chars))!=-1){
            String string = new String(chars,0,len);
            System.out.println(string);
        }
        inputStream.close();
        inputStreamReader.close();

    }

}

在这里插入图片描述
测试成功

视频处理实现

    //使用ProcessFFmpeg来调用第三饭程序
    @Test
    public void testProcessFFmpeg() throws IOException {
//       首先,创建一个processBuilder对象
        ProcessBuilder processBuilder = new ProcessBuilder();
//      设置第三方程序的命令
//因为调用FFmpeg会有很多的参数,所以这里用list
        List<String> command = new ArrayList<>();
        command.add("D:\\BaiduNetdiskDownload\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe");
        command.add("-i");
        command.add("E:\\jetbrain\\video\\lucene.avi");
        command.add("-y");
        command.add("-c:v");
        command.add("libx264");
        command.add("-s");
        command.add("1280x720");
        command.add("-pix_fmt");
        command.add("yuv420p");
        command.add("-b:a");
        command.add("63k");
        command.add("-b:v");
        command.add("753k");
        command.add("-r");
        command.add("18");
        command.add("E:\\jetbrain\\video\\1.mp4");
        processBuilder.command(command);

//        将标输入流和错误流合并
        processBuilder.redirectErrorStream(true);

//        启动进程,相当于在命令行输入了这样一个命令
        Process process = processBuilder.start();

//        通过标准输入流,拿到正常和错误的信息了
        InputStream inputStream = process.getInputStream();

//        装成字符流
//        相当于包装了一个字符流
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"gbk");

//        缓存
        char[] chars = new char[1024];
        int len = -1;
        while ((len = inputStreamReader.read(chars))!=-1){
            String string = new String(chars,0,len);
            System.out.println(string);
        }
        inputStream.close();
        inputStreamReader.close();

    }
   

在这里插入图片描述
处理成功,将lucene.avi转换成1.MP4
在这里插入图片描述

//    调用mp4Util使用避免直接写processBuilder
    @Test
    public void testMp4VideoUtil(){
//String ffmpeg_path,Stirng videoo_path ,String mp4_name,  String mp4folder_path
        String ffmpeg_path = "D:\\BaiduNetdiskDownload\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe";
        String video_path  = "E:\\jetbrain\\video\\lucene.avi";
        String mp4_name = "2.mp4";
        String mp4folder_path = "E:\\jetbrain\\video\\";

        Mp4VideoUtil mp4VideoUtil = new Mp4VideoUtil(ffmpeg_path,video_path,mp4_name,mp4folder_path);
//        生成mp4
        String result = mp4VideoUtil.generateMp4();
        System.out.println(result);
    }

}




在这里插入图片描述
在这里插入图片描述
使用mp4VedioUtil工具类成功

确定消息格式

MQ消息统一采用json格式,视频处理生产方会向MQ发送如下消息,视频处理消费方接受此消息后会进行视频处理:
{“mediald”:XXX}

处理流程

1.接受视频处理消息
2.判断媒体文件是否需要处理(本视频处理程序目前只接收avi视频的处理)
当前只有avi文件需要处理,其他文件需要更新处理状态为"无需处理".
3.处理前初始化处理状态为"未处理:
4.处理失败需要在数据库记录处理日志,及处理状态为"处理失败".
5.处理成功记录处理状态为"处理成功"
在这里插入图片描述

package com.xuecheng.manage_media_process.mq;

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @author Andrewer
 * @version 1.0
 * @project xcEduService01
 * @description Rabbitmq
 * @date 2023/1/7 10:10:57
 */
@Component
public class MediaProcessTask {
//    接收视频处理消息进行视频处理
    @RabbitListener(queues = "${xc-service-manage-media.mq.queue-media-video-processor}")
    public void receiveMediaProcessTask(String msg){
//        1.解析消息内容得到mediaid
//        2.那mediaId从数据库查询文件信息
//        3.使用工具类将avi文件生成mp4
//        4.将MP4生成m3u8和ts文件     

    }

}

数据模型
视频处理生成Mp4
视频处理生成m3u8
package com.xuecheng.manage_media_process.mq;

import com.alibaba.fastjson.JSON;
import com.xuecheng.framework.domain.media.MediaFile;
import com.xuecheng.framework.domain.media.MediaFileProcess_m3u8;
import com.xuecheng.framework.utils.HlsVideoUtil;
import com.xuecheng.framework.utils.Mp4VideoUtil;
import com.xuecheng.manage_media_process.dao.MediaFileRepository;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @author Andrewer
 * @version 1.0
 * @project xcEduService01
 * @description Rabbitmq
 * @date 2023/1/7 10:10:57
 */
@Component
public class MediaProcessTask {
    @Value("${xc-service-manage-media.ffmpeg-path}")
    String ffmpeg_path;
    @Value("${xc-service-manage-media.video-location}")
    String video_location;
    @Autowired
    MediaFileRepository mediaFileRepository;

    //    接收视频处理消息进行视频处理
    @RabbitListener(queues = "${xc-service-manage-media.mq.queue-media-video-processor}")
    public void receiveMediaProcessTask(String msg) {
//        1.解析消息内容得到mediaid
        Map map = JSON.parseObject(msg, Map.class);
        Object mediaId = map.get("mediaId");
//        2.那mediaId从数据库查询文件信息
        Optional<MediaFile> optional = mediaFileRepository.findById((String) mediaId);
        if (!optional.isPresent()) {
            return;

        }
        MediaFile mediaFile = optional.get();
//        判断文件的拓展名是什么类型
        String fileType = mediaFile.getFileType();
        if (!fileType.equals("avi")) {
            mediaFile.setProcessStatus("303004");//无需处理
            mediaFileRepository.save(mediaFile);
        } else {
            mediaFile.setProcessStatus("303001");//处理中
        }
//        3.使用工具类将avi文件生成mp4

//String ffmpeg_path,Stirng videoo_path ,String mp4_name,  String mp4folder_path
//        要处理的视频文件的路径
//        生成的mp4的文件名

        String vedeo_path = video_location + mediaFile.getFilePath() + mediaFile.getFileName();
        String mp4_name = mediaFile.getFileId() + ".mp4";
//        生成的mp4所在的路径

        String mp4folder_path = vedeo_path + mediaFile.getFilePath();
        Mp4VideoUtil mp4VideoUtil = new Mp4VideoUtil(ffmpeg_path, vedeo_path, mp4_name, mp4folder_path);
//        运行处理
        String result = mp4VideoUtil.generateMp4();
        if (result == null || !result.equals("success")) {
//            处理失败
//              记录处理失败的状态
            mediaFile.setProcessStatus("303003");
//            记录失败原因
            MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
            mediaFileProcess_m3u8.setErrormsg(result);
            mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);//记录失败原因
            mediaFileRepository.save(mediaFile);
            return;
        }
//            处理成功
//        4.将MP4生成m3u8和ts文件
//        String ffmpeg_path,String video_path,String m3u8_name,String m3u8folder_path
//        mp4视频文件的路径
        String mp4_video_path = vedeo_path + mediaFile.getFilePath() + mp4_name;
//        m3u8的文件名称
        String m3u8_name = mediaFile.getFileId() + ".m3u8";
//        m3u8文件所在的目录
        String m3u8folder_path = vedeo_path + mediaFile.getFilePath() + "hls/";
        HlsVideoUtil hlsVideoUtil = new HlsVideoUtil(ffmpeg_path, mp4_video_path, m3u8_name, m3u8folder_path);
//生成m3u8文件和ts文件
        String tsresult = hlsVideoUtil.generateM3u8();
        if (tsresult == null || !tsresult.equals("success")) {
//            和上边的代码一摸一样
//            处理失败
//              记录处理失败的状态
            mediaFile.setProcessStatus("303003");
//            记录失败原因
            MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
            mediaFileProcess_m3u8.setErrormsg(result);
            mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);//记录失败原因
            mediaFileRepository.save(mediaFile);
            return;

        }
//        处理成功
//        获取ts文件的列表
        List<String> ts_list = hlsVideoUtil.get_ts_list();

        mediaFile.setProcessStatus("303002");

//        定义mediaFileProcess_m3u8
        MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
        mediaFileProcess_m3u8.setTslist(ts_list);
        mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);

//        保存fileURL
//        这个url就是视频播放的相对路径
        String fileUrl = mediaFile.getFilePath() + "hls/" + m3u8_name;
        mediaFile.setFileUrl(fileUrl);


        mediaFileRepository.save(mediaFile);

    }

}

发送视频处理消息

当视频上传成功后,向MQ发送视频,处理消息.
修改媒资管理类服务的文件上传代码,当文件上传成功向MQ发送视频处理消息.

RabbitMq配置

1.将media-processor工程下的RabbitMQConfig配置类拷贝到media工程下.
2.在media工程下配置mq队列等消息
修改application.yml

修改Service

视频处理测试

package com.xuecheng.manage_media_process.mq;

import com.alibaba.fastjson.JSON;
import com.xuecheng.framework.domain.media.MediaFile;
import com.xuecheng.framework.domain.media.MediaFileProcess_m3u8;
import com.xuecheng.framework.utils.HlsVideoUtil;
import com.xuecheng.framework.utils.Mp4VideoUtil;
import com.xuecheng.manage_media_process.dao.MediaFileRepository;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @author Andrewer
 * @version 1.0
 * @project xcEduService01
 * @description Rabbitmq
 * @date 2023/1/7 10:10:57
 */
@Component
public class MediaProcessTask {
    @Value("${xc-service-manage-media.ffmpeg-path}")
    String ffmpeg_path;
    @Value("${xc-service-manage-media.video-location}")
    String video_location;
    @Autowired
    MediaFileRepository mediaFileRepository;

    //    接收视频处理消息进行视频处理
    @RabbitListener(queues = "${xc-service-manage-media.mq.queue-media-video-processor}")
    public void receiveMediaProcessTask(String msg) {
//        1.解析消息内容得到mediaid
        Map map = JSON.parseObject(msg, Map.class);
        Object mediaId = map.get("mediaId");
//        2.那mediaId从数据库查询文件信息
        Optional<MediaFile> optional = mediaFileRepository.findById((String) mediaId);
        if (!optional.isPresent()) {
            return;

        }
        MediaFile mediaFile = optional.get();
//        判断文件的拓展名是什么类型
        String fileType = mediaFile.getFileType();
        if (!fileType.equals("avi")) {
            mediaFile.setProcessStatus("303004");//无需处理
            mediaFileRepository.save(mediaFile);
        } else {
            mediaFile.setProcessStatus("303001");//处理中
        }
//        3.使用工具类将avi文件生成mp4

//String ffmpeg_path,Stirng videoo_path ,String mp4_name,  String mp4folder_path
//        要处理的视频文件的路径
//        生成的mp4的文件名

        String vedeo_path = video_location + mediaFile.getFilePath() + mediaFile.getFileName();
        String mp4_name = mediaFile.getFileId() + ".mp4";
//        生成的mp4所在的路径

        String mp4folder_path = video_location + mediaFile.getFilePath();
        Mp4VideoUtil mp4VideoUtil = new Mp4VideoUtil(ffmpeg_path, vedeo_path, mp4_name, mp4folder_path);
//        运行处理
        String result = mp4VideoUtil.generateMp4();
        if (result == null || !result.equals("success")) {
//            处理失败
//              记录处理失败的状态
            mediaFile.setProcessStatus("303003");
//            记录失败原因
            MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
            mediaFileProcess_m3u8.setErrormsg(result);
            mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);//记录失败原因
            mediaFileRepository.save(mediaFile);
            return;
        }
//            处理成功
//        4.将MP4生成m3u8和ts文件
//        String ffmpeg_path,String video_path,String m3u8_name,String m3u8folder_path
//        mp4视频文件的路径
        String mp4_video_path = video_location + mediaFile.getFilePath() + mp4_name;
//        m3u8的文件名称
        String m3u8_name = mediaFile.getFileId() + ".m3u8";
//        m3u8文件所在的目录
        String m3u8folder_path = video_location + mediaFile.getFilePath() + "hls/";
        HlsVideoUtil hlsVideoUtil = new HlsVideoUtil(ffmpeg_path, mp4_video_path, m3u8_name, m3u8folder_path);
//生成m3u8文件和ts文件
        String tsresult = hlsVideoUtil.generateM3u8();
        if (tsresult == null || !tsresult.equals("success")) {
//            和上边的代码一摸一样
//            处理失败
//              记录处理失败的状态
            mediaFile.setProcessStatus("303003");
//            记录失败原因
            MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
            mediaFileProcess_m3u8.setErrormsg(result);
            mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);//记录失败原因
            mediaFileRepository.save(mediaFile);
            return;

        }
//        处理成功
//        获取ts文件的列表
        List<String> ts_list = hlsVideoUtil.get_ts_list();

        mediaFile.setProcessStatus("303002");

//        定义mediaFileProcess_m3u8
        MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
        mediaFileProcess_m3u8.setTslist(ts_list);
        mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);

//        保存fileURL
//        这个url就是视频播放的相对路径
        String fileUrl = mediaFile.getFilePath() + "hls/" + m3u8_name;
        mediaFile.setFileUrl(fileUrl);


        mediaFileRepository.save(mediaFile);

     }

}

视频处理并发设置

代码中使用@RabbitListener注解指定消费方法,默认情况是单线程监听队列,可以观察当队列有多个任务是消费端每次只消费一个消息,单线程处理消息容易引起消息处理缓慢,消息堆积,不能最大利用硬件资源.
可以配置mq的容器工厂参数,增加并发处理数量即可实现多线程监听队列,实现多线程处理消息.
1.在RabbitmqConfig.java中添加容器工厂配置:

    //消费者并发数量
    public static final int DEFAULT_CONCURRENT = 10;


    @Bean("customContainerFactory")
    public SimpleRabbitListenerContainerFactory containerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer,
                                                                 ConnectionFactory connectionFactory){
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConcurrentConsumers(DEFAULT_CONCURRENT);
        factory.setMaxConcurrentConsumers(DEFAULT_CONCURRENT);
        configurer.configure(factory,connectionFactory);
        return factory;

    }


在mqTask中指定容器

    //    接收视频处理消息进行视频处理
    @RabbitListener(queues = "${xc-service-manage-media.mq.queue-media-video-processor}", containerFactory = "customContainerFactory")
    public void receiveMediaProcessTask(String msg) {
//        1.解析消息内容得到mediaid
        Map map = JSON.parseObject(msg, Map.class);
        Object mediaId = map.get("mediaId");
//        2.那mediaId从数据库查询文件信息
        Optional<MediaFile> optional = mediaFileRepository.findById((String) mediaId);
        if (!optional.isPresent()) {
            return;

在这里插入图片描述

我的媒资

需求分析

通过我的媒资可以铲鲟被教育机构拥有的妹子文件,阱行文件处理,删除文件,修改文件信息等操作,具体需求如下:
1.分页查询我的媒资文件
2.删除媒资文件
3.处理媒资文件
4.修改媒资文件
在这里插入图片描述

在这里插入图片描述

API

接口编写:

package com.xuecheng.api.media;

import com.xuecheng.framework.domain.media.MediaFile;
import com.xuecheng.framework.domain.media.request.QueryMediaFileRequest;
import com.xuecheng.framework.model.response.QueryResponseResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

/**
 * @author Andrewer
 * @version 1.0
 * @project xcEduService01
 * @description 管理媒资文件
 * @date 2023/1/7 16:35:16
 */
    @Api(value = "媒体文件管理" ,description = "媒体文件管理接口",tags = "媒体文件的增删改查")
public interface MediaFileController {
        @ApiOperation("我的媒体文件查询列表")
//这里的查询条件,专门写一个类类型
    public QueryResponseResult<MediaFile>  findList(int page, int size, QueryMediaFileRequest queryMediaFileRequest);
}

服务端开发

Dao

package com.xuecheng.manage_media.dao;

import com.xuecheng.framework.domain.media.MediaFile;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;


public interface MediaFileRepository extends MongoRepository<MediaFile,String> {
}

Service

Service

package com.xuecheng.manage_media.service;

import com.xuecheng.framework.domain.media.MediaFile;
import com.xuecheng.framework.domain.media.request.QueryMediaFileRequest;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.QueryResponseResult;
import com.xuecheng.framework.model.response.QueryResult;
import com.xuecheng.manage_media.dao.MediaFileRepository;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author Andrewer
 * @version 1.0
 * @project xcEduService01
 * @description
 * @date 2023/1/7 16:46:38
 */
@Service
public class MediaFileService {
    @Autowired
    MediaFileRepository mediaFileRepository;

    //分页查询
    public QueryResponseResult<MediaFile> findList(int page, int size, QueryMediaFileRequest queryMediaFileRequest) {

//        为了保证代码的正确性,这里对queryMediaFileRequest进行条件判断
        if (queryMediaFileRequest == null) {
            queryMediaFileRequest = new QueryMediaFileRequest();
        }

//       条件值对象
        MediaFile mediaFile = new MediaFile();


        if (StringUtils.isNotEmpty(queryMediaFileRequest.getTag())) {
            mediaFile.setTag(queryMediaFileRequest.getTag());
        }
        if (StringUtils.isNotEmpty(queryMediaFileRequest.getFileOriginalName())) {
            mediaFile.setFileOriginalName(queryMediaFileRequest.getFileOriginalName());
        }
        if (StringUtils.isNotEmpty(queryMediaFileRequest.getProcessStatus())) {
            mediaFile.setProcessStatus(queryMediaFileRequest.getProcessStatus());
        }


//       条件匹配器
        ExampleMatcher exampleMatcher = ExampleMatcher.matching()
                .withMatcher("tag", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("fileOriginalName", ExampleMatcher.GenericPropertyMatchers.contains());

//        定义example 条件对象
        Example<MediaFile> example = Example.of(mediaFile, exampleMatcher);
//        分页查询对象
        if (page <= 0) {
            page = 1;
        }
        page = page - 1;
        if (size <= 0) {
            size = 10;
        }
        Pageable pageable = new PageRequest(page, size);
//        分页查询
        Page<MediaFile> all = mediaFileRepository.findAll(example, pageable);
//        总记录数
        long totalElements = all.getTotalElements();
//        数据列表
        List<MediaFile> content = all.getContent();
//返回的数据集
        QueryResult<MediaFile> queryResult = new QueryResult<>();
        queryResult.setList(content);
        queryResult.setTotal(totalElements);
        QueryResponseResult queryResponseResult = new QueryResponseResult(CommonCode.SUCCESS, queryResult);
        return queryResponseResult;

    }
}

Controller

package com.xuecheng.manage_media.controller;

import com.xuecheng.api.media.MediaFileControllerApi;
import com.xuecheng.framework.domain.media.MediaFile;
import com.xuecheng.framework.domain.media.request.QueryMediaFileRequest;
import com.xuecheng.framework.model.response.QueryResponseResult;
import com.xuecheng.manage_media.service.MediaFileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Andrewer
 * @version 1.0
 * @project xcEduService01
 * @description 媒资管理的增删改查
 * @date 2023/1/7 16:43:23
 */


@RestController
@RequestMapping("/media/file")
public class MediaFileController implements MediaFileControllerApi {
    @Autowired
    MediaFileService mediaFileService;
    @Override
    @GetMapping("/list/{page}/{size}")
    public QueryResponseResult<MediaFile> findList(@PathVariable("page") int page, @PathVariable("size") int size, QueryMediaFileRequest queryMediaFileRequest) {

        return mediaFileService.findList(page,size,queryMediaFileRequest);
    }
}

前端开发

API方法

  query(){
        mediaApi.media_list(this.params.page,this.params.size,this.params).then((res)=>{
          console.log(res)
          this.total = res.queryResult.total
          this.list = res.queryResult.list
        })
      }
    },

路由路径

/*页面列表*/
export const media_list = (page,size,params) => {
  //params为json格式
  //使用querystring将json对象转成key/value串
  let querys = querystring.stringify(params)
  return http.requestQuickGet(apiUrl+'/media/file/list/'+page+'/'+size+'/?'+querys)
}

页面

在这里插入图片描述

这里还有增删改查,我懒得做了

媒资与课程计划关联

需求分析

到目前位置,媒资管理已完成文件上传,视频处理,我的媒资等基本功能,其他模块亦可以使用媒资管理功能,本节主要讲解课程计划在编辑时如何选媒资文件.
操作的业务流程如下:
1.进入课程计划修改页面
2.选择视频
打开媒资文件查询窗口,找到该课程章节的视频,选择此视频

点击选择媒资文件,打开媒资文件列表
在这里插入图片描述

选择视频

Vue父子组件通信

上一章已是西安我的媒资页面,所以媒资查询窗口页面不需要在开发,将"我的媒资页面"作为一个组件在秀嘎i课程计划你页面中引用,如下图:

在这里插入图片描述
修改课程计划页面为父组件,我的媒资查询页面为子组件
问题1:
我的媒资页面在选择妹子文件时不允许显示,比如"视频处理"按钮,该如何控制?
这时,就需要父组件(修改课程计划页面)向子组件(我的媒资页面)传入一个变量,使用此变量来控制当前是否进入选择妹子文件业务,从而控制那些元素不显示,如下图:
在这里插入图片描述
问题2:
在我的妹子页面选择了妹子文件,如何将选择的妹子文件信息传到父组件?
这是,就需要子组件调用父组件的方法来解决此问题,如下图:
在这里插入图片描述

父组件(修改课程计划)

在这里插入图片描述

子组件(我的媒资查询)

保存视频信息

需求分析

用户进入课程计划页面,选择视频,将课程计划与视频信息保存在课程管理数据库中.
用户操作流程:
1.进入课程计划,点击选择视频,打开我的媒资查询页面
2.为课程计划选择对一个视频,点击选择
3.前端请求课程管理服务保存课程计划与视频信息

数据模型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TIt2SvKD-1673147037567)(null)]

package com.xuecheng.framework.domain.course;

import lombok.Data;
import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;
import java.io.Serializable;

/**
 * Created by admin on 2018/2/7.
 */
@Data
@ToString
@Entity
@Table(name="teachplan_media")
@GenericGenerator(name = "jpa-assigned", strategy = "assigned")
public class TeachplanMedia implements Serializable {
    private static final long serialVersionUID = -916357110051689485L;
    @Id
    @GeneratedValue(generator = "jpa-assigned")
    @Column(name="teachplan_id")
    private String teachplanId;

    @Column(name="media_id")
    private String mediaId;

    @Column(name="media_fileoriginalname")
    private String mediaFileOriginalName;

    @Column(name="media_url")
    private String mediaUrl;
    private String courseId;

}

API接口

在courseControllerApi中写入

 @ApiOperation("保存课程计划与媒资文件的关联")
    public ResponseResult savemedia(TeachplanMedia teachplanMedia);

服务端开发

DAO
package com.xuecheng.manage_course.dao;

import com.xuecheng.framework.domain.course.CourseBase;
import com.xuecheng.framework.domain.course.TeachplanMedia;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * Created by Administrator.
 */
public interface TeachplanMediaRepository extends JpaRepository<TeachplanMedia,String> {
}

Service
   //    保存视频媒资计划的关联
    public ResponseResult saveMedia(TeachplanMedia teachplanMedia) {
//        关键参数的校验
        if (teachplanMedia == null || StringUtils.isEmpty(teachplanMedia.getCourseId())) {
//           抛出异常
            ExceptionCast.cast(CommonCode.INVALID_PARAM);
        }

//        校验课程计划是否是三级
        String teachplanId = teachplanMedia.getTeachplanId();
//        查询课程计划
        Optional<Teachplan> optional = teachplanRepository.findById(teachplanId);
        if (!optional.isPresent()){
            ExceptionCast.cast(CommonCode.MEIDA_TEACHPLAN_ISNULL);
        }

//        查询到教学计划
            Teachplan teachplan = optional.get();
//        取出等级
        String grade = teachplan.getGrade();
        if (StringUtils.isEmpty(grade)||!grade.equals("3")){
//          只允许选择第三级目录
            ExceptionCast.cast(CommonCode.MEDIA_TEACHPLAN_GRADEISNULL);
        }
//      查询teachplanMedia
        Optional<TeachplanMedia> mediaOptional = teachplanMediaRepository.findById(teachplanId);
        TeachplanMedia teachplanMedia1 = null;
        if (mediaOptional.isPresent()){
             teachplanMedia1 = mediaOptional.get();
        }else{
            teachplanMedia1 = new TeachplanMedia()  ;
        }
//        将TeachplanMedia保存到数据库
        teachplanMedia1.setCourseId(teachplan.getCourseid());//课程id

        teachplanMedia1.setMediaId(teachplanMedia.getMediaId());//媒资文件的id

         teachplanMedia1.setMediaFileOriginalName(teachplanMedia.getMediaFileOriginalName());//原始名称

        teachplanMedia1.setMediaUrl(teachplanMedia.getMediaUrl());//媒资文件的路径

        teachplanMediaRepository.save(teachplanMedia1);


//

        return new ResponseResult(CommonCode.SUCCESS);
    }
Controller
  @Override
    @PostMapping("/savemedia")
    public ResponseResult savemedia(TeachplanMedia teachplanMedia) {
      return    courseService.saveMedia(teachplanMedia);
    }

前端开发

API方法
API调用

测试

查询视频信息

需求分析

课程计划的视频信息保存后在也米娜无法查看,本解决课程计划页面显示相关联的媒资信息.
解决方案:
在获取课成计划树节点的信息是,将关联的媒资信息一并查询,在前端显示,下图说明了客户菜呢个计划显示的区域.
在这里插入图片描述

Dao

        <!--这就是二级节点一对多的标签-->
        <id column="two_id" property="id"></id>
            <result column="two_pname" property="pname"></result>
            <collection property="children" ofType="com.xuecheng.framework.domain.course.ext.TeachplanNode">
                <id column="three_id" property="id"></id>
                <result column="three_pname" property="pname"></result>
                <result column="media_id" property="mediaId"></result>
                <result column="media_fileoriginalname" property="mediaFileOriginalName"></result>
            </collection>
        </collection>
    </resultMap>
    <select id="selectList" parameterType="java.lang.String"

            resultMap="teachplanMap">
        SELECT a.id    one_id,
        a.pname one_pname,
        b.id    two_id,
        b.pname two_pname,
        c.id    three_id,
        c.pname three_pname,
        teachplan_media.media_id,
        teachplan_media.media_fileoriginalname
        FROM teachplan a
        LEFT JOIN teachplan b
        ON b.parentid = a.id
        LEFT JOIN teachplan c
        ON c.parentid = b.id
        LEFT JOIN teachplan_media
        ON  c.id = teachplan_media.teachplan_id
        WHERE a.parentid = '0'
        <if test="_parameter!=null and _parameter!=''">

          AND a.courseid = #{courseId}
        </if>
        ORDER BY a.orderby,
                 b.orderby,
                 c.orderby

    </select>

里面的TeachPlanNode

package com.xuecheng.framework.domain.course.ext;

import com.xuecheng.framework.domain.course.Teachplan;
import lombok.Data;
import lombok.ToString;

import java.util.List;

/**
 * Created by admin on 2018/2/7.
 */
@Data
@ToString
public class TeachplanNode extends Teachplan {
//树状结构的子节点
    List<TeachplanNode> children;

//    媒资文件的id
    String mediaId;
//    媒资文件的原始名称
   String mediaFileOriginalName;

}

页面查询视频

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值