第三次课程提交 声音系统的管理和提交

This commit is contained in:
Y1NanPing 2025-07-21 20:23:24 +08:00
parent 8f88132e32
commit 6034ffa8c5
25 changed files with 781 additions and 4 deletions

View File

@ -24,6 +24,8 @@ public class SystemConstant {
//声音状态 0501-审核通过 0502"-审核不通过 //声音状态 0501-审核通过 0502"-审核不通过
public static final String TRACK_STATUS_PASS="0501"; // 审核通过 public static final String TRACK_STATUS_PASS="0501"; // 审核通过
public static final String TRACK_STATUS_NO_PASS="0502"; // 审核不通过 public static final String TRACK_STATUS_NO_PASS="0502"; // 审核不通过
public static final String TRACK_STATUS_REVIEWING = "0503";// 审核中
public static final String TRACK_STATUS_ARTIFICIAL = "0504";// 人工复审
//声音来源 0601-用户原创 0602-上传 //声音来源 0601-用户原创 0602-上传
public static final String TRACK_SOURCE_USER="0601"; // 用户原创 public static final String TRACK_SOURCE_USER="0601"; // 用户原创

View File

@ -46,6 +46,16 @@
<artifactId>rabbit-util</artifactId> <artifactId>rabbit-util</artifactId>
<version>1.0</version> <version>1.0</version>
</dependency> </dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.1014</version>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-tms</artifactId>
<version>3.1.1010</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -4,10 +4,12 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication @SpringBootApplication
@EnableDiscoveryClient @EnableDiscoveryClient
@EnableFeignClients @EnableFeignClients
@EnableScheduling //开启定时任务功能
public class ServiceAlbumApplication { public class ServiceAlbumApplication {
public static void main(String[] args) { public static void main(String[] args) {

View File

@ -76,6 +76,21 @@ public class AlbumInfoApiController {
albumInfoService.updateAlbumInfo(id, albumInfoVo); albumInfoService.updateAlbumInfo(id, albumInfoVo);
return Result.ok(); return Result.ok();
} }
/**
* TODO 改接口登录才能访问
* 查询当前用户专辑列表
* @return
*/
@Operation(summary = "查询当前用户专辑列表")
@GetMapping("/albumInfo/findUserAllAlbumList")
public Result<List<AlbumInfo>> findUserAllAlbumList(){
//1.获取用户ID
Long userId = AuthContextHolder.getUserId();
//2.调用service方法查询
List<AlbumInfo> list = albumInfoService.findUserAllAlbumList(userId);
//3.响应结果
return Result.ok(list);
}

View File

@ -1,10 +1,22 @@
package com.atguigu.tingshu.album.api; package com.atguigu.tingshu.album.api;
import com.atguigu.tingshu.album.service.TrackInfoService; import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.Map;
@Tag(name = "声音管理") @Tag(name = "声音管理")
@RestController @RestController
@ -15,5 +27,93 @@ public class TrackInfoApiController {
@Autowired @Autowired
private TrackInfoService trackInfoService; private TrackInfoService trackInfoService;
//这个接口是专门为了对上传的音频文件进行增删改查的
//trackInfoService 这个接口是专门去写项目中关于声音的一些功能
@Autowired
private VodService vodService;
//用MultipartFile来接收文件
@Operation(summary = "将音频文件上传到腾讯云点播平台")
@PostMapping("/trackInfo/uploadTrack")
public Result<Map<String,String>> uploadTrack(@RequestParam("file") MultipartFile file){
Map<String,String> map=vodService.uploadTrack(file);
return Result.ok(map);
}
/**
* TODO 该接口必须登录才能访问
* 保存声音
*
* @param trackInfoVo
* @return
*/
@Operation(summary = "保存声音")
@PostMapping("/trackInfo/saveTrackInfo")
public Result saveTrackInfo(@Validated @RequestBody TrackInfoVo trackInfoVo) {
Long userId = AuthContextHolder.getUserId();
trackInfoService.saveTrackInfo(trackInfoVo, userId);
return Result.ok();
}
/**
* TODO 该接口必须登录才能访问
* 分页条件查询当前用户声音列表
*
* @param page
* @param limit
* @param trackInfoQuery
* @return
*/
@Operation(summary = "分页条件查询当前用户声音列表")
@PostMapping("/trackInfo/findUserTrackPage/{page}/{limit}")
public Result<Page<TrackListVo>> findUserTrackPage(
@PathVariable Long page,
@PathVariable Long limit,
@RequestBody TrackInfoQuery trackInfoQuery
){
//1.获取用户ID
Long userId = AuthContextHolder.getUserId();
trackInfoQuery.setUserId(userId);
//2.封装分页对象页码页大小
Page<TrackListVo> pageInfo = new Page<>(page, limit);
//3.调用业务层分页获取结果
pageInfo = trackInfoService.findUserTrackPage(pageInfo, trackInfoQuery);
//4.响应结果
return Result.ok(pageInfo);
}
/**
* 查询声音信息
* @param id
* @return
*/
@Operation(summary = "查询声音信息")
@GetMapping("/trackInfo/getTrackInfo/{id}")
public Result<TrackInfo> getTrackInfo(@PathVariable Long id){
TrackInfo trackInfo = trackInfoService.getById(id);
return Result.ok(trackInfo);
}
/**
* 修改声音
* @param id
* @param trackInfoVo
* @return
*/
@Operation(summary = "修改声音")
@PutMapping("/trackInfo/updateTrackInfo/{id}")
public Result updateTrackInfo(@PathVariable Long id, @Validated @RequestBody TrackInfoVo trackInfoVo){
trackInfoService.updateTrackInfo(id, trackInfoVo);
return Result.ok();
}
/**
* 删除声音
* @param id
* @return
*/
@Operation(summary = "删除声音")
@DeleteMapping("/trackInfo/removeTrackInfo/{id}")
public Result removeTrackInfo(@PathVariable Long id){
trackInfoService.removeTrackInfo(id);
return Result.ok();
}
} }

View File

@ -1,8 +1,11 @@
package com.atguigu.tingshu.album.config; package com.atguigu.tingshu.album.config;
import com.qcloud.vod.VodUploadClient;
import com.tencentcloudapi.common.Credential;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
@ -18,4 +21,18 @@ public class VodConstantProperties {
private String procedure; private String procedure;
private String tempPath; private String tempPath;
private String playKey; private String playKey;
/**
* 注册用于文件上传客户端对象
*
* @return
*/
@Bean
public VodUploadClient vodUploadClient() {
return new VodUploadClient(secretId, secretKey);
}
@Bean
public Credential credential(){
return new Credential(secretId, secretKey);
}
} }

View File

@ -1,11 +1,15 @@
package com.atguigu.tingshu.album.mapper; package com.atguigu.tingshu.album.mapper;
import com.atguigu.tingshu.model.album.TrackInfo; 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.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
@Mapper @Mapper
public interface TrackInfoMapper extends BaseMapper<TrackInfo> { public interface TrackInfoMapper extends BaseMapper<TrackInfo> {
Page<TrackListVo> findUserTrackPage(Page<TrackListVo> pageInfo, TrackInfoQuery trackInfoQuery);
} }

View File

@ -7,6 +7,8 @@ import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface AlbumInfoService extends IService<AlbumInfo> { public interface AlbumInfoService extends IService<AlbumInfo> {
@ -23,4 +25,6 @@ public interface AlbumInfoService extends IService<AlbumInfo> {
AlbumInfo getAlbumInfo(Long id); AlbumInfo getAlbumInfo(Long id);
void updateAlbumInfo(Long id, AlbumInfoVo albumInfoVo); void updateAlbumInfo(Long id, AlbumInfoVo albumInfoVo);
List<AlbumInfo> findUserAllAlbumList(Long userId);
} }

View File

@ -0,0 +1,38 @@
package com.atguigu.tingshu.album.service;
import org.springframework.web.multipart.MultipartFile;
public interface AuditService {
//这个是专门封装出来为音频审核做的接口
/**
* 对文本进行审核
* @param content
* @return
*/
String auditText(String content);
/**
* 对图片进行审核
* @param file
* @return
*/
String auditImage(MultipartFile file);
/**
* 发起审核任务
* @param mediaFileId
* @return 任务ID
*/
String startReviewTask(String mediaFileId);
/**
* 根据任务ID 获取审核结果
* @param taskId
* @return 建议
*/
String getReviewResult(String taskId);
}

View File

@ -1,8 +1,35 @@
package com.atguigu.tingshu.album.service; package com.atguigu.tingshu.album.service;
import com.atguigu.tingshu.model.album.TrackInfo; import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import java.util.Map;
public interface TrackInfoService extends IService<TrackInfo> { public interface TrackInfoService extends IService<TrackInfo> {
/**
* 保存声音
*
* @param trackInfoVo
* @param userId
*/
void saveTrackInfo(TrackInfoVo trackInfoVo, Long userId);
/**
* 保存声音统计信息
* @param trackId 声音ID
* @param statType 统计类型
* @param statNum 统计数值
*/
void saveTrackStat(Long trackId, String statType, int statNum);
Page<TrackListVo> findUserTrackPage(Page<TrackListVo> pageInfo, TrackInfoQuery trackInfoQuery);
void updateTrackInfo(Long id, TrackInfoVo trackInfoVo);
void removeTrackInfo(Long id);
} }

View File

@ -1,5 +1,19 @@
package com.atguigu.tingshu.album.service; package com.atguigu.tingshu.album.service;
import com.atguigu.tingshu.vo.album.TrackMediaInfoVo;
import org.springframework.web.multipart.MultipartFile;
import java.util.Map;
public interface VodService { public interface VodService {
Map<String, String> uploadTrack(MultipartFile file);
TrackMediaInfoVo getMediaInfo(String mediaFileId);
/**
* 从点播平台删除音频
* @param mediaFileId
*/
void deleteMedia(String mediaFileId);
} }

View File

@ -18,7 +18,7 @@ import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo; import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo; import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -173,4 +173,23 @@ public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo
} }
} }
@Override
public List<AlbumInfo> findUserAllAlbumList(Long userId) {
LambdaQueryWrapper<AlbumInfo> queryWrapper = new LambdaQueryWrapper<>();
//1.查询当前用户专辑列表
queryWrapper.eq(AlbumInfo::getUserId, userId);
//2.专辑状态为审核通过
queryWrapper.eq(AlbumInfo::getStatus,SystemConstant.ALBUM_STATUS_PASS);
//3.专辑ID专辑标题
queryWrapper.select(AlbumInfo::getId, AlbumInfo::getAlbumTitle);
//4.限制返回记录数造成前端卡顿 TODO 后期前端改为带分页下拉框
queryWrapper.last("limit 200");
//5.按照专辑ID降序
queryWrapper.orderByDesc(AlbumInfo::getId);
return albumInfoMapper.selectList(queryWrapper);
}
} }

