package com.imooc.controller; import com.fasterxml.jackson.databind.ObjectMapper; import com.imooc.base.BaseInfoProperties; import com.imooc.bo.VlogBO; import com.imooc.config.MinIOConfig; import com.imooc.enums.YesOrNo; import com.imooc.grace.result.GraceJSONResult; import com.imooc.service.VlogService; import com.imooc.utils.MinIOUtils; 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; import org.springframework.beans.factory.annotation.Autowired; 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; @Autowired private MinIOConfig minIOConfig; @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) { String coverUrl=null; // 首帧图 String transVdUrl=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"); coverUrl = (String) output.get("CoverUrl"); // 正确获取 CoverUrl } if("Transcode".equals(type)){ // 拿到output中的url Map TranscodeTask = (Map) processResult.get("TranscodeTask"); Map output = (Map) TranscodeTask.get("Output"); transVdUrl = (String) output.get("Url"); } } log.info("任务流 [截取首帧,视频转码] 完成, FileId: {}, 封面图 URL: {},视频地址: {}", fileId, coverUrl,transVdUrl); // TODO: 更新数据库,存储首帧图,设置云端地址,删除minio视频文件 vlogService.updateVlogFirstImg(fileId,coverUrl,transVdUrl); } 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) 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("视频发布ID:"+fileId); vlogBO.setFileId(fileId); // 删除minio文件 // MinIOUtils.removeFile(minIOConfig.getBucketName(),fileName); // log.info("删除minio文件:"+fileName); // FIXME 校验VlogBO vlogService.createVlog(vlogBO); return GraceJSONResult.ok(); } @GetMapping("indexList") public GraceJSONResult indexList(@RequestParam(defaultValue = "") String userId, @RequestParam(defaultValue = "") String search, @RequestParam(defaultValue = "") String cityCode, @RequestParam(defaultValue = "") String status, @RequestParam Integer page, @RequestParam Integer pageSize) { if (page == null) { page = COMMON_START_PAGE; } if (pageSize == null) { pageSize = COMMON_PAGE_SIZE; } PagedGridResult gridResult = vlogService.getIndexVlogList(userId, search, cityCode,status, page, pageSize); return GraceJSONResult.ok(gridResult); } @GetMapping("detail") public GraceJSONResult detail(@RequestParam(defaultValue = "") String userId, @RequestParam String vlogId) { return GraceJSONResult.ok(vlogService.getVlogDetailById(userId, vlogId)); } @PostMapping("changeToPrivate") public GraceJSONResult changeToPrivate(@RequestParam String userId, @RequestParam String vlogId) { vlogService.changeToPrivateOrPublic(userId, vlogId, YesOrNo.YES.type); return GraceJSONResult.ok(); } @PostMapping("changeToPublic") public GraceJSONResult changeToPublic(@RequestParam String userId, @RequestParam String vlogId) { vlogService.changeToPrivateOrPublic(userId, vlogId, YesOrNo.NO.type); return GraceJSONResult.ok(); } @GetMapping("myPublicList") public GraceJSONResult myPublicList(@RequestParam String userId, @RequestParam Integer page, @RequestParam Integer pageSize) { if (page == null) { page = COMMON_START_PAGE; } if (pageSize == null) { pageSize = COMMON_PAGE_SIZE; } PagedGridResult gridResult = vlogService.queryMyVlogList(userId, page, pageSize, YesOrNo.NO.type); return GraceJSONResult.ok(gridResult); } @GetMapping("myPrivateList") public GraceJSONResult myPrivateList(@RequestParam String userId, @RequestParam Integer page, @RequestParam Integer pageSize) { if (page == null) { page = COMMON_START_PAGE; } if (pageSize == null) { pageSize = COMMON_PAGE_SIZE; } PagedGridResult gridResult = vlogService.queryMyVlogList(userId, page, pageSize, YesOrNo.YES.type); return GraceJSONResult.ok(gridResult); } @GetMapping("myLikedList") public GraceJSONResult myLikedList(@RequestParam String userId, @RequestParam Integer page, @RequestParam Integer pageSize) { if (page == null) { page = COMMON_START_PAGE; } if (pageSize == null) { pageSize = COMMON_PAGE_SIZE; } PagedGridResult gridResult = vlogService.getMyLikedVlogList(userId, page, pageSize); return GraceJSONResult.ok(gridResult); } @Value(("${nacos.counts}")) private Integer nacosConuts; @PostMapping("like") public GraceJSONResult like(@RequestParam String userId, @RequestParam String vlogerId, @RequestParam String vlogId) { // 我点赞的视频,关联关系保存到数据库 vlogService.userLikeVlog(userId, vlogId); // 点赞后,视频和视频发布者的获赞都会 +1 redis.increment(REDIS_VLOGER_BE_LIKED_COUNTS + ":" + vlogerId, 1); redis.increment(REDIS_VLOG_BE_LIKED_COUNTS + ":" + vlogId, 1); // 我点赞的视频,需要在redis中保存关联关系 redis.set(REDIS_USER_LIKE_VLOG + ":" + userId + ":" + vlogId, "1"); log.info("nacosConuts="+nacosConuts); String countsStr = redis.get(REDIS_VLOG_BE_LIKED_COUNTS + ":" + vlogId); Integer counts=0; if (StringUtils.isNotBlank(countsStr)){ counts=Integer.valueOf(countsStr); if (counts>=nacosConuts){ vlogService.flushCounts(vlogId, counts); } } return GraceJSONResult.ok(); } @PostMapping("unlike") public GraceJSONResult unlike(@RequestParam String userId, @RequestParam String vlogerId, @RequestParam String vlogId) { // 我取消点赞的视频,关联关系删除 vlogService.userUnLikeVlog(userId, vlogId); redis.decrement(REDIS_VLOGER_BE_LIKED_COUNTS + ":" + vlogerId, 1); redis.decrement(REDIS_VLOG_BE_LIKED_COUNTS + ":" + vlogId, 1); redis.del(REDIS_USER_LIKE_VLOG + ":" + userId + ":" + vlogId); return GraceJSONResult.ok(); } @PostMapping("totalLikedCounts") public GraceJSONResult totalLikedCounts(@RequestParam String vlogId) { return GraceJSONResult.ok(vlogService.getVlogBeLikedCounts(vlogId)); } @GetMapping("followList") public GraceJSONResult followList(@RequestParam String myId, @RequestParam Integer page, @RequestParam Integer pageSize) { if (page == null) { page = COMMON_START_PAGE; } if (pageSize == null) { pageSize = COMMON_PAGE_SIZE; } PagedGridResult gridResult = vlogService.getMyFollowVlogList(myId, page, pageSize); return GraceJSONResult.ok(gridResult); } @GetMapping("friendList") public GraceJSONResult friendList(@RequestParam String myId, @RequestParam Integer page, @RequestParam Integer pageSize) { if (page == null) { page = COMMON_START_PAGE; } if (pageSize == null) { pageSize = COMMON_PAGE_SIZE; } PagedGridResult gridResult = vlogService.getMyFriendVlogList(myId, page, pageSize); return GraceJSONResult.ok(gridResult); } }