Browse Source

删除图片及视频审核 只保留文字审核

lutong 2 weeks ago
parent
commit
418f2f76a1
17 changed files with 263 additions and 885 deletions
  1. 2 16
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/LicenseController.java
  2. 66 137
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/OperationalActivityServiceImpl.java
  3. 66 137
      alien-store-platform/src/main/java/shop/alien/storeplatform/util/AiContentModerationUtil.java
  4. 1 1
      alien-store/src/main/java/shop/alien/store/aspect/AiAuditAspect.java
  5. 4 15
      alien-store/src/main/java/shop/alien/store/controller/LifeUserDynamicsController.java
  6. 4 4
      alien-store/src/main/java/shop/alien/store/controller/StoreCuisineController.java
  7. 2 46
      alien-store/src/main/java/shop/alien/store/controller/StoreImgController.java
  8. 4 4
      alien-store/src/main/java/shop/alien/store/controller/StorePriceController.java
  9. 2 26
      alien-store/src/main/java/shop/alien/store/service/impl/BarPerformanceAuditServiceImpl.java
  10. 2 20
      alien-store/src/main/java/shop/alien/store/service/impl/BarPerformanceServiceImpl.java
  11. 3 5
      alien-store/src/main/java/shop/alien/store/service/impl/CommonRatingServiceImpl.java
  12. 10 21
      alien-store/src/main/java/shop/alien/store/service/impl/StoreClockInServiceImpl.java
  13. 3 5
      alien-store/src/main/java/shop/alien/store/service/impl/StoreRenovationRequirementServiceImpl.java
  14. 7 12
      alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffAuditAsyncService.java
  15. 3 4
      alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffReviewServiceImpl.java
  16. 74 130
      alien-store/src/main/java/shop/alien/store/util/ai/AiContentModerationUtil.java
  17. 10 302
      alien-store/src/main/java/shop/alien/store/util/ai/AiVideoModerationUtil.java

+ 2 - 16
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/LicenseController.java

@@ -13,9 +13,7 @@ import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryDto;
 import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryVO;
 import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryVO;
 import shop.alien.mapper.WebAuditMapper;
 import shop.alien.mapper.WebAuditMapper;
 import shop.alien.storeplatform.service.LicenseService;
 import shop.alien.storeplatform.service.LicenseService;
-import shop.alien.storeplatform.util.AiContentModerationUtil;
 
 
-import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 
 