View File

@ -0,0 +1,153 @@
package com.atguigu.tingshu.album.service.impl;
import cn.hutool.core.codec.Base64;
import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.service.AuditService;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.ims.v20201229.ImsClient;
import com.tencentcloudapi.ims.v20201229.models.ImageModerationRequest;
import com.tencentcloudapi.ims.v20201229.models.ImageModerationResponse;
import com.tencentcloudapi.tms.v20201229.TmsClient;
import com.tencentcloudapi.tms.v20201229.models.TextModerationRequest;
import com.tencentcloudapi.tms.v20201229.models.TextModerationResponse;
import com.tencentcloudapi.vod.v20180717.VodClient;
import com.tencentcloudapi.vod.v20180717.models.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
/**
* @author: atguigu
* @create: 2025-07-19 14:24
*/
@Slf4j
@Service
public class AuditServiceImpl implements AuditService {
@Autowired
private Credential credential;
@Autowired
private VodConstantProperties vodConstantProperties;
/**
* 审核文本
*
* @param content
* @return
*/
@Override
public String auditText(String content) {
try {
//1.实例化要请求产品的client对象,clientProfile是可选的
TmsClient client = new TmsClient(credential, vodConstantProperties.getRegion());
//2.实例化一个请求对象,每个接口都会对应一个request对象
TextModerationRequest req = new TextModerationRequest();
//对文本进行Base64编码
req.setContent(Base64.encode(content));
//3.返回的resp是一个TextModerationResponse的实例与请求对象对应
TextModerationResponse resp = client.TextModeration(req);
if (resp != null) {
String suggestion = resp.getSuggestion();
return suggestion.toLowerCase();
}
} catch (TencentCloudSDKException e) {
log.error("文本审核失败");
}
return null;
}
/**
* 对图片进行审核
*
* @param file
* @return
*/
@Override
public String auditImage(MultipartFile file) {
try {
//1.实例化要请求产品的client对象,clientProfile是可选的
ImsClient client = new ImsClient(credential, vodConstantProperties.getRegion());
//2.实例化一个请求对象,每个接口都会对应一个request对象
ImageModerationRequest req = new ImageModerationRequest();
req.setFileContent(Base64.encode(file.getInputStream()));
//3.返回的resp是一个ImageModerationResponse的实例与请求对象对应
ImageModerationResponse resp = client.ImageModeration(req);
if (resp != null) {
String suggestion = resp.getSuggestion();
return suggestion.toLowerCase();
}
} catch (Exception e) {
log.error("图片审核失败");
}
return null;
}
/**
* 发起审核任务
*
* @param mediaFileId
* @return 任务ID
*/
@Override
public String startReviewTask(String mediaFileId) {
try {
//1.实例化要请求产品的client对象,clientProfile是可选的
VodClient client = new VodClient(credential, vodConstantProperties.getRegion());
//2.实例化一个请求对象,每个接口都会对应一个request对象
ReviewAudioVideoRequest req = new ReviewAudioVideoRequest();
req.setFileId(mediaFileId);
//3.返回的resp是一个ReviewAudioVideoResponse的实例与请求对象对应
ReviewAudioVideoResponse resp = client.ReviewAudioVideo(req);
if (resp != null) {
return resp.getTaskId();
}
} catch (TencentCloudSDKException e) {
log.error("音视频审核任务发起失败");
}
return null;
}
/**
* 根据任务ID 获取审核结果
*
* @param taskId
* @return 建议
*/
@Override
public String getReviewResult(String taskId) {
try {
//1.实例化要请求产品的client对象,clientProfile是可选的
VodClient client = new VodClient(credential, vodConstantProperties.getRegion());
//2.例化一个请求对象,每个接口都会对应一个request对象
DescribeTaskDetailRequest req = new DescribeTaskDetailRequest();
req.setTaskId(taskId);
//3.返回的resp是一个DescribeTaskDetailResponse的实例与请求对象对应
DescribeTaskDetailResponse resp = client.DescribeTaskDetail(req);
//4.解析任务状态以及审核任务建议结果
if (resp != null) {
String taskType = resp.getTaskType();
//4.1 获取任务类型是否为音频审核
if ("ReviewAudioVideo".equals(taskType)) {
//4.2 获取任务状态是否为已完成
String status = resp.getStatus();
if ("FINISH".equals(status)) {
ReviewAudioVideoTask reviewAudioVideoTask = resp.getReviewAudioVideoTask();
if (reviewAudioVideoTask != null) {
ReviewAudioVideoTaskOutput output = reviewAudioVideoTask.getOutput();
String suggestion = output.getSuggestion();
//4.3 获取建议
return suggestion.toLowerCase();
}
}
}
}
} catch (TencentCloudSDKException e) {
System.out.println(e.toString());
}
return null;
}
}

