第2章 声音管理.md 33 KB

谷粒随享

第2章 声音管理

学习目标:

  • 声音相关业务流程&数据模型
  • 新增声音(音视频文件上传到腾讯云点播服务)
  • 修改声音
  • 删除声音

1、新增声音

功能入口:运行小程序-->我的-->创作中心-->声音-->点击 + 添加声音

业务需求:当创作者新增专辑后,创作者将自己录制的音视频文件保存到声音。

  • 选择声音所属专辑
  • 设置声音封面图片
  • 选择本地录制好音频文件上传
  • 用户填写其他信息(声音标题、声音简介、是否公开)
  • 提交表单完成声音新增

1.1 获取用户专辑列表

需求:点击添加声音的时候会触发一个查询所有专辑列表,主要目的是为了让专辑与声音进行挂钩!

主要是根据userId,查询专辑Id 与专辑标题,然后按照专辑Id 进行降序排列。

image-20231010091237839

YAPI接口地址:http://192.168.200.6:3000/project/11/interface/api/27

AlbumInfoApiController控制器

/**
 * TODO 该接口登录才能访问
 * 查询当前登录用户专辑列表
 * @return
 */
@Operation(summary = "查询当前登录用户专辑列表")
@GetMapping("/albumInfo/findUserAllAlbumList")
public Result<List<AlbumInfo>> getUserAllAlbumList(){
	//1.获取当前登录用户ID
	Long userId = AuthContextHolder.getUserId();
	//2.调用业务层获取用户专辑列表
	List<AlbumInfo> list = albumInfoService.getUserAllAlbumList(userId);
	return Result.ok(list);
}

AlbumInfoService接口

/**
 * 查询当指定用户专辑列表
 * @param userId
 * @return
 */
List<AlbumInfo> getUserAllAlbumList(Long userId);

AlbumInfoServiceImpl实现类

/**
 * 查询当指定用户专辑列表
 * @param userId
 * @return
 */
@Override
public List<AlbumInfo> getUserAllAlbumList(Long userId) {
    //1.创建条件构造器封装查询条件
    LambdaQueryWrapper<AlbumInfo> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(AlbumInfo::getUserId, userId);
    //2.指定查询字段 select 后面列名称
    queryWrapper.select(AlbumInfo::getId, AlbumInfo::getAlbumTitle, AlbumInfo::getStatus);
    //3.TODO 避免小程序响应1千多条记录导致卡顿 需要指定加载数量  limit 200
    queryWrapper.last("limit 200");
    //4.设置排序按照最新专辑
    queryWrapper.orderByDesc(AlbumInfo::getId);
    return albumInfoMapper.selectList(queryWrapper);
}

1.2 上传声音到点播服务

腾讯云点播服务面向音视频、图片等媒体,提供制作上传、存储、转码、媒体处理、媒体 AI、加速分发播放、版权保护等一体化高品质媒体服务。使用腾讯云服务可以为音视频文件的存储、传输、处理和分发提供可靠高效安全的解决方案。这可以节省你自己构建和维护存储基础设施的成本和精力,并为用户提供更好的音视频体验。

  1. 可靠性和可用性: 腾讯云提供高可靠性的存储服务,确保音视频文件的安全和持久保存。腾讯云具有跨多个地理区域和数据中心的数据冗余备份,以确保数据的高可用性和容灾能力。
  2. 扩展性和弹性: 腾讯云提供高度可扩展的存储解决方案,可以根据需要轻松扩展存储容量,适应不断增长的音视频数据需求。你可以根据实际情况动态调整存储空间,并随时新增或删除文件。
  3. 快速传输和低延迟: 腾讯云拥有全球范围的网络基础设施,可以提供快速的音视频文件上传和下载速度,同时降低传输延迟,使用户能够更快速地访问和共享文件。
  4. 安全性和权限控制: 腾讯云提供多层次的安全机制来保护用户的音视频文件。你可以使用腾讯云提供的身份验证和权限控制功能确保只有授权用户才能访问和管理文件。此外,腾讯云也提供数据加密和防止盗链等功能,以增强音视频文件的安全性。
  5. 多媒体处理和分发: 腾讯云提供了丰富的多媒体处理和分发服务,可以对上传的音视频文件进行转码、截图、剪辑等处理,并提供内容分发网络(CDN)加速服务,使用户能够高效地将音视频内容传送到全球各地的用户。

