vlogReviewTest

This commit is contained in:
abu 2025-04-03 11:25:24 +08:00
parent 0d1395fa4b
commit ef5823cd43
11 changed files with 337 additions and 41 deletions

View File

@ -16,6 +16,11 @@ import org.springframework.web.multipart.MultipartFile;
import com.imooc.utils.QcCloud; import com.imooc.utils.QcCloud;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
@Slf4j @Slf4j
@Api(tags = "FileController 文件上传测试的接口") @Api(tags = "FileController 文件上传测试的接口")
@ -27,9 +32,35 @@ public class FileController {
@PostMapping("upload") @PostMapping("upload")
public GraceJSONResult upload(MultipartFile file) throws Exception { public GraceJSONResult upload(MultipartFile file) throws Exception {
// 获取 vlogdata 目录路径
String storagePath = new File(System.getProperty("user.dir"), "vlogdata").getAbsolutePath();
log.info(storagePath);
File storageDir = new File(storagePath);
if (!storageDir.exists()) {
storageDir.mkdirs(); // 确保目录存在
}
// 组装完整的存储路径
File destFile = new File(storageDir, file.getOriginalFilename());
log.info(destFile.getAbsolutePath());
// 存储文件
// file.transferTo(destFile);
// 使用流手动存储文件
try (InputStream inputStream = file.getInputStream();
OutputStream outputStream = new FileOutputStream(destFile)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
log.info("文件存储成功: " + destFile.getAbsolutePath());
String fileName = file.getOriginalFilename(); String fileName = file.getOriginalFilename();
MinIOUtils.uploadFile(minIOConfig.getBucketName(), MinIOUtils.uploadFile(minIOConfig.getBucketName(),
fileName, fileName,
file.getInputStream()); file.getInputStream());
@ -42,11 +73,4 @@ public class FileController {
return GraceJSONResult.ok(imgUrl); return GraceJSONResult.ok(imgUrl);
} }
private QcCloud qcCloud;
@PostMapping("uploadVd")
public GraceJSONResult uploadVd(MultipartFile file) throws Exception {
String vdFileId = qcCloud.uploadViaTempFile(file);
return GraceJSONResult.ok(vdFileId);
}
} }

View File

@ -1,11 +1,14 @@
package com.imooc.controller; package com.imooc.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.imooc.base.BaseInfoProperties; import com.imooc.base.BaseInfoProperties;
import com.imooc.bo.VlogBO; import com.imooc.bo.VlogBO;
import com.imooc.enums.YesOrNo; import com.imooc.enums.YesOrNo;
import com.imooc.grace.result.GraceJSONResult; import com.imooc.grace.result.GraceJSONResult;
import com.imooc.service.VlogService; import com.imooc.service.VlogService;
import com.imooc.utils.PagedGridResult; import com.imooc.utils.PagedGridResult;
import com.qcloud.vod.model.VodUploadResponse;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -14,20 +17,134 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import com.imooc.utils.QcCloud;
import java.util.List;
import java.util.Map;
@Slf4j @Slf4j
@Api(tags = "VlogController 短视频相关业务功能的接口") @Api(tags = "VlogController 短视频相关业务功能的接口")
@RequestMapping("vlog") @RequestMapping("vlog")
@RestController @RestController
@RefreshScope @RefreshScope
public class VlogController extends BaseInfoProperties { public class VlogController extends BaseInfoProperties {
@Autowired @Autowired
private VlogService vlogService; private VlogService vlogService;
@Autowired
private QcCloud qcCloud;
@PostMapping("vodCallBack")
public GraceJSONResult vodCallBack(@RequestBody Map<String, Object> callbackData) {
try {
// 解析回调事件类型
String eventType = (String) callbackData.get("EventType");
log.info("收到腾讯云 VOD 回调, 事件类型: {}", eventType);
log.info(callbackData.toString());
if ("ProcedureStateChanged".equals(eventType)) {
// 获取 ProcedureStateChangeEvent
Object eventObject = callbackData.get("ProcedureStateChangeEvent");
if (eventObject instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> procedureStateChangeEvent = (Map<String, Object>) eventObject;
String fileId = (String) procedureStateChangeEvent.get("FileId");
String fileUrl = (String) procedureStateChangeEvent.get("FileUrl"); // 原视频地址
String status = (String) procedureStateChangeEvent.get("Status"); // FINISH
// 只处理完成状态
if ("FINISH".equals(status)) {
// 获取 MediaProcessResultSet可能包含任务流信息
List<Map<String, Object>> mediaProcessResultSet =
(List<Map<String, Object>>) procedureStateChangeEvent.get("MediaProcessResultSet");
if (mediaProcessResultSet != null) {
for (Map<String, Object> processResult : mediaProcessResultSet) {
String type = (String) processResult.get("Type");
if ("CoverBySnapshot".equals(type)) {
// 任务流首帧截图
Map<String, Object> coverBySnapshotTask =
(Map<String, Object>) processResult.get("CoverBySnapshotTask");
Map<String, Object> output = (Map<String, Object>) coverBySnapshotTask.get("Output");
String coverUrl = (String) output.get("CoverUrl"); // 正确获取 CoverUrl
log.info("任务流 [截取首帧] 完成, FileId: {}, 封面图 URL: {}", fileId, coverUrl);
// TODO: 更新数据库存储首帧图,设置云端地址删除本地视频文件
vlogService.updateVlogFirstImg(fileId,coverUrl,fileUrl);
}
}
} else {
log.warn("MediaProcessResultSet 为空,回调数据: {}", callbackData);
}
}
log.info("任务流完成, FileId: {}, Status: {}", fileId, status);
} else {
log.warn("ProcedureStateChangeEvent 为空或格式错误,回调数据: {}", callbackData);
}
}
if("ReviewAudioVideoComplete".equals(eventType)){
//处理审核结果
Map<String, Object> reviewEvent = (Map<String, Object>) callbackData.get("ReviewAudioVideoCompleteEvent");
if (reviewEvent != null) {
String status = (String) reviewEvent.get("Status"); // FINISH
if ("FINISH".equals(status)) {
Map<String, Object> input = (Map<String, Object>) reviewEvent.get("Input");
String fileId = (String) input.get("FileId"); // 查询的唯一标识
// 获取审核结果
Map<String, Object> output = (Map<String, Object>) reviewEvent.get("Output");
String suggestion = (String) output.get("Suggestion");
if ("block".equals(suggestion)) {
// 审核不通过修改视频状态为2写入reason发送站内消息告知审核结果
// label
//Porn色情
//Terror暴力
//Polity不适宜的信息
//Ad广告
//Illegal违法
//Abuse谩骂
//Moan娇喘
String label = (String) output.get("Label"); // 违规内容
String subLabel = (String) output.get("SubLabel"); // 违规子标签
log.info("视频文件 {} 审核驳回, 原因: {} - {}", fileId, label, subLabel);
// TODO: 更新数据库
vlogService.updateVlogStatus(fileId,2,label);
} else if ("pass".equals(suggestion)) {
// 审核通过处理通过逻辑,修改视频状态为1发送站内消息告知审核结果
log.info("视频文件 {} 审核通过", fileId);
// TODO: 更新数据库
vlogService.updateVlogStatus(fileId,1,"通过");
} else if ("review".equals(suggestion)){
// 建议复审修改状态为3发送站内消息告知审核结果
String label = (String) output.get("Label"); // 违规内容
String subLabel = (String) output.get("SubLabel"); // 违规子标签
log.info("视频文件 {} 建议复审, 原因: {} - {}", fileId, label, subLabel);
// TODO: 更新数据库
vlogService.updateVlogStatus(fileId,3,"等待复审");
}
}
}
}
// 处理完成返回成功防止腾讯云重复发送回调
return GraceJSONResult.ok();
} catch (Exception e) {
log.error("处理腾讯云 VOD 回调异常: ", e);
return GraceJSONResult.error();
}
}
@PostMapping("publish") @PostMapping("publish")
public GraceJSONResult publish(@RequestBody VlogBO vlogBO) { public GraceJSONResult publish(@RequestBody VlogBO vlogBO) throws Exception {
String url = vlogBO.getUrl();
log.info(url);
String fileName = url.substring(url.lastIndexOf("/") + 1);
log.info(fileName);
String fileId = qcCloud.uploadViaTempFile(fileName);
log.info(fileId);
vlogBO.setFileId(fileId);
// FIXME 作业校验VlogBO // FIXME 作业校验VlogBO
vlogService.createVlog(vlogBO); vlogService.createVlog(vlogBO);
return GraceJSONResult.ok(); return GraceJSONResult.ok();
} }

View File

@ -16,9 +16,9 @@ spring:
servlet: servlet:
multipart: multipart:
max-file-size: 1024MB # 文件上传大小限制,设置最大值,不能超过该值,否则报错 max-file-size: 1024MB # 文件上传大小限制,设置最大值,不能超过该值,否则报错
# max-file-size: 500KB # 文件上传大小限制,设置最大值,不能超过该值,否则报错
max-request-size: 1024MB # 文件最大请求限制,用于批量上传 max-request-size: 1024MB # 文件最大请求限制,用于批量上传
# max-request-size: 500KB # location: /Users/wuzhongjie/Desktop/vlog-1.0.0/vlogdata
nacos: nacos:
counts: 10 counts: 10
# 整合mybatis # 整合mybatis

View File

@ -3,18 +3,12 @@ import com.qcloud.vod.model.VodUploadResponse;
import com.qcloud.vod.VodUploadClient; import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest; import com.qcloud.vod.model.VodUploadRequest;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.File; import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
import java.util.UUID;
@ -36,31 +30,30 @@ public class QcCloud {
public String uploadViaTempFile(MultipartFile file) throws Exception { public String uploadViaTempFile(String fileName) throws Exception {
// 1. 获取文件扩展名 //确保 vlogdata 目录存在
String extension = FilenameUtils.getExtension(file.getOriginalFilename()); String storagePath = new File(System.getProperty("user.dir"), "vlogdata").getAbsolutePath();
System.out.println("绝对路径: " + storagePath);
// 2. 获取当前时间戳和 UUID 作为文件名 File storageDir = new File(storagePath);
String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); // 组装完整的文件路径
String uniqueFilename = "vod-" + timestamp + "-" + UUID.randomUUID().toString().replace("-", "").substring(8) + "." + extension; File sourceFile = new File(storageDir, fileName);
System.out.println("完整文件路径: " + sourceFile.toPath());
// 3. 创建临时文件并转存
Path tempFile = Files.createTempFile("vod-", "-" + uniqueFilename); // 创建临时文件
file.transferTo(tempFile.toFile()); // 将文件上传内容写入临时文件
System.out.println("文件临时路径: " + tempFile.toString());
if (!sourceFile.exists()) {
throw new RuntimeException("文件不存在: " + sourceFile.getAbsolutePath());
}
try { try {
VodUploadClient client = new VodUploadClient(getSecretId(), getSecretKey()); VodUploadClient client = new VodUploadClient(getSecretId(), getSecretKey());
VodUploadRequest request = new VodUploadRequest(); VodUploadRequest request = new VodUploadRequest();
request.setMediaFilePath(tempFile.toString()); // 指定本地文件路径 request.setMediaFilePath(sourceFile.getAbsolutePath());// 指定本地文件路径
request.setProcedure("视频处理"); // 智能审核+水印 request.setProcedure("视频处理"); // 智能审核+水印
// 4. 上传文件 // 4. 上传文件
VodUploadResponse response = client.upload(getRegion(), request); VodUploadResponse response = client.upload(getRegion(), request);
return response.getFileId(); // 返回腾讯云 VOD id // return response;
return response.getFileId();
} finally { } finally {
// 5. 删除临时文件 // 5. 删除文件
Files.deleteIfExists(tempFile); Files.deleteIfExists(sourceFile.toPath());
} }
} }
} }

View File

@ -17,5 +17,11 @@
<result column="is_private" property="isPrivate" jdbcType="INTEGER" /> <result column="is_private" property="isPrivate" jdbcType="INTEGER" />
<result column="created_time" property="createdTime" jdbcType="TIMESTAMP" /> <result column="created_time" property="createdTime" jdbcType="TIMESTAMP" />
<result column="updated_time" property="updatedTime" jdbcType="TIMESTAMP" /> <result column="updated_time" property="updatedTime" jdbcType="TIMESTAMP" />
<result column="status" property="status" jdbcType="INTEGER" />
<result column="reason" property="reason" jdbcType="VARCHAR" />
<result column="city_code" property="cityCode" jdbcType="INTEGER" />
<result column="file_id" property="fileId" jdbcType="VARCHAR" />
<result column="first_frame_img" property="firstFrameImg" jdbcType="VARCHAR" />
</resultMap> </resultMap>
</mapper> </mapper>

View File

@ -15,7 +15,12 @@
v.height as height, v.height as height,
v.like_counts as likeCounts, v.like_counts as likeCounts,
v.comments_counts as commentsCounts, v.comments_counts as commentsCounts,
v.is_private as isPrivate v.is_private as isPrivate,
v.city_code as cityCode,
v.reason as reason,
v.file_id as fileId,
v.status as status,
v.first_frame_img as firstFrameImg
FROM FROM
t_vlog v t_vlog v
LEFT JOIN LEFT JOIN
@ -25,6 +30,7 @@
WHERE WHERE
v.is_private = 0 v.is_private = 0
AND v.status = 1 AND v.status = 1
AND v.first_frame_img IS NOT NULL
<if test="paramMap.search != null and paramMap.search != ''"> <if test="paramMap.search != null and paramMap.search != ''">
AND v.title like '%${paramMap.search}%' AND v.title like '%${paramMap.search}%'
</if> </if>
@ -47,7 +53,12 @@
v.height as height, v.height as height,
v.like_counts as likeCounts, v.like_counts as likeCounts,
v.comments_counts as commentsCounts, v.comments_counts as commentsCounts,
v.is_private as isPrivate v.is_private as isPrivate,
v.city_code as cityCode,
v.reason as reason,
v.file_id as fileId,
v.status as status,
v.first_frame_img as firstFrameImg
FROM FROM
t_vlog v t_vlog v
LEFT JOIN LEFT JOIN
@ -56,6 +67,8 @@
v.vloger_id = u.id v.vloger_id = u.id
WHERE WHERE
v.id = #{paramMap.vlogId} v.id = #{paramMap.vlogId}
AND v.status = 1
AND v.first_frame_img IS NOT NULL
</select> </select>
<select id="getMyLikedVlogList" parameterType="map" resultType="com.imooc.vo.IndexVlogVO"> <select id="getMyLikedVlogList" parameterType="map" resultType="com.imooc.vo.IndexVlogVO">
@ -71,7 +84,12 @@
v.height as height, v.height as height,
v.like_counts as likeCounts, v.like_counts as likeCounts,
v.comments_counts as commentsCounts, v.comments_counts as commentsCounts,
v.is_private as isPrivate v.is_private as isPrivate,
v.city_code as cityCode,
v.reason as reason,
v.status as status,
v.file_id as fileId,
v.first_frame_img as firstFrameImg
FROM FROM
t_vlog v t_vlog v
LEFT JOIN LEFT JOIN
@ -84,8 +102,10 @@
mlv.user_id = u.id mlv.user_id = u.id
WHERE WHERE
u.id = #{paramMap.userId} u.id = #{paramMap.userId}
AND AND v.status = 1
v.is_private = 0 AND v.first_frame_img IS NOT NULL
AND v.is_private = 0
ORDER BY ORDER BY
v.created_time v.created_time
DESC DESC
@ -104,7 +124,12 @@
v.height as height, v.height as height,
v.like_counts as likeCounts, v.like_counts as likeCounts,
v.comments_counts as commentsCounts, v.comments_counts as commentsCounts,
v.is_private as isPrivate v.is_private as isPrivate,
v.city_code as cityCode,
v.reason as reason,
v.status as status,
v.file_id as fileId,
v.first_frame_img as firstFrameImg
FROM FROM
t_vlog v t_vlog v
LEFT JOIN LEFT JOIN
@ -117,6 +142,8 @@
f.vloger_id = u.id f.vloger_id = u.id
WHERE WHERE
v.is_private = 0 v.is_private = 0
AND v.status = 1
AND v.first_frame_img IS NOT NULL
AND AND
f.fan_id = #{paramMap.myId} f.fan_id = #{paramMap.myId}
ORDER BY ORDER BY
@ -137,7 +164,12 @@
v.height as height, v.height as height,
v.like_counts as likeCounts, v.like_counts as likeCounts,
v.comments_counts as commentsCounts, v.comments_counts as commentsCounts,
v.is_private as isPrivate v.is_private as isPrivate,
v.city_code as cityCode,
v.reason as reason,
v.status as status,
v.file_id as fileId,
v.first_frame_img as firstFrameImg
FROM FROM
t_vlog v t_vlog v
LEFT JOIN LEFT JOIN
@ -150,6 +182,8 @@
f.fan_id = u.id f.fan_id = u.id
WHERE WHERE
v.is_private = 0 v.is_private = 0
AND v.status = 1
AND v.first_frame_img IS NOT NULL
AND AND
f.vloger_id = #{paramMap.myId} f.vloger_id = #{paramMap.myId}
AND AND

View File

@ -19,4 +19,9 @@ public class VlogBO {
private Integer height; private Integer height;
private Integer likeCounts; private Integer likeCounts;
private Integer commentsCounts; private Integer commentsCounts;
private String fileId;
private Integer status;
private String reason;
private Integer cityCode;
private String firstFrameImg;
} }