@@ -33,9 +31,6 @@ public class LicenseController {
 
 
     private final WebAuditMapper webAuditMapper;
     private final WebAuditMapper webAuditMapper;
 
 
-    private final AiContentModerationUtil aiContentModerationUtil;
-
-
     @ApiOperation("获取营业执照图片信息")
     @ApiOperation("获取营业执照图片信息")
     @ApiOperationSupport(order = 1)
     @ApiOperationSupport(order = 1)
     @ApiImplicitParams({
     @ApiImplicitParams({
@@ -164,23 +159,14 @@ public class LicenseController {
     }
     }
 
 
 
 
-    @ApiOperation(value = "门店装修-修改娱乐经营许可证", notes = "上传前会同步进行AI图片审核,不通过则直接返回失败原因")
+    @ApiOperation(value = "门店装修-修改娱乐经营许可证", notes = "许可证图片由前端直连AI审核,服务端不再同步审图")
     @PostMapping("/uploadEntertainmentLicence")
     @PostMapping("/uploadEntertainmentLicence")
     public R<String> uploadEntertainmentLicence(@RequestBody StoreImg storeImg) {
     public R<String> uploadEntertainmentLicence(@RequestBody StoreImg storeImg) {
         log.info("StoreInfoController.uploadEntertainmentLicence?storeImg={}", storeImg);
         log.info("StoreInfoController.uploadEntertainmentLicence?storeImg={}", storeImg);
         if (storeImg == null) {
         if (storeImg == null) {
             return R.fail("参数不能为空");
             return R.fail("参数不能为空");
         }
         }
-        // AI 图片审核(同步):不通过则直接返回原因给前台
-        if (storeImg.getImgUrl() != null && !storeImg.getImgUrl().trim().isEmpty()) {
-            AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(
-                    null, Collections.singletonList(storeImg.getImgUrl().trim()));
-            if (!auditResult.isPassed()) {
-                String reason = auditResult.getFailureReason() != null ? auditResult.getFailureReason() : "图片审核未通过";
-                log.warn("娱乐经营许可证图片AI审核不通过, storeId={}, reason={}", storeImg.getStoreId(), reason);
-                return R.fail(reason);
-            }
-        }
+        // 许可证配图由前端直连 AI;此处不再调用 aiContentModerationUtil 审图,避免因服务端不审图而拦截上传
         int num = licenseService.uploadEntertainmentLicence(storeImg);
         int num = licenseService.uploadEntertainmentLicence(storeImg);
         if (num > 0) {
         if (num > 0) {
             WebAudit webAudit = new WebAudit();
             WebAudit webAudit = new WebAudit();

+ 66 - 137
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/OperationalActivityServiceImpl.java

@@ -6,7 +6,6 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.redisson.api.RBucket;
 import org.redisson.api.RBucket;
@@ -46,8 +45,6 @@ import java.util.Calendar;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Date;
 import java.util.List;
 import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeUnit;
 
 
 
 
@@ -180,86 +177,27 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
         if (activity.getStatus() == null) {
         if (activity.getStatus() == null) {
             activity.setStatus(1);
             activity.setStatus(1);
         }
         }
-        Integer result =0;
-            try {
-                String accessToken = getToken();
-                if (accessToken == null || accessToken.isEmpty()) {
-                    log.error("获取AI服务access_token失败,无法生成促销图片");
-                } else {
-                    // AI登录成功
-                    // 先调用AI进行运营名称和图片的审核,同步调用
-                    String authorization = "Bearer " + accessToken;
-                    
-                    JsonNode auditParam = dto.getAuditParam();
-                    // 如果 auditParam 是字符串,先解析为 JsonNode
-                    if (auditParam != null && auditParam.isTextual()) {
-                        try {
-                            auditParam = objectMapper.readTree(auditParam.asText());
-                        } catch (Exception e) {
-                            log.error("解析 auditParam JSON 字符串失败: {}", auditParam.asText(), e);
-                            auditParam = null;
-                        }
-                    }
-                    
-                    String auditText = (auditParam != null && auditParam.has("text")) ? auditParam.get("text").asText() : "";
-                    JsonNode imagesNode = (auditParam != null) ? auditParam.get("image_urls") : null;
-                    
-                    List<String> imageUrls = (imagesNode != null && imagesNode.isArray())
-                            ? StreamSupport.stream(imagesNode.spliterator(), false)
-                                .map(JsonNode::asText)
-                                .collect(Collectors.toList())
-                            : new ArrayList<>();
-
-                    // 调用同步审核工具类
-                    AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(auditText, imageUrls);
-                    
-                    // 审核结束后,设置审核时间
-                    Date auditTime = new Date();
-                    activity.setAuditStatus(auditResult.isPassed() ? 1 : 2);
-                    activity.setAuditTime(auditTime);
-                    
-                    // 审核通过,根据活动时间自动设置状态
-                    Date currentTime = new Date();
-                    Date startTime = activity.getStartTime();
-                    Date endTime = activity.getEndTime();
-                    
-                    int status;
-                    if (currentTime.before(startTime)) {
-                        // 当前时间在活动开始时间之前,设置为未开始
-                        status = 2;
-                    } else if (currentTime.compareTo(startTime) >= 0 && currentTime.compareTo(endTime) <= 0) {
-                        // 当前时间在活动时间之间,设置为进行中
-                        status = 5;
-                    } else {
-                        // 当前时间在活动结束时间之后,设置为已结束
-                        status = 7;
-                    }
-                    activity.setStatus(status);
-
-                    // 如果审核不通过,记录原因并提前结束
-                    if (!auditResult.isPassed()) {
-                        log.warn("AI内容审核未通过: {}", auditResult.getFailureReason());
-                        failureReasonHolder.set(auditResult.getFailureReason());
-                        activity.setApprovalComments(auditResult.getFailureReason());
-                        activity.setStatus(3);
-//                        return 2; // 返回2表示审核失败
-                    }
+        Integer result = 0;
+        try {
+            String accessToken = getToken();
+            if (accessToken == null || accessToken.isEmpty()) {
+                log.error("获取AI服务 access_token 失败,无法创建活动");
+            } else {
+                AiContentModerationUtil.AuditResult auditResult = runActivityTextModeration(dto.getAuditParam());
+                applyModerationOutcomeToActivity(activity, auditResult);
 
 
-                    result = activityMapper.insert(activity);
-                    
-                    // AI审核后向商户发送通知
-                    if (result > 0) {
-                        try {
-                            sendActivityAuditNotice(activity, auditResult.isPassed(), auditResult.getFailureReason());
-                        } catch (Exception e) {
-                            log.error("发送活动审核通知失败,activityId={}, error={}", activity.getId(), e.getMessage(), e);
-                        }
+                result = activityMapper.insert(activity);
+                if (result > 0) {
+                    try {
+                        sendActivityAuditNotice(activity, auditResult.isPassed(), auditResult.getFailureReason());
+                    } catch (Exception e) {
+                        log.error("发送活动审核通知失败,activityId={}, error={}", activity.getId(), e.getMessage(), e);
                     }
                     }
                 }
                 }
-            } catch (Exception e) {
-                // AI调用失败,也可以添加数据
-                log.error("调用AI服务生成促销图片失败", e);
             }
             }
+        } catch (Exception e) {
+            log.error("创建活动/审核流程异常", e);
+        }
             dto.getActivityTitleImg().setBusinessId(activity.getId());
             dto.getActivityTitleImg().setBusinessId(activity.getId());
             dto.getActivityTitleImg().setImgType(26);
             dto.getActivityTitleImg().setImgType(26);
             imgMapper.insert(dto.getActivityTitleImg());
             imgMapper.insert(dto.getActivityTitleImg());
@@ -312,70 +250,16 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
 
 
         StoreOperationalActivity activity = new StoreOperationalActivity();
         StoreOperationalActivity activity = new StoreOperationalActivity();
         BeanUtils.copyProperties(dto, activity);
         BeanUtils.copyProperties(dto, activity);
-        Integer result =0;
+        Integer result = 0;
         try {
         try {
             String accessToken = getToken();
             String accessToken = getToken();
             if (accessToken == null || accessToken.isEmpty()) {
             if (accessToken == null || accessToken.isEmpty()) {
-                log.error("获取AI服务access_token失败,无法生成促销图片");
+                log.error("获取AI服务 access_token 失败,无法更新活动");
             } else {
             } else {
-                // AI登录成功
-                // 先调用AI进行运营名称和图片的审核,同步调用
                 String authorization = "Bearer " + accessToken;
                 String authorization = "Bearer " + accessToken;
 
 
-                JsonNode auditParam = dto.getAuditParam();
-                // 如果 auditParam 是字符串,先解析为 JsonNode
-                if (auditParam != null && auditParam.isTextual()) {
-                    try {
-                        auditParam = objectMapper.readTree(auditParam.asText());
-                    } catch (Exception e) {
-                        log.error("解析 auditParam JSON 字符串失败: {}", auditParam.asText(), e);
-                        auditParam = null;
-                    }
-                }
-
-                String auditText = (auditParam != null && auditParam.has("text")) ? auditParam.get("text").asText() : "";
-                JsonNode imagesNode = (auditParam != null) ? auditParam.get("image_urls") : null;
-
-                List<String> imageUrls = (imagesNode != null && imagesNode.isArray())
-                        ? StreamSupport.stream(imagesNode.spliterator(), false)
-                        .map(JsonNode::asText)
-                        .collect(Collectors.toList())
-                        : new ArrayList<>();
-
-                // 调用同步审核工具类
-                AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(auditText, imageUrls);
-
-                // 审核结束后,设置审核时间
-                Date auditTime = new Date();
-                activity.setAuditStatus(auditResult.isPassed() ? 1 : 2);
-                activity.setAuditTime(auditTime);
-
-                // 审核通过,根据活动时间自动设置状态
-                Date currentTime = new Date();
-                Date startTime = activity.getStartTime();
-                Date endTime = activity.getEndTime();
-
-                int status;
-                if (currentTime.before(startTime)) {
-                    // 当前时间在活动开始时间之前,设置为未开始
-                    status = 2;
-                } else if (currentTime.compareTo(startTime) >= 0 && currentTime.compareTo(endTime) <= 0) {
-                    // 当前时间在活动时间之间,设置为进行中
-                    status = 5;
-                } else {
-                    // 当前时间在活动结束时间之后,设置为已结束
-                    status = 7;
-                }
-                activity.setStatus(status);
-
-                // 如果审核不通过,记录原因并提前结束
-                if (!auditResult.isPassed()) {
-                    log.warn("AI内容审核未通过: {}", auditResult.getFailureReason());
-                    failureReasonHolder.set(auditResult.getFailureReason());
-                    activity.setApprovalComments(auditResult.getFailureReason());
-                    activity.setStatus(3);
-//                        return 2; // 返回2表示审核失败
-                }
+                AiContentModerationUtil.AuditResult auditResult = runActivityTextModeration(dto.getAuditParam());
+                applyModerationOutcomeToActivity(activity, auditResult);
 
 
                 result = activityMapper.updateById(activity);
                 result = activityMapper.updateById(activity);
                 sendActivityAuditNotice(activity, auditResult.isPassed(), auditResult.getFailureReason());
                 sendActivityAuditNotice(activity, auditResult.isPassed(), auditResult.getFailureReason());
@@ -687,6 +571,51 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
         return activityMapper.updateById(activity);
         return activityMapper.updateById(activity);
     }
     }
 
 
+    /** auditParam 可为 JSON 对象或 JSON 字符串;其中 image_urls 仅前端使用,服务端只审 text */
+    private AiContentModerationUtil.AuditResult runActivityTextModeration(JsonNode rawAuditParam) {
+        JsonNode param = unwrapAuditParam(rawAuditParam);
+        String text = (param != null && param.has("text")) ? param.get("text").asText() : "";
+        return aiContentModerationUtil.auditContent(text, null);
+    }
+
+    private JsonNode unwrapAuditParam(JsonNode auditParam) {
+        if (auditParam == null || !auditParam.isTextual()) {
+            return auditParam;
+        }
+        try {
+            return objectMapper.readTree(auditParam.asText());
+        } catch (Exception e) {
+            log.error("解析 auditParam 失败: {}", auditParam.asText(), e);
+            return null;
+        }
+    }
+
+    /** 写入审核结果、业务时间轴状态;不通过时 status=3 并记录原因 */
+    private void applyModerationOutcomeToActivity(StoreOperationalActivity activity,
+                                                  AiContentModerationUtil.AuditResult auditResult) {
+        activity.setAuditStatus(auditResult.isPassed() ? 1 : 2);
+        activity.setAuditTime(new Date());
+
+        Date now = new Date();
+        Date start = activity.getStartTime();
+        Date end = activity.getEndTime();
+        int timelineStatus;
+        if (now.before(start)) {
+            timelineStatus = 2;
+        } else if (now.compareTo(start) >= 0 && now.compareTo(end) <= 0) {
+            timelineStatus = 5;
+        } else {
+            timelineStatus = 7;
+        }
+        activity.setStatus(timelineStatus);
+
+        if (!auditResult.isPassed()) {
+            failureReasonHolder.set(auditResult.getFailureReason());
+            activity.setApprovalComments(auditResult.getFailureReason());
+            activity.setStatus(3);
+        }
+    }
+
     /**
     /**
      * 发送活动审核通知给商户
      * 发送活动审核通知给商户
      *
      *

+ 66 - 137
alien-store-platform/src/main/java/shop/alien/storeplatform/util/AiContentModerationUtil.java

@@ -1,25 +1,28 @@
 package shop.alien.storeplatform.util;
 package shop.alien.storeplatform.util;
 
 
+import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import com.alibaba.fastjson2.JSONObject;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cloud.context.config.annotation.RefreshScope;
 import org.springframework.cloud.context.config.annotation.RefreshScope;
-import org.springframework.http.*;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
 import org.springframework.util.MultiValueMap;
 import org.springframework.util.StringUtils;
 import org.springframework.util.StringUtils;
 import org.springframework.web.client.RestTemplate;
 import org.springframework.web.client.RestTemplate;
 
 
-import java.util.ArrayList;
 import java.util.LinkedHashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.List;
 import java.util.Set;
 import java.util.Set;
 
 
 /**
 /**
- * 通用图文审核工具类
- * 调用AI图文审核接口审核文本和图片内容
+ * 服务端文本内容审核(moderate-url)。无有效文本则直接通过;图/视频不参与,第二参请传 {@code null}。
  */
  */
 @Slf4j
 @Slf4j
 @Component
 @Component
@@ -27,20 +30,18 @@ import java.util.Set;
 @RequiredArgsConstructor
 @RequiredArgsConstructor
 public class AiContentModerationUtil {
 public class AiContentModerationUtil {
 
 
+    private static final String RESULTS = "results";
+    private static final String TEXT_INPUT_MARKER = "[TEXT INPUT]";
+    private static final String GENERIC_FAILURE = "审核异常";
+
     private final RestTemplate restTemplate;
     private final RestTemplate restTemplate;
 
 
-    /**
-     * AI审核接口地址
-     */
     @Value("${ai.service.moderate-url}")
     @Value("${ai.service.moderate-url}")
     private String moderateUrl;
     private String moderateUrl;
 
 
-    /**
-     * 审核结果类
-     */
     public static class AuditResult {
     public static class AuditResult {
-        private boolean passed;
-        private String failureReason;
+        private final boolean passed;
+        private final String failureReason;
 
 
         public AuditResult(boolean passed, String failureReason) {
         public AuditResult(boolean passed, String failureReason) {
             this.passed = passed;
             this.passed = passed;
@@ -56,157 +57,85 @@ public class AiContentModerationUtil {
         }
         }
     }
     }
 
 
-    /**
-     * 审核文本和图片内容
-     *
-     * @param text 文本内容
-     * @param imageUrls 图片URL列表
-     * @return 审核结果
-     */
     public AuditResult auditContent(String text, List<String> imageUrls) {
     public AuditResult auditContent(String text, List<String> imageUrls) {
-        log.info("开始审核内容:text={}, imageCount={}",
-                text, imageUrls != null ? imageUrls.size() : 0);
-
+        if (imageUrls != null && !imageUrls.isEmpty()) {
+            log.debug("auditContent: 忽略 imageUrls(size={}),请统一传 null", imageUrls.size());
+        }
+        if (!StringUtils.hasText(text)) {
+            log.debug("auditContent: 无有效文本,跳过 moderate");
+            return new AuditResult(true, null);
+        }
         try {
         try {
-            // 如果没有任何内容,直接返回审核通过,避免400错误
-            if (!StringUtils.hasText(text) && (imageUrls == null || imageUrls.isEmpty())) {
-                log.info("审核内容为空,自动跳过审核");
-                return new AuditResult(true, null);
-            }
-
-            // 调用审核接口
-            return callModerateApi(text, imageUrls);
-
+            return requestModerate(text);
         } catch (Exception e) {
         } catch (Exception e) {
-            log.error("审核内容异常", e);
-            return new AuditResult(false, "审核异常");
+            log.error("auditContent 异常", e);
+            return new AuditResult(false, GENERIC_FAILURE);
         }
         }
     }
     }
 
 
-    /**
-     * 调用AI审核接口
-     *
-     * @param text 文本内容
-     * @param imageUrls 图片URL列表
-     * @return 审核结果
-     */
-    private AuditResult callModerateApi(String text, List<String> imageUrls) {
+    private AuditResult requestModerate(String text) {
         try {
         try {
-            // 构建 form-data 请求头
             HttpHeaders headers = new HttpHeaders();
             HttpHeaders headers = new HttpHeaders();
             headers.setContentType(MediaType.MULTIPART_FORM_DATA);
             headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+            MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
+            body.add("text", text);
 
 
-            // 构建 form-data 请求体
-            MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
-            if (StringUtils.hasText(text)) {
-                formData.add("text", text);
-            }
-            if (imageUrls != null) {
-                for (String url : imageUrls) {
-                    if (StringUtils.hasText(url)) {
-                        // 支持多张图片:同一个 key 重复多次
-                        formData.add("image_urls", url);
-                    }
-                }
-            }
-
-            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(formData, headers);
+            ResponseEntity<String> response =
+                    restTemplate.postForEntity(moderateUrl, new HttpEntity<>(body, headers), String.class);
 
 
-            log.info("调用AI审核接口:url={}, text={}, imageCount={}",
-                    moderateUrl, text, imageUrls != null ? imageUrls.size() : 0);
-
-            // 发送请求
-            ResponseEntity<String> response = restTemplate.postForEntity(moderateUrl, requestEntity, String.class);
-
-            if (response.getStatusCode() == HttpStatus.OK) {
-                String responseBody = response.getBody();
-                log.info("AI审核接口响应:{}", responseBody);
-
-                if (StringUtils.hasText(responseBody)) {
-                    JSONObject jsonResponse = JSONObject.parseObject(responseBody);
-                    return parseAuditResult(jsonResponse);
-                } else {
-                    log.error("AI审核接口返回空响应");
-                    return new AuditResult(false, "审核异常");
-                }
-            } else {
-                log.error("AI审核接口调用失败,状态码:{}", response.getStatusCode());
-                return new AuditResult(false, "审核异常");
+            if (response.getStatusCode() != HttpStatus.OK) {
+                log.error("moderate HTTP 非 200: {}", response.getStatusCode());
+                return new AuditResult(false, GENERIC_FAILURE);
             }
             }
-
+            String responseBody = response.getBody();
+            if (!StringUtils.hasText(responseBody)) {
+                log.error("moderate 响应体为空");
+                return new AuditResult(false, GENERIC_FAILURE);
+            }
+            log.debug("moderate 响应: {}", responseBody);
+            return parseModerateResponse(JSONObject.parseObject(responseBody));
         } catch (Exception e) {
         } catch (Exception e) {
-            log.error("调用AI审核接口异常", e);
-            return new AuditResult(false, "审核异常");
+            log.error("调用 moderate 失败", e);
+            return new AuditResult(false, GENERIC_FAILURE);
         }
         }
     }
     }
 
 
-    /**
-     * 解析审核结果
-     *
-     * @param jsonResponse API响应JSON
-     * @return 审核结果
-     */
-    private AuditResult parseAuditResult(JSONObject jsonResponse) {
+    private AuditResult parseModerateResponse(JSONObject json) {
         try {
         try {
-            // API返回格式:
-            // {
-            //     "results": [
-            //         {
-            //             "filename": "[TEXT INPUT]" 或 URL,
-            //             "flagged": true/false,
-            //             "risk_level": "high"/"safe"等,
-            //             "violation_categories": [...],
-            //             "reason": "原因描述"
-            //         }
-            //     ],
-            //     "summary": "处理摘要"
-            // }
-
-            com.alibaba.fastjson2.JSONArray results = jsonResponse.getJSONArray("results");
+            JSONArray results = json.getJSONArray(RESULTS);
             if (results == null || results.isEmpty()) {
             if (results == null || results.isEmpty()) {
-                log.warn("AI审核接口返回结果为空");
-                return new AuditResult(false, "审核异常");
+                log.warn("moderate 返回 results 为空");
+                return new AuditResult(false, GENERIC_FAILURE);
             }
             }
 
 
-            // 检查是否有任何项目被标记为违规
-            Set<String> violationReasons = new LinkedHashSet<>();
-//            List<String> violationReasons = new ArrayList<>();
-            boolean hasViolations = false;
-
+            Set<String> reasons = new LinkedHashSet<>();
             for (int i = 0; i < results.size(); i++) {
             for (int i = 0; i < results.size(); i++) {
-                JSONObject result = results.getJSONObject(i);
-                if (result != null) {
-                    Boolean flagged = result.getBoolean("flagged");
-                    String filename = result.getString("filename");
-
-                    if (flagged != null && flagged) {
-                        hasViolations = true;
-                        // 根据filename判断是文本还是图片违规
-                        if ("[TEXT INPUT]".equals(filename)) {
-                            violationReasons.add("含违规词汇");
-                        } else if (StringUtils.hasText(filename) && filename.startsWith("http")) {
-                            violationReasons.add("图片内容违规");
-                        } else {
-                            violationReasons.add("内容违规");
-                        }
-                    }
+                JSONObject item = results.getJSONObject(i);
+                if (item == null || !Boolean.TRUE.equals(item.getBoolean("flagged"))) {
+                    continue;
                 }
                 }
+                reasons.add(flaggedItemReason(item.getString("filename")));
             }
             }
 
 
-            if (hasViolations) {
-                // 有违规内容,审核失败
-                String failureReason = String.join("; ", violationReasons);
-                log.warn("AI审核失败:{}", failureReason);
-                return new AuditResult(false, failureReason);
-            } else {
-                // 所有内容都安全,审核通过
-                log.info("AI审核通过:所有内容安全");
+            if (reasons.isEmpty()) {
                 return new AuditResult(true, null);
                 return new AuditResult(true, null);
             }
             }
-
+            String joined = String.join("; ", reasons);
+            log.warn("moderate 命中违规: {}", joined);
+            return new AuditResult(false, joined);
         } catch (Exception e) {
         } catch (Exception e) {
-            log.error("解析审核结果异常", e);
-            return new AuditResult(false, "审核异常");
+            log.error("解析 moderate 响应失败", e);
+            return new AuditResult(false, GENERIC_FAILURE);
+        }
+    }
+
+    private static String flaggedItemReason(String filename) {
+        if (TEXT_INPUT_MARKER.equals(filename)) {
+            return "含违规词汇";
+        }
+        if (StringUtils.hasText(filename) && filename.startsWith("http")) {
+            return "图片内容违规";
         }
         }
+        return "内容违规";
     }
     }
-}
+}

+ 1 - 1
alien-store/src/main/java/shop/alien/store/aspect/AiAuditAspect.java

@@ -79,7 +79,7 @@ public class AiAuditAspect {
     private boolean performAiAudit(String payload, List<String> imageUrls, Object[] args) {
     private boolean performAiAudit(String payload, List<String> imageUrls, Object[] args) {
         try {
         try {
             // token 目前仅预留,如后续需要可添加到header或payload
             // token 目前仅预留,如后续需要可添加到header或payload
-            AiContentModerationUtil.AuditResult result = aiContentModerationUtil.auditContent(payload, imageUrls);
+            AiContentModerationUtil.AuditResult result = aiContentModerationUtil.auditContent(payload, null);
             if (result == null) {
             if (result == null) {
                 log.warn("AI审核返回为空,视为未通过");
                 log.warn("AI审核返回为空,视为未通过");
                 applyFailureReason(args, "审核异常");
                 applyFailureReason(args, "审核异常");

+ 4 - 15
alien-store/src/main/java/shop/alien/store/controller/LifeUserDynamicsController.java

@@ -36,9 +36,6 @@ public class LifeUserDynamicsController {
     @Autowired
     @Autowired
     private TextModerationUtil textModerationUtil;
     private TextModerationUtil textModerationUtil;
 
 
-    @Autowired
-    private ImageModerationUtil imageModerationUtil;
-
     @ApiOperation("社区列表")
     @ApiOperation("社区列表")
     @ApiOperationSupport(order = 1)
     @ApiOperationSupport(order = 1)
     @ApiImplicitParams({@ApiImplicitParam(name = "page", value = "分页页数", dataType = "String", paramType = "query", required = true),
     @ApiImplicitParams({@ApiImplicitParam(name = "page", value = "分页页数", dataType = "String", paramType = "query", required = true),
@@ -95,7 +92,6 @@ public class LifeUserDynamicsController {
             return R.data(3);
             return R.data(3);
         }*/
         }*/
         try {
         try {
-
             List<String> servicesList = Lists.newArrayList();
             List<String> servicesList = Lists.newArrayList();
             servicesList.add(TextReviewServiceEnum.COMMENT_DETECTION_PRO.getService());
             servicesList.add(TextReviewServiceEnum.COMMENT_DETECTION_PRO.getService());
             servicesList.add(TextReviewServiceEnum.LLM_QUERY_MODERATION.getService());
             servicesList.add(TextReviewServiceEnum.LLM_QUERY_MODERATION.getService());
@@ -103,21 +99,14 @@ public class LifeUserDynamicsController {
             if ("high".equals(textCheckResult.getRiskLevel())) {
             if ("high".equals(textCheckResult.getRiskLevel())) {
                 return R.data(2);
                 return R.data(2);
             }
             }
-
-            List<String> imgServicesList = Lists.newArrayList();
-            imgServicesList.add(ImageReviewServiceEnum.TONALITY_IMPROVE.getService());
-            imgServicesList.add(ImageReviewServiceEnum.AIGC_CHECK.getService());
-            ImageModerationResultVO response = imageModerationUtil.productPublishCheck(lifeUserDynamics.getImagePath(),imgServicesList);
-            if ("high".equals(response.getRiskLevel())) {
-                return R.data(3);
-            }
-            lifeUserDynamics.setUpdatedTime(new Date());
+            // 配图由前端直连AI审核,服务端只做动态正文文本审核(ImageModerationUtil已去掉)
+            lifeUserDynamics.setUpdatedTime(new java.util.Date());
             int cnt = lifeUserDynamicsService.addOrUpdateStore(lifeUserDynamics);
             int cnt = lifeUserDynamicsService.addOrUpdateStore(lifeUserDynamics);
             if (cnt == 0) {
             if (cnt == 0) {
                 return R.data(1);
                 return R.data(1);
             }
             }
-        } catch (Exception e) {
-            log.error("LifeUserDynamicsController.addOrUpdate ERROR Msg={}", e.getMessage());
+        } catch (Exception ex) {
+            log.error("LifeUserDynamicsController.addOrUpdate ERROR Msg={}", ex.getMessage());
             return R.data(1);
             return R.data(1);
         }
         }
         return R.data(0);
         return R.data(0);

+ 4 - 4
alien-store/src/main/java/shop/alien/store/controller/StoreCuisineController.java

@@ -156,9 +156,9 @@ public class StoreCuisineController {
                 }
                 }
             }
             }
 
 
-            // 执行AI审核
+            // AI:菜品图前端审;auditContent 仅 textContent,第二参固定 null(imageUrls 仍用于是否进入审核分支)
             if (StringUtils.isNotEmpty(textContent.toString()) || imageUrls.size() > 0) {
             if (StringUtils.isNotEmpty(textContent.toString()) || imageUrls.size() > 0) {
-                AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(textContent.toString(), imageUrls);
+                AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(textContent.toString(), null);
                 
                 
                 LambdaUpdateWrapper<StoreCuisine> auditUpdateWrapper = new LambdaUpdateWrapper<>();
                 LambdaUpdateWrapper<StoreCuisine> auditUpdateWrapper = new LambdaUpdateWrapper<>();
                 auditUpdateWrapper.eq(StoreCuisine::getId, savedCuisine.getId());
                 auditUpdateWrapper.eq(StoreCuisine::getId, savedCuisine.getId());
@@ -283,9 +283,9 @@ public class StoreCuisineController {
                 }
                 }
             }
             }
 
 
-            // 执行AI审核
+            // AI:菜品图前端审;auditContent 仅 textContent,第二参固定 null(imageUrls 仍用于是否进入审核分支)
             if (StringUtils.isNotEmpty(textContent.toString()) || imageUrls.size() > 0) {
             if (StringUtils.isNotEmpty(textContent.toString()) || imageUrls.size() > 0) {
-                AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(textContent.toString(), imageUrls);
+                AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(textContent.toString(), null);
 //                boolean allPassed = (auditResult != null);
 //                boolean allPassed = (auditResult != null);
                 
                 
                 LambdaUpdateWrapper<StoreCuisine> auditUpdateWrapper = new LambdaUpdateWrapper<>();
                 LambdaUpdateWrapper<StoreCuisine> auditUpdateWrapper = new LambdaUpdateWrapper<>();

+ 2 - 46
alien-store/src/main/java/shop/alien/store/controller/StoreImgController.java

@@ -16,7 +16,6 @@ import shop.alien.store.service.StoreOfficialAlbumService;
 import shop.alien.store.util.GroupConstant;
 import shop.alien.store.util.GroupConstant;
 import shop.alien.store.util.ai.AiContentModerationUtil;
 import shop.alien.store.util.ai.AiContentModerationUtil;
 
 
-import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.concurrent.*;
 import java.util.concurrent.*;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
@@ -40,7 +39,6 @@ public class StoreImgController {
     private final StoreImgService storeImgService;
     private final StoreImgService storeImgService;
     private final StoreInfoService storeInfoService;
     private final StoreInfoService storeInfoService;
     private final StoreOfficialAlbumService storeOfficialAlbumService;
     private final StoreOfficialAlbumService storeOfficialAlbumService;
-    private final AiContentModerationUtil aiContentModerationUtil;
     @Qualifier("imgAuditExecutor")
     @Qualifier("imgAuditExecutor")
     private final ExecutorService imgAuditExecutor;
     private final ExecutorService imgAuditExecutor;
 
 
@@ -92,12 +90,9 @@ public class StoreImgController {
         }
         }
         Integer imgType = storeImgInfoVo.getImgType();
         Integer imgType = storeImgInfoVo.getImgType();
         Integer storeId = storeImgInfoVo.getStoreId();
         Integer storeId = storeImgInfoVo.getStoreId();
-        List<String> imageUrls = storeImgList.stream()
-                .map(StoreImg::getImgUrl)
-                .collect(Collectors.toList());
-        // 审核与环境相册查询并行,减少总耗时(imgType==4 时相册查询与审核同时进行)
+        // 图片由前端直连 AI 审核;服务端不再对门店图调用 moderate,避免保存被误拦截(仅与环境相册查询并行占位)
         CompletableFuture<AiContentModerationUtil.AuditResult> auditFuture = CompletableFuture
         CompletableFuture<AiContentModerationUtil.AuditResult> auditFuture = CompletableFuture
-                .supplyAsync(() -> auditImagesInParallel(imageUrls), imgAuditExecutor);
+                .completedFuture(new AiContentModerationUtil.AuditResult(true, null));
         CompletableFuture<List<StoreOfficialAlbum>> albumFuture = (imgType != null && imgType == 4)
         CompletableFuture<List<StoreOfficialAlbum>> albumFuture = (imgType != null && imgType == 4)
                 ? CompletableFuture.supplyAsync(() -> storeOfficialAlbumService.lambdaQuery()
                 ? CompletableFuture.supplyAsync(() -> storeOfficialAlbumService.lambdaQuery()
                         .eq(StoreOfficialAlbum::getStoreId, storeId)
                         .eq(StoreOfficialAlbum::getStoreId, storeId)
@@ -346,43 +341,4 @@ public class StoreImgController {
         return R.data(storeImgService.getByCover(storeId, imgType));
         return R.data(storeImgService.getByCover(storeId, imgType));
     }
     }
 
 
-    /**
-     * 使用共享线程池并行审核,每张图片单独审核,线程数不超过 IMG_AUDIT_MAX_PARALLEL
-     *
-     * @param imageUrls 图片URL列表
-     * @return 合并后的审核结果,任一张不通过则整体不通过
-     */
-    private AiContentModerationUtil.AuditResult auditImagesInParallel(List<String> imageUrls) {
-        if (imageUrls == null || imageUrls.isEmpty()) {
-            return new AiContentModerationUtil.AuditResult(true, null);
-        }
-        try {
-            List<CompletableFuture<AiContentModerationUtil.AuditResult>> futures = imageUrls.stream()
-                    .map(url -> CompletableFuture.supplyAsync(
-                            () -> aiContentModerationUtil.auditContent(null, Collections.singletonList(url)),
-                            imgAuditExecutor))
-                    .collect(Collectors.toList());
-            String firstFailureReason = null;
-            for (CompletableFuture<AiContentModerationUtil.AuditResult> future : futures) {
-                AiContentModerationUtil.AuditResult result = future.get(30, TimeUnit.SECONDS);
-                if (!result.isPassed() && firstFailureReason == null) {
-                    firstFailureReason = result.getFailureReason();
-                }
-            }
-            return firstFailureReason != null
-                    ? new AiContentModerationUtil.AuditResult(false, firstFailureReason)
-                    : new AiContentModerationUtil.AuditResult(true, null);
-        } catch (TimeoutException e) {
-            log.error("图片审核超时", e);
-            return new AiContentModerationUtil.AuditResult(false, "审核超时");
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            log.error("图片审核被中断", e);
-            return new AiContentModerationUtil.AuditResult(false, "审核被中断");
-        } catch (ExecutionException e) {
-            log.error("图片审核异常", e);
-            return new AiContentModerationUtil.AuditResult(false, "审核异常");
-        }
-    }
-
 }
 }

+ 4 - 4
alien-store/src/main/java/shop/alien/store/controller/StorePriceController.java

@@ -185,9 +185,9 @@ public class StorePriceController {
                 }
                 }
             }
             }
 
 
-            // 执行AI审核
+            // AI:价目图前端审;auditContent 仅 textContent,第二参固定 null
             if (StringUtils.isNotEmpty(textContent.toString()) || imageUrls.size() > 0) {
             if (StringUtils.isNotEmpty(textContent.toString()) || imageUrls.size() > 0) {
-                AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(textContent.toString(), imageUrls);
+                AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(textContent.toString(), null);
 //                boolean allPassed = (auditResult != null);
 //                boolean allPassed = (auditResult != null);
                 
                 
                 LambdaUpdateWrapper<StorePrice> auditUpdateWrapper = new LambdaUpdateWrapper<>();
                 LambdaUpdateWrapper<StorePrice> auditUpdateWrapper = new LambdaUpdateWrapper<>();
@@ -314,9 +314,9 @@ public class StorePriceController {
                 }
                 }
             }
             }
 
 
-            // 执行AI审核
+            // AI:价目图前端审;auditContent 仅 textContent,第二参固定 null
             if (StringUtils.isNotEmpty(textContent.toString()) || imageUrls.size() > 0) {
             if (StringUtils.isNotEmpty(textContent.toString()) || imageUrls.size() > 0) {
-                AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(textContent.toString(), imageUrls);
+                AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(textContent.toString(), null);
 //                boolean allPassed = (auditResult != null);
 //                boolean allPassed = (auditResult != null);
                 
                 
                 LambdaUpdateWrapper<StorePrice> auditUpdateWrapper = new LambdaUpdateWrapper<>();
                 LambdaUpdateWrapper<StorePrice> auditUpdateWrapper = new LambdaUpdateWrapper<>();

+ 2 - 26
alien-store/src/main/java/shop/alien/store/service/impl/BarPerformanceAuditServiceImpl.java

@@ -10,9 +10,6 @@ import shop.alien.mapper.BarPerformanceMapper;
 import shop.alien.store.service.BarPerformanceAuditService;
 import shop.alien.store.service.BarPerformanceAuditService;
 import shop.alien.store.util.ai.AiContentModerationUtil;
 import shop.alien.store.util.ai.AiContentModerationUtil;
 
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
 /**
  * 酒吧演出审核服务实现类
  * 酒吧演出审核服务实现类
  * 负责异步执行AI内容审核
  * 负责异步执行AI内容审核
@@ -53,30 +50,9 @@ public class BarPerformanceAuditServiceImpl implements BarPerformanceAuditServic
                 textContent.append(barPerformance.getPerformanceNotice());
                 textContent.append(barPerformance.getPerformanceNotice());
             }
             }
             
             
-            // 组装图片URL列表
-            List<String> imageUrls = new ArrayList<>();
-            if (StringUtils.isNotEmpty(barPerformance.getPerformancePoster())) {
-                // 演出海报(可能有多张,逗号分隔)
-                String[] posterUrls = barPerformance.getPerformancePoster().split(",");
-                for (String url : posterUrls) {
-                    if (StringUtils.isNotEmpty(url.trim())) {
-                        imageUrls.add(url.trim());
-                    }
-                }
-            }
-            if (StringUtils.isNotEmpty(barPerformance.getPerformanceDetail())) {
-                // 演出详情图片(可能有多张,逗号分隔)
-                String[] detailUrls = barPerformance.getPerformanceDetail().split(",");
-                for (String url : detailUrls) {
-                    if (StringUtils.isNotEmpty(url.trim())) {
-                        imageUrls.add(url.trim());
-                    }
-                }
-            }
-            
-            // 调用AI审核接口
+            // 配图前端审;auditContent 第二参固定 null
             AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(
             AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(
-                    textContent.toString().trim(), imageUrls);
+                    textContent.toString().trim(), null);
             
             
             // 根据AI审核结果更新审核状态和拒绝原因
             // 根据AI审核结果更新审核状态和拒绝原因
             BarPerformance auditUpdate = new BarPerformance();
             BarPerformance auditUpdate = new BarPerformance();

+ 2 - 20
alien-store/src/main/java/shop/alien/store/service/impl/BarPerformanceServiceImpl.java

@@ -202,27 +202,9 @@ public class BarPerformanceServiceImpl implements BarPerformanceService {
             throw new IllegalArgumentException("图文详情文字不能超过300个字符");
             throw new IllegalArgumentException("图文详情文字不能超过300个字符");
         }
         }
 
 
-        // 8. 图文内容审核(调用AI内容审核接口)- 新增或更新时都必须进行
-        // - 文本:名称 + 风格 + 演出详情文字 + 演出须知(文本字段,不包含图片URL)
-        // - 图片:海报URL(如果有) + 图文详情图片URL(如果有)
+        // 8. AI:海报/详情图前端审;auditContent 仅传文案,第二参固定 null
         String moderationText = buildModerationText(barPerformance);
         String moderationText = buildModerationText(barPerformance);
-        List<String> imageUrls = new ArrayList<>();
-        // 添加演出海报到图片审核列表
-        if (StringUtils.isNotEmpty(barPerformance.getPerformancePoster())) {
-            imageUrls.add(barPerformance.getPerformancePoster());
-        }
-        // 添加图文详情图片到图片审核列表(performanceDetail是图片URL列表,只做图片审核)
-        if (StringUtils.isNotEmpty(barPerformance.getPerformanceDetail())) {
-            String[] detailImages = barPerformance.getPerformanceDetail().split(",");
-            for (String imageUrl : detailImages) {
-                if (StringUtils.isNotEmpty(imageUrl.trim())) {
-                    imageUrls.add(imageUrl.trim());
-                }
-            }
-        }
-        // 调用AI内容审核接口,文本和图片分开审核
-        // 审核失败时保存数据但标记为审核拒绝状态
-        AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(moderationText, imageUrls);
+        AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(moderationText, null);
         if (auditResult == null || !auditResult.isPassed()) {
         if (auditResult == null || !auditResult.isPassed()) {
             // AI审核失败,设置审核状态为2(审核拒绝)并记录拒绝原因,但仍然保存数据
             // AI审核失败,设置审核状态为2(审核拒绝)并记录拒绝原因,但仍然保存数据
             String failureReason = (auditResult != null && StringUtils.isNotEmpty(auditResult.getFailureReason()))
             String failureReason = (auditResult != null && StringUtils.isNotEmpty(auditResult.getFailureReason()))

+ 3 - 5
alien-store/src/main/java/shop/alien/store/service/impl/CommonRatingServiceImpl.java

@@ -141,12 +141,10 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
             List<String> imageUrls = urlCategoryMap.get("image");
             List<String> imageUrls = urlCategoryMap.get("image");
             List<String> videoUrls = urlCategoryMap.get("video");
             List<String> videoUrls = urlCategoryMap.get("video");
             
             
-            // 3. 内容审核(基础审核,必须通过)
-            // 文本、图片、视频必须符合法律法规要求
+            // 3. 内容审核:评价正文走服务端 moderate;配图/视频由前端直连 AI(util 忽略图,视频审核 util 恒通过)
             AiContentModerationUtil.AuditResult contentAuditResult = new AiContentModerationUtil.AuditResult(true, "");
             AiContentModerationUtil.AuditResult contentAuditResult = new AiContentModerationUtil.AuditResult(true, "");
-            // 只要有内容或图片,就必须进行内容审核
             if (StringUtils.isNotEmpty(commonRating.getContent()) || !imageUrls.isEmpty()) {
             if (StringUtils.isNotEmpty(commonRating.getContent()) || !imageUrls.isEmpty()) {
-                contentAuditResult = aiContentModerationUtil.auditContent(commonRating.getContent(), imageUrls);
+                contentAuditResult = aiContentModerationUtil.auditContent(commonRating.getContent(), null);
             }
             }
             
             
             // 内容审核不通过,直接判定为审核不通过
             // 内容审核不通过,直接判定为审核不通过
@@ -310,7 +308,7 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
                 }
                 }
             }
             }
             
             
-            // 7. 如果有视频,进行异步视频审核
+            // 7. 视频由前端审;此处异步分支仍更新状态但 auditVideos 已不再请求后端视频接口(见 AiVideoModerationUtil)
             if (!videoUrls.isEmpty()) {
             if (!videoUrls.isEmpty()) {
                 CompletableFuture.runAsync(() -> {
                 CompletableFuture.runAsync(() -> {
                     AiVideoModerationUtil.VideoAuditResult videoAuditResult = null;
                     AiVideoModerationUtil.VideoAuditResult videoAuditResult = null;

+ 10 - 21
alien-store/src/main/java/shop/alien/store/service/impl/StoreClockInServiceImpl.java

@@ -321,7 +321,7 @@ public class StoreClockInServiceImpl extends ServiceImpl<StoreClockInMapper, Sto
         wrapper.eq(StoreClockIn::getId, id);
         wrapper.eq(StoreClockIn::getId, id);
         int updateResult = storeClockInMapper.update(null, wrapper);
         int updateResult = storeClockInMapper.update(null, wrapper);
         
         
-        // 2. 异步调用AI接口审核图片
+        // 2. 异步:配图由前端直连 AI;服务端仅审核 maybeAiContent 文字(无文字则视为通过)
         StoreClockIn storeClockIn = storeClockInMapper.selectById(id);
         StoreClockIn storeClockIn = storeClockInMapper.selectById(id);
         if (storeClockIn == null) {
         if (storeClockIn == null) {
             log.warn("打卡记录不存在,id={}", id);
             log.warn("打卡记录不存在,id={}", id);
@@ -338,23 +338,13 @@ public class StoreClockInServiceImpl extends ServiceImpl<StoreClockInMapper, Sto
         final Integer clockInId = id;
         final Integer clockInId = id;
         final Integer userId = storeClockIn.getUserId();
         final Integer userId = storeClockIn.getUserId();
         
         
-        List<String> imgList = new ArrayList<>();
-        if (StringUtils.isNotBlank(img)) {
-            String[] imgArray = img.split(",");
-            for (String imgUrl : imgArray) {
-                String trimmed = imgUrl.trim();
-                if (StringUtils.isNotBlank(trimmed)) {
-                    imgList.add(trimmed);
-                }
-            }
-        }
-        
-        // 异步执行AI审核任务
+        // 异步执行:仅文本走 AiContentModerationUtil(图片由前端审,不再传 imgList)
         CompletableFuture.runAsync(() -> {
         CompletableFuture.runAsync(() -> {
             AiContentModerationUtil.AuditResult imgAuditResult = null;
             AiContentModerationUtil.AuditResult imgAuditResult = null;
             try {
             try {
+                String textForAudit = StringUtils.isNotBlank(aiContent) ? aiContent.trim() : null;
                 imgAuditResult = CompletableFuture.supplyAsync(
                 imgAuditResult = CompletableFuture.supplyAsync(
-                        () -> aiContentModerationUtil.auditContent(null, imgList),
+                        () -> aiContentModerationUtil.auditContent(textForAudit, null),
                         imgAuditExecutor
                         imgAuditExecutor
                 ).get();
                 ).get();
 
 
@@ -363,26 +353,25 @@ public class StoreClockInServiceImpl extends ServiceImpl<StoreClockInMapper, Sto
                 updateWrapper.eq(StoreClockIn::getId, clockInId);
                 updateWrapper.eq(StoreClockIn::getId, clockInId);
                 
                 
                 if (imgAuditResult != null && imgAuditResult.isPassed()) {
                 if (imgAuditResult != null && imgAuditResult.isPassed()) {
-                    // 审核通过,修改状态,打卡可显示在列表中
+                    // 配文文字审核通过(配图由前端审)
                     updateWrapper.set(StoreClockIn::getCheckFlag, 2); // 2-审核通过
                     updateWrapper.set(StoreClockIn::getCheckFlag, 2); // 2-审核通过
                     updateWrapper.set(StoreClockIn::getReason, null); // 清除拒绝原因
                     updateWrapper.set(StoreClockIn::getReason, null); // 清除拒绝原因
                     storeClockInMapper.update(null, updateWrapper);
                     storeClockInMapper.update(null, updateWrapper);
-                    log.info("打卡图片审核通过,打卡ID:{}", clockInId);
+                    log.info("打卡配文审核通过,打卡ID:{}", clockInId);
                 } else {
                 } else {
-                    // 审核拒绝,修改状态,打卡不可显示在列表中,通知用户,打卡审核不通过
                     String rejectReason = (imgAuditResult != null && StringUtils.isNotEmpty(imgAuditResult.getFailureReason()))
                     String rejectReason = (imgAuditResult != null && StringUtils.isNotEmpty(imgAuditResult.getFailureReason()))
                             ? imgAuditResult.getFailureReason()
                             ? imgAuditResult.getFailureReason()
-                            : "图片内容不符合规范";
-                    updateWrapper.set(StoreClockIn::getCheckFlag, 3); // 2-审核完成(但审核未通过)
+                            : "文字内容不符合规范";
+                    updateWrapper.set(StoreClockIn::getCheckFlag, 3); // 3-审核拒绝
                     updateWrapper.set(StoreClockIn::getReason, rejectReason); // 记录拒绝原因
                     updateWrapper.set(StoreClockIn::getReason, rejectReason); // 记录拒绝原因
                     storeClockInMapper.update(null, updateWrapper);
                     storeClockInMapper.update(null, updateWrapper);
-                    log.warn("打卡图片审核拒绝,打卡ID:{},原因:{}", clockInId, rejectReason);
+                    log.warn("打卡配文审核拒绝,打卡ID:{},原因:{}", clockInId, rejectReason);
                     
                     
                     // 通知用户打卡审核不通过
                     // 通知用户打卡审核不通过
                     sendAuditRejectNotification(clockInId, userId, phoneId, rejectReason);
                     sendAuditRejectNotification(clockInId, userId, phoneId, rejectReason);
                 }
                 }
             } catch (Exception e) {
             } catch (Exception e) {
-                log.error("图片审核接口调用异常,打卡ID:{}", clockInId, e);
+                log.error("打卡配文审核异常,打卡ID:{}", clockInId, e);
                 // 审核异常时,保持审核中状态,记录错误原因
                 // 审核异常时,保持审核中状态,记录错误原因
                 LambdaUpdateWrapper<StoreClockIn> updateWrapper = new LambdaUpdateWrapper<>();
                 LambdaUpdateWrapper<StoreClockIn> updateWrapper = new LambdaUpdateWrapper<>();
                 updateWrapper.eq(StoreClockIn::getId, clockInId);
                 updateWrapper.eq(StoreClockIn::getId, clockInId);

+ 3 - 5
alien-store/src/main/java/shop/alien/store/service/impl/StoreRenovationRequirementServiceImpl.java

@@ -184,12 +184,10 @@ public class StoreRenovationRequirementServiceImpl extends ServiceImpl<StoreReno
             webSocketProcess.sendMessage("store_" + storeUser.getPhone(), JSONObject.from(websocketVo).toJSONString());
             webSocketProcess.sendMessage("store_" + storeUser.getPhone(), JSONObject.from(websocketVo).toJSONString());
 
 
 
 
-            // TODO 审核文本和图片内容是否违规
-            // 一次遍历完成分类,避免多次流式处理
+            // 附件图/视频由前端直连 AI;服务端仅审 detailedRequirement 文本(图片 URL 在 util 内不传 moderate)
             Map<String, List<String>> urlCategoryMap = classifyUrls(dto.getAttachmentUrls());
             Map<String, List<String>> urlCategoryMap = classifyUrls(dto.getAttachmentUrls());
             List<String> videoUrls = urlCategoryMap.get("video");
             List<String> videoUrls = urlCategoryMap.get("video");
-            // 1.调用文本+图片审核接口 ai为同步接口
-            AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(requirement.getDetailedRequirement(), urlCategoryMap.get("image"));
+            AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(requirement.getDetailedRequirement(), null);
             if (!auditResult.isPassed()) {
             if (!auditResult.isPassed()) {
                 requirement.setAuditStatus(2);
                 requirement.setAuditStatus(2);
                 requirement.setAuditReason(auditResult.getFailureReason());
                 requirement.setAuditReason(auditResult.getFailureReason());
@@ -208,7 +206,7 @@ public class StoreRenovationRequirementServiceImpl extends ServiceImpl<StoreReno
                         sendAuditNotification(storeUser.getPhone(), 1, null, "text_image");
                         sendAuditNotification(storeUser.getPhone(), 1, null, "text_image");
                     }
                     }
                 } else {
                 } else {
-                    // 异步调用视频审核接口,图片审核通过后调用(核心优化)
+                    // 有视频时走异步分支;视频不在服务端审(前端已审),auditVideos 恒通过
                     // 保存storeUser信息供异步任务使用
                     // 保存storeUser信息供异步任务使用
                     final StoreUser finalStoreUser = storeUser;
                     final StoreUser finalStoreUser = storeUser;
                     CompletableFuture.runAsync(() -> {
                     CompletableFuture.runAsync(() -> {

+ 7 - 12
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffAuditAsyncService.java

@@ -85,40 +85,35 @@ public class StoreStaffAuditAsyncService {
                 textContent.append(storeStaffConfig.getProficientProjects());
                 textContent.append(storeStaffConfig.getProficientProjects());
             }
             }
 
 
-            List<String> imageUrls = new ArrayList<>();
             List<String> videoUrls = new ArrayList<>();
             List<String> videoUrls = new ArrayList<>();
-
-            if (StringUtils.isNotEmpty(storeStaffConfig.getStaffImage())) {
-                imageUrls.add(storeStaffConfig.getStaffImage());
+            if (StringUtils.isNotEmpty(storeStaffConfig.getStaffImage()) && isVideoUrl(storeStaffConfig.getStaffImage())) {
+                videoUrls.add(storeStaffConfig.getStaffImage());
             }
             }
             if (StringUtils.isNotEmpty(storeStaffConfig.getBackgroundUrl())) {
             if (StringUtils.isNotEmpty(storeStaffConfig.getBackgroundUrl())) {
                 String[] urls = storeStaffConfig.getBackgroundUrl().split(",");
                 String[] urls = storeStaffConfig.getBackgroundUrl().split(",");
                 for (String url : urls) {
                 for (String url : urls) {
                     if (StringUtils.isNotEmpty(url.trim())) {
                     if (StringUtils.isNotEmpty(url.trim())) {
                         String trimmedUrl = url.trim();
                         String trimmedUrl = url.trim();
-                        // 判断是视频还是图片
                         if (isVideoUrl(trimmedUrl)) {
                         if (isVideoUrl(trimmedUrl)) {
                             videoUrls.add(trimmedUrl);
                             videoUrls.add(trimmedUrl);
-                        } else {
-                            imageUrls.add(trimmedUrl);
                         }
                         }
                     }
                     }
                 }
                 }
             }
             }
 
 
-            // 1. 审核文本和图片
+            // 1. auditContent 仅文本;配图前端审(第二参固定 null)
             AiContentModerationUtil.AuditResult textImageAuditResult = aiContentModerationUtil.auditContent(
             AiContentModerationUtil.AuditResult textImageAuditResult = aiContentModerationUtil.auditContent(
-                    textContent.toString().trim(), imageUrls
+                    textContent.toString().trim(), null
             );
             );
 
 
-            // 2. 审核视频(如果有
+            // 2. 视频不在服务端审(前端已审
             AiVideoModerationUtil.VideoAuditResult videoAuditResult = null;
             AiVideoModerationUtil.VideoAuditResult videoAuditResult = null;
             if (!videoUrls.isEmpty()) {
             if (!videoUrls.isEmpty()) {
-                log.info("开始审核视频,视频数量:{}", videoUrls.size());
+                log.info("服务端跳过视频审核(前端已审),视频数量:{}", videoUrls.size());
                 videoAuditResult = aiVideoModerationUtil.auditVideos(videoUrls);
                 videoAuditResult = aiVideoModerationUtil.auditVideos(videoUrls);
             }
             }
 
 
-            // 3. 综合审核结果:文本图片审核和视频审核都必须通过
+            // 3. 综合结果:仅文本不通过会拦(图/视频不再导致失败)
             boolean allPassed = (textImageAuditResult != null && textImageAuditResult.isPassed()) &&
             boolean allPassed = (textImageAuditResult != null && textImageAuditResult.isPassed()) &&
                                 (videoAuditResult == null || videoAuditResult.isPassed());
                                 (videoAuditResult == null || videoAuditResult.isPassed());
 
 

+ 3 - 4
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffReviewServiceImpl.java

@@ -70,13 +70,12 @@ public class StoreStaffReviewServiceImpl extends ServiceImpl<StoreStaffReviewMap
         // 创建评价
         // 创建评价
         StoreStaffReview review = buildReviewFromDto(reviewDto);
         StoreStaffReview review = buildReviewFromDto(reviewDto);
         boolean success = this.save(review);
         boolean success = this.save(review);
-        // AI审核
-        // 一次遍历完成分类,避免多次流式处理
+        // AI:评价图/视频前端直连审;服务端仅审 reviewContent 文本(图列表在 util 内忽略)
         Map<String, List<String>> urlCategoryMap = StoreRenovationRequirementServiceImpl.classifyUrls(reviewDto.getReviewImages());
         Map<String, List<String>> urlCategoryMap = StoreRenovationRequirementServiceImpl.classifyUrls(reviewDto.getReviewImages());
 
 
         AiContentModerationUtil.AuditResult auditResult = new AiContentModerationUtil.AuditResult(true, "");
         AiContentModerationUtil.AuditResult auditResult = new AiContentModerationUtil.AuditResult(true, "");
         if( StringUtils.isNotEmpty(reviewDto.getReviewContent()) || urlCategoryMap.get("image").size() > 0){
         if( StringUtils.isNotEmpty(reviewDto.getReviewContent()) || urlCategoryMap.get("image").size() > 0){
-            auditResult = aiContentModerationUtil.auditContent(reviewDto.getReviewContent(), urlCategoryMap.get("image"));
+            auditResult = aiContentModerationUtil.auditContent(reviewDto.getReviewContent(), null);
         }
         }
         if (!auditResult.isPassed()) {
         if (!auditResult.isPassed()) {
             // 审核不通过
             // 审核不通过
@@ -85,10 +84,10 @@ public class StoreStaffReviewServiceImpl extends ServiceImpl<StoreStaffReviewMap
             staffReview.setAuditReason(auditResult.getFailureReason());
             staffReview.setAuditReason(auditResult.getFailureReason());
             this.saveOrUpdate(staffReview);
             this.saveOrUpdate(staffReview);
         } else{
         } else{
+            // 视频审核由前端完成;此处 auditVideos 恒通过,仅保留异步更新员工分等原流程
             CompletableFuture.runAsync(() -> {
             CompletableFuture.runAsync(() -> {
                 AiVideoModerationUtil.VideoAuditResult videoAuditResult = null;
                 AiVideoModerationUtil.VideoAuditResult videoAuditResult = null;
                 try {
                 try {
-                    // 调用审核接口,增加超时控制(避免接口挂死)
                     videoAuditResult = CompletableFuture.supplyAsync(
                     videoAuditResult = CompletableFuture.supplyAsync(
                             () -> aiVideoModerationUtil.auditVideos(urlCategoryMap.get("video")),
                             () -> aiVideoModerationUtil.auditVideos(urlCategoryMap.get("video")),
                             commonVideoTaskExecutor
                             commonVideoTaskExecutor

+ 74 - 130
alien-store/src/main/java/shop/alien/store/util/ai/AiContentModerationUtil.java

@@ -1,7 +1,7 @@
 package shop.alien.store.util.ai;
 package shop.alien.store.util.ai;
 
 
+import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import com.alibaba.fastjson2.JSONObject;
-import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cloud.context.config.annotation.RefreshScope;
 import org.springframework.cloud.context.config.annotation.RefreshScope;
@@ -11,38 +11,42 @@ import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
 import org.springframework.util.MultiValueMap;
+import org.springframework.util.StringUtils;
 import org.springframework.web.client.RestTemplate;
 import org.springframework.web.client.RestTemplate;
 
 
-import java.util.List;
 import java.util.ArrayList;
 import java.util.ArrayList;
+import java.util.List;
 
 
 /**
 /**
- * 通用图文审核工具类
- * 调用AI图文审核接口审核文本和图片内容
+ * 服务端文本内容审核(moderate-url)。
+ * <ul>
+ *   <li>无有效文本 → 直接通过,不请求接口</li>
+ *   <li>有文本 → 仅提交文本字段;图/视频由前端处理,{@code auditContent} 第二参应传 {@code null}</li>
+ * </ul>
  */
  */
 @Slf4j
 @Slf4j
 @Component
 @Component
 @RefreshScope
 @RefreshScope
-@RequiredArgsConstructor
 public class AiContentModerationUtil {
 public class AiContentModerationUtil {
 
 
+    private static final String RESULTS = "results";
+    private static final String TEXT_INPUT_MARKER = "[TEXT INPUT]";
+    private static final String GENERIC_FAILURE = "审核异常";
+
     private final RestTemplate restTemplate;
     private final RestTemplate restTemplate;
 
 
-    /**
-     * AI审核接口地址
-     */
+    public AiContentModerationUtil(RestTemplate restTemplate) {
+        this.restTemplate = restTemplate;
+    }
+
     @Value("${ai.service.moderate-url}")
     @Value("${ai.service.moderate-url}")
     private String moderateUrl;
     private String moderateUrl;
 
 
-    /**
-     * 审核结果类
-     */
     public static class AuditResult {
     public static class AuditResult {
-        private boolean passed;
-        private String failureReason;
+        private final boolean passed;
+        private final String failureReason;
 
 
         public AuditResult(boolean passed, String failureReason) {
         public AuditResult(boolean passed, String failureReason) {
             this.passed = passed;
             this.passed = passed;
@@ -59,149 +63,89 @@ public class AiContentModerationUtil {
     }
     }
 
 
     /**
     /**
-     * 审核文本和图片内容
-     *
-     * @param text 文本内容
-     * @param imageUrls 图片URL列表
-     * @return 审核结果
+     * @param text      待审文案;null/空白则视为通过
+     * @param imageUrls 固定 {@code null}(兼容旧签名,非 null 会被忽略并打 debug)
      */
      */
     public AuditResult auditContent(String text, List<String> imageUrls) {
     public AuditResult auditContent(String text, List<String> imageUrls) {
-        log.info("开始审核内容:text={}, imageCount={}",
-                text, imageUrls != null ? imageUrls.size() : 0);
-
+        if (imageUrls != null && !imageUrls.isEmpty()) {
+            log.debug("auditContent: 忽略 imageUrls(size={}),请统一传 null", imageUrls.size());
+        }
+        if (!StringUtils.hasText(text)) {
+            log.debug("auditContent: 无有效文本,跳过 moderate");
+            return new AuditResult(true, null);
+        }
         try {
         try {
-            // 调用审核接口
-            return callModerateApi(text, imageUrls);
-
+            return requestModerate(text);
         } catch (Exception e) {
         } catch (Exception e) {
-            log.error("审核内容异常", e);
-            return new AuditResult(false, "审核异常");
+            log.error("auditContent 异常", e);
+            return new AuditResult(false, GENERIC_FAILURE);
         }
         }
     }
     }
 
 
-    /**
-     * 调用AI审核接口
-     *
-     * @param text 文本内容
-     * @param imageUrls 图片URL列表
-     * @return 审核结果
-     */
-    private AuditResult callModerateApi(String text, List<String> imageUrls) {
+    /** 仅在有非空 text 时调用 */
+    private AuditResult requestModerate(String text) {
         try {
         try {
-            // 构建 form-data 请求头
             HttpHeaders headers = new HttpHeaders();
             HttpHeaders headers = new HttpHeaders();
             headers.setContentType(MediaType.MULTIPART_FORM_DATA);
             headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+            MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
+            body.add("text", text);
 
 
-            // 构建 form-data 请求体
-            MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
-            if (StringUtils.hasText(text)) {
-                formData.add("text", text);
-            }
-            if (imageUrls != null) {
-                for (String url : imageUrls) {
-                    if (StringUtils.hasText(url)) {
-                        // 支持多张图片:同一个 key 重复多次
-                        formData.add("image_urls", url);
-                    }
-                }
-            }
-
-            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(formData, headers);
-
-            log.info("调用AI审核接口:url={}, text={}, imageCount={}",
-                    moderateUrl, text, imageUrls != null ? imageUrls.size() : 0);
-
-            // 发送请求
-            ResponseEntity<String> response = restTemplate.postForEntity(moderateUrl, requestEntity, String.class);
+            ResponseEntity<String> response =
+                    restTemplate.postForEntity(moderateUrl, new HttpEntity<>(body, headers), String.class);
 
 
-            if (response.getStatusCode() == HttpStatus.OK) {
-                String responseBody = response.getBody();
-                log.info("AI审核接口响应:{}", responseBody);
-
-                if (StringUtils.hasText(responseBody)) {
-                    JSONObject jsonResponse = JSONObject.parseObject(responseBody);
-                    return parseAuditResult(jsonResponse);
-                } else {
-                    log.error("AI审核接口返回空响应");
-                    return new AuditResult(false, "审核异常");
-                }
-            } else {
-                log.error("AI审核接口调用失败,状态码:{}", response.getStatusCode());
-                return new AuditResult(false, "审核异常");
+            if (response.getStatusCode() != HttpStatus.OK) {
+                log.error("moderate HTTP 非 200: {}", response.getStatusCode());
+                return new AuditResult(false, GENERIC_FAILURE);
             }
             }
-
+            String responseBody = response.getBody();
+            if (!StringUtils.hasText(responseBody)) {
+                log.error("moderate 响应体为空");
+                return new AuditResult(false, GENERIC_FAILURE);
+            }
+            log.debug("moderate 响应: {}", responseBody);
+            return parseModerateResponse(JSONObject.parseObject(responseBody));
         } catch (Exception e) {
         } catch (Exception e) {
-            log.error("调用AI审核接口异常", e);
-            return new AuditResult(false, "审核异常");
+            log.error("调用 moderate 失败", e);
+            return new AuditResult(false, GENERIC_FAILURE);
         }
         }
     }
     }
 
 
-    /**
-     * 解析审核结果
-     *
-     * @param jsonResponse API响应JSON
-     * @return 审核结果
-     */
-    private AuditResult parseAuditResult(JSONObject jsonResponse) {
+    private AuditResult parseModerateResponse(JSONObject json) {
         try {
         try {
-            // API返回格式:
-            // {
-            //     "results": [
-            //         {
-            //             "filename": "[TEXT INPUT]" 或 URL,
-            //             "flagged": true/false,
-            //             "risk_level": "high"/"safe"等,
-            //             "violation_categories": [...],
-            //             "reason": "原因描述"
-            //         }
-            //     ],
-            //     "summary": "处理摘要"
-            // }
-
-            com.alibaba.fastjson2.JSONArray results = jsonResponse.getJSONArray("results");
+            JSONArray results = json.getJSONArray(RESULTS);
             if (results == null || results.isEmpty()) {
             if (results == null || results.isEmpty()) {
-                log.warn("AI审核接口返回结果为空");
-                return new AuditResult(false, "审核异常");
+                log.warn("moderate 返回 results 为空");
+                return new AuditResult(false, GENERIC_FAILURE);
             }
             }
 
 
-            // 检查是否有任何项目被标记为违规
-            List<String> violationReasons = new ArrayList<>();
-            boolean hasViolations = false;
-
+            List<String> reasons = new ArrayList<>();
             for (int i = 0; i < results.size(); i++) {
             for (int i = 0; i < results.size(); i++) {
-                JSONObject result = results.getJSONObject(i);
-                if (result != null) {
-                    Boolean flagged = result.getBoolean("flagged");
-                    String filename = result.getString("filename");
-
-                    if (flagged != null && flagged) {
-                        hasViolations = true;
-                        // 根据filename判断是文本还是图片违规
-                        if ("[TEXT INPUT]".equals(filename)) {
-                            violationReasons.add("含违规词汇");
-                        } else if (StringUtils.hasText(filename) && filename.startsWith("http")) {
-                            violationReasons.add("图片内容违规");
-                        } else {
-                            violationReasons.add("内容违规");
-                        }
-                    }
+                JSONObject item = results.getJSONObject(i);
+                if (item == null || !Boolean.TRUE.equals(item.getBoolean("flagged"))) {
+                    continue;
                 }
                 }
+                reasons.add(flaggedItemReason(item.getString("filename")));
             }
             }
 
 
-            if (hasViolations) {
-                // 有违规内容,审核失败
-                String failureReason = String.join("; ", violationReasons);
-                log.warn("AI审核失败:{}", failureReason);
-                return new AuditResult(false, failureReason);
-            } else {
-                // 所有内容都安全,审核通过
-                log.info("AI审核通过:所有内容安全");
+            if (reasons.isEmpty()) {
                 return new AuditResult(true, null);
                 return new AuditResult(true, null);
             }
             }
-
+            String joined = String.join("; ", reasons);
+            log.warn("moderate 命中违规: {}", joined);
+            return new AuditResult(false, joined);
         } catch (Exception e) {
         } catch (Exception e) {
-            log.error("解析审核结果异常", e);
-            return new AuditResult(false, "审核异常");
+            log.error("解析 moderate 响应失败", e);
+            return new AuditResult(false, GENERIC_FAILURE);
+        }
+    }
+
+    private static String flaggedItemReason(String filename) {
+        if (TEXT_INPUT_MARKER.equals(filename)) {
+            return "含违规词汇";
+        }
+        if (StringUtils.hasText(filename) && filename.startsWith("http")) {
+            return "图片内容违规";
         }
         }
+        return "内容违规";
     }
     }
-}
+}

+ 10 - 302
alien-store/src/main/java/shop/alien/store/util/ai/AiVideoModerationUtil.java

@@ -1,72 +1,27 @@
 package shop.alien.store.util.ai;
 package shop.alien.store.util.ai;
 
 
 import com.alibaba.fastjson2.JSONObject;
 import com.alibaba.fastjson2.JSONObject;
-import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.cloud.context.config.annotation.RefreshScope;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-import org.springframework.web.client.RestTemplate;
 
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 
 
 /**
 /**
- * 视频审核工具类
- * 调用AI视频审核接口审核视频内容
+ * 视频审核占位:视频由前端直连 AI,服务端不调用视频审核接口。
+ * <p>保留 {@link #auditVideos} 供业务异步流程调用,恒为通过,避免历史代码空指针。</p>
  */
  */
 @Slf4j
 @Slf4j
 @Component
 @Component
-@RefreshScope
-@RequiredArgsConstructor
 public class AiVideoModerationUtil {
 public class AiVideoModerationUtil {
 
 
-    private final RestTemplate restTemplate;
-
-    /**
-     * 视频上传接口地址
-     */
-    @Value("${ai.service.video-submit-url}")
-    private String videoSubmitUrl;
-
-    /**
-     * 视频审查接口地址
-     */
-    @Value("${ai.service.video-status-url}")
-    private String videoStatusUrl;
-
-    /**
-     * 轮询间隔(毫秒)
-     */
-    @Value("${ai.service.video-poll-interval:2000}")
-    private long pollInterval;
-
-    /**
-     * 最大轮询次数
-     */
-    @Value("${ai.service.video-max-poll-count:30}")
-    private int maxPollCount;
-
-    /**
-     * 审核结果类
-     */
     public static class VideoAuditResult {
     public static class VideoAuditResult {
-        private boolean passed;
-        private String failureReason;
-        private String taskId;
-        private JSONObject rawResult;
+        private final boolean passed;
+        private final String failureReason;
+        private final String taskId;
+        private final JSONObject rawResult;
 
 
         public VideoAuditResult(boolean passed, String failureReason) {
         public VideoAuditResult(boolean passed, String failureReason) {
-            this.passed = passed;
-            this.failureReason = failureReason;
+            this(passed, failureReason, null, null);
         }
         }
 
 
         public VideoAuditResult(boolean passed, String failureReason, String taskId, JSONObject rawResult) {
         public VideoAuditResult(boolean passed, String failureReason, String taskId, JSONObject rawResult) {
@@ -94,260 +49,13 @@ public class AiVideoModerationUtil {
     }
     }
 
 
     /**
     /**
-     * 审核视频内容
-     *
-     * @param videoUrls 视频URL列表
-     * @return 审核结果
+     * 不进行服务端视频审核;前端已审则此处仅打日志并返回通过。
      */
      */
     public VideoAuditResult auditVideos(List<String> videoUrls) {
     public VideoAuditResult auditVideos(List<String> videoUrls) {
         if (videoUrls == null || videoUrls.isEmpty()) {
         if (videoUrls == null || videoUrls.isEmpty()) {
-            log.info("视频URL列表为空,跳过审核");
             return new VideoAuditResult(true, null);
             return new VideoAuditResult(true, null);
         }
         }
-
-        log.info("开始审核视频:videoCount={}", videoUrls.size());
-
-        try {
-            List<VideoAuditResult> results = new ArrayList<>();
-            for (String videoUrl : videoUrls) {
-                if (StringUtils.hasText(videoUrl)) {
-                    VideoAuditResult result = auditSingleVideo(videoUrl);
-                    results.add(result);
-                }
-            }
-
-            // 如果任何一个视频审核失败,整体审核失败
-            for (VideoAuditResult result : results) {
-                if (!result.isPassed()) {
-                    log.warn("视频审核失败:{}", result.getFailureReason());
-                    return result;
-                }
-            }
-
-            log.info("所有视频审核通过");
-            return new VideoAuditResult(true, null);
-
-        } catch (Exception e) {
-            log.error("审核视频异常", e);
-            return new VideoAuditResult(false, "审核服务异常:" + e.getMessage());
-        }
-    }
-
-    /**
-     * 审核单个视频
-     *
-     * @param videoUrl 视频URL
-     * @return 审核结果
-     */
-    private VideoAuditResult auditSingleVideo(String videoUrl) {
-        try {
-            // 1. 提交视频审核任务
-            String taskId = submitVideo(videoUrl);
-            if (taskId == null) {
-                return new VideoAuditResult(false, "视频提交失败");
-            }
-
-            log.info("视频提交成功,taskId={}, videoUrl={}", taskId, videoUrl);
-
-            // 2. 轮询审核状态
-            return pollVideoStatus(taskId, videoUrl);
-
-        } catch (Exception e) {
-            log.error("审核视频异常,videoUrl={}", videoUrl, e);
-            return new VideoAuditResult(false, "审核服务异常:" + e.getMessage());
-        }
-    }
-
-    /**
-     * 提交视频审核任务
-     *
-     * @param videoUrl 视频URL
-     * @return 任务ID
-     */
-    private String submitVideo(String videoUrl) {
-        try {
-            // 构建 form-data 请求头
-            HttpHeaders headers = new HttpHeaders();
-            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
-
-            // 构建 form-data 请求体,兼容服务端可能的三种参数名
-            MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
-            formData.add("videoUrl", videoUrl);
-            formData.add("video_path", videoUrl);
-            formData.add("video_url", videoUrl);
-
-            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(formData, headers);
-
-            log.info("提交视频审核任务:url={}, videoUrl={}", videoSubmitUrl, videoUrl);
-
-            // 发送请求
-            ResponseEntity<String> response = restTemplate.postForEntity(videoSubmitUrl, requestEntity, String.class);
-
-            if (response.getStatusCode() == HttpStatus.OK) {
-                String responseBody = response.getBody();
-                log.info("视频提交接口响应:{}", responseBody);
-
-                if (StringUtils.hasText(responseBody)) {
-                    JSONObject jsonResponse = JSONObject.parseObject(responseBody);
-                    String taskId = jsonResponse.getString("task_id");
-                    if (StringUtils.hasText(taskId)) {
-                        return taskId;
-                    } else {
-                        log.error("视频提交接口返回的task_id为空");
-                        return null;
-                    }
-                } else {
-                    log.error("视频提交接口返回空响应");
-                    return null;
-                }
-            } else {
-                log.error("视频提交接口调用失败,状态码:{}", response.getStatusCode());
-                return null;
-            }
-
-        } catch (org.springframework.web.client.HttpClientErrorException e) {
-            log.error("调用视频提交接口异常,status={}, body={}", e.getStatusCode(), e.getResponseBodyAsString(), e);
-            return null;
-        } catch (Exception e) {
-            log.error("调用视频提交接口异常", e);
-            return null;
-        }
-    }
-
-    /**
-     * 轮询视频审核状态
-     *
-     * @param taskId 任务ID
-     * @param videoUrl 视频URL(用于日志)
-     * @return 审核结果
-     */
-    private VideoAuditResult pollVideoStatus(String taskId, String videoUrl) {
-        int pollCount = 0;
-        while (pollCount < maxPollCount) {
-            try {
-                // 等待一段时间再查询
-                if (pollCount > 0) {
-                    Thread.sleep(pollInterval);
-                }
-
-                String statusUrl = videoStatusUrl + "/" + taskId;
-                log.info("查询视频审核状态:url={}, taskId={}, pollCount={}", statusUrl, taskId, pollCount + 1);
-
-                ResponseEntity<String> response = restTemplate.getForEntity(statusUrl, String.class);
-
-                if (response.getStatusCode() == HttpStatus.OK) {
-                    String responseBody = response.getBody();
-                    log.info("视频审核状态接口响应:{}", responseBody);
-
-                    if (StringUtils.hasText(responseBody)) {
-                        JSONObject jsonResponse = JSONObject.parseObject(responseBody);
-                        String status = jsonResponse.getString("status");
-
-                        if ("completed".equals(status)) {
-                            // 审核完成,解析结果
-                            return parseVideoAuditResult(jsonResponse, taskId);
-                        } else if ("failed".equals(status) || "error".equals(status)) {
-                            // 审核失败
-                            String error = jsonResponse.getString("error");
-                            log.error("视频审核失败,taskId={}, error={}", taskId, error);
-                            return new VideoAuditResult(false, "视频审核失败:" + (error != null ? error : "未知错误"), taskId, jsonResponse);
-                        } else {
-                            // 审核中,继续轮询
-                            log.info("视频审核中,taskId={}, status={}, 继续等待...", taskId, status);
-                            pollCount++;
-                        }
-                    } else {
-                        log.error("视频审核状态接口返回空响应");
-                        return new VideoAuditResult(false, "审核状态接口返回空响应");
-                    }
-                } else {
-                    log.error("视频审核状态接口调用失败,状态码:{}", response.getStatusCode());
-                    return new VideoAuditResult(false, "审核状态接口调用失败,状态码:" + response.getStatusCode());
-                }
-
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-                log.error("轮询视频审核状态被中断,taskId={}", taskId, e);
-                return new VideoAuditResult(false, "审核被中断");
-            } catch (Exception e) {
-                log.error("查询视频审核状态异常,taskId={}", taskId, e);
-                pollCount++;
-                // 继续重试
-            }
-        }
-
-        log.error("视频审核超时,taskId={}, maxPollCount={}", taskId, maxPollCount);
-        return new VideoAuditResult(false, "视频审核超时");
-    }
-
-    /**
-     * 解析视频审核结果
-     *
-     * @param jsonResponse API响应JSON
-     * @param taskId 任务ID
-     * @return 审核结果
-     */
-    private VideoAuditResult parseVideoAuditResult(JSONObject jsonResponse, String taskId) {
-        try {
-            JSONObject result = jsonResponse.getJSONObject("result");
-            if (result == null) {
-                log.warn("视频审核结果中result字段为空");
-                return new VideoAuditResult(false, "审核结果格式错误:result字段为空", taskId, jsonResponse);
-            }
-
-            Boolean flagged = result.getBoolean("flagged");
-            String riskLevel = result.getString("risk_level");
-            String summary = result.getString("summary");
-
-            if (flagged != null && flagged) {
-                // 有违规内容,审核失败
-                List<String> violationReasons = new ArrayList<>();
-                if (StringUtils.hasText(riskLevel)) {
-                    violationReasons.add("风险等级:" + riskLevel);
-                }
-                if (StringUtils.hasText(summary)) {
-                    violationReasons.add(summary);
-                }
-
-                // 解析违规证据
-                com.alibaba.fastjson2.JSONArray evidence = result.getJSONArray("evidence");
-                if (evidence != null && !evidence.isEmpty()) {
-                    List<String> evidenceReasons = new ArrayList<>();
-                    for (int i = 0; i < evidence.size(); i++) {
-                        JSONObject item = evidence.getJSONObject(i);
-                        if (item != null) {
-                            String reason = item.getString("reason");
-                            String timeOffset = item.getString("time_offset");
-                            if (StringUtils.hasText(reason)) {
-                                String evidenceReason = StringUtils.hasText(timeOffset)
-                                    ? String.format("时间点%s: %s", timeOffset, reason)
-                                    : reason;
-                                evidenceReasons.add(evidenceReason);
-                            }
-                        }
-                    }
-                    if (!evidenceReasons.isEmpty()) {
-                        violationReasons.add("违规证据:" + String.join("; ", evidenceReasons));
-                    }
-                }
-
-                String failureReason = String.join("; ", violationReasons);
-                if (failureReason.isEmpty()) {
-                    failureReason = "视频内容违规";
-                }
-
-                log.warn("视频审核失败:{}", failureReason);
-                return new VideoAuditResult(false, failureReason, taskId, jsonResponse);
-            } else {
-                // 无违规内容,审核通过
-                log.info("视频审核通过:taskId={}", taskId);
-                return new VideoAuditResult(true, null, taskId, jsonResponse);
-            }
-
-        } catch (Exception e) {
-            log.error("解析视频审核结果异常", e);
-            return new VideoAuditResult(false, "解析审核结果异常:" + e.getMessage(), taskId, jsonResponse);
-        }
+        log.info("跳过服务端视频审核(前端已处理),条数={}", videoUrls.size());
+        return new VideoAuditResult(true, null);
     }
     }
 }
 }
-