package com.youlai.boot.common.util; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import java.util.List; import com.aliyun.green20220302.Client; import com.aliyun.green20220302.models.*; import com.aliyun.teaopenapi.models.Config; import com.youlai.boot.common.exception.MsgException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * 阿里云内容安全统一审核工具类(v2.0) * ---------------------------------------- * 支持: * 1. 文本审核 * 2. 图片审核 * 3. 视频审核(异步) * 4. 取消视频审核任务(直播流) * * 特性: * - 自动切换备用节点(cn-beijing) * - 从 application.yml 中读取配置 * - 返回 null 表示请求失败(业务层自行判断) */ @Slf4j @Component public class AliyunContentAuditUtil { @Value("${audit.aliyun.green.accessKeyId}") private String accessKeyId; @Value("${audit.aliyun.green.accessKeySecret}") private String accessKeySecret; // 主节点(上海) private static final String REGION_PRIMARY = "cn-shanghai"; private static final String ENDPOINT_PRIMARY = "green-cip.cn-shanghai.aliyuncs.com"; // 备用节点(北京) private static final String REGION_BACKUP = "cn-beijing"; private static final String ENDPOINT_BACKUP = "green-cip.cn-beijing.aliyuncs.com"; // 批量审核上限 private static final int BATCH_TEXT_MAX = 100; private static final int BATCH_TEXT_SINGLE_MAX_CHARS = 600; /** * 构建客户端 */ private Client createClient(boolean useBackup) throws Exception { String region = useBackup ? REGION_BACKUP : REGION_PRIMARY; String endpoint = useBackup ? ENDPOINT_BACKUP : ENDPOINT_PRIMARY; Config config = new Config() .setAccessKeyId(accessKeyId) .setAccessKeySecret(accessKeySecret) .setRegionId(region) .setEndpoint(endpoint) .setReadTimeout(6000) .setConnectTimeout(3000); return new Client(config); } /** * 自动调用机制: * - 优先使用上海节点 * - 如果响应码为 500 或出现异常,自动切换至北京节点重试一次 */ private T executeWithFailover(AliyunRequestExecutor executor) { try { Client primary = createClient(false); T result = executor.execute(primary); if (isServerError(result)) { log.warn("阿里云内容安全服务端异常,切换到北京节点重试..."); Client backup = createClient(true); return executor.execute(backup); } return result; } catch (Exception e) { log.error("阿里云内容安全调用异常(主节点失败),切换至备用节点", e); try { Client backup = createClient(true); return executor.execute(backup); } catch (Exception ex) { log.error("备用节点调用也失败", ex); return null; } } } /** * 判断是否服务端异常(statusCode=500) */ private boolean isServerError(Object response) { try { Integer code = (Integer) response.getClass().getMethod("getStatusCode").invoke(response); return code != null && code == 500; } catch (Exception ignored) { return false; } } // ===================== 文本审核 ===================== public TextModerationResponse textModeration(String content) { return executeWithFailover(client -> { JSONObject params = new JSONObject(); params.put("content", content); TextModerationRequest request = new TextModerationRequest() .setService("comment_detection") .setServiceParameters(params.toJSONString()); return client.textModeration(request); }); } /** * 批量文本审核(上限100条,单条不超过600字) */ public TextModerationResponse batchTextModeration(List contents) { if (contents == null || contents.isEmpty()) { return null; } if (contents.size() > BATCH_TEXT_MAX) { throw new MsgException("批量文本审核单次上限" + BATCH_TEXT_MAX + "条,当前" + contents.size() + "条"); } for (int i = 0; i < contents.size(); i++) { String text = contents.get(i); if (text != null && text.length() > BATCH_TEXT_SINGLE_MAX_CHARS) { throw new MsgException("第" + (i + 1) + "条文本超过" + BATCH_TEXT_SINGLE_MAX_CHARS + "字上限"); } } return executeWithFailover(client -> { JSONObject params = new JSONObject(); JSONArray arr = new JSONArray(); arr.addAll(contents); params.put("content", arr); TextModerationRequest request = new TextModerationRequest() .setService("comment_detection") .setServiceParameters(params.toJSONString()); return client.textModeration(request); }); } // ===================== 文本审核Plus ===================== //nickname_detection_pro 用户昵称检测_专业版(用户昵称) ;ugc_moderation_byllm_pro UGC场景文本审核大模型服务_专业版(个人简介 / 作品内容) ; //comment_detection_pro 公聊评论内容检测_专业版(评论); ugc_moderation_byllm UGC场景文本审核大模型服务 (作品标题) public TextModerationPlusResponse textModerationPlus(String content, String service) { return executeWithFailover(client -> { JSONObject params = new JSONObject(); params.put("content", content); TextModerationPlusRequest textModerationPlusRequest = new TextModerationPlusRequest() .setService(service != null ? service : "ugc_moderation_byllm_pro") .setServiceParameters(params.toJSONString()); return client.textModerationPlus(textModerationPlusRequest); }); } /** * 批量文本审核Plus(上限100条,单条不超过600字) */ public TextModerationPlusResponse batchTextModerationPlus(List contents) { if (contents == null || contents.isEmpty()) { return null; } if (contents.size() > BATCH_TEXT_MAX) { throw new MsgException("批量文本审核单次上限" + BATCH_TEXT_MAX + "条,当前" + contents.size() + "条"); } for (int i = 0; i < contents.size(); i++) { String text = contents.get(i); if (text != null && text.length() > BATCH_TEXT_SINGLE_MAX_CHARS) { throw new MsgException("第" + (i + 1) + "条文本超过" + BATCH_TEXT_SINGLE_MAX_CHARS + "字上限"); } } return executeWithFailover(client -> { JSONObject params = new JSONObject(); JSONArray arr = new JSONArray(); arr.addAll(contents); params.put("content", arr); TextModerationPlusRequest request = new TextModerationPlusRequest() .setService("ugc_moderation_byllm_pro") .setServiceParameters(params.toJSONString()); return client.textModerationPlus(request); }); } // ===================== 图片审核 ===================== // 头像图片检测:profilePhotoCheck (头像) ; AI生成图片鉴别_含隐式标识版:aigcDetectorFull(AI生成图片) ; // 大小模型融合图片审核服务:postlmageCheckByVL (用户上传的图片) ; OSS基线检测(OSS普惠版专用):oss_baselineCheck // 通用图片审核大模型服务:baselineCheckByVL(用户作品);图片万物识别:generalRecognition ;营销素材检测:advertisingCheck public ImageModerationResponse imageModeration(String imageUrl, String service) { return executeWithFailover(client -> { JSONObject params = new JSONObject(); params.put("imageUrl", imageUrl); ImageModerationRequest request = new ImageModerationRequest() .setService(service != null ? service : "baselineCheckByVL") .setServiceParameters(params.toJSONString()); return client.imageModeration(request); }); } // ===================== 图片审核(异步) ===================== public ImageAsyncModerationResponse imageAsyncModeration(String imageUrl, String service) { return executeWithFailover(client -> { JSONObject params = new JSONObject(); params.put("imageUrl", imageUrl); params.put("dataId", java.util.UUID.randomUUID().toString()); ImageAsyncModerationRequest request = new ImageAsyncModerationRequest() .setService(service != null ? service : "baselineCheckByVL") .setServiceParameters(params.toJSONString()); return client.imageAsyncModeration(request); }); } public DescribeImageModerationResultResponse describeImageModerationResult(String reqId) { return executeWithFailover(client -> { DescribeImageModerationResultRequest request = new DescribeImageModerationResultRequest() .setReqId(reqId); return client.describeImageModerationResult(request); }); } // ===================== 视频审核(异步) ===================== //视频文件检测:videoDetection ; AI生成视频判定:videoAigcDetector ; 视频文件检测_大模型版:videoDetectionByVL public VideoModerationResponse videoModeration(String videoUrl, String service) { return executeWithFailover(client -> { JSONObject params = new JSONObject(); params.put("url", videoUrl); VideoModerationRequest request = new VideoModerationRequest() .setService(service != null ? service : "videoDetection") .setServiceParameters(params.toJSONString()); return client.videoModeration(request); }); } // 视频审核不支持批量,使用 videoModeration 逐个调用 // ===================== 查询视频审核结果 ===================== public VideoModerationResultResponse videoModerationResult(String taskId) { return executeWithFailover(client -> { JSONObject params = new JSONObject(); params.put("taskId", taskId); VideoModerationResultRequest request = new VideoModerationResultRequest() .setService("videoDetection") .setServiceParameters(params.toJSONString()); return client.videoModerationResult(request); }); } // ===================== 取消视频直播流检测任务 ===================== public VideoModerationCancelResponse cancelVideoTask(String taskId) { return executeWithFailover(client -> { JSONObject params = new JSONObject(); params.put("taskId", taskId); VideoModerationCancelRequest request = new VideoModerationCancelRequest() .setService("liveStreamDetection") .setServiceParameters(params.toJSONString()); return client.videoModerationCancel(request); }); } /** * 函数式接口:用于统一封装 SDK 调用逻辑 */ @FunctionalInterface private interface AliyunRequestExecutor { T execute(Client client) throws Exception; } }