快速接入流程:云点播 快速入门-文档中心-腾讯云 (tencent.com)

  • 微信扫码登录

  • 关注公众号

  • 搜索云点播

  • 微信认证

  • 实名认证

  • 立即开通服务

  • 右边:点击访问管理

准备工作:

  1. 应用管理导航菜单获取APPID

image-20231010095600292

  1. 访问管理新建用户,分配点播服务权限

    image-20231010095827697

image-20231010100059988

image-20231010100134657

  1. 进入用户详情,获取秘钥

image-20231010100320526

  1. 在nacos配置中心service-album-dev.yaml中定义点播服务相关配置信息

    vod:
     appId: 1255727855 #需要修改为自己的
     secretId: AKIDTOFybJvQqCWnDdyDNRQJ54xkT8hBbxCK #需要修改为自己的
     secretKey: DKLH87saCmKZowS09IPRLE4pVCqQuKeu #需要修改为自己的
     region: ap-beijing #需要修改为自己的
     procedure: SimpleAesEncryptPreset #任务流
     #tempPath: /root/tingshu/tempPath
     tempPath: D:\code\workspace2023\tingshu\temp
     playKey: wrTwwu8U3DRSRDgC8l7q  #播放加密key
    
  2. 通过配置类读取(已提供)

    package com.atguigu.tingshu.album.config;
       
       
    import com.qcloud.vod.VodUploadClient;
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
       
    @Configuration
    @ConfigurationProperties(prefix="vod") //读取节点
    @Data
    public class VodConstantProperties {
       
       private Integer appId;
       private String secretId;
       private String secretKey;
       //https://cloud.tencent.com/document/api/266/31756#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8
       private String region;
       private String procedure;
       private String tempPath;
       private String playKey;
       
       
       /**
        * 注册用于文件上传客户端对象
        * @return
        */
       @Bean
       public VodUploadClient vodUploadClient(){
           return new VodUploadClient(secretId, secretKey);
       }
    }
    

需求:主播录制的音频文件需要上传到腾讯云-云点播云服务器,在声音表中存储音频文件腾讯云地址。

YAPI接口地址:http://192.168.200.6:3000/project/11/interface/api/29

TrackInfoApiController控制器:

package com.atguigu.tingshu.album.api;

import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.common.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.util.Map;

@Tag(name = "声音管理")
@RestController
@RequestMapping("api/album")
@SuppressWarnings({"all"})
public class TrackInfoApiController {

    @Autowired
    private TrackInfoService trackInfoService;

        @Autowired
    private VodService vodService;

    /**
     * 将音频文件上传到腾讯云点播平台
     *
     * @param file 音频文件
     * @return {mediaFileId:"文件唯一标识",mediaUrl:"播放地址"}
     */
    @Operation(summary = "将音频文件上传到腾讯云点播平台")
    @PostMapping("/trackInfo/uploadTrack")
    public Result<Map<String, String>> uploadTrack(MultipartFile file) {
        Map<String, String> mapResult = vodService.uploadTrack(file);
        return Result.ok(mapResult);
    }
}

VodService接口

package com.atguigu.tingshu.album.service;

import org.springframework.web.multipart.MultipartFile;

import java.util.Map;

public interface VodService {

    /**
     * 将音频文件上传到腾讯云点播平台
     * @param file 音频文件
     * @return {mediaFileId:"文件唯一标识",mediaUrl:"播放地址"}
     */
    Map<String, String> uploadTrack(MultipartFile file);
}

VodServiceImpl实现类 云点播 Java SDK-开发指南-文档中心-腾讯云 (tencent.com) Java 语言实现声音上传功能API

package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.util.UploadFileUtil;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.HashMap;
import java.util.Map;


@Slf4j
@Service
public class VodServiceImpl implements VodService {

    @Autowired
    private VodConstantProperties vodConstantProperties;

    @Autowired
    private VodUploadClient vodUploadClient;

