From ef5823cd43a7fc1733d4e3865b66475254d297d1 Mon Sep 17 00:00:00 2001 From: abu <3109389044@qq.com> Date: Thu, 3 Apr 2025 11:25:24 +0800 Subject: [PATCH] vlogReviewTest --- .../com/imooc/controller/FileController.java | 38 +++++- .../com/imooc/controller/VlogController.java | 121 +++++++++++++++++- book-api/src/main/resources/application.yml | 4 +- .../main/java/com/imooc/utils/QcCloud.java | 39 +++--- .../src/main/resources/mapper/VlogMapper.xml | 6 + .../resources/mapper/VlogMapperCustom.xml | 48 ++++++- .../src/main/java/com/imooc/bo/VlogBO.java | 5 + .../src/main/java/com/imooc/pojo/Vlog.java | 76 +++++++++++ .../main/java/com/imooc/vo/IndexVlogVO.java | 5 + .../java/com/imooc/service/VlogService.java | 8 ++ .../imooc/service/impl/VlogServiceImpl.java | 28 ++++ 11 files changed, 337 insertions(+), 41 deletions(-) diff --git a/book-api/src/main/java/com/imooc/controller/FileController.java b/book-api/src/main/java/com/imooc/controller/FileController.java index e9a5044..a65b7d3 100644 --- a/book-api/src/main/java/com/imooc/controller/FileController.java +++ b/book-api/src/main/java/com/imooc/controller/FileController.java @@ -16,6 +16,11 @@ import org.springframework.web.multipart.MultipartFile; import com.imooc.utils.QcCloud; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + @Slf4j @Api(tags = "FileController 文件上传测试的接口") @@ -27,9 +32,35 @@ public class FileController { @PostMapping("upload") 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(); + + MinIOUtils.uploadFile(minIOConfig.getBucketName(), fileName, file.getInputStream()); @@ -42,11 +73,4 @@ public class FileController { 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); - } } diff --git a/book-api/src/main/java/com/imooc/controller/VlogController.java b/book-api/src/main/java/com/imooc/controller/VlogController.java index b20673b..15fcba0 100644 --- a/book-api/src/main/java/com/imooc/controller/VlogController.java +++ b/book-api/src/main/java/com/imooc/controller/VlogController.java @@ -1,11 +1,14 @@ package com.imooc.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; import com.imooc.base.BaseInfoProperties; import com.imooc.bo.VlogBO; import com.imooc.enums.YesOrNo; import com.imooc.grace.result.GraceJSONResult; import com.imooc.service.VlogService; import com.imooc.utils.PagedGridResult; +import com.qcloud.vod.model.VodUploadResponse; import io.swagger.annotations.Api; import lombok.extern.slf4j.Slf4j; 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.web.bind.annotation.*; +import com.imooc.utils.QcCloud; + +import java.util.List; +import java.util.Map; + @Slf4j @Api(tags = "VlogController 短视频相关业务功能的接口") @RequestMapping("vlog") @RestController @RefreshScope public class VlogController extends BaseInfoProperties { - @Autowired private VlogService vlogService; + @Autowired + private QcCloud qcCloud; + + + @PostMapping("vodCallBack") + public GraceJSONResult vodCallBack(@RequestBody Map 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 procedureStateChangeEvent = (Map) 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> mediaProcessResultSet = + (List>) procedureStateChangeEvent.get("MediaProcessResultSet"); + + if (mediaProcessResultSet != null) { + for (Map processResult : mediaProcessResultSet) { + String type = (String) processResult.get("Type"); + if ("CoverBySnapshot".equals(type)) { + // 任务流:首帧截图 + Map coverBySnapshotTask = + (Map) processResult.get("CoverBySnapshotTask"); + Map output = (Map) 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 reviewEvent = (Map) callbackData.get("ReviewAudioVideoCompleteEvent"); + if (reviewEvent != null) { + String status = (String) reviewEvent.get("Status"); // FINISH + if ("FINISH".equals(status)) { + Map input = (Map) reviewEvent.get("Input"); + String fileId = (String) input.get("FileId"); // 查询的唯一标识 + // 获取审核结果 + Map output = (Map) 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") - 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 vlogService.createVlog(vlogBO); + return GraceJSONResult.ok(); } diff --git a/book-api/src/main/resources/application.yml b/book-api/src/main/resources/application.yml index bd6daaf..2e9a473 100644 --- a/book-api/src/main/resources/application.yml +++ b/book-api/src/main/resources/application.yml @@ -16,9 +16,9 @@ spring: servlet: multipart: max-file-size: 1024MB # 文件上传大小限制,设置最大值,不能超过该值,否则报错 -# max-file-size: 500KB # 文件上传大小限制,设置最大值,不能超过该值,否则报错 max-request-size: 1024MB # 文件最大请求限制,用于批量上传 -# max-request-size: 500KB +# location: /Users/wuzhongjie/Desktop/vlog-1.0.0/vlogdata + nacos: counts: 10 # 整合mybatis diff --git a/book-common/src/main/java/com/imooc/utils/QcCloud.java b/book-common/src/main/java/com/imooc/utils/QcCloud.java index a64f116..00590d9 100644 --- a/book-common/src/main/java/com/imooc/utils/QcCloud.java +++ b/book-common/src/main/java/com/imooc/utils/QcCloud.java @@ -3,18 +3,12 @@ import com.qcloud.vod.model.VodUploadResponse; import com.qcloud.vod.VodUploadClient; import com.qcloud.vod.model.VodUploadRequest; -import org.apache.commons.io.FilenameUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.springframework.web.multipart.MultipartFile; import java.io.File; -import java.text.SimpleDateFormat; -import java.util.Date; 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 { - // 1. 获取文件扩展名 - String extension = FilenameUtils.getExtension(file.getOriginalFilename()); - - // 2. 获取当前时间戳和 UUID 作为文件名 - String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); - String uniqueFilename = "vod-" + timestamp + "-" + UUID.randomUUID().toString().replace("-", "").substring(8) + "." + extension; - - // 3. 创建临时文件并转存 - Path tempFile = Files.createTempFile("vod-", "-" + uniqueFilename); // 创建临时文件 - file.transferTo(tempFile.toFile()); // 将文件上传内容写入临时文件 - - System.out.println("文件临时路径: " + tempFile.toString()); + public String uploadViaTempFile(String fileName) throws Exception { + //确保 vlogdata 目录存在 + String storagePath = new File(System.getProperty("user.dir"), "vlogdata").getAbsolutePath(); + System.out.println("绝对路径: " + storagePath); + File storageDir = new File(storagePath); + // 组装完整的文件路径 + File sourceFile = new File(storageDir, fileName); + System.out.println("完整文件路径: " + sourceFile.toPath()); + if (!sourceFile.exists()) { + throw new RuntimeException("文件不存在: " + sourceFile.getAbsolutePath()); + } try { VodUploadClient client = new VodUploadClient(getSecretId(), getSecretKey()); VodUploadRequest request = new VodUploadRequest(); - request.setMediaFilePath(tempFile.toString()); // 指定本地文件路径 + request.setMediaFilePath(sourceFile.getAbsolutePath());// 指定本地文件路径 request.setProcedure("视频处理"); // 智能审核+水印 // 4. 上传文件 VodUploadResponse response = client.upload(getRegion(), request); - return response.getFileId(); // 返回腾讯云 VOD 的 id +// return response; + return response.getFileId(); } finally { - // 5. 删除临时文件 - Files.deleteIfExists(tempFile); + // 5. 删除文件 + Files.deleteIfExists(sourceFile.toPath()); } } } \ No newline at end of file diff --git a/book-mapper/src/main/resources/mapper/VlogMapper.xml b/book-mapper/src/main/resources/mapper/VlogMapper.xml index b035451..c124bbe 100644 --- a/book-mapper/src/main/resources/mapper/VlogMapper.xml +++ b/book-mapper/src/main/resources/mapper/VlogMapper.xml @@ -17,5 +17,11 @@ + + + + + + \ No newline at end of file diff --git a/book-mapper/src/main/resources/mapper/VlogMapperCustom.xml b/book-mapper/src/main/resources/mapper/VlogMapperCustom.xml index 41c9858..99d7b29 100644 --- a/book-mapper/src/main/resources/mapper/VlogMapperCustom.xml +++ b/book-mapper/src/main/resources/mapper/VlogMapperCustom.xml @@ -15,7 +15,12 @@ v.height as height, v.like_counts as likeCounts, 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 t_vlog v LEFT JOIN @@ -25,6 +30,7 @@ WHERE v.is_private = 0 AND v.status = 1 + AND v.first_frame_img IS NOT NULL AND v.title like '%${paramMap.search}%' @@ -47,7 +53,12 @@ v.height as height, v.like_counts as likeCounts, 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 t_vlog v LEFT JOIN @@ -56,6 +67,8 @@ v.vloger_id = u.id WHERE v.id = #{paramMap.vlogId} + AND v.status = 1 + AND v.first_frame_img IS NOT NULL