Kaynağa Gözat

上线测试更改BUG

wangwenju 1 ay önce
ebeveyn
işleme
8ec6370dee

+ 1 - 4
LiangZhiYUMao/components/common-tabbar/common-tabbar.vue

@@ -45,10 +45,7 @@ export default {
       tabbarList: [],
       currentTab: '',
       defaultTabbar: [
-        { id: 1, name: '首页', icon: '🏠', iconSelected: '🏠', path: '/pages/index/index', tabKey: 'index', sortOrder: 1 },
-        { id: 2, name: '动态', icon: '💕', iconSelected: '💕', path: '/pages/plaza/index', tabKey: 'plaza', sortOrder: 2 },
-        { id: 3, name: '推荐', icon: '👍', iconSelected: '👍', path: '/pages/recommend/index', tabKey: 'recommend', sortOrder: 3 },
-        { id: 4, name: '我的', icon: '👤', iconSelected: '👤', path: '/pages/mine/index', tabKey: 'mine', sortOrder: 4 }
+       
       ]
     }
   },

+ 6 - 0
service/dynamic/pom.xml

@@ -38,6 +38,12 @@
             <artifactId>spring-boot-starter-validation</artifactId>
         </dependency>
 
+        <!-- Apache HttpClient for 网易易盾 -->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>

+ 23 - 429
service/dynamic/src/main/java/com/zhentao/controller/ContentSecurityController.java

@@ -1,8 +1,8 @@
 package com.zhentao.controller;
 
+import com.zhentao.service.YidunSecurityService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -11,8 +11,6 @@ import org.springframework.web.bind.annotation.RestController;
 
 import javax.servlet.http.HttpServletRequest;
 import java.io.*;
-import java.net.HttpURLConnection;
-import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.List;
@@ -22,7 +20,7 @@ import java.util.regex.Pattern;
 /**
  * 内容安全检测控制器
  * 用于检测用户发布的文字、图片是否包含违规内容
- * 使用微信小程序内容安全API
+ * 使用网易易盾内容安全API
  */
 @RestController
 @RequestMapping("/api/content-security")