    /**
     * 将音频文件上传到腾讯云点播平台
     *
     * @param file 音频文件
     * @return {mediaFileId:"文件唯一标识",mediaUrl:"播放地址"}
     */
    @Override
    public Map<String, String> uploadTrack(MultipartFile file) {
        try {
            //1.将上传文件保存到临时上传目录下
            String tempFilePath = UploadFileUtil.uploadTempPath(vodConstantProperties.getTempPath(), file);

            //1.TODO 已有该对象注入即可 获取到上传客户端对象VodUploadClient

            //2.构造上传请求对象VodUploadRequest
            VodUploadRequest request = new VodUploadRequest();
            request.setMediaFilePath(tempFilePath);
            //3.调用上传
            VodUploadResponse uploadResponse = vodUploadClient.upload(vodConstantProperties.getRegion(), request);
            //4.封装响应结果Map
            if (uploadResponse != null) {
                Map<String, String> mapResult = new HashMap<>();
                mapResult.put("mediaFileId", uploadResponse.getFileId());
                mapResult.put("mediaUrl", uploadResponse.getMediaUrl());
                return mapResult;
            }
        } catch (Exception e) {
            log.error("[专辑服务]上传音频文件到点播平台异常:文件:{},错误信息:{}", file, e);
            throw new RuntimeException(e);
        }
        return null;
    }
}

上传之后可以在:音视频管理 - 媒资管理 - 云点播 - 控制台 (tencent.com)-应用管理-点击主应用-媒资管理-音视频管理看是否有音频

1.3 保存声音

YAPI接口地址:http://192.168.200.6:3000/project/11/interface/api/31

前端传递Json 字符串,此时我们可以使用封装好的TrackInfoVo实体类进行接收,方便处理数据

