diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 5967e647b..83bff12cd 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -49,35 +49,35 @@ spring: driverClassName: com.mysql.cj.jdbc.Driver # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) - url: jdbc:mysql://127.0.0.1:3306/sys?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai - username: root - password: 123 - # # 从库数据源 - # slave: - # lazy: true - # type: ${spring.datasource.type} - # driverClassName: com.mysql.cj.jdbc.Driver - # url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true - # username: - # password: - # oracle: - # type: ${spring.datasource.type} - # driverClassName: oracle.jdbc.OracleDriver - # url: jdbc:oracle:thin:@//localhost:1521/XE - # username: ROOT - # password: root - # postgres: - # type: ${spring.datasource.type} - # driverClassName: org.postgresql.Driver - # url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true - # username: root - # password: root - # sqlserver: - # type: ${spring.datasource.type} - # driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver - # url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true - # username: SA - # password: root + url: jdbc:mysql://82.156.121.2:23306/wzj?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + username: wzj + password: A085F27A43B0 +# # 从库数据源 +# slave: +# lazy: true +# type: ${spring.datasource.type} +# driverClassName: com.mysql.cj.jdbc.Driver +# url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true +# username: +# password: +# oracle: +# type: ${spring.datasource.type} +# driverClassName: oracle.jdbc.OracleDriver +# url: jdbc:oracle:thin:@//localhost:1521/XE +# username: ROOT +# password: root +# postgres: +# type: ${spring.datasource.type} +# driverClassName: org.postgresql.Driver +# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true +# username: root +# password: root +# sqlserver: +# type: ${spring.datasource.type} +# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver +# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true +# username: SA +# password: root hikari: # 最大连接池数量 maxPoolSize: 20 diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 3a8d2b399..7e584b3dd 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -117,6 +117,7 @@ security: - /*/api-docs/** - /warm-flow-ui/token-name - /admin/vlog/upload/detail/** + - /admin/vlog/upload/** # - /admin/vlog/upload/pull # - /admin/vlog/upload/pull/batch # - /admin/vlog/upload/list diff --git a/ruoyi-modules/ruoyi-content/pom.xml b/ruoyi-modules/ruoyi-content/pom.xml index dbd6f002e..75faf0a54 100644 --- a/ruoyi-modules/ruoyi-content/pom.xml +++ b/ruoyi-modules/ruoyi-content/pom.xml @@ -38,7 +38,7 @@ com.tencentcloudapi tencentcloud-sdk-java - 3.1.270 + 3.1.1030 @@ -169,19 +169,48 @@ - - - - - - + + + com.tencentcloudapi + tencentcloud-sdk-java-common + 3.1.1030 + - + - + + + + + + tencent-cloud-sdk + tencent-cloud-sdk + https://mirrors.tencent.com/nexus/repository/maven-public/ + + + + + aliyun + aliyun + https://maven.aliyun.com/repository/public + + true + + + false + + + + + + central + Maven Central + https://repo1.maven.org/maven2/ + + diff --git a/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/controller/VlogController.java b/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/controller/VlogController.java index 0246a4806..27dbbda19 100644 --- a/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/controller/VlogController.java +++ b/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/controller/VlogController.java @@ -7,9 +7,11 @@ import com.wzj.soopin.content.domain.bo.VlogBO; import com.wzj.soopin.content.enums.YesOrNo; import com.wzj.soopin.content.result.GraceJSONResult; import com.wzj.soopin.content.service.VlogService; +import com.wzj.soopin.content.service.VlogUploadService; import com.wzj.soopin.content.utils.PagedGridResult; import com.wzj.soopin.content.utils.QcCloud; import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -30,6 +32,8 @@ public class VlogController extends BaseInfoProperties { private VlogService vlogService; @Autowired private QcCloud qcCloud; + @Autowired + private VlogUploadService vlogUploadService; @PostMapping("vodCallBack") public GraceJSONResult vodCallBack(@RequestBody Map callbackData) { @@ -364,4 +368,6 @@ public class VlogController extends BaseInfoProperties { pageSize); return GraceJSONResult.ok(gridResult); } + + } diff --git a/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/controller/admin/VlogUploadController.java b/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/controller/admin/VlogUploadController.java index 7cba65770..04e826501 100644 --- a/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/controller/admin/VlogUploadController.java +++ b/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/controller/admin/VlogUploadController.java @@ -10,8 +10,10 @@ import io.swagger.annotations.ApiParam; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import org.springframework.util.StringUtils; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -30,11 +32,14 @@ public class VlogUploadController { @Autowired private TencentCloudUtil tencentCloudUtil; + @Autowired + private com.wzj.soopin.content.mapper.VlogMapper vlogMapper; + @ApiOperation("获取腾讯云点播视频列表") @GetMapping("/list") public GraceJSONResult getVodList( @ApiParam(value = "页码", defaultValue = "1") @RequestParam(required = false, defaultValue = "1") Integer pageNum, - @ApiParam(value = "每页记录数", defaultValue = "20") @RequestParam(required = false, defaultValue = "20") Integer pageSize, + @ApiParam(value = "每页记录数", defaultValue = "10") @RequestParam(required = false, defaultValue = "10") Integer pageSize, @ApiParam(value = "文件状态,可选值:Normal(正常)、SystemForbidden(平台封禁)、Forbidden(主动封禁)") @RequestParam(required = false) String[] status, @ApiParam(value = "文件类型,可选值:Video(视频)、Audio(音频)、Image(图片)") @RequestParam(required = false) String[] categories, @ApiParam(value = "媒体文件来源,可选值:Upload(上传)、Record(直播录制)等") @RequestParam(required = false) String[] sourceTypes, @@ -192,10 +197,64 @@ public class VlogUploadController { @GetMapping("/detail") public GraceJSONResult vlogDetail(@RequestParam String vlogId) { try { - return GraceJSONResult.ok(vlogUploadService.getVlogDetail(vlogId)); + // 先查询本地数据库获取 fileId + com.wzj.soopin.content.domain.po.Vlog vlog = vlogMapper.selectById(vlogId); + if (vlog != null && StringUtils.hasText(vlog.getFileId())) { + // 如果找到 fileId,使用 fileId 查询腾讯云 + return GraceJSONResult.ok(vlogUploadService.getVlogDetail(vlog.getFileId())); + } else { + // 如果没有找到 fileId,直接使用 vlogId 查询 + return GraceJSONResult.ok(vlogUploadService.getVlogDetail(vlogId)); + } } catch (Exception e) { log.error("获取视频详情失败", e); return GraceJSONResult.errorMsg("获取视频详情失败: " + e.getMessage()); } } + @ApiOperation(value = "禁播/解禁视频", notes = "禁播/解禁视频接口") + @PostMapping("/forbid") + public GraceJSONResult forbidMediaDistribution(@RequestBody Map params) { + try { + // 参数验证 + if (params == null) { + return GraceJSONResult.errorMsg("参数不能为空"); + } + + Object fileIdsObj = params.get("fileIds"); + String operation = (String) params.get("operation"); + + List fileIds; + if (fileIdsObj instanceof String) { + // 如果是单个字符串,转换为列表 + fileIds = Collections.singletonList((String) fileIdsObj); + } else if (fileIdsObj instanceof List) { + // 如果已经是列表,直接使用 + fileIds = (List) fileIdsObj; + } else { + return GraceJSONResult.errorMsg("媒体文件ID格式不正确"); + } + + if (fileIds.isEmpty()) { + return GraceJSONResult.errorMsg("媒体文件ID列表不能为空"); + } + if (fileIds.size() > 20) { + return GraceJSONResult.errorMsg("每次最多可提交20个媒体文件"); + } + if (!"forbid".equals(operation) && !"recover".equals(operation)) { + return GraceJSONResult.errorMsg("操作类型无效,只能是forbid(禁播)或recover(解禁)"); + } + + // 调用服务层方法 + vlogUploadService.forbidMediaDistribution(fileIds, operation); + + // 返回成功结果 + return GraceJSONResult.ok("操作成功"); + } catch (IllegalArgumentException e) { + log.error("参数错误: {}", e.getMessage()); + return GraceJSONResult.errorMsg(e.getMessage()); + } catch (Exception e) { + log.error("操作失败: {}", e.getMessage(), e); + return GraceJSONResult.errorMsg("操作失败: " + e.getMessage()); + } + } } diff --git a/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/mapper/VlogMapper.java b/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/mapper/VlogMapper.java index 560f00d8e..9a53d685d 100644 --- a/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/mapper/VlogMapper.java +++ b/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/mapper/VlogMapper.java @@ -56,4 +56,11 @@ public interface VlogMapper extends BaseMapper { public List selectMyPublic(@Param("paramMap")Map map); public List getVlogDetailFromId(@Param("paramMap")Map map); + + /** + * 更新视频审核状态 + * @param params 包含vlogId、status、reason和auditTime的Map + * @return 影响的行数 + */ + int updateVlogStatus(@Param("params") Map params); } diff --git a/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/service/VlogUploadService.java b/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/service/VlogUploadService.java index a7abd3071..4878fea2d 100644 --- a/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/service/VlogUploadService.java +++ b/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/service/VlogUploadService.java @@ -5,6 +5,7 @@ import com.tencentcloudapi.vod.v20180717.models.PullUploadResponse; import com.tencentcloudapi.vod.v20180717.models.SearchMediaRequest; import com.tencentcloudapi.vod.v20180717.models.SearchMediaResponse; +import java.util.List; import java.util.Map; public interface VlogUploadService { @@ -39,4 +40,11 @@ public interface VlogUploadService { void deleteVlog(String vlogId); // 新增:视频详情 Object getVlogDetail(String vlogId); + + /** + * 禁播/解禁媒体 + * @param fileIds 媒体文件ID列表 + * @param operation 操作类型:forbid-禁播,recover-解禁 + */ + void forbidMediaDistribution(List fileIds, String operation); } diff --git a/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/service/impl/VlogUploadServiceImpl.java b/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/service/impl/VlogUploadServiceImpl.java index 7e1f5259b..c4039f7b7 100644 --- a/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/service/impl/VlogUploadServiceImpl.java +++ b/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/service/impl/VlogUploadServiceImpl.java @@ -16,6 +16,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -152,13 +154,117 @@ public class VlogUploadServiceImpl implements VlogUploadService { // 视频审核 @Override public void auditVlog(String vlogId, Integer status, String reason) { - // 这里假设 status=1为通过,status=2为不通过 - // 可根据实际业务调整 - // 直接更新数据库状态 - // 你可以用 VlogMapper 或 VlogService - // 这里只做伪代码示例 - // vlogMapper.updateStatus(vlogId, status, reason); - log.info("审核视频 vlogId={}, status={}, reason={}", vlogId, status, reason); + try { + // 参数验证 + if (vlogId == null || vlogId.trim().isEmpty()) { + throw new IllegalArgumentException("视频ID不能为空"); + } + if (status == null || (status != 1 && status != 2)) { + throw new IllegalArgumentException("审核状态无效,只能是1(通过)或2(不通过)"); + } + // 只有当状态为不通过(2)时才需要原因说明 + if (status == 2 && (reason == null || reason.trim().isEmpty())) { + throw new IllegalArgumentException("审核不通过时,必须提供原因说明"); + } + + // 获取VOD客户端实例 + VodClient client = tencentCloudUtil.getVodClient(); + + // 创建审核请求 + com.tencentcloudapi.vod.v20180717.models.ReviewAudioVideoRequest req = new com.tencentcloudapi.vod.v20180717.models.ReviewAudioVideoRequest(); + req.setFileId(vlogId); + + // 设置审核内容,包括视频和封面 + req.setReviewContents(new String[]{"Media", "Cover"}); + + // 设置审核模板ID,10为预置模板 + req.setDefinition(10L); + + // 设置任务优先级 + req.setTasksPriority(0L); + + // 设置来源上下文,用于透传用户请求信息 + req.setSessionContext(String.format("status=%d&reason=%s", status, reason != null ? reason : "")); + + // 调用审核API + com.tencentcloudapi.vod.v20180717.models.ReviewAudioVideoResponse resp = client.ReviewAudioVideo(req); + + // 记录审核结果 + log.info("视频审核任务已提交 - vlogId: {}, status: {}, reason: {}, taskId: {}", + vlogId, status, reason, resp.getTaskId()); + + // TODO: 暂时注释掉数据库操作,等待数据库表创建完成后再启用 + /* + // 更新本地数据库状态 + Map updateMap = new HashMap<>(); + updateMap.put("vlogId", vlogId); + updateMap.put("status", status); + updateMap.put("reason", reason); + updateMap.put("auditTime", new Date()); + vlogMapper.updateVlogStatus(updateMap); + + // 记录审核日志 + log.info("视频状态已更新 - vlogId: {}, status: {}, reason: {}", vlogId, status, reason); + */ + + } catch (TencentCloudSDKException e) { + log.error("视频审核失败 - vlogId: {}, error: {}", vlogId, e.getMessage(), e); + throw new RuntimeException("视频审核失败: " + e.getMessage()); + } catch (Exception e) { + log.error("更新视频状态失败 - vlogId: {}, error: {}", vlogId, e.getMessage(), e); + throw new RuntimeException("更新视频状态失败: " + e.getMessage()); + } + } + + /** + * 禁播/解禁媒体 + * @param fileIds 媒体文件ID列表 + * @param operation 操作类型:forbid-禁播,recover-解禁 + */ + @Override + public void forbidMediaDistribution(List fileIds, String operation) { + try { + // 参数验证 + if (fileIds == null || fileIds.isEmpty()) { + throw new IllegalArgumentException("媒体文件ID列表不能为空"); + } + if (fileIds.size() > 20) { + throw new IllegalArgumentException("每次最多可提交20个媒体文件"); + } + if (!"forbid".equals(operation) && !"recover".equals(operation)) { + throw new IllegalArgumentException("操作类型无效,只能是forbid(禁播)或recover(解禁)"); + } + + // 获取VOD客户端实例 + VodClient client = tencentCloudUtil.getVodClient(); + + // 创建禁播/解禁请求 + com.tencentcloudapi.vod.v20180717.models.ForbidMediaDistributionRequest req = new com.tencentcloudapi.vod.v20180717.models.ForbidMediaDistributionRequest(); + req.setFileIds(fileIds.toArray(new String[0])); + req.setOperation(operation); + + // 调用禁播/解禁API + com.tencentcloudapi.vod.v20180717.models.ForbidMediaDistributionResponse resp = client.ForbidMediaDistribution(req); + + // 记录操作结果 + log.info("媒体{}操作已提交 - fileIds: {}, notExistFileIds: {}", + operation.equals("forbid") ? "禁播" : "解禁", + fileIds, + resp.getNotExistFileIdSet()); + + // 如果有不存在的文件ID,记录警告日志 + if (resp.getNotExistFileIdSet() != null && resp.getNotExistFileIdSet().length > 0) { + log.warn("部分文件不存在 - fileIds: {}", Arrays.toString(resp.getNotExistFileIdSet())); + } + + } catch (TencentCloudSDKException e) { + log.error("媒体{}操作失败 - fileIds: {}, error: {}", + operation.equals("forbid") ? "禁播" : "解禁", + fileIds, + e.getMessage(), + e); + throw new RuntimeException("媒体" + (operation.equals("forbid") ? "禁播" : "解禁") + "失败: " + e.getMessage()); + } } // 视频删除 @@ -178,12 +284,13 @@ public class VlogUploadServiceImpl implements VlogUploadService { paramMap.put("vlogId", vlogId); List vlogList = vlogMapperCustom.getVlogDetailById(paramMap); com.wzj.soopin.content.domain.vo.IndexVlogVO vlog = vlogList != null && !vlogList.isEmpty() ? vlogList.get(0) : null; - + com.tencentcloudapi.vod.v20180717.models.MediaInfo mediaInfo = null; if (vlog == null) { // 若本地无,尝试从云点播获取 com.tencentcloudapi.vod.v20180717.models.DescribeMediaInfosRequest req = new com.tencentcloudapi.vod.v20180717.models.DescribeMediaInfosRequest(); - req.setFileIds(new String[]{vlogId}); + String fileId = vlogId; // 这里假设传入的 vlogId 就是 fileId + req.setFileIds(new String[]{fileId}); VodClient client = tencentCloudUtil.getVodClient(); com.tencentcloudapi.vod.v20180717.models.DescribeMediaInfosResponse resp = client.DescribeMediaInfos(req); if (resp.getMediaInfoSet() != null && resp.getMediaInfoSet().length > 0) { diff --git a/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/utils/Props.java b/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/utils/Props.java index c906b8673..1a4fb563a 100644 --- a/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/utils/Props.java +++ b/ruoyi-modules/ruoyi-content/src/main/java/com/wzj/soopin/content/utils/Props.java @@ -6,7 +6,8 @@ import org.slf4j.LoggerFactory; import java.io.*; import java.util.Properties; -import static com.squareup.okhttp.internal.Util.closeQuietly; + +import static com.google.common.io.Closeables.closeQuietly; import static java.io.File.separator; diff --git a/ruoyi-modules/ruoyi-content/src/main/resources/mapper/content/VlogMapper.xml b/ruoyi-modules/ruoyi-content/src/main/resources/mapper/content/VlogMapper.xml index b64289b24..89b1af3a5 100644 --- a/ruoyi-modules/ruoyi-content/src/main/resources/mapper/content/VlogMapper.xml +++ b/ruoyi-modules/ruoyi-content/src/main/resources/mapper/content/VlogMapper.xml @@ -155,6 +155,14 @@ AND v.first_frame_img IS NOT NULL + + + UPDATE t_vlog + SET status = #{params.status}, + reason = #{params.reason}, + audit_time = #{params.auditTime} + WHERE id = #{params.vlogId} +