View File

@ -71,6 +71,36 @@ public class Vlog {
@Column(name = "updated_time") @Column(name = "updated_time")
private Date updatedTime; private Date updatedTime;
/**
* 视频状态
*/
@Column(name = "status")
private Integer status;
/**
* 云点播文件ID
*/
@Column(name = "file_id")
private String fileId;
/**
* 审核结果
*/
@Column(name = "reason")
private String reason;
/**
* 视频归属地
*/
@Column(name = "city_code")
private Integer cityCode;
/**
* 首帧图
*/
@Column(name = "first_frame_img")
private String firstFrameImg;
/** /**
* @return id * @return id
*/ */
@ -282,4 +312,50 @@ public class Vlog {
public void setUpdatedTime(Date updatedTime) { public void setUpdatedTime(Date updatedTime) {
this.updatedTime = updatedTime; this.updatedTime = updatedTime;
} }
/**
* 设置视频状态
*/
public void setStatus(Integer status) {
this.status = status;
}
/**
* 设置视频状态
*/
public Integer getStatus() {
return status;
}
public String getFileId() {
return fileId;
}
public void setFileId(String fileId) {
this.fileId = fileId;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public Integer getCityCode() {
return cityCode;
}
public void setCityCode(Integer cityCode) {
this.cityCode = cityCode;
}
public String getFirstFrameImg() {
return firstFrameImg;
}
public void setFirstFrameImg(String firstFrameImg) {
this.firstFrameImg = firstFrameImg;
}
} }

View File

@ -25,4 +25,9 @@ public class IndexVlogVO {
private boolean isPlay = false; private boolean isPlay = false;
private boolean doIFollowVloger = false; private boolean doIFollowVloger = false;
private boolean doILikeThisVlog = false; private boolean doILikeThisVlog = false;
private Integer status;
private String reason;
private Integer cityCode;
private String fileId;
private String firstFrameImg;
} }