View File

@ -1,12 +1,32 @@
package com.atguigu.tingshu.album.service.impl; package com.atguigu.tingshu.album.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper; import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.AuditService;
import com.atguigu.tingshu.album.service.TrackInfoService; 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.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo; import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.atguigu.tingshu.vo.album.TrackMediaInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Map;
@Slf4j @Slf4j
@Service @Service
@ -15,5 +35,176 @@ public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo
@Autowired @Autowired
private TrackInfoMapper trackInfoMapper; private TrackInfoMapper trackInfoMapper;
@Autowired
private AlbumInfoMapper albumInfoMapper;
@Autowired
private VodService vodService;
@Autowired
private AuditService auditService;
@Autowired
private TrackStatMapper trackStatMapper;
@Override
public void saveTrackInfo(TrackInfoVo trackInfoVo, Long userId) {
//1. 根据专辑id查询专辑信息 用于更新专辑声音数量
AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfoVo.getAlbumId());
//2. 保存声音信息
//2.1 将VO转为PO对象
TrackInfo trackInfo = BeanUtil.copyProperties(trackInfoVo, TrackInfo.class);
//2.2 属性赋值
//2.2.1 基本属性用户ID审核状态封面图片来源
trackInfo.setUserId(userId);
trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS);
trackInfo.setSource(SystemConstant.TRACK_SOURCE_USER);
if (StringUtils.isBlank(trackInfo.getCoverUrl())) {
//如果用户没有选择封面图片 则用户所属专辑的封面图片
trackInfo.setCoverUrl(albumInfo.getCoverUrl());
}
//2.2.2 计算声音序号 现有专辑包含声音数量+1
trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);
//2.2.3 查询点播平台获取音频详情得到时长大小类型
TrackMediaInfoVo mediaInfoVo = vodService.getMediaInfo(trackInfo.getMediaFileId());
if (mediaInfoVo != null) {
trackInfo.setMediaDuration(BigDecimal.valueOf(mediaInfoVo.getDuration()));
trackInfo.setMediaSize(mediaInfoVo.getSize());
trackInfo.setMediaType(mediaInfoVo.getType());
}
albumInfoMapper.updateById(albumInfo);
//2.2.5 保存声音记录
trackInfoMapper.insert(trackInfo);
//2.2.4 TODO 调用内容审核接口发起内容审核更新审核任务ID
String text = trackInfo.getTrackTitle() + trackInfo.getTrackIntro();
String suggestion = auditService.auditText(text);
if ("pass".equals(suggestion)) {
trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
//TODO 文本审核通过后还需要进一步审核音频内容
String reviewTaskId = auditService.startReviewTask(trackInfo.getMediaFileId());
trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
trackInfo.setReviewTaskId(reviewTaskId);
} else if ("block".equals(suggestion)) {
trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS);
} else if ("review".equals(suggestion)) {
trackInfo.setStatus(SystemConstant.TRACK_STATUS_ARTIFICIAL);
}
trackInfoMapper.updateById(trackInfo);
//3. 更新专辑包含声音数量
albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
albumInfoMapper.updateById(albumInfo);
//3.新增声音统计信息
this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY, 0);
this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT, 0);
this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE, 0);
this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT, 0);
}
/**
* 保存声音统计信息
*
* @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);
}
@Override
public Page<TrackListVo> findUserTrackPage(Page<TrackListVo> pageInfo, TrackInfoQuery trackInfoQuery) {
return trackInfoMapper.findUserTrackPage(pageInfo, trackInfoQuery);
}
@Override
public void updateTrackInfo(Long id, TrackInfoVo trackInfoVo) {
//1. 根据id查询声音信息 得到"原有的"音频唯一标识
TrackInfo trackInfo = trackInfoMapper.selectById(id);
String oldMediaFileId = trackInfo.getMediaFileId();
//对文本进行审核
String text = trackInfoVo.getTrackTitle() + trackInfoVo.getTrackIntro();
String suggestion = auditService.auditText(text);
if ("pass".equals(suggestion)) {
trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
} else if ("block".equals(suggestion)) {
trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS);
} else if ("review".equals(suggestion)) {
trackInfo.setStatus(SystemConstant.TRACK_STATUS_ARTIFICIAL);
}
//2. 判断音频文件是否发生更新如果更新
if (!trackInfoVo.getMediaFileId().equals(oldMediaFileId)) {
// 2.1 调用点播平台获取最新音频详情信息
TrackMediaInfoVo mediaInfo = vodService.getMediaInfo(trackInfoVo.getMediaFileId());
if (mediaInfo != null) {
trackInfo.setMediaFileId(trackInfoVo.getMediaFileId());
trackInfo.setMediaUrl(trackInfoVo.getMediaUrl());
trackInfo.setMediaDuration(BigDecimal.valueOf(mediaInfo.getDuration()));
trackInfo.setMediaType(mediaInfo.getType());
trackInfo.setMediaSize(mediaInfo.getSize());
// 2.2 TODO 再次对变更后音频文件进行内容审核
String reviewTaskId = auditService.startReviewTask(trackInfo.getMediaFileId());
trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
trackInfo.setReviewTaskId(reviewTaskId);
// 2.3原有音频从点播平台删除
vodService.deleteMedia(oldMediaFileId);
}
}
trackInfo.setTrackTitle(trackInfoVo.getTrackTitle());
trackInfo.setTrackIntro(trackInfoVo.getTrackIntro());
trackInfo.setCoverUrl(trackInfoVo.getCoverUrl());
//3. 更新声音记录
trackInfoMapper.updateById(trackInfo);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removeTrackInfo(Long id) {
//1.根据ID查询要被删除声音记录 得到专辑ID跟序号
TrackInfo trackInfo = trackInfoMapper.selectById(id);
Long albumId = trackInfo.getAlbumId();
Integer orderNum = trackInfo.getOrderNum();
//2.更新序号确保声音表中序号连续
trackInfoMapper.update(
null,
new LambdaUpdateWrapper<TrackInfo>()
.eq(TrackInfo::getAlbumId, albumId)
.gt(TrackInfo::getOrderNum, orderNum)
.setSql("order_num = order_num - 1")
);
//3.删除声音
trackInfoMapper.deleteById(id);
//4.删除统计信息
trackStatMapper.delete(
new LambdaQueryWrapper<TrackStat>()
.eq(TrackStat::getTrackId, id)
);
//5.更新专辑包含声音数量
albumInfoMapper.update(null,
new LambdaUpdateWrapper<AlbumInfo>().eq(AlbumInfo::getId, albumId)
.setSql("include_track_count = include_track_count - 1")
);
//6.删除点播平台音频文件
vodService.deleteMedia(trackInfo.getMediaFileId());
}
} }

View File

@ -2,14 +2,105 @@ package com.atguigu.tingshu.album.service.impl;
import com.atguigu.tingshu.album.config.VodConstantProperties; import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.service.VodService; import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.util.UploadFileUtil;
import com.atguigu.tingshu.vo.album.TrackMediaInfoVo;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.vod.v20180717.VodClient;
import com.tencentcloudapi.vod.v20180717.models.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.util.Map;
@Slf4j
@Service @Service
public class VodServiceImpl implements VodService { public class VodServiceImpl implements VodService {
@Autowired @Autowired
private VodConstantProperties vodConstantProperties; private VodConstantProperties vodConstantProperties;
@Autowired
private VodUploadClient vodUploadClient;
@Autowired
private Credential credential;
@Override
public Map<String, String> uploadTrack(MultipartFile file) {
//1.首先将上传文件保存到本地临时文件夹中 后期采取定时任务去清理临时文件夹
try {
String tempPath = UploadFileUtil.uploadTempPath(vodConstantProperties.getTempPath(), file);
//2.上传到腾讯云点播平台
//2.1 构造上传请求对象
VodUploadRequest request = new VodUploadRequest();
request.setMediaFilePath(tempPath);
//2.2 调用上传
VodUploadResponse response = vodUploadClient.upload(vodConstantProperties.getRegion(), request);
//2.3 获取上传结果得到音频媒体唯一标识mediaFileId,以及音频在线地址mediaUrl
if (response != null) {
String fileId = response.getFileId();
String mediaUrl = response.getMediaUrl();
return Map.of("mediaFileId", fileId, "mediaUrl", mediaUrl);
}
return null;
} catch (Exception e) {
log.error("上传音频文件失败");
throw new RuntimeException(e);
}
}
@Override
public TrackMediaInfoVo getMediaInfo(String mediaFileId) {
try {
//1.实例化要请求产品的client对象,
VodClient client = new VodClient(credential, vodConstantProperties.getRegion());
//2.实例化一个请求对象,每个接口都会对应一个request对象
DescribeMediaInfosRequest req = new DescribeMediaInfosRequest();
String[] fileIds1 = {mediaFileId};
req.setFileIds(fileIds1);
//3.返回的resp是一个DescribeMediaInfosResponse的实例与请求对象对应
DescribeMediaInfosResponse resp = client.DescribeMediaInfos(req);
//4.解析结果获取基本信息中类型以及元数据时长跟大小
if (resp != null) {
MediaInfo mediaInfo = resp.getMediaInfoSet()[0];
if (mediaInfo != null) {
//4.1 获取基本信息
MediaBasicInfo basicInfo = mediaInfo.getBasicInfo();
//4.2 获取元数据
MediaMetaData metaData = mediaInfo.getMetaData();
//4.3 封装音频文件详情VO
TrackMediaInfoVo trackMediaInfoVo = new TrackMediaInfoVo();
trackMediaInfoVo.setType(basicInfo.getType());
trackMediaInfoVo.setSize(metaData.getSize());
trackMediaInfoVo.setDuration(metaData.getDuration());
return trackMediaInfoVo;
}
}
} catch (TencentCloudSDKException e) {
log.error("获取音频信息失败");
throw new RuntimeException(e);
}
return null;
}
@Override
public void deleteMedia(String mediaFileId) {
try {
//1.实例化要请求产品的client对象,clientProfile是可选的
VodClient client = new VodClient(credential, vodConstantProperties.getRegion());
//2.实例化一个请求对象,每个接口都会对应一个request对象
DeleteMediaRequest req = new DeleteMediaRequest();
req.setFileId(mediaFileId);
//3.返回的resp是一个DeleteMediaResponse的实例与请求对象对应
client.DeleteMedia(req);
} catch (TencentCloudSDKException e) {
log.error("删除失败");
}
} }
}

View File

@ -0,0 +1,63 @@
package com.atguigu.tingshu.album.task;
import cn.hutool.core.collection.CollUtil;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.service.AuditService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author: atguigu
* @create: 2025-07-19 15:38
*/
@Slf4j
@Component
public class ReviewTask {
@Autowired
private TrackInfoMapper trackInfoMapper;
@Autowired
private AuditService auditService;
/**
* 每5秒检查一次审核结果
*/
@Scheduled(cron = "0/5 * * * * ?")
public void checkReviewResultTask() {
log.info("开始检查审核任务");
//1.查询审核状态为"审核中"声音列表
List<TrackInfo> trackInfoList = trackInfoMapper.selectList(
new LambdaQueryWrapper<TrackInfo>()
.eq(TrackInfo::getStatus, SystemConstant.TRACK_STATUS_REVIEWING)
.select(TrackInfo::getId, TrackInfo::getReviewTaskId)
.last("limit 100")
);
//2.遍历列表查询审核结果
if (CollUtil.isNotEmpty(trackInfoList)) {
for (TrackInfo trackInfo : trackInfoList) {
String suggestion = auditService.getReviewResult(trackInfo.getReviewTaskId());
if (StringUtils.isNotBlank(suggestion)) {
//3.根据审核任务结果更新声音审核状态
if ("pass".equals(suggestion)) {
trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
} else if ("block".equals(suggestion)) {
trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS);
} else if ("review".equals(suggestion)) {
trackInfo.setStatus(SystemConstant.TRACK_STATUS_ARTIFICIAL);
}
trackInfoMapper.updateById(trackInfo);
}
}
}
}
}

View File

@ -3,7 +3,34 @@
<mapper namespace="com.atguigu.tingshu.album.mapper.TrackInfoMapper"> <mapper namespace="com.atguigu.tingshu.album.mapper.TrackInfoMapper">
<select id="findUserTrackPage" resultType="com.atguigu.tingshu.vo.album.TrackListVo">
select
ti.id track_id,
ti.track_title,
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 inner join track_stat stat
on stat.track_id = ti.id and stat.is_deleted = 0
<where>
<if test="vo.userId != null">
and ti.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> </mapper>