涉及到的表:

  • track_info 声音信息表

    • user_id、order_num、media_duration、media_size、media_type、source status 需要手动设置数据
    • user_id : 直接从工具类中获取(TODO暂时用固定用户ID
    • order_num : 声音在专辑中的排序值,从1开始依次递增,值越小排序越前,根据专辑Id上一条声音的排序值 并且按照声音Id 进行降序排列 并且获取第一条数.
    • media_duration media_size:根据流媒体Id腾讯点播平台获取到数据并赋值!
    • source status : 自己设置
  • track_stat 声音统计表

  • album_info 专辑表

TrackInfoApiController控制器

/**
 * TODO:该接口需要登录才能访问
 * 保存专辑下声音
 *
 * @param trackInfoVo 声音信息VO对象
 * @return
 */
@Operation(summary = "保存专辑下声音")
@PostMapping("/trackInfo/saveTrackInfo")
public Result saveTrackInfo(@RequestBody @Validated TrackInfoVo trackInfoVo) {
    //1.获取用户ID
    Long userId = AuthContextHolder.getUserId();
    //2.调用业务层新增声音
    trackInfoService.saveTrackInfo(userId, trackInfoVo);
    return Result.ok();
}

TrackInfoService接口

package com.atguigu.tingshu.album.service;

import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.baomidou.mybatisplus.extension.service.IService;

public interface TrackInfoService extends IService<TrackInfo> {

    /**
     * 保存专辑下声音
     * @param userId 用户ID
     * @param trackInfoVo 声音信息VO对象
     * @return
     */
    void saveTrackInfo(Long userId, TrackInfoVo trackInfoVo);


    /**
     * 新增专辑统计信息
     * @param trackId 声音ID
     * @param statType 统计类型
     * @param statNum 统计数值
     */
    void saveTrackStat(Long trackId, String statType, int statNum);
}

TrackInfoServiceImpl实现类

package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.util.UploadFileUtil;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackMediaInfoVo;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private VodConstantProperties props;

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private VodService vodService;

    @Autowired
    private TrackStatMapper trackStatMapper;

    /**
     * 保存专辑下声音
     *
     * @param userId      用户ID
     * @param trackInfoVo 声音信息VO对象
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTrackInfo(Long userId, TrackInfoVo trackInfoVo) {
        //1.保存声音
        //1.1 将前端提交声音VO对象转为声音PO对象
        TrackInfo trackInfo = BeanUtil.copyProperties(trackInfoVo, TrackInfo.class);
        //1.2 为声音对象中基本属性赋值:用户ID、声音来源、声音审核状态(审核通过)
        trackInfo.setUserId(userId);
        trackInfo.setSource("1");
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
        //1.3 根据专辑ID查询专辑信息得到专辑已有声音数量,计算出新增声音序号
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfoVo.getAlbumId());
        trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);
        //1.4 根据文件唯一标识远程调用腾讯点播平台接口获取音频文件信息(音频时长、大小、音频类型)
        TrackMediaInfoVo trackMediaInfoVo = vodService.getTrackMediaInfo(trackInfoVo.getMediaFileId());
        if (trackMediaInfoVo != null) {
            trackInfo.setMediaDuration(new BigDecimal(trackMediaInfoVo.getDuration()));
            trackInfo.setMediaSize(trackMediaInfoVo.getSize());
            trackInfo.setMediaType(trackMediaInfoVo.getType());
        }
        //1.5 保存声音得到声音ID
        trackInfoMapper.insert(trackInfo);
        Long trackId = trackInfo.getId();

        //2.新增声音统计信息
        this.saveTrackStat(trackId, SystemConstant.TRACK_STAT_PLAY, 0);
        this.saveTrackStat(trackId, SystemConstant.TRACK_STAT_COLLECT, 0);
        this.saveTrackStat(trackId, SystemConstant.TRACK_STAT_PRAISE, 0);
        this.saveTrackStat(trackId, SystemConstant.TRACK_STAT_COMMENT, 0);

        //3.更新专辑包含声音数量
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount()+1);
        albumInfoMapper.updateById(albumInfo);
    }

}

初始化统计数据

/**
 * 新增专辑统计信息
 * @param trackId 声音ID
 * @param statType 统计类型
 * @param statNum 统计数值
 */
@Override
public void saveTrackStat(Long trackId, String statType, int statNum) {
    TrackStat trackStat = new TrackStat();
    trackStat.setTrackId(trackId);
    trackStat.setStatType(statType);
    trackStat.setStatNum(statNum);
    trackStatMapper.insert(trackStat);
}

获取流媒体数据方法实现 参考地址API Explorer - 云 API - 控制台 (tencent.com)

VodService

/**
 * 根据云点播平台文件唯一标识,获取音频文件详情信息
 * @param mediaFileId 文件唯一标识
 * @return
 */
TrackMediaInfoVo getTrackMediaInfo(String mediaFileId);

VodServiceImpl

/**
 * 根据云点播平台文件唯一标识,获取音频文件详情信息
 *
 * @param mediaFileId 文件唯一标识
 * @return
 */
@Override
public TrackMediaInfoVo getTrackMediaInfo(String mediaFileId) {
    try {
        //1.实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
        Credential cred = new Credential(vodConstantProperties.getSecretId(), vodConstantProperties.getSecretKey());
        //2.实例化要请求产品的client对象,clientProfile是可选的
        VodClient client = new VodClient(cred, vodConstantProperties.getRegion());
        //3.实例化一个请求对象,每个接口都会对应一个request对象
        DescribeMediaInfosRequest req = new DescribeMediaInfosRequest();
        String[] fileIds1 = {mediaFileId};
        //3.1 封装获取详情对应文件唯一标识
        req.setFileIds(fileIds1);

        //4.调用获取文件详情接口。返回的resp是一个DescribeMediaInfosResponse的实例,与请求对象对应
        DescribeMediaInfosResponse describeMediaInfosResponse = client.DescribeMediaInfos(req);
        //5.解析结果对象获取音频文件时长,大小,类型
        if (describeMediaInfosResponse != null) {
            MediaInfo[] mediaInfoSet = describeMediaInfosResponse.getMediaInfoSet();
            if (mediaInfoSet != null && mediaInfoSet.length > 0) {
                TrackMediaInfoVo vo = new TrackMediaInfoVo();
                MediaInfo mediaInfo = mediaInfoSet[0];
                //5.1 获取媒体文件基本信息对象
                MediaBasicInfo basicInfo = mediaInfo.getBasicInfo();
                vo.setType(basicInfo.getType());

                //5.2 获取媒体文件元信息对象
                MediaMetaData metaData = mediaInfo.getMetaData();
                vo.setDuration(metaData.getAudioDuration());
                vo.setSize(metaData.getSize());
                return vo;
            }
        }
    } catch (Exception e) {
        log.error("[专辑服务]获取点播平台文件:{},详情异常:{}", mediaFileId, e);
        throw new GuiguException(400, "音频文件详情获取异常!");
    }
    return null;
}

2、查询声音列表

功能入口:运行小程序-->我的-->创作中心-->声音-->查询当前用户的声音列表,如下图所示:

查询分析:需要根据用户Id,状态或标题查询当前声音列表!这三个条件被封装到一个实体类中 TrackInfoQuery,返回结果对象封装到 TrackListVo 实体类中 修改下评论数属性:commentStatNum

YAPI接口地址:http://192.168.200.6:3000/project/11/interface/api/33

TrackInfoApiController 控制器

/**
 * TODO 必须登录才可以访问
 * 获取当前登录声音分页列表
 *
 * @param page           页码
 * @param limit          页大小
 * @param trackInfoQuery 查询条件
 * @return
 */
@Operation(summary = "获取当前登录声音分页列表")
@PostMapping("/trackInfo/findUserTrackPage/{page}/{limit}")
public Result<Page<TrackListVo>> getUserTrackByPage(@PathVariable int page, @PathVariable int limit, @RequestBody TrackInfoQuery trackInfoQuery) {
    //1.获取用户ID
    Long userId = AuthContextHolder.getUserId();
    //2.封装分页查询对象
    trackInfoQuery.setUserId(userId);
    //3.调用业务层进行分页
    Page<TrackListVo> PageInfo = new Page<>(page, limit);
    PageInfo = trackInfoService.getUserTrackByPage(PageInfo, trackInfoQuery);
    return Result.ok(PageInfo);
}

TrackInfoService接口

/**
 * 获取当前登录声音分页列表
 * @param pageInfo MP分页对象
 * @param trackInfoQuery 查询声音条件对象
 * @return
 */
Page<TrackListVo> getUserTrackByPage(Page<TrackListVo> pageInfo, TrackInfoQuery trackInfoQuery);

TrackInfoServiceImpl实现类

/**
 * 获取当前登录声音分页列表
 * @param pageInfo MP分页对象
 * @param trackInfoQuery 查询声音条件对象
 * @return
 */
@Override
public Page<TrackListVo> getUserTrackByPage(Page<TrackListVo> pageInfo, TrackInfoQuery trackInfoQuery) {
    return trackInfoMapper.getUserTrackByPage(pageInfo, trackInfoQuery);
}

TrackInfoMapper接口

package com.atguigu.tingshu.album.mapper;

import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface TrackInfoMapper extends BaseMapper<TrackInfo> {


    /**
     * MP框架发现参数中包含分页对象,自动完成分页查询(自动查询总记录数,总页数,当前页数据自动封装到Page对象中)
     * @param pageInfo
     * @param trackInfoQuery
     * @return
     */
    Page<TrackListVo> getUserTrackByPage(Page<TrackListVo> pageInfo, @Param("vo") TrackInfoQuery trackInfoQuery);
}

SQL实现:

#分页查询当前用户声音列表,得到声音信息包含声音统计信息。查询条件:根据状态、根据标题模糊
#查询表:声音表track_info、声音统计表track_stat
#关联条件:track_stat表中逻辑外键track_id 等于 track_info表主键
#查询条件:用户ID、状态、标题、删除标志
select * from track_info;
select * from track_stat;


select *
from track_info ti
         inner join track_stat stat on stat.track_id = ti.id;

#指定查询列 参考封装VO对象TrackListVo
select
    ti.id trackId,
    ti.track_title,
    ti.album_id,
    ti.cover_url,
    ti.media_duration,
    ti.status,
    stat.stat_type,
    stat.stat_num
from track_info ti
         inner join track_stat stat on stat.track_id = ti.id;

#根据声音ID进行分组,违背SQL严格模式only_full_group_by规则:在select中只能出现group by子句中列或者聚合函数
select
    ti.id trackId,
    ti.track_title,
    ti.album_id,
    ti.cover_url,
    ti.media_duration,
    ti.status,
    stat.stat_type,
    stat.stat_num
from track_info ti
         inner join track_stat stat on stat.track_id = ti.id
group by ti.id

#解决:采用两个函数  if判断后得到统计数值 max获取数值-》将统计行记录转为列
select
    ti.id trackId,
    ti.track_title,
    ti.album_id,
    ti.cover_url,
    ti.media_duration,
    ti.status,
    max(if(stat.stat_type = '0701', stat.stat_num, 0)) playStatNum,
    max(if(stat.stat_type = '0702', stat.stat_num, 0)) collectStatNum,
    max(if(stat.stat_type = '0703', stat.stat_num, 0)) praiseStatNum,
    max(if(stat.stat_type = '0704', stat.stat_num, 0)) commentStatNum
from track_info ti
         inner join track_stat stat on stat.track_id = ti.id
group by ti.id


#增加查询条件
select
    ti.id trackId,
    ti.track_title,
    ti.album_id,
    ti.cover_url,
    ti.media_duration,
    ti.status,
    max(if(stat.stat_type = '0701', stat.stat_num, 0)) playStatNum,
    max(if(stat.stat_type = '0702', stat.stat_num, 0)) collectStatNum,
    max(if(stat.stat_type = '0703', stat.stat_num, 0)) praiseStatNum,
    max(if(stat.stat_type = '0704', stat.stat_num, 0)) commentStatNum
from track_info ti
         inner join track_stat stat on stat.track_id = ti.id
where ti.user_id = ? and ti.status = ? and ti.track_title like ? and ti.is_deleted = 0
group by ti.id
order by ti.id desc

TrackInfoMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >


<mapper namespace="com.atguigu.tingshu.album.mapper.TrackInfoMapper">


   <!--查询当前创作者发布声音列表-包含统计信息-->
    <select id="getUserTrackPage" resultType="com.atguigu.tingshu.vo.album.TrackListVo">
        select
            ti.id trackId,
            ti.track_title,
            ti.album_id,
            ti.cover_url,
            ti.media_duration,
            ti.status,
            max(if(stat_type='0701', stat_num, 0)) playStatNum,
            max(if(stat_type='0702', stat_num, 0)) collectStatNum,
            max(if(stat_type='0703', stat_num, 0)) praiseStatNum,
            max(if(stat_type='0704', stat_num, 0)) commentStatNum
        from track_info ti left join track_stat ts on ts.track_id = ti.id
        <where>
            <if test="vo.userId != null">
                user_id = #{vo.userId}
            </if>
            <if test="vo.status != null and vo.status != ''">
                and status = #{vo.status}
            </if>
            <if test="vo.trackTitle != null and vo.trackTitle != ''">
                and track_title like concat('%', #{vo.trackTitle}, '%')
            </if>
            and  ti.is_deleted = 0
        </where>
        group by ti.id
        order by ti.id desc
    </select>
</mapper>

3、修改声音

3.1 回显声音详情

YAPI接口地址:http://192.168.200.6:3000/project/11/interface/api/35

  1. 根据Id获取数据并回显
  2. 保存修改之后的数据

TrackInfoApiController控制器

/**
 * 根据声音ID查询声音信息
 *
 * @param id
 * @return
 */
@Operation(summary = "根据声音ID查询声音信息")
@GetMapping("/trackInfo/getTrackInfo/{id}")
public Result<TrackInfo> getTrackInfo(@PathVariable Long id) {
    TrackInfo trackInfo = trackInfoService.getById(id);
    return Result.ok(trackInfo);
}

3.2 修改声音

YAPI接口地址:http://192.168.200.6:3000/project/11/interface/api/37

TrackInfoApiController控制器

传递声音Id ,封装好的TrackInfoVo 实体类。

/**
 * TODO 该接口登录才可访问
 * 修改声音信息
 *
 * @param id
 * @param trackInfoVo
 * @return
 */
@Operation(summary = "修改声音信息")
@PutMapping("/trackInfo/updateTrackInfo/{id}")
public Result updateTrackInfo(@PathVariable Long id, @RequestBody @Validated TrackInfoVo trackInfoVo){
    trackInfoService.updateTrackInfo(id, trackInfoVo);
    return Result.ok();
}

TrackInfoService接口

/**
 * 修改声音
 * @param id 声音ID
 * @param trackInfoVo 声音VO对象
 */
void updateTrackInfo(Long id, TrackInfoVo trackInfoVo);

TrackInfoServiceImpl实现类

/**
 * 修改声音
 *
 * @param id          声音ID
 * @param trackInfoVo 声音VO对象
 */
@Override
@Transactional(rollbackFor = Exception.class)
public void updateTrackInfo(Long id, TrackInfoVo trackInfoVo) {
    //1.根据声音ID查询声音对象-判断声音是否变更
    TrackInfo trackInfo = trackInfoMapper.selectById(id);
    //1.2 变更前音频文件唯一标识
    String mediaFileIdBefore = trackInfo.getMediaFileId();
    //1.1 最新提交音频文件唯一标识
    String mediaFileIdAfter = trackInfoVo.getMediaFileId();
    //1.2 拷贝属性
    BeanUtil.copyProperties(trackInfoVo, trackInfo);

    //2.如果声音变更,获取最新声音相关音频信息(时长、大小、类型)为PO对象赋值
    if (!mediaFileIdBefore.equals(mediaFileIdAfter)) {
        TrackMediaInfoVo trackMediaInfo = vodService.getTrackMediaInfo(mediaFileIdAfter);
        if(trackMediaInfo!=null){
            trackInfo.setMediaType(trackMediaInfo.getType());
            trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfo.getDuration()));
            trackInfo.setMediaSize(trackMediaInfo.getSize());
            //4.将变更前声音文件从云点播平台删除
            vodService.deleteTrackMedia(mediaFileIdBefore);
        }
    }
    //3.更新声音
    trackInfoMapper.updateById(trackInfo);
}

