|
@@ -1,14 +1,21 @@
|
|
|
package shop.alien.util.common.safe;
|
|
|
+import cn.hutool.core.collection.CollectionUtil;
|
|
|
import com.alibaba.fastjson.JSON;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import com.aliyun.green20220302.Client;
|
|
|
-import com.aliyun.green20220302.models.ImageModerationResponse;
|
|
|
-import com.aliyun.green20220302.models.TextModerationPlusRequest;
|
|
|
-import com.aliyun.green20220302.models.TextModerationPlusResponse;
|
|
|
-import com.aliyun.green20220302.models.TextModerationPlusResponseBody;
|
|
|
+import com.aliyun.green20220302.models.*;
|
|
|
import com.aliyun.teaopenapi.models.Config;
|
|
|
+import com.aliyun.teautil.models.RuntimeOptions;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
|
|
+import com.google.common.collect.Lists;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
|
|
+import java.util.List;
|
|
|
+import java.util.regex.Matcher;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+
|
|
|
+@Component
|
|
|
public class TextModerationUtil {
|
|
|
|
|
|
|
|
@@ -50,12 +57,12 @@ public class TextModerationUtil {
|
|
|
/**
|
|
|
* 根据业务类型执行文本审核
|
|
|
* @param text 待审核文本
|
|
|
- * @param services 文本审核服务枚举
|
|
|
+ * @param servicesList 文本审核服务枚举
|
|
|
* @return 审核结果VO
|
|
|
* @throws Exception 调用服务异常
|
|
|
*/
|
|
|
- public TextModerationResultVO invokeFunction(String text, ImageReviewServiceEnum... services) throws Exception {
|
|
|
- if (services == null || services.length == 0) {
|
|
|
+ public TextModerationResultVO invokeFunction(String text, List<String> servicesList) throws Exception {
|
|
|
+ if (CollectionUtil.isEmpty(servicesList)) {
|
|
|
throw new IllegalArgumentException("至少需要一个审核服务");
|
|
|
}
|
|
|
|
|
@@ -64,102 +71,185 @@ public class TextModerationUtil {
|
|
|
TextModerationResultVO resultVO = new TextModerationResultVO();
|
|
|
|
|
|
// 存储风险等级和原因的字符串
|
|
|
- StringBuilder riskTipsBuilder = new StringBuilder();
|
|
|
StringBuilder riskWordsBuilder = new StringBuilder();
|
|
|
- StringBuilder customizedWordsBuilder = new StringBuilder();
|
|
|
- StringBuilder customizedLibsBuilder = new StringBuilder();
|
|
|
- String highestRiskLevel = "low"; // 默认最低风险等级
|
|
|
+ String highestRiskLevel = "none"; // 默认未检测到风险
|
|
|
|
|
|
try {
|
|
|
- for (ImageReviewServiceEnum service : services) {
|
|
|
- TextModerationPlusRequest textModerationPlusRequest = new TextModerationPlusRequest();
|
|
|
- textModerationPlusRequest.setService(service.getService());
|
|
|
-
|
|
|
- // 构建服务参数
|
|
|
- textModerationPlusRequest.setServiceParameters(buildServiceParameters(text));
|
|
|
-
|
|
|
- TextModerationPlusResponse response = client.textModerationPlus(textModerationPlusRequest);
|
|
|
- if (response.getStatusCode() == 200) {
|
|
|
- TextModerationPlusResponseBody result = response.getBody();
|
|
|
-
|
|
|
- if (200 == result.getCode()) {
|
|
|
- // 解析审核结果
|
|
|
- TextModerationPlusResponseBody.TextModerationPlusResponseBodyData data = result.getData();
|
|
|
-
|
|
|
- // 解析审核结果
|
|
|
- JSONObject dataJson = JSON.parseObject(JSON.toJSONString(result.getData()));
|
|
|
- String labels = dataJson.getString("labels");
|
|
|
- String reason = dataJson.getString("reason");
|
|
|
-
|
|
|
- // 解析风险等级和详细信息
|
|
|
- JSONObject reasonJson = JSON.parseObject(reason);
|
|
|
- String riskLevel = reasonJson.getString("riskLevel");
|
|
|
- String riskTip = reasonJson.getString("riskTips");
|
|
|
- String riskWord = reasonJson.getString("riskWords");
|
|
|
- String customizedWord = reasonJson.getString("customizedWords");
|
|
|
- String customizedLib = reasonJson.getString("customizedLibs");
|
|
|
-
|
|
|
- // 如果存在风险信息,则添加到构建器中
|
|
|
- if (riskTip != null && !riskTip.isEmpty()) {
|
|
|
- if (riskTipsBuilder.length() > 0) {
|
|
|
- riskTipsBuilder.append(", ");
|
|
|
- }
|
|
|
- riskTipsBuilder.append(riskTip);
|
|
|
- }
|
|
|
-
|
|
|
- if (riskWord != null && !riskWord.isEmpty()) {
|
|
|
- if (riskWordsBuilder.length() > 0) {
|
|
|
- riskWordsBuilder.append(", ");
|
|
|
- }
|
|
|
- riskWordsBuilder.append(riskWord);
|
|
|
- }
|
|
|
-
|
|
|
- if (customizedWord != null && !customizedWord.isEmpty()) {
|
|
|
- if (customizedWordsBuilder.length() > 0) {
|
|
|
- customizedWordsBuilder.append(", ");
|
|
|
- }
|
|
|
- customizedWordsBuilder.append(customizedWord);
|
|
|
- }
|
|
|
-
|
|
|
- if (customizedLib != null && !customizedLib.isEmpty()) {
|
|
|
- if (customizedLibsBuilder.length() > 0) {
|
|
|
- customizedLibsBuilder.append(", ");
|
|
|
+ for (String service : servicesList) {
|
|
|
+ // 判断传入参service参数是否包含 chat_detection_pro、ad_compliance_detection_pro、llm_query_moderation、comment_detection_pro、
|
|
|
+ if (isSpecialService(service)) {
|
|
|
+ // 处理特殊服务
|
|
|
+ highestRiskLevel = handleSpecialService(text, service, client, riskWordsBuilder, highestRiskLevel);
|
|
|
+ } else {
|
|
|
+ // 判断service中包含 url_detection ,将文本中的url进行检测 提取url 传入进行检测
|
|
|
+ if ("url_detection".equals( service )) {
|
|
|
+ // 写一个文本提取url 的方法
|
|
|
+ List<String> urls = extractUrls(text);
|
|
|
+ if (CollectionUtil.isNotEmpty(urls)){
|
|
|
+ for (String url : urls) {
|
|
|
+ // 链接调取
|
|
|
+ highestRiskLevel = processTextModeration(client, url, service, riskWordsBuilder,highestRiskLevel);
|
|
|
}
|
|
|
- customizedLibsBuilder.append(customizedLib);
|
|
|
}
|
|
|
-
|
|
|
- // 判断风险等级优先级
|
|
|
- if (riskLevel != null) {
|
|
|
- if (riskLevel.equals("high") && !highestRiskLevel.equals("high")) {
|
|
|
- highestRiskLevel = "high";
|
|
|
- } else if (riskLevel.equals("medium") && highestRiskLevel.equals("low")) {
|
|
|
- highestRiskLevel = "medium";
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 设置基础信息
|
|
|
- resultVO.setLabels(labels);
|
|
|
+ }else {
|
|
|
+ // 文本调取
|
|
|
+ highestRiskLevel = processTextModeration(client, text, service, riskWordsBuilder,highestRiskLevel);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
// 设置结果VO
|
|
|
- resultVO.setCode(200);
|
|
|
- resultVO.setMessage("OK");
|
|
|
resultVO.setRiskLevel(highestRiskLevel);
|
|
|
- resultVO.setRiskTips(riskTipsBuilder.toString());
|
|
|
resultVO.setRiskWords(riskWordsBuilder.toString());
|
|
|
- resultVO.setCustomizedWords(customizedWordsBuilder.toString());
|
|
|
- resultVO.setCustomizedLibs(customizedLibsBuilder.toString());
|
|
|
-
|
|
|
} catch (Exception e) {
|
|
|
- resultVO.setCode(500);
|
|
|
- resultVO.setMessage("调用服务失败: " + e.getMessage());
|
|
|
+ throw new RuntimeException("解析失败" + e);
|
|
|
}
|
|
|
-
|
|
|
return resultVO;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 通用服务调取
|
|
|
+ * @param text 文本
|
|
|
+ * @param service 服务
|
|
|
+ * @param client 客户端
|
|
|
+ * @param riskWordsBuilder 危险词
|
|
|
+ * @param highestRiskLevel 最高危险等级
|
|
|
+ * @throws Exception 错误
|
|
|
+ */
|
|
|
+ private String processTextModeration(Client client, String text, String service, StringBuilder riskWordsBuilder,String highestRiskLevel) throws Exception {
|
|
|
+ // 调用文本检测服务
|
|
|
+ TextModerationResponse response = callTextModeration(client, text, service);
|
|
|
+ if (response != null) {
|
|
|
+ if (response.getStatusCode() == 200) {
|
|
|
+ TextModerationResponseBody result = response.getBody();
|
|
|
+ Integer code = result.getCode();
|
|
|
+ if (code != null && code == 200) {
|
|
|
+ TextModerationResponseBody.TextModerationResponseBodyData data = result.getData();
|
|
|
+ String reason = data.getReason();
|
|
|
+ if (reason != null) {
|
|
|
+ try {
|
|
|
+ JSONObject reasonObj = JSON.parseObject(reason);
|
|
|
+ String riskLevel = reasonObj.getString("riskLevel");
|
|
|
+ if ("high".equals(riskLevel)) {
|
|
|
+ highestRiskLevel = riskLevel;
|
|
|
+ String riskWords = reasonObj.getString("riskWords");
|
|
|
+ if (riskWords != null && !riskWords.isEmpty()) {
|
|
|
+ riskWordsBuilder.append(riskWords).append(",");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 解析失败,忽略该条原因
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return highestRiskLevel;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理TextModerationPlus服务
|
|
|
+ * @param text 文本
|
|
|
+ * @param service 服务
|
|
|
+ * @param client 客户端
|
|
|
+ * @param riskWordsBuilder 危险词
|
|
|
+ * @param highestRiskLevel 最高危险等级
|
|
|
+ * @throws Exception 错误
|
|
|
+ */
|
|
|
+ private String handleSpecialService(String text, String service, Client client, StringBuilder riskWordsBuilder, String highestRiskLevel) throws Exception {
|
|
|
+ // 调用plus服务并获取响应
|
|
|
+ TextModerationPlusResponse response = callTextModerationPlusService(client, text, service);
|
|
|
+ // 根据response 返回参数处理结果 返回vo所需的参数
|
|
|
+ TextModerationPlusResponseBody body = response.body;
|
|
|
+ TextModerationPlusResponseBody.TextModerationPlusResponseBodyData data = body.getData();
|
|
|
+ if (data != null) {
|
|
|
+ // 分级:riskLevel
|
|
|
+ String riskLevel = data.getRiskLevel();
|
|
|
+ // high:高风险 medium:中风险 low:低风险 none:未检测到风险
|
|
|
+ if ("high".equals(riskLevel)) {
|
|
|
+ highestRiskLevel = riskLevel;
|
|
|
+ List<TextModerationPlusResponseBody.TextModerationPlusResponseBodyDataResult> resultList = data.getResult();
|
|
|
+ for (TextModerationPlusResponseBody.TextModerationPlusResponseBodyDataResult result : resultList) {
|
|
|
+ // 文字内容检测运算后返回的标签,可能会检出多个标签和分值。
|
|
|
+ String label = result.getLabel();
|
|
|
+ // 检测到的敏感词,多个词用逗号分隔,部分标签不会返回敏感词。
|
|
|
+ String riskWords = result.getRiskWords();
|
|
|
+ String description = result.getDescription();
|
|
|
+ if (StringUtils.isNotEmpty(riskWords)){
|
|
|
+ riskWordsBuilder.append(riskWords).append(",");
|
|
|
+ }
|
|
|
+ // 获取 集合 getCustomizedHit 中 所有KeyWords 拼接为字符串并追加到 riskWordsBuilder
|
|
|
+ if (CollectionUtil.isNotEmpty(result.getCustomizedHit())){
|
|
|
+ result.getCustomizedHit().forEach(hit -> riskWordsBuilder.append(hit.getKeyWords()).append(","));
|
|
|
+ }
|
|
|
+ //置信分值,0到100分,保留到小数点后2位。部分标签无置信分。
|
|
|
+ Float confidence = result.getConfidence();
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return highestRiskLevel;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 调用TextModerationPlus服务
|
|
|
+ * @param text 文本
|
|
|
+ * @param service 服务
|
|
|
+ * @param client 客户端
|
|
|
+ * @return 检测结果
|
|
|
+ * @throws Exception 检测异常
|
|
|
+ */
|
|
|
+ private TextModerationPlusResponse callTextModerationPlusService(Client client, String text, String service) throws Exception {
|
|
|
+ // 包含调取plus服务
|
|
|
+ TextModerationPlusRequest textModerationPlusRequest = new TextModerationPlusRequest();
|
|
|
+ textModerationPlusRequest.setService(service);
|
|
|
+ // 构建服务参数
|
|
|
+ textModerationPlusRequest.setServiceParameters(buildServiceParameters(text));
|
|
|
+ return client.textModerationPlus(textModerationPlusRequest);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 调取普通服务
|
|
|
+ * @param text 文本
|
|
|
+ * @param service 服务
|
|
|
+ * @param client 客户端
|
|
|
+ * @return 检测结果
|
|
|
+ * @throws Exception 检测异常
|
|
|
+ */
|
|
|
+ private TextModerationResponse callTextModeration(Client client, String text, String service) throws Exception {
|
|
|
+ // 创建RuntimeObject实例并设置运行参数。
|
|
|
+ RuntimeOptions runtime = new RuntimeOptions();
|
|
|
+ runtime.readTimeout = 10000;
|
|
|
+ runtime.connectTimeout = 10000;
|
|
|
+ //检测参数构造
|
|
|
+ JSONObject serviceParameters = new JSONObject();
|
|
|
+ serviceParameters.put("content", text);
|
|
|
+ // 调取普通服务
|
|
|
+ TextModerationRequest textModerationRequest = new TextModerationRequest();
|
|
|
+ textModerationRequest.setService(service);
|
|
|
+ textModerationRequest.setServiceParameters(serviceParameters.toJSONString());
|
|
|
+ return client.textModerationWithOptions(textModerationRequest, runtime);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提取URL
|
|
|
+ * @param text 文本
|
|
|
+ * @return URL列表
|
|
|
+ */
|
|
|
+ private List<String> extractUrls(String text) {
|
|
|
+ List<String> urls = Lists.newArrayList();
|
|
|
+
|
|
|
+ // 使用改进后的正则表达式匹配URL
|
|
|
+ Pattern urlPattern = Pattern.compile("(https?://\\S+)(:\\d+)?(/\\S*)?");
|
|
|
+
|
|
|
+ Matcher matcher = urlPattern.matcher(text);
|
|
|
+
|
|
|
+ while (matcher.find()) {
|
|
|
+ urls.add(matcher.group());
|
|
|
+ }
|
|
|
+
|
|
|
+ return urls;
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
private String buildServiceParameters(String text) {
|
|
|
// 创建服务参数JSON
|
|
@@ -167,4 +257,16 @@ public class TextModerationUtil {
|
|
|
serviceParameters.put("content", text);
|
|
|
return serviceParameters.toJSONString();
|
|
|
}
|
|
|
+ /**
|
|
|
+ * 判断服务是否为特殊服务
|
|
|
+ * @param service 服务名称
|
|
|
+ * @return 是否为特殊服务
|
|
|
+ */
|
|
|
+ public static boolean isSpecialService(String service) {
|
|
|
+ return TextReviewServiceEnum.CHAT_DETECTION_PRO.getService().equals(service)
|
|
|
+ || TextReviewServiceEnum.AD_COMPLIANCE_DETECTION_PRO.getService().equals(service)
|
|
|
+ || TextReviewServiceEnum.LLM_QUERY_MODERATION.getService().equals(service)
|
|
|
+ || TextReviewServiceEnum.COMMENT_DETECTION_PRO.getService().equals(service);
|
|
|
+ }
|
|
|
+
|
|
|
}
|