View File

@ -6,6 +6,14 @@ import com.imooc.utils.PagedGridResult;
import com.imooc.vo.IndexVlogVO; import com.imooc.vo.IndexVlogVO;
public interface VlogService { public interface VlogService {
/**
* 修改视频首帧图
*/
public void updateVlogFirstImg(String fileId,String url,String fileUrl);
/**
* 修改视频状态
*/
public void updateVlogStatus(String fileId,Integer status,String reason);
/** /**
* 新增vlog视频 * 新增vlog视频

View File

@ -48,6 +48,33 @@ public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
@Autowired @Autowired
private Sid sid; private Sid sid;
@Transactional
@Override
public void updateVlogStatus(String fileId,Integer status,String reason) {
Example example = new Example(Vlog.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("fileId", fileId);
Vlog pendingVlog = new Vlog();
pendingVlog.setStatus(status);
pendingVlog.setReason(reason);
vlogMapper.updateByExampleSelective(pendingVlog, example);
}
@Transactional
@Override
public void updateVlogFirstImg(String fileId,String url,String fileUrl) {
Example example = new Example(Vlog.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("fileId", fileId);
Vlog pendingVlog = new Vlog();
pendingVlog.setFirstFrameImg(url);
pendingVlog.setUrl(fileUrl);
vlogMapper.updateByExampleSelective(pendingVlog, example);
}
@Transactional @Transactional
@Override @Override
public void createVlog(VlogBO vlogBO) { public void createVlog(VlogBO vlogBO) {
@ -61,6 +88,7 @@ public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
vlog.setLikeCounts(0); vlog.setLikeCounts(0);
vlog.setCommentsCounts(0); vlog.setCommentsCounts(0);
vlog.setStatus(0);
vlog.setIsPrivate(YesOrNo.NO.type); vlog.setIsPrivate(YesOrNo.NO.type);
vlog.setCreatedTime(new Date()); vlog.setCreatedTime(new Date());