VodService接口与实现类

/**
 * 删除云点播平台文件
 * @param mediaFileId
 */
void deleteTrackMedia(String mediaFileId);

API Explorer - 云 API - 控制台 (tencent.com)

/**
 * 删除音频文件
 *
 * @param mediaFileId
 */
@Override
public void deleteTrackMedia(String mediaFileId) {
    try {
        //1.实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
        Credential cred = new Credential(vodConstantProperties.getSecretId(), vodConstantProperties.getSecretKey());
        //2.实例化一个请求对象,每个接口都会对应一个request对象
        DeleteMediaRequest req = new DeleteMediaRequest();
        req.setFileId(mediaFileId);
        //3.实例化要请求产品的client对象,clientProfile是可选的
        VodClient client = new VodClient(cred, vodConstantProperties.getRegion());
        //4.返回的resp是一个DeleteMediaResponse的实例,与请求对象对应
        client.DeleteMedia(req);
    } catch (TencentCloudSDKException e) {
        log.info("[专辑服务]删除云点播文件:{},失败{}", mediaFileId, e);
    }
}

4、删除声音

涉及的表: track_info,album_info,track_stat,media

YAPI接口地址:http://192.168.200.6:3000/project/11/interface/api/39

TrackInfoApiController控制器

/**
 * TODO 该接口登录才可访问
 * 根据ID删除声音
 * @param id
 * @return
 */
