You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
6.5 KiB
190 lines
6.5 KiB
|
2 weeks ago
|
package com.youlai.boot.common.util;
|
||
|
|
|
||
|
|
import com.alibaba.fastjson.JSON;
|
||
|
|
import com.alibaba.fastjson.JSONArray;
|
||
|
|
import com.alibaba.fastjson.JSONObject;
|
||
|
|
import com.aliyun.green20220302.Client;
|
||
|
|
import com.aliyun.green20220302.models.*;
|
||
|
|
import com.aliyun.teaopenapi.models.Config;
|
||
|
|
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 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> T executeWithFailover(AliyunRequestExecutor<T> 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("commentDetection")
|
||
|
|
.setServiceParameters(params.toJSONString());
|
||
|
|
|
||
|
|
return client.textModeration(request);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// ===================== 图片审核 =====================
|
||
|
|
public ImageModerationResponse imageModeration(String imageUrl) {
|
||
|
|
return executeWithFailover(client -> {
|
||
|
|
JSONObject params = new JSONObject();
|
||
|
|
JSONArray arr = new JSONArray();
|
||
|
|
arr.add(imageUrl);
|
||
|
|
params.put("images", arr);
|
||
|
|
|
||
|
|
ImageModerationRequest request = new ImageModerationRequest()
|
||
|
|
.setService("imageDetection")
|
||
|
|
.setServiceParameters(params.toJSONString());
|
||
|
|
|
||
|
|
return client.imageModeration(request);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// ===================== 视频审核(异步) =====================
|
||
|
|
public VideoModerationResponse videoModeration(String videoUrl) {
|
||
|
|
return executeWithFailover(client -> {
|
||
|
|
JSONObject params = new JSONObject();
|
||
|
|
params.put("url", videoUrl);
|
||
|
|
|
||
|
|
VideoModerationRequest request = new VideoModerationRequest()
|
||
|
|
.setService("videoDetection")
|
||
|
|
.setServiceParameters(params.toJSONString());
|
||
|
|
|
||
|
|
return client.videoModeration(request);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// ===================== 查询视频审核结果 =====================
|
||
|
|
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);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 输出调试日志
|
||
|
|
*/
|
||
|
|
public static void print(Object result) {
|
||
|
|
System.out.println(JSON.toJSONString(result, true));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 函数式接口:用于统一封装 SDK 调用逻辑
|
||
|
|
*/
|
||
|
|
@FunctionalInterface
|
||
|
|
private interface AliyunRequestExecutor<T> {
|
||
|
|
T execute(Client client) throws Exception;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|