BackupMaterialServiceImpl.java 15.8 KB
package com.wondertek.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.wondertek.dto.*;
import com.wondertek.dto.image.ImageVideoReq;
import com.wondertek.dto.image.OutPut;
import com.wondertek.entity.BackupConfig;
import com.wondertek.entity.BackupMaterial;
import com.wondertek.entity.StreamTask;
import com.wondertek.enums.MaterialSourceEnum;
import com.wondertek.enums.PlayTypeEnum;
import com.wondertek.exception.ServiceException;
import com.wondertek.mapper.BackupConfigMapper;
import com.wondertek.mapper.BackupMaterialMapper;
import com.wondertek.mapper.StreamTaskMapper;
import com.wondertek.service.BackupMaterialService;
import com.wondertek.service.FileService;
import com.wondertek.util.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class BackupMaterialServiceImpl extends ServiceImpl<BackupMaterialMapper, BackupMaterial> implements BackupMaterialService {

    @Resource
    private BackupMaterialMapper backupMaterialMapper;
    @Resource
    private FileService fileService;
    @Value("${file.realPath}")
    private String realPath;
    @Resource
    private BackupConfigMapper backupConfigMapper;
    @Resource
    private StreamTaskMapper streamTaskMapper;
    @Value("${transcode.switchStreamUrl}")
    private String switchStreamUrl;
    @Value("${transcode.swapBackupUrl}")
    private String swapBackupUrl;
    @Value("${rec.getImageUrl}")
    private String recGetImageUrl;


    @Override
    public PageBean queryPage(BackupMaterialDto backupMaterialDto) {
        Integer page = backupMaterialDto.getPage();
        Integer size = backupMaterialDto.getSize();



        LambdaQueryWrapper<BackupMaterial> queryWrapper = new LambdaQueryWrapper<>();

        if (ObjectUtil.isNotEmpty(backupMaterialDto.getId())){
            queryWrapper.eq(BackupMaterial::getId,backupMaterialDto.getId());
        }
        if(StringUtils.isNotBlank(backupMaterialDto.getBackupName())){
            queryWrapper.like(BackupMaterial::getBackupName,backupMaterialDto.getBackupName());
        }

        Page<BackupMaterial> pageInfo = new Page<>(page, size);

        IPage<BackupMaterial> resultPage = backupMaterialMapper.selectPage(pageInfo, queryWrapper);
        return new PageBean(Integer.parseInt(String.valueOf(resultPage.getPages())),resultPage.getTotal(),resultPage.getRecords());
    }

    @Override
    public ResultBean upload(MultipartFile backupFile, BackupUploadVo backupUploadVo) {
        //校验
//        if(!"mp4".equalsIgnoreCase(backupUploadVo.getFileType())){
//            throw new ServiceException("仅支持上传mp4格式的垫片文件!");
//        }
        //校验backupFile后缀名
        String realName = backupFile.getOriginalFilename();
        String suffix = realName.substring(realName.lastIndexOf(".") + 1);
        if (!suffix.equalsIgnoreCase("mp4")) {
            throw new ServiceException("仅支持上传mp4格式的垫片文件!");
        }

        //对应垫片任务
        LambdaQueryWrapper<StreamTask> backupWrapper = new LambdaQueryWrapper<>();
        backupWrapper.eq(StreamTask::getRoomId,backupUploadVo.getRoomId());
        String palyType = backupUploadVo.getBackupOrder() == 1? PlayTypeEnum.BACKUP1.getCode() : PlayTypeEnum.BACKUP2.getCode();
        backupWrapper.eq(StreamTask::getPlayType,palyType);
        StreamTask streamTask = streamTaskMapper.selectOne(backupWrapper);
        if (streamTask == null){
            return ResultBean.error("未找到该直播间对应的垫片任务");
        }
        //文件上传
        String fileId = UUIDUtil.randomUUID();
        String fileName = backupUploadVo.getBackupName() + "." + backupUploadVo.getFileType();
        ///home/wondertek/material_file_assets/dianpian/2025/07/24/roomid/
        String filedir = FileUtils.generateDianPianDir(backupUploadVo.getRoomId().toString(), realPath);
        String destFilePath = filedir + realName;//文件上传目录,绝对路径
        try {
            fileService.upload(backupFile, destFilePath);
        } catch (Exception e) {
            return ResultBean.error("上传文件异常");
        }
        //保存垫片素材表
        BackupMaterial backupMaterial = new BackupMaterial();
        backupMaterial.setFileId(fileId);
        backupMaterial.setBackupName(backupUploadVo.getBackupName());
        String relativePath = destFilePath.replace(realPath, "");
        backupMaterial.setFilePath(relativePath);
        backupMaterial.setFileType(backupUploadVo.getFileType());
        backupMaterial.setCreatedTime(LocalDateTime.now());
        backupMaterial.setCreatedBy("admin");
        backupMaterialMapper.insert(backupMaterial);
        //添加审片间垫片配置
        LambdaQueryWrapper<BackupConfig> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BackupConfig::getRoomId,backupUploadVo.getRoomId());
        wrapper.eq(BackupConfig::getBackupOrder,backupUploadVo.getBackupOrder());
        BackupConfig backupConfig = backupConfigMapper.selectOne(wrapper);
        if(ObjectUtil.isNull(backupConfig)){
            backupConfig = new BackupConfig();
        }
        backupConfig.setRoomId(backupUploadVo.getRoomId());
        backupConfig.setBackupId(backupMaterial.getId());
        backupConfig.setBackupOrder(backupUploadVo.getBackupOrder());
        backupConfig.setBackupStatus(backupUploadVo.getBackupStatus());
        backupConfig.setBackupPath(relativePath);
        backupConfigMapper.insertOrUpdate(backupConfig);

        //对应垫片任务切源(转码平台演示环境已挂载统一存储),请求转码平台修改垫片任务直播源
        changeChannelSource(streamTask.getTaskId(), destFilePath);
        //判断是否需要修改当前播出流的垫片
        if("1".equals(backupConfig.getBackupStatus())){
            //切换播出流垫片(主、备)
            LambdaQueryWrapper<StreamTask> playWrapper = new LambdaQueryWrapper<>();
            playWrapper.eq(StreamTask::getRoomId,backupUploadVo.getRoomId());
            playWrapper.eq(StreamTask::getPlayType,"play");
            List<StreamTask> playTasks = streamTaskMapper.selectList(playWrapper);
            if(CollectionUtil.isNotEmpty(playTasks)){
                playTasks.forEach(playTask -> {
                    changeBackup(playTask.getTaskId(), destFilePath);
                });
            }
        }

        return ResultBean.ok("上传成功");
    }

    /**
     * 修改转码任务垫片
     * @param taskId
     * @param destFilePath
     */
    private void changeBackup(String taskId, String destFilePath) {
        Map<String, Object> paramsMap = new HashMap<>();
        paramsMap.put("taskId", taskId);
        paramsMap.put("backupUrl", destFilePath);
        HttpResponse execute = null;
        try {
            execute = HttpRequest.post(swapBackupUrl)
                    .header("Content-Type", "application/json")
                    .body(JSONUtils.obj2json(paramsMap)).timeout(30000).execute();
            if (execute.isOk()) {
                log.info("-->请求转码系统换垫片接口,url:{},paramsMap:{}",swapBackupUrl,paramsMap);
                String body = execute.body();
                ResponseData response = JSONUtils.jsonToObject(body, new TypeReference<ResponseData>() {
                });
                if (response.getResultCode().equals("0")) {
                    log.info("请求转码系统换垫片接口成功");
                }else {
                    log.info("请求转码系统换垫片接口响应失败,response:{}",response);
                    throw new ServiceException("请求转码系统换垫片接口响应失败");
                }
            } else {
                log.error("请求转码系统换垫片接口失败,url:{},body:{}", swapBackupUrl, execute.body());
            }
        } catch (Exception e) {
            log.error("请求转码系统换垫片接口失败,url:{}", swapBackupUrl, e);
        }
    }

    private void changeChannelSource(String taskId, String destFilePath) {
        ChangeSourceParam param = new ChangeSourceParam();
        param.setTaskId(taskId);
        SourceUrl sourceUrl = new SourceUrl();
        sourceUrl.setSourceUrl(destFilePath);
        sourceUrl.setPri(0);
        param.setSourceUrls(Arrays.asList(sourceUrl));
        HttpResponse execute = null;
        try {
            execute = HttpRequest.post(switchStreamUrl)
                    .header("Content-Type", "application/json")
                    .body(JSONUtils.obj2json(param)).timeout(30000).execute();
            if (execute.isOk()) {
                log.info("-->请求转码系统切换直播源接口,url:{},paramsMap:{}",switchStreamUrl,param);
                String body = execute.body();
                ResponseData response = JSONUtils.jsonToObject(body, new TypeReference<ResponseData>() {
                });
                if (response.getResultCode().equals("0")) {
                    log.info("请求转码系统切换直播源接口成功");
                }else {
                    log.info("请求转码系统切换直播源接口响应失败,response:{}",response);
                    throw new ServiceException("请求转码系统切换直播源接口响应失败");
                }
            } else {
                log.error("请求转码系统切换直播源接口失败,url:{},body:{}", switchStreamUrl, execute.body());
            }
        } catch (Exception e) {
            log.error("请求转码系统切换直播源接口失败,url:{}", switchStreamUrl, e);
        }
    }

    @Override
    public ResultBean getByRoom(Long roomId) {
        LambdaQueryWrapper<BackupConfig> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BackupConfig::getRoomId,roomId);
        List<BackupConfig> backupConfigs = backupConfigMapper.selectList(wrapper);
        return ResultBean.ok(backupConfigs);
    }

    @Override
    public ResultBean change(BackupChangeDto backupChangeDto) {
        LambdaQueryWrapper<BackupConfig> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BackupConfig::getRoomId,backupChangeDto.getRoomId());
        List<BackupConfig> backupConfigs = backupConfigMapper.selectList(wrapper);
        if(CollectionUtil.isNotEmpty(backupConfigs)){
            backupConfigs.forEach(backupConfig -> {
                if(backupConfig.getBackupId().equals(backupChangeDto.getBackupId())){
                    backupConfig.setBackupStatus("1");
                    //将播放流的垫片配置切到此垫片上,播放流主、备都切换
                    LambdaQueryWrapper<StreamTask> playWrapper = new LambdaQueryWrapper<>();
                    playWrapper.eq(StreamTask::getRoomId,backupChangeDto.getRoomId());
                    playWrapper.eq(StreamTask::getPlayType,"play");
                    List<StreamTask> playTasks = streamTaskMapper.selectList(playWrapper);
                    if(CollectionUtil.isNotEmpty(playTasks)){
                        String backupPath = realPath + backupConfig.getBackupPath();
                        playTasks.forEach(playTask -> {
                            changeBackup(playTask.getTaskId(), backupPath);
                        });
                    }
                }else {
                    //将其他垫片配置切到默认状态
                    backupConfig.setBackupStatus("0");
                }
                backupConfigMapper.updateById(backupConfig);
            });

        }
        return ResultBean.ok();
    }

    @Override
    public ResultBean uploadToMaterial(MultipartFile backupFile, MaterialUploadVo materialUploadVo) {
        //校验backupFile后缀名
        String realName = backupFile.getOriginalFilename();
        String suffix = realName.substring(realName.lastIndexOf(".") + 1);
        if (!suffix.equalsIgnoreCase("mp4")) {
            throw new ServiceException("仅支持上传mp4格式的垫片文件!");
        }

        //文件上传
        String fileId = UUIDUtil.randomUUID();
        String fileName = materialUploadVo.getBackupName() + "." + materialUploadVo.getFileType();
        ///home/wondertek/material_file_assets/dianpian/2025/07/24/uuid/
        String filedir = FileUtils.generateDianPianDir(fileId, realPath);
        String destFilePath = filedir + fileName;//文件上传目录,绝对路径
        try {
            fileService.upload(backupFile, destFilePath);
        } catch (Exception e) {
            return ResultBean.error("上传文件异常");
        }
        //


        //保存垫片素材表
        BackupMaterial backupMaterial = new BackupMaterial();
        backupMaterial.setFileId(fileId);
        backupMaterial.setBackupName(materialUploadVo.getBackupName());
        String relativePath = destFilePath.replace(realPath, "");
        backupMaterial.setFilePath(relativePath);
        backupMaterial.setFileType(materialUploadVo.getFileType());
        backupMaterial.setCreatedTime(LocalDateTime.now());
        backupMaterial.setCreatedBy("admin");
        backupMaterial.setDel("0");
        backupMaterial.setSource(MaterialSourceEnum.UPLOAD.getCode());

        //抽封面图,调用定帧抽图接口
        ImageVideoReq imageVideoReq = new ImageVideoReq();
        imageVideoReq.setInput(destFilePath);
        imageVideoReq.setOffset(0L);
        OutPut outPut = new OutPut();
        outPut.setIndex(0);
        String coverPath = filedir + realName.substring(0, realName.lastIndexOf(".")) + ".jpg";
        outPut.setPath(coverPath);
        imageVideoReq.setOutput(Arrays.asList(outPut));
        try {
            HttpResponse execute = HttpRequest.post(recGetImageUrl)
                    .header("Content-Type", "application/json")
                    .body(JSONUtils.obj2json(imageVideoReq)).timeout(30000).execute();
            if (execute.isOk()) {
                log.info("-->请求能力平台定帧抽图接口,url:{},paramsMap:{}",recGetImageUrl,imageVideoReq);
                String body = execute.body();
                AbilityResp response = JSONUtils.jsonToObject(body, new TypeReference<AbilityResp>() {
                });
                if (response.getRet() == 0) {
                    backupMaterial.setCoverPath(coverPath.replace(realPath, ""));
                    log.info("请求能力平台定帧抽图接口成功");
                }else {
                    log.info("请求能力平台定帧抽图接口响应失败,response:{}",response);
                    throw new ServiceException("请求能力平台定帧抽图接口响应失败");
                }
            } else {
                log.error("请求能力平台定帧抽图接口失败,url:{},body:{}", switchStreamUrl, execute.body());
            }
        } catch (Exception e) {
            log.error("请求能力平台定帧抽图接口失败,url:{}", switchStreamUrl, e);
        }

        backupMaterialMapper.insert(backupMaterial);

        return ResultBean.ok("新增素材成功");
    }

    @Override
    public ResultBean batchDelete(Long[] ids) {
        LambdaQueryWrapper<BackupMaterial> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(BackupMaterial::getId,ids);
        int i = backupMaterialMapper.delete(wrapper);
        log.info("删除素材库记录条数:{}", i);
        return i > 0 ? ResultBean.ok() : ResultBean.error();
    }

}