@Operation(summary = "根据ID删除声音")
@DeleteMapping("/trackInfo/removeTrackInfo/{id}")
public Result removeTrackInfo(@PathVariable Long id){
    trackInfoService.removeTrackInfo(id);
    return Result.ok();
}

TrackInfoService接口:

/**
 * 根据ID删除声音
 * @param id 声音ID
 * @return
 */
void removeTrackInfo(Long id);

TrackInfoServiceImpl实现类

/**
 * 根据ID删除声音
 * @param id 声音ID
 * @return
 */
@Override
@Transactional(rollbackFor = Exception.class)
public void removeTrackInfo(Long id) {
    //1.根据要删除声音ID查询声音对象-获取当前声音序号用于更新其他声音序号
    TrackInfo trackInfo = trackInfoMapper.selectById(id);
    Integer orderNum = trackInfo.getOrderNum();
    //2.删除声音-逻辑删除
    trackInfoMapper.deleteById(id);

    //3.将比删除声音序号大的声音序号进行递减
    trackInfoMapper.updateTrackNum(trackInfo.getAlbumId(), orderNum);

    //4.删除声音统计记录
    LambdaQueryWrapper<TrackStat> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(TrackStat::getTrackId, id);
    trackStatMapper.delete(queryWrapper);

    //5.更新专辑包含声音数量减一
    AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
    albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount()-1);
    albumInfoMapper.updateById(albumInfo);

    //6.删除云点播平台声音文件
    vodService.deleteTrackMedia(trackInfo.getMediaFileId());
}

TrackInfoMapper

/**
 * 修改声音序号
 * @param albumId
 * @param orderNum
 */
void updateTrackNum(@Param("albumId") Long albumId, @Param("orderNum") Integer orderNum);

TrackInfoMapper.xml

<update id="updateTrackNum">
    UPDATE track_info
    SET order_num = order_num - 1
    WHERE
        album_id = #{albumId}
      AND order_num > #{orderNum}
      AND is_deleted = 0
</update>