@@ -30,23 +28,13 @@ public class ContentSecurityController {
 
     private static final Logger logger = LoggerFactory.getLogger(ContentSecurityController.class);
 
-    // ====================== 常量配置 (统一维护,无硬编码) ======================
-    private static final int MAX_IMAGE_SIZE = 2 * 1024 * 1024; // 图片最大下载大小 2MB,防止OOM
-    private static final int WX_IMAGE_LIMIT_SIZE = 1 * 1024 * 1024; // 微信同步检测图片限制1MB
-    private static final int TOKEN_EXPIRE_ADVANCE = 300; // token提前300秒刷新
-    private static final int WX_ERRCODE_ILLEGAL_CONTENT = 87014; // 内容违规错误码
-    private static final int WX_ERRCODE_TOKEN_EXPIRE_1 = 40001; // token过期错误码1
-    private static final int WX_ERRCODE_TOKEN_EXPIRE_2 = 42001; // token过期错误码2
+    // 注入网易易盾内容安全服务
+    private final YidunSecurityService yidunSecurityService;
 
-    @Value("${wechat.miniapp.appid:}")
-    private String appId;
-
-    @Value("${wechat.miniapp.secret:}")
-    private String appSecret;
-
-    // AccessToken缓存,volatile保证多线程可见性
-    private volatile String cachedAccessToken;
-    private volatile long tokenExpireTime = 0;
+    // 构造函数注入
+    public ContentSecurityController(YidunSecurityService yidunSecurityService) {
+        this.yidunSecurityService = yidunSecurityService;
+    }
 
     private static final List<String> SENSITIVE_WORDS = Arrays.asList(
             "色情", "裸体", "性爱", "约炮", "一夜情", "援交", "卖淫", "嫖娼", "小姐服务",
@@ -79,31 +67,31 @@ public class ContentSecurityController {
 
         logger.info("开始检测文字内容,长度: {}", content.length());
 
-        // 本地敏感词检测
+        // 第一层:本地敏感词快速检测
         String localCheckResult = localTextCheck(content);
         if (localCheckResult != null) {
             logger.info("本地敏感词检测不通过: {}", localCheckResult);
             return buildResponse(false, "内容包含敏感词,请修改后重试");
         }
 
-        // 微信内容安全检测
+        // 第二层:网易易盾内容安全检测(AI智能检测)
         try {
-            String wxResult = wxMsgSecCheck(content);
-            if (wxResult != null) {
-                logger.info("微信内容安全检测不通过");
-                return buildResponse(false, wxResult);
+            String yidunResult = yidunSecurityService.checkText(content);
+            if (yidunResult != null) {
+                logger.info("网易易盾内容安全检测不通过: {}", yidunResult);
+                return buildResponse(false, yidunResult);
             }
         } catch (Exception e) {
-            logger.error("微信文字内容安全检测异常", e);
+            logger.error("网易易盾文字内容安全检测异常", e);
         }
 
-        logger.info("文字内容检测通过");
+        logger.info("文字内容检测通过(已通过本地+网易易盾检测)");
         return buildResponse(true, "");
     }
 
     /**
      * 检测图片是否安全
-     * 检测链路:同步接口(img_sec_check) → 异步接口(media_check_async)
+     * 使用网易易盾内容安全API进行检测
      */
     @PostMapping(value = "/check-image", produces = MediaType.APPLICATION_JSON_VALUE)
     @ResponseBody
@@ -125,26 +113,15 @@ public class ContentSecurityController {
 
         logger.info("开始检测图片: {}", imageUrl);
 
-        // 1. 先尝试使用微信同步接口 img_sec_check (精度高,实时返回结果)
-        try {
-            String wxResult = wxImgSecCheck(imageUrl);
-            if (wxResult != null) {
-                logger.info("微信图片同步检测不通过: {}", imageUrl);
-                return buildResponse(false, wxResult);
-            }
-        } catch (Exception e) {
-            logger.error("微信图片同步检测异常: {}", imageUrl, e);
-        }
-
-        // 2. 如果同步接口无结果/图片过大,使用异步接口补充检测
+        // 使用网易易盾图片内容安全检测
         try {
-            String asyncResult = wxMediaCheckAsync(imageUrl);
-            if (asyncResult != null) {
-                logger.info("微信图片异步检测不通过: {}", imageUrl);
-                return buildResponse(false, asyncResult);
+            String yidunResult = yidunSecurityService.checkImage(imageUrl);
+            if (yidunResult != null) {
+                logger.info("网易易盾图片检测不通过: {}", yidunResult);
+                return buildResponse(false, yidunResult);
             }
         } catch (Exception e) {
-            logger.error("微信图片异步检测异常: {}", imageUrl, e);
+            logger.error("网易易盾图片内容安全检测异常", e);
         }
 
         logger.info("图片检测通过: {}", imageUrl);
@@ -187,323 +164,7 @@ public class ContentSecurityController {
         return null;
     }
 
-    /**
-     * 调用微信文字内容安全检测API (msg_sec_check) - 同步接口
-     */
-    private String wxMsgSecCheck(String content) {
-        HttpURLConnection conn = null;
-        try {
-            String accessToken = getAccessToken();
-            if (accessToken == null || accessToken.isEmpty()) {
-                logger.warn("获取access_token失败,跳过微信文字检测");
-                return null;
-            }
-
-            String urlStr = "https://api.weixin.qq.com/wxa/msg_sec_check?access_token=" + accessToken;
-            URL url = new URL(urlStr);
-            conn = (HttpURLConnection) url.openConnection();
-            conn.setRequestMethod("POST");
-            conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
-            conn.setDoOutput(true);
-            conn.setConnectTimeout(10000);
-            conn.setReadTimeout(10000);
-
-            // 修复:openid传空字符串(无用户openid时的兼容写法,微信允许),不再传appId
-            String jsonBody = "{\"content\":\"" + escapeJson(content) + "\",\"version\":2,\"scene\":2,\"openid\":\"\"}";
-            logger.debug("微信文字检测请求体: {}", jsonBody);
-
-            try (OutputStream os = conn.getOutputStream()) {
-                os.write(jsonBody.getBytes(StandardCharsets.UTF_8));
-            }
-
-            int responseCode = conn.getResponseCode();
-            String responseBody = readHttpResponse(conn);
-            logger.info("微信文字检测响应: code={}, body={}", responseCode, responseBody);
-
-            if (responseCode == 200 && responseBody != null) {
-                int errcode = getJsonInt(responseBody, "errcode");
-                // 全局处理token过期
-                if (isTokenExpireCode(errcode)) {
-                    clearTokenCache();
-                    return null;
-                }
-                if (errcode == 0) {
-                    String suggest = getNestedJsonString(responseBody, "result", "suggest");
-                    if ("risky".equals(suggest)) {
-                        String label = getNestedJsonString(responseBody, "result", "label");
-                        return "内容包含" + getLabelText(label) + ",请修改后重试";
-                    }
-                    return null;
-                } else if (errcode == WX_ERRCODE_ILLEGAL_CONTENT) {
-                    return "内容包含违规信息,请修改后重试";
-                } else {
-                    logger.warn("微信文字检测返回错误码: {}, 描述: {}", errcode, getJsonString(responseBody, "errmsg"));
-                }
-            }
-        } catch (Exception e) {
-            logger.error("调用微信文字安全检测API异常", e);
-        } finally {
-            if (conn != null) conn.disconnect();
-        }
-        return null;
-    }
-
-    /**
-     * 调用微信图片内容安全检测API (img_sec_check) - 同步接口
-     * 限制:图片大小 <= 1MB,实时返回检测结果
-     */
-    private String wxImgSecCheck(String imageUrl) {
-        HttpURLConnection conn = null;
-        try {
-            String accessToken = getAccessToken();
-            if (accessToken == null || accessToken.isEmpty()) {
-                logger.warn("获取access_token失败,跳过微信图片同步检测");
-                return null;
-            }
-
-            logger.info("开始下载图片: {}", imageUrl);
-            byte[] imageBytes = downloadImage(imageUrl);
-            if (imageBytes == null || imageBytes.length == 0) {
-                logger.warn("下载图片失败: {}", imageUrl);
-                return null;
-            }
-            logger.info("图片下载成功,大小: {} bytes", imageBytes.length);
-
-            // 微信同步检测限制图片1MB,超过则跳过走异步
-            if (imageBytes.length > WX_IMAGE_LIMIT_SIZE) {
-                logger.warn("图片大小超过1MB({} bytes),跳过同步检测,走异步检测", imageBytes.length);
-                return null;
-            }
-
-            String boundary = "----WebKitFormBoundary" + System.currentTimeMillis();
-            String urlStr = "https://api.weixin.qq.com/wxa/img_sec_check?access_token=" + accessToken;
-
-            URL url = new URL(urlStr);
-            conn = (HttpURLConnection) url.openConnection();
-            conn.setRequestMethod("POST");
-            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
-            conn.setDoOutput(true);
-            conn.setConnectTimeout(15000);
-            conn.setReadTimeout(30000);
-
-            try (OutputStream os = conn.getOutputStream()) {
-                String header = "--" + boundary + "\r\n" +
-                        "Content-Disposition: form-data; name=\"media\"; filename=\"image.jpg\"\r\n" +
-                        "Content-Type: application/octet-stream\r\n\r\n";
-                os.write(header.getBytes(StandardCharsets.UTF_8));
-                os.write(imageBytes);
-                os.write(("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8));
-                os.flush();
-            }
-
-            int responseCode = conn.getResponseCode();
-            String responseBody = readHttpResponse(conn);
-            logger.info("微信图片同步检测响应: code={}, body={}", responseCode, responseBody);
-
-            if (responseCode == 200 && responseBody != null) {
-                int errcode = getJsonInt(responseBody, "errcode");
-                if (isTokenExpireCode(errcode)) {
-                    clearTokenCache();
-                    return null;
-                }
-                if (errcode == 0) {
-                    return null;
-                } else if (errcode == WX_ERRCODE_ILLEGAL_CONTENT) {
-                    return "图片包含违规内容,请更换后重试";
-                } else {
-                    logger.warn("微信图片同步检测错误码: {}, 描述: {}", errcode, getJsonString(responseBody, "errmsg"));
-                }
-            }
-        } catch (Exception e) {
-            logger.error("调用微信图片同步检测API异常", e);
-        } finally {
-            if (conn != null) conn.disconnect();
-        }
-        return null;
-    }
-
-    /**
-     * 调用微信媒体内容安全检测API (media_check_async) - 异步接口
-     * 适用:图片>1MB,提交检测后返回trace_id,结果需回调获取
-     */
-    private String wxMediaCheckAsync(String imageUrl) {
-        HttpURLConnection conn = null;
-        try {
-            String accessToken = getAccessToken();
-            if (accessToken == null || accessToken.isEmpty()) {
-                logger.warn("获取access_token失败,跳过微信图片异步检测");
-                return null;
-            }
-
-            String urlStr = "https://api.weixin.qq.com/wxa/media_check_async?access_token=" + accessToken;
-            URL url = new URL(urlStr);
-            conn = (HttpURLConnection) url.openConnection();
-            conn.setRequestMethod("POST");
-            conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
-            conn.setDoOutput(true);
-            conn.setConnectTimeout(10000);
-            conn.setReadTimeout(10000);
-
-            // 修复:openid传空字符串,不再传appId
-            String jsonBody = "{\"media_url\":\"" + escapeJson(imageUrl) + "\",\"media_type\":2,\"version\":2,\"scene\":2,\"openid\":\"\"}";
-            logger.info("微信图片异步检测请求体: {}", jsonBody);
-
-            try (OutputStream os = conn.getOutputStream()) {
-                os.write(jsonBody.getBytes(StandardCharsets.UTF_8));
-            }
-
-            int responseCode = conn.getResponseCode();
-            String responseBody = readHttpResponse(conn);
-            logger.info("微信图片异步检测响应: code={}, body={}", responseCode, responseBody);
-
-            if (responseCode == 200 && responseBody != null) {
-                int errcode = getJsonInt(responseBody, "errcode");
-                if (isTokenExpireCode(errcode)) {
-                    clearTokenCache();
-                    return null;
-                }
-                if (errcode == 0) {
-                    String traceId = getJsonString(responseBody, "trace_id");
-                    logger.info("微信异步检测已提交,trace_id: {}", traceId);
-                    return null;
-                } else if (errcode == WX_ERRCODE_ILLEGAL_CONTENT) {
-                    return "图片包含违规内容,请更换后重试";
-                } else {
-                    logger.warn("微信异步检测错误码: {}, 描述: {}", errcode, getJsonString(responseBody, "errmsg"));
-                }
-            }
-        } catch (Exception e) {
-            logger.error("调用微信图片异步检测API异常", e);
-        } finally {
-            if (conn != null) conn.disconnect();
-        }
-        return null;
-    }
-
-    /**
-     * 下载图片字节数组,增加大小限制防止OOM
-     */
-    private byte[] downloadImage(String imageUrl) {
-        HttpURLConnection conn = null;
-        try {
-            URL url = new URL(imageUrl);
-            conn = (HttpURLConnection) url.openConnection();
-            conn.setRequestMethod("GET");
-            conn.setConnectTimeout(10000);
-            conn.setReadTimeout(30000);
-            conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
-            conn.setInstanceFollowRedirects(true);
-
-            int responseCode = conn.getResponseCode();
-            if (responseCode != 200) {
-                logger.warn("下载图片失败,HTTP状态码: {}", responseCode);
-                return null;
-            }
-
-            try (InputStream is = conn.getInputStream();
-                 ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
-                byte[] buffer = new byte[8192];
-                int bytesRead;
-                while ((bytesRead = is.read(buffer)) != -1) {
-                    // 超过最大限制,直接返回null,防止OOM
-                    if (baos.size() + bytesRead > MAX_IMAGE_SIZE) {
-                        logger.warn("图片大小超过{}MB,终止下载", MAX_IMAGE_SIZE / 1024 / 1024);
-                        return null;
-                    }
-                    baos.write(buffer, 0, bytesRead);
-                }
-                return baos.toByteArray();
-            }
-        } catch (Exception e) {
-            logger.error("下载图片异常: {}", imageUrl, e);
-            return null;
-        } finally {
-            if (conn != null) conn.disconnect();
-        }
-    }
-
-    /**
-     * 获取微信AccessToken,双重检查锁保证并发安全+性能,缓存过期自动刷新
-     */
-    private String getAccessToken() {
-        // 第一次无锁检查,提高并发性能
-        if (cachedAccessToken != null && System.currentTimeMillis() < tokenExpireTime) {
-            logger.debug("使用缓存的access_token,剩余有效期:{}ms", tokenExpireTime - System.currentTimeMillis());
-            return cachedAccessToken;
-        }
-
-        synchronized (this) {
-            // 第二次加锁检查,防止多线程重复请求
-            if (cachedAccessToken != null && System.currentTimeMillis() < tokenExpireTime) {
-                return cachedAccessToken;
-            }
-
-            if (appId == null || appId.isEmpty() || appSecret == null || appSecret.isEmpty()) {
-                logger.error("微信小程序appId或appSecret未配置! appId={}", appId != null ? appId.substring(0, 4) + "***" : "null");
-                return null;
-            }
-
-            HttpURLConnection conn = null;
-            try {
-                String urlStr = String.format(
-                        "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
-                        appId, appSecret
-                );
-                logger.info("开始请求微信access_token,appId:{}***", appId.substring(0, 4));
-
-                URL url = new URL(urlStr);
-                conn = (HttpURLConnection) url.openConnection();
-                conn.setRequestMethod("GET");
-                conn.setConnectTimeout(10000);
-                conn.setReadTimeout(10000);
-
-                int responseCode = conn.getResponseCode();
-                String responseBody = readHttpResponse(conn);
-                logger.info("获取token响应: code={}, body={}", responseCode, responseBody);
-
-                if (responseCode == 200 && responseBody != null) {
-                    String token = getJsonString(responseBody, "access_token");
-                    if (token != null && !token.isEmpty()) {
-                        cachedAccessToken = token;
-                        int expiresIn = getJsonInt(responseBody, "expires_in");
-                        if (expiresIn == 0) expiresIn = 7200;
-                        tokenExpireTime = System.currentTimeMillis() + (expiresIn - TOKEN_EXPIRE_ADVANCE) * 1000L;
-                        logger.info("获取access_token成功,有效期:{}秒", expiresIn);
-                        return cachedAccessToken;
-                    } else {
-                        int errcode = getJsonInt(responseBody, "errcode");
-                        String errmsg = getJsonString(responseBody, "errmsg");
-                        logger.error("获取token失败: errcode={}, errmsg={}", errcode, errmsg);
-                    }
-                }
-            } catch (Exception e) {
-                logger.error("获取微信access_token异常", e);
-            } finally {
-                if (conn != null) conn.disconnect();
-            }
-            return null;
-        }
-    }
-
     // ====================== 通用工具方法 ======================
-    /**
-     * 读取Http响应体,兼容成功流和错误流
-     */
-    private String readHttpResponse(HttpURLConnection conn) throws IOException {
-        InputStream is = conn.getResponseCode() >= 400 ? conn.getErrorStream() : conn.getInputStream();
-        if (is == null) return "";
-        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
-            StringBuilder sb = new StringBuilder();
-            String line;
-            while ((line = reader.readLine()) != null) {
-                sb.append(line);
-            }
-            return sb.toString();
-        } finally {
-            if (is != null) is.close();
-        }
-    }
-
     /**
      * 标准JSON字符串转义,补全所有缺失的转义字符
      */
@@ -535,71 +196,4 @@ public class ContentSecurityController {
         Matcher matcher = pattern.matcher(json);
         return matcher.find() ? matcher.group(1) : null;
     }
-
-    /**
-     * 正则解析JSON字符串中的数字值
-     */
-    private int getJsonInt(String json, String key) {
-        if (json == null || key == null) return 0;
-        Pattern pattern = Pattern.compile("\"" + key + "\"\\s*:\\s*(-?\\d+)");
-        Matcher matcher = pattern.matcher(json);
-        if (matcher.find()) {
-            try {
-                return Integer.parseInt(matcher.group(1));
-            } catch (NumberFormatException e) {
-                return 0;
-            }
-        }
-        return 0;
-    }
-
-    /**
-     * 解析嵌套JSON中的字符串值
-     */
-    private String getNestedJsonString(String json, String parentKey, String childKey) {
-        if (json == null || parentKey == null || childKey == null) return null;
-        Pattern parentPattern = Pattern.compile("\"" + parentKey + "\"\\s*:\\s*\\{([^}]*)\\}");
-        Matcher parentMatcher = parentPattern.matcher(json);
-        if (parentMatcher.find()) {
-            String parentContent = parentMatcher.group(1);
-            return getJsonString("{" + parentContent + "}", childKey);
-        }
-        return null;
-    }
-
-    /**
-     * 新增:缺失的标签转换方法,解决运行报错
-     */
-    private String getLabelText(String label) {
-        if (label == null) return "违规内容";
-        switch (label) {
-            case "100": return "违规内容";
-            case "10001": return "广告推广内容";
-            case "20001": return "时政敏感内容";
-            case "20002": return "色情低俗内容";
-            case "20003": return "辱骂攻击内容";
-            case "20006": return "违法犯罪内容";
-            case "20008": return "欺诈诱导内容";
-            case "20012": return "低俗恶心内容";
-            case "20013": return "侵权版权内容";
-            case "21000": return "其他违规内容";
-            default: return "违规内容";
-        }
-    }
-
-    /**
-     * 判断是否为token过期错误码
-     */
-    private boolean isTokenExpireCode(int errcode) {
-        return errcode == WX_ERRCODE_TOKEN_EXPIRE_1 || errcode == WX_ERRCODE_TOKEN_EXPIRE_2;
-    }
-
-    /**
-     * 清空token缓存
-     */
-    private void clearTokenCache() {
-        cachedAccessToken = null;
-        tokenExpireTime = 0;
-        logger.info("检测到access_token过期,已清空缓存");
-    }
 }

+ 396 - 0
service/dynamic/src/main/java/com/zhentao/service/YidunSecurityService.java

@@ -0,0 +1,396 @@
+package com.zhentao.service;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 网易易盾内容安全服务
+ * 支持文本和图片内容检测
+ */
+@Service
+public class YidunSecurityService {
+    
+    private static final Logger logger = LoggerFactory.getLogger(YidunSecurityService.class);
+    
+    // 网易易盾API地址
+    private static final String TEXT_CHECK_URL = "https://as.dun.163.com/v5/text/check";
+    private static final String IMAGE_CHECK_URL = "https://as.dun.163.com/v5/image/check";
+    
+    @Value("${yidun.secret-id:}")
+    private String secretId;
+    
+    @Value("${yidun.secret-key:}")
+    private String secretKey;
+    
+    @Value("${yidun.business-id:}")
+    private String businessId;
+    
+    @PostConstruct
+    public void init() {
+        if (secretId == null || secretId.isEmpty() || 
+            secretKey == null || secretKey.isEmpty() ||
+            secretId.startsWith("YOUR_")) {
+            logger.warn("网易易盾内容安全未配置,将跳过易盾检测");
+            return;
+        }
+        
+        logger.info("网易易盾内容安全服务初始化成功");
+    }
+    
+    /**
+     * 检测文本内容
+     * @param content 要检测的文本
+     * @return 检测结果,null表示通过,非null表示违规原因
+     */
+    public String checkText(String content) {
+        if (secretId == null || secretId.isEmpty() || secretId.startsWith("YOUR_")) {
+            logger.debug("网易易盾未配置,跳过检测");
+            return null;
+        }
+        
+        if (content == null || content.trim().isEmpty()) {
+            return null;
+        }
+        
+        try {
+            // 构建请求参数
+            Map<String, String> params = new HashMap<>();
+            params.put("secretId", secretId);
+            params.put("businessId", "984b3b628144e0e8bebc9861b7c0b9fc");
+            params.put("version", "v5");
+            params.put("timestamp", String.valueOf(System.currentTimeMillis()));
+            params.put("nonce", UUID.randomUUID().toString().replace("-", ""));
+            
+            // dataId用于标识请求
+            params.put("dataId", UUID.randomUUID().toString());
+            params.put("content", content);
+            
+            // 生成签名
+            String signature = genSignature(params);
+            params.put("signature", signature);
+            
+            logger.debug("网易易盾文本检测请求,内容长度: {}", content.length());
+            
+            // 发送请求
+            String response = sendPost(TEXT_CHECK_URL, params);
+            
+            logger.info("网易易盾文本检测响应: {}", response);
+            
+            // 解析响应
+            return parseTextResponse(response);
+        } catch (Exception e) {
+            logger.error("网易易盾文本检测异常", e);
+        }
+        
+        return null;
+    }
+    
+    /**
+     * 检测图片内容
+     * @param imageUrl 图片URL
+     * @return 检测结果,null表示通过,非null表示违规原因
+     */
+    public String checkImage(String imageUrl) {
+        if (secretId == null || secretId.isEmpty() || secretId.startsWith("YOUR_")) {
+            logger.debug("网易易盾未配置,跳过检测");
+            return null;
+        }
+        
+        if (imageUrl == null || imageUrl.trim().isEmpty()) {
+            return null;
+        }
+        
+        try {
+            // 构建请求参数
+            Map<String, String> params = new HashMap<>();
+            params.put("secretId", secretId);
+            params.put("businessId", businessId);
+            params.put("version", "v5");
+            params.put("timestamp", String.valueOf(System.currentTimeMillis()));
+            params.put("nonce", UUID.randomUUID().toString().replace("-", ""));
+            
+            // dataId用于标识请求
+            params.put("dataId", UUID.randomUUID().toString());
+            params.put("url", imageUrl);
+            
+            // 生成签名
+            String signature = genSignature(params);
+            params.put("signature", signature);
+            
+            logger.debug("网易易盾图片检测请求: {}", imageUrl);
+            
+            // 发送请求
+            String response = sendPost(IMAGE_CHECK_URL, params);
+            
+            logger.info("网易易盾图片检测响应: {}", response);
+            
+            // 解析响应
+            return parseImageResponse(response);
+        } catch (Exception e) {
+            logger.error("网易易盾图片检测异常", e);
+        }
+        
+        return null;
+    }
+    
+    /**
+     * 生成签名
+     */
+    private String genSignature(Map<String, String> params) throws Exception {
+        // 1. 参数排序
+        List<String> paramList = new ArrayList<>(params.keySet());
+        Collections.sort(paramList);
+        
+        // 2. 拼接参数
+        StringBuilder sb = new StringBuilder();
+        for (String key : paramList) {
+            String value = params.get(key);
+            sb.append(key).append(value);
+        }
+        sb.append(secretKey);
+        
+        // 3. MD5加密
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        byte[] bytes = md.digest(sb.toString().getBytes(StandardCharsets.UTF_8));
+        
+        // 4. 转16进制
+        StringBuilder result = new StringBuilder();
+        for (byte b : bytes) {
+            result.append(String.format("%02x", b));
+        }
+        
+        return result.toString();
+    }
+    
+    /**
+     * 发送POST请求
+     */
+    private String sendPost(String urlStr, Map<String, String> params) throws Exception {
+        // 构建请求体
+        StringBuilder postData = new StringBuilder();
+        for (Map.Entry<String, String> entry : params.entrySet()) {
+            if (postData.length() > 0) {
+                postData.append("&");
+            }
+            postData.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
+            postData.append("=");
+            postData.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
+        }
+        
+        byte[] postDataBytes = postData.toString().getBytes(StandardCharsets.UTF_8);
+        
+        URL url = new URL(urlStr);
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("POST");
+        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+        conn.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
+        conn.setDoOutput(true);
+        conn.setConnectTimeout(10000);
+        conn.setReadTimeout(10000);
+        
+        try (OutputStream os = conn.getOutputStream()) {
+            os.write(postDataBytes);
+        }
+        
+        int responseCode = conn.getResponseCode();
+        StringBuilder response = new StringBuilder();
+        
+        try (BufferedReader br = new BufferedReader(
+                new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
+            String line;
+            while ((line = br.readLine()) != null) {
+                response.append(line);
+            }
+        }
+        
+        return response.toString();
+    }
+    
+    /**
+     * 解析文本检测响应
+     */
+    private String parseTextResponse(String response) {
+        try {
+            // 提取code
+            int code = getJsonInt(response, "code");
+            if (code != 200) {
+                logger.warn("网易易盾返回错误码: {}", code);
+                String msg = getJsonString(response, "msg");
+                logger.warn("错误信息: {}", msg);
+                return null;
+            }
+            
+            // 提取result
+            String result = extractJsonObject(response, "result");
+            if (result == null) {
+                return null;
+            }
+            
+            // 提取antispam
+            String antispam = extractJsonObject(result, "antispam");
+            if (antispam == null) {
+                return null;
+            }
+            
+            // 提取suggestion: 0-通过,1-嫌疑,2-不通过
+            int suggestion = getJsonInt(antispam, "suggestion");
+            
+            logger.debug("检测建议: {}", suggestion);
+            
+            if (suggestion == 2) {
+                // 不通过
+                int label = getJsonInt(antispam, "label");
+                return "内容包含违规信息(" + getTextLabelText(label) + "),请修改后重试";
+            } else if (suggestion == 1) {
+                // 嫌疑,建议人工审核
+                return "内容需要人工审核,请修改后重试";
+            }
+            
+            // 0 - 通过
+            return null;
+        } catch (Exception e) {
+            logger.error("解析网易易盾文本响应失败", e);
+            return null;
+        }
+    }
+    
+    /**
+     * 解析图片检测响应
+     */
+    private String parseImageResponse(String response) {
+        try {
+            // 提取code
+            int code = getJsonInt(response, "code");
+            if (code != 200) {
+                logger.warn("网易易盾返回错误码: {}", code);
+                String msg = getJsonString(response, "msg");
+                logger.warn("错误信息: {}", msg);
+                return null;
+            }
+            
+            // 提取result
+            String result = extractJsonObject(response, "result");
+            if (result == null) {
+                return null;
+            }
+            
+            // 提取antispam
+            String antispam = extractJsonObject(result, "antispam");
+            if (antispam == null) {
+                return null;
+            }
+            
+            // 提取suggestion: 0-通过,1-嫌疑,2-不通过
+            int suggestion = getJsonInt(antispam, "suggestion");
+            
+            logger.debug("检测建议: {}", suggestion);
+            
+            if (suggestion == 2) {
+                // 不通过
+                int label = getJsonInt(antispam, "label");
+                return "图片包含违规内容(" + getImageLabelText(label) + "),请更换后重试";
+            } else if (suggestion == 1) {
+                // 嫌疑,建议人工审核
+                return "图片需要人工审核,请更换后重试";
+            }
+            
+            // 0 - 通过
+            return null;
+        } catch (Exception e) {
+            logger.error("解析网易易盾图片响应失败", e);
+            return null;
+        }
+    }
+    
+    /**
+     * 获取文本标签文本
+     */
+    private String getTextLabelText(int label) {
+        Map<Integer, String> labelMap = new HashMap<>();
+        labelMap.put(100, "正常");
+        labelMap.put(200, "色情");
+        labelMap.put(260, "广告");
+        labelMap.put(300, "暴恐");
+        labelMap.put(400, "违禁");
+        labelMap.put(500, "涉政");
+        labelMap.put(600, "谩骂");
+        labelMap.put(700, "灌水");
+        labelMap.put(900, "其他");
+        return labelMap.getOrDefault(label, "违规内容");
+    }
+    
+    /**
+     * 获取图片标签文本
+     */
+    private String getImageLabelText(int label) {
+        Map<Integer, String> labelMap = new HashMap<>();
+        labelMap.put(100, "正常");
+        labelMap.put(200, "色情");
+        labelMap.put(210, "性感");
+        labelMap.put(260, "广告");
+        labelMap.put(300, "暴恐");
+        labelMap.put(400, "违禁");
+        labelMap.put(500, "涉政");
+        labelMap.put(700, "黑名单");
+        labelMap.put(900, "其他");
+        return labelMap.getOrDefault(label, "违规内容");
+    }
+    
+    /**
+     * 正则解析JSON字符串中的字符串值
+     */
+    private String getJsonString(String json, String key) {
+        if (json == null || key == null) return null;
+        Pattern pattern = Pattern.compile("\"" + key + "\"\\s*:\\s*\"([^\"]*)\"");
+        Matcher matcher = pattern.matcher(json);
+        if (matcher.find()) {
+            return matcher.group(1);
+        }
+        return null;
+    }
+    
+    /**
+     * 正则解析JSON字符串中的数字值
+     */
+    private int getJsonInt(String json, String key) {
+        if (json == null || key == null) return 0;
+        Pattern pattern = Pattern.compile("\"" + key + "\"\\s*:\\s*(-?\\d+)");
+        Matcher matcher = pattern.matcher(json);
+        if (matcher.find()) {
+            try {
+                return Integer.parseInt(matcher.group(1));
+            } catch (NumberFormatException e) {
+                return 0;
+            }
+        }
+        return 0;
+    }
+    
+    /**
+     * 提取JSON对象
+     */
+    private String extractJsonObject(String json, String key) {
+        if (json == null || key == null) return null;
+        Pattern pattern = Pattern.compile("\"" + key + "\"\\s*:\\s*\\{([^}]+)\\}");
+        Matcher matcher = pattern.matcher(json);
+        if (matcher.find()) {
+            return "{" + matcher.group(1) + "}";
+        }
+        return null;
+    }
+}

+ 6 - 0
service/dynamic/src/main/resources/application.yml

@@ -81,3 +81,9 @@ wechat:
   miniapp:
     appid: wx3e90d662a801266e
     secret: d82ce405f04a47de14382bef4180239d
+
+# 网易易盾内容安全配置
+yidun:
+  secret-id: 6153a3748fb5b2be97b73be77d04ba17  # 替换为你的网易易盾SecretId
+  secret-key: 3bd803e720aeceb3fcd54c664e88b9cd  # 替换为你的网易易盾SecretKey
+  business-id: 1d3aa4372481126b9ec151ef84f9a7ee  # 替换为你的业务ID