소스 검색

Merge branch 'sit' of http://8.152.195.41:3000/alien/alien_cloud into sit

lutong 2 달 전
부모
커밋
1ea722be39

+ 5 - 0
alien-entity/src/main/java/shop/alien/mapper/CommonRatingMapper.java

@@ -1,8 +1,11 @@
 package shop.alien.mapper;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
@@ -67,5 +70,7 @@ public interface CommonRatingMapper extends BaseMapper<CommonRating> {
      */
     @Update("UPDATE common_rating SET delete_flag = 1, updated_time = NOW() WHERE id = #{id}")
     int logicDeleteById(@Param("id") Integer id);
+
+    IPage<CommonRating> getMyRatingList(Page<CommonRating> page, @Param(Constants.WRAPPER) QueryWrapper<CommonRating> wrapper);
 }
 

+ 6 - 0
alien-entity/src/main/resources/mapper/CommonRatingMapper.xml

@@ -31,6 +31,12 @@
         is_anonymous, is_show, audit_status, like_count, score_one, score_two, score_three,
         delete_flag, created_user_id, updated_time, updated_user_id
     </sql>
+    <select id="getMyRatingList" resultType="shop.alien.entity.store.vo.CommonRatingVo">
+        select cr.*,si.store_name storeName
+        from common_rating cr
+        left join store_info si on cr.business_id = si.id
+        ${ew.customSqlSegment}
+    </select>
 
 </mapper>
 

+ 110 - 31
alien-entity/src/main/resources/mapper/LifeUserDynamicsMapper.xml

@@ -3,60 +3,139 @@
 <mapper namespace="shop.alien.mapper.LifeUserDynamicsMapper">
 
     <select id="getDynamicsList" resultType="shop.alien.entity.store.vo.LifeUserDynamicsVo">
-        select dyna1.*, COUNT(dyna1.id) AS fansCount from (
-        select dyna.*,COUNT(sc.id) AS commentCount,  COUNT(lm.id) AS transferNum from (
-        with dynamice as(
-        select
+        SELECT
+        -- 1. 用ANY_VALUE()包裹非聚合列,符合only_full_group_by规则
+        ANY_VALUE(dyna1.id) AS id,
+        ANY_VALUE(dyna1.dynamicsType) AS dynamicsType,
+        ANY_VALUE(dyna1.title) AS title,
+        ANY_VALUE(dyna1.phoneId) AS phoneId,
+        ANY_VALUE(dyna1.context) AS context,
+        ANY_VALUE(dyna1.image_path) AS image_path,
+        ANY_VALUE(dyna1.address) AS address,
+        ANY_VALUE(dyna1.address_name) AS address_name,
+        ANY_VALUE(dyna1.address_context) AS address_context,
+        ANY_VALUE(dyna1.liulan_count) AS liulan_count,
+        ANY_VALUE(dyna1.dianzan_count) AS dianzan_count,
+        ANY_VALUE(dyna1.type) AS type,
+        ANY_VALUE(dyna1.created_time) AS created_time,
+        ANY_VALUE(dyna1.userType) AS userType,
+        ANY_VALUE(dyna1.phone) AS phone,
+        ANY_VALUE(dyna1.draft) AS draft,
+        ANY_VALUE(dyna1.address_province) AS address_province,
+        ANY_VALUE(dyna1.top_status) AS top_status,
+        ANY_VALUE(dyna1.top_time) AS top_time,
+        ANY_VALUE(dyna1.enable_status) AS enable_status,
+        ANY_VALUE(dyna1.userName) AS userName,
+        ANY_VALUE(dyna1.userImage) AS userImage,
+        ANY_VALUE(dyna1.storeUserId) AS storeUserId,
+        ANY_VALUE(dyna1.storeOrUserId) AS storeOrUserId,
+        ANY_VALUE(dyna1.isExpert) AS isExpert,
+        ANY_VALUE(dyna1.store_name) AS store_name,
+        -- 2. 修正聚合计数逻辑:用COUNT(DISTINCT)避免重复计数,粉丝数统计lf1.id而非dyna1.id
+        COUNT(DISTINCT lf1.id) AS fansCount,
+        -- 保留内层的评论数、转发数(从dyna1中取聚合后的值)
+        ANY_VALUE(dyna1.commentCount) AS commentCount,
+        ANY_VALUE(dyna1.transferNum) AS transferNum
+        FROM (
+        SELECT
+        -- 内层同样用ANY_VALUE()包裹非聚合列
+        ANY_VALUE(dyna.id) AS id,
+        ANY_VALUE(dyna.dynamicsType) AS dynamicsType,
+        ANY_VALUE(dyna.title) AS title,
+        ANY_VALUE(dyna.phoneId) AS phoneId,
+        ANY_VALUE(dyna.context) AS context,
+        ANY_VALUE(dyna.image_path) AS image_path,
+        ANY_VALUE(dyna.address) AS address,
+        ANY_VALUE(dyna.address_name) AS address_name,
+        ANY_VALUE(dyna.address_context) AS address_context,
+        ANY_VALUE(dyna.liulan_count) AS liulan_count,
+        ANY_VALUE(dyna.dianzan_count) AS dianzan_count,
+        ANY_VALUE(dyna.type) AS type,
+        ANY_VALUE(dyna.created_time) AS created_time,
+        ANY_VALUE(dyna.userType) AS userType,
+        ANY_VALUE(dyna.phone) AS phone,
+        ANY_VALUE(dyna.draft) AS draft,
+        ANY_VALUE(dyna.address_province) AS address_province,
+        ANY_VALUE(dyna.top_status) AS top_status,
+        ANY_VALUE(dyna.top_time) AS top_time,
+        ANY_VALUE(dyna.enable_status) AS enable_status,
+        ANY_VALUE(dyna.userName) AS userName,
+        ANY_VALUE(dyna.userImage) AS userImage,
+        ANY_VALUE(dyna.storeUserId) AS storeUserId,
+        ANY_VALUE(dyna.storeOrUserId) AS storeOrUserId,
+        ANY_VALUE(dyna.isExpert) AS isExpert,
+        ANY_VALUE(dyna.store_name) AS store_name,
+        -- 3. 修正评论数/转发数:COUNT(DISTINCT)避免LEFT JOIN导致的重复计数
+        COUNT(DISTINCT sc.id) AS commentCount,
+        COUNT(DISTINCT lm.id) AS transferNum
+        FROM (
+        WITH dynamice AS (
+        SELECT
         CASE
         WHEN image_path REGEXP '.mp4|.avi|.flv|.mkv|.rmvb|.wmv|.3gp|.mov' THEN 2
         WHEN image_path REGEXP '.jpg|.jpeg|.png|.bmp|.webp|.gif|.svg' THEN 1
         ELSE 0
-        END AS dynamicsType,id, title, phone_id phoneId, context, image_path, address,address_name,address_context, liulan_count, dianzan_count, type, created_time, substring_index(phone_id, '_', 1) userType, substring_index(phone_id, '_', -1) phone, draft , address_province, top_status, top_time, enable_status
-        from life_user_dynamics
-        where delete_flag = 0 and draft = 0 order by created_time desc
+        END AS dynamicsType,
+        id, title, phone_id phoneId, context, image_path, address,address_name,address_context,
+        liulan_count, dianzan_count, type, created_time,
+        SUBSTRING_INDEX(phone_id, '_', 1) userType,
+        SUBSTRING_INDEX(phone_id, '_', -1) phone,
+        draft , address_province, top_status, top_time, enable_status
+        FROM life_user_dynamics
+        WHERE delete_flag = 0 AND draft = 0
         )
-        select dynamice.*, user.nick_name userName, user.head_img userImage, info.id storeUserId, user.id storeOrUserId, 0 isExpert, info.store_name
-        from dynamice
-        left join store_user user on dynamice.phone = user.phone and user.delete_flag = 0
-        left join store_info info on info.id = user.store_id and info.delete_flag = 0
-        left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0
-        where dynamice.userType = 'store'
-        union
-        select dynamice.*, user.user_name userName, user.user_image userImage, user.id storeUserId, user.id storeOrUserId, IF(lue.expert_code IS NOT NULL , 1, 0) AS isExpert, '' store_name
-        from dynamice
-        join life_user user on dynamice.phone = user.user_phone and user.delete_flag = 0
-        left join life_user_expert  lue on lue.user_id = user.id and lue.delete_flag = 0
-        where dynamice.userType = 'user') dyna
-        left join life_comment lc on lc.dongtai_shequ_id = dyna.id
-        left join store_comment sc on sc.business_id = dyna.id and sc.business_type = 2 and sc.delete_flag = 0
-        left join life_message lm on lm.business_id = dyna.id
-        GROUP BY dyna.id order by  dyna.created_time desc) dyna1
-        left join life_fans lf1 on lf1.followed_id =  dyna1.phoneId
-        where 1=1
+        SELECT
+        dynamice.*,
+        user.nick_name userName, user.head_img userImage,
+        info.id storeUserId, user.id storeOrUserId,
+        0 isExpert, info.store_name
+        FROM dynamice
+        LEFT JOIN store_user user ON dynamice.phone = user.phone AND user.delete_flag = 0
+        LEFT JOIN store_info info ON info.id = user.store_id AND info.delete_flag = 0
+        LEFT JOIN store_img img ON img.store_id = user.store_id AND img.img_type = '10' AND img.delete_flag = 0
+        WHERE dynamice.userType = 'store'
+        UNION
+        SELECT
+        dynamice.*,
+        user.user_name userName, user.user_image userImage,
+        user.id storeUserId, user.id storeOrUserId,
+        IF(lue.expert_code IS NOT NULL , 1, 0) AS isExpert,
+        '' store_name
+        FROM dynamice
+        JOIN life_user user ON dynamice.phone = user.user_phone AND user.delete_flag = 0
+        LEFT JOIN life_user_expert  lue ON lue.user_id = user.id AND lue.delete_flag = 0
+        WHERE dynamice.userType = 'user'
+        ) dyna
+        LEFT JOIN life_comment lc ON lc.dongtai_shequ_id = dyna.id
+        LEFT JOIN store_comment sc ON sc.business_id = dyna.id AND sc.business_type = 2 AND sc.delete_flag = 0
+        LEFT JOIN life_message lm ON lm.business_id = dyna.id
+        -- 4. GROUP BY仅保留主键id(因dyna.id是唯一主键,ANY_VALUE()包裹的列在同一id下值唯一)
+        GROUP BY dyna.id
+        ) dyna1
+        LEFT JOIN life_fans lf1 ON lf1.followed_id = dyna1.phoneId
+        WHERE 1=1
         <if test="nickName != null and nickName != ''">
             AND dyna1.userName LIKE CONCAT('%', #{nickName}, '%')
         </if>
-
         <if test="userType != null and userType != ''">
             AND dyna1.userType = #{userType}
         </if>
-
         <if test="dynamicsType != null and dynamicsType != ''">
             AND dyna1.dynamicsType = #{dynamicsType}
         </if>
-
         <if test="releaseStartTime != null and releaseStartTime != ''">
             AND dyna1.created_time >= #{releaseStartTime}
         </if>
-
         <if test="storeName != null and storeName != ''">
             AND dyna1.store_name LIKE CONCAT('%', #{storeName}, '%')
         </if>
-
         <if test="releaseEndTime != null and releaseEndTime != ''">
             AND dyna1.created_time &lt;= #{releaseEndTime}
         </if>
-        GROUP by dyna1.id order by dyna1.top_status desc, dyna1.top_time desc
+        -- 5. 外层GROUP BY仅保留主键id,保证分组逻辑正确
+        GROUP BY dyna1.id
+        -- 6. 最终排序移到外层,子查询的ORDER BY无意义且影响性能
+        ORDER BY dyna1.top_status DESC, dyna1.top_time DESC;
     </select>
 
     <select id="getDynamicsDetail" resultType="shop.alien.entity.store.vo.LifeUserDynamicsVo">

+ 1 - 0
alien-entity/src/main/resources/mapper/StoreStaffReviewMapper.xml

@@ -65,6 +65,7 @@
             AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
             AND llr.delete_flag = 0
         WHERE ssr.delete_flag = 0
+          AND ssr.audit_status = 1
         <if test="staffUserId != null">
             AND ssr.staff_user_id = #{staffUserId}
         </if>

+ 26 - 15
alien-store-platform/src/main/java/shop/alien/storeplatform/feign/AlienAIFeign.java

@@ -33,27 +33,38 @@ public interface AlienAIFeign {
                  produces = MediaType.APPLICATION_JSON_VALUE)
     JsonNode generatePromotionImage(@RequestHeader("Authorization") String authorization, @RequestBody JsonNode requestBody);
 
-    /**
-     * 使用 JsonNode 灵活调用接口 - 发起AI审核(多模态)
-     *
-     * @param authorization Bearer token,格式为 "Bearer {access_token}"
-     * @param requestBody JsonNode 请求体,可以灵活构建
-     * @return JsonNode 响应体,可以灵活解析
-     */
-    @PostMapping(value = "/ai/auto-review/api/v1/multimodal_audit_task/submit",
-            consumes = MediaType.APPLICATION_JSON_VALUE,
-            produces = MediaType.APPLICATION_JSON_VALUE)
-    JsonNode multiModelAudit(@RequestHeader("Authorization") String authorization, @RequestBody JsonNode requestBody);
-
+//    /**
+//     * 使用 JsonNode 灵活调用接口 - 发起AI审核(多模态)
+//     *
+//     * @param authorization Bearer token,格式为 "Bearer {access_token}"
+//     * @param requestBody JsonNode 请求体,可以灵活构建
+//     * @return JsonNode 响应体,可以灵活解析
+//     */
+//    @PostMapping(value = "/ai/auto-review/api/v1/multimodal_audit_task/submit",
+//            consumes = MediaType.APPLICATION_JSON_VALUE,
+//            produces = MediaType.APPLICATION_JSON_VALUE)
+//    JsonNode multiModelAudit(@RequestHeader("Authorization") String authorization, @RequestBody JsonNode requestBody);
+//
+//
+//    /**
+//     * 获取 AI审核(多模态) 结果
+//     *
+//     * @param authorization Bearer token,格式为 "Bearer {access_token}"
+//     * @param taskId 任务ID
+//     * @return JsonNode 响应体,包含审核结果
+//     */
+//    @GetMapping(value = "/ai/auto-review/api/v1/multimodal_audit_task/getResult",
+//            produces = MediaType.APPLICATION_JSON_VALUE)
+//    JsonNode getMultiModelAuditResult(@RequestHeader("Authorization") String authorization, @RequestParam("task_id") String taskId);
 
     /**
      * 获取 AI审核(多模态) 结果
      *
      * @param authorization Bearer token,格式为 "Bearer {access_token}"
-     * @param taskId 任务ID
+     * @param requestBody
      * @return JsonNode 响应体,包含审核结果
      */
-    @GetMapping(value = "/ai/auto-review/api/v1/multimodal_audit_task/getResult",
+    @GetMapping(value = "/api/v1/moderate",
             produces = MediaType.APPLICATION_JSON_VALUE)
-    JsonNode getMultiModelAuditResult(@RequestHeader("Authorization") String authorization, @RequestParam("task_id") String taskId);
+    JsonNode syncAudit(@RequestHeader("Authorization") String authorization, @RequestBody JsonNode requestBody);
 }

+ 27 - 24
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/OperationalActivityServiceImpl.java

@@ -26,12 +26,15 @@ import shop.alien.mapper.StoreImgMapper;
 import shop.alien.mapper.storePlantform.StoreOperationalActivityMapper;
 import shop.alien.storeplatform.feign.AlienAIFeign;
 import shop.alien.storeplatform.service.OperationalActivityService;
+import shop.alien.storeplatform.util.AiContentModerationUtil;
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 import java.util.concurrent.TimeUnit;
 
 
@@ -62,6 +65,7 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
 
     private final RedissonClient redissonClient;
 
+    private final AiContentModerationUtil aiContentModerationUtil;
     @Value("${ai.aiAccount}")
     private String aiAccount;
 
@@ -109,31 +113,30 @@ public class OperationalActivityServiceImpl implements OperationalActivityServic
                     // AI登录成功
                     // 先调用AI进行运营名称和图片的审核,同步调用
                     String authorization = "Bearer " + accessToken;
-                    JsonNode audioResponse = alienAIFeign.multiModelAudit(authorization, dto.getAuditParam());
-                    // 如果审核失败,不进行文字生成海报图片,提前短路。
-                    if (audioResponse.has("data")) {
-                        String taskId = audioResponse.get("data").get("task_id").asText();
-                        JsonNode audioResResponse = alienAIFeign.getMultiModelAuditResult(authorization, taskId);
-                        String status=audioResResponse.get("data").get("status").asText();
-                        for (int i = 0; i < 60; i++) { // AI审核接口速度还在优化中 todo
-                            if (status.equals("completed")||status.equals("failed")) {
-                                break;
-                            }
-                            audioResResponse = alienAIFeign.getMultiModelAuditResult(authorization, taskId);
-                            status = audioResResponse.get("data").get("status").asText();
-                            Thread.sleep(1000);
-                        }
-                        String auditRes = audioResResponse.get("data").get("audit_result").asText();
-                        if (!status.equals("failed")&&auditRes != null && auditRes.equals("compliant")) {
-                            activity.setStatus(8);
-                            result = activityMapper.insert(activity);
-                        } else {
-                            String failureReason = audioResResponse.get("data").get("failure_reason").asText();
-                            failureReasonHolder.set(failureReason);
-                            // 审核不成功 不生成任何记录,返回原因
-                            return 2;
-                        }
+                    
+                    JsonNode auditParam = dto.getAuditParam();
+                    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);
+                    
+                    // 如果审核不通过,记录原因并提前结束
+                    if (!auditResult.isPassed()) {
+                        log.warn("AI内容审核未通过: {}", auditResult.getFailureReason());
+                        failureReasonHolder.set(auditResult.getFailureReason());
+                        return 2; // 返回2表示审核失败
                     }
+                    
+                    // 审核通过,执行入库操作(状态设为8:待生成海报)
+                    activity.setStatus(8);
+                    result = activityMapper.insert(activity);
                     // 使用用户描述和页面输入框其他信息,让AI生成海报图片。
                     if (dto.getUploadImgType() == 2) {
                         // 格式化输入AI参数

+ 209 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/util/AiContentModerationUtil.java

@@ -0,0 +1,209 @@
+package shop.alien.storeplatform.util;
+
+import com.alibaba.fastjson2.JSONObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.http.*;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 通用图文审核工具类
+ * 调用AI图文审核接口审核文本和图片内容
+ */
+@Slf4j
+@Component
+@RefreshScope
+@RequiredArgsConstructor
+public class AiContentModerationUtil {
+
+    private final RestTemplate restTemplate;
+
+    /**
+     * AI审核接口地址
+     */
+    @Value("${ai.service.moderate-url}")
+    private String moderateUrl;
+
+    /**
+     * 审核结果类
+     */
+    public static class AuditResult {
+        private boolean passed;
+        private String failureReason;
+
+        public AuditResult(boolean passed, String failureReason) {
+            this.passed = passed;
+            this.failureReason = failureReason;
+        }
+
+        public boolean isPassed() {
+            return passed;
+        }
+
+        public String getFailureReason() {
+            return failureReason;
+        }
+    }
+
+    /**
+     * 审核文本和图片内容
+     *
+     * @param text 文本内容
+     * @param imageUrls 图片URL列表
+     * @return 审核结果
+     */
+    public AuditResult auditContent(String text, List<String> imageUrls) {
+        log.info("开始审核内容:text={}, imageCount={}",
+                text, imageUrls != null ? imageUrls.size() : 0);
+
+        try {
+            // 如果没有任何内容,直接返回审核通过,避免400错误
+            if (!StringUtils.hasText(text) && (imageUrls == null || imageUrls.isEmpty())) {
+                log.info("审核内容为空,自动跳过审核");
+                return new AuditResult(true, null);
+            }
+
+            // 调用审核接口
+            return callModerateApi(text, imageUrls);
+
+        } catch (Exception e) {
+            log.error("审核内容异常", e);
+            return new AuditResult(false, "审核异常");
+        }
+    }
+
+    /**
+     * 调用AI审核接口
+     *
+     * @param text 文本内容
+     * @param imageUrls 图片URL列表
+     * @return 审核结果
+     */
+    private AuditResult callModerateApi(String text, List<String> imageUrls) {
+        try {
+            // 构建 form-data 请求头
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+
+            // 构建 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);
+
+            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, "审核异常");
+            }
+
+        } catch (Exception e) {
+            log.error("调用AI审核接口异常", e);
+            return new AuditResult(false, "审核异常");
+        }
+    }
+
+    /**
+     * 解析审核结果
+     *
+     * @param jsonResponse API响应JSON
+     * @return 审核结果
+     */
+    private AuditResult parseAuditResult(JSONObject jsonResponse) {
+        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");
+            if (results == null || results.isEmpty()) {
+                log.warn("AI审核接口返回结果为空");
+                return new AuditResult(false, "审核异常");
+            }
+
+            // 检查是否有任何项目被标记为违规
+            List<String> violationReasons = new ArrayList<>();
+            boolean hasViolations = false;
+
+            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("内容违规");
+                        }
+                    }
+                }
+            }
+
+            if (hasViolations) {
+                // 有违规内容,审核失败
+                String failureReason = String.join("; ", violationReasons);
+                log.warn("AI审核失败:{}", failureReason);
+                return new AuditResult(false, failureReason);
+            } else {
+                // 所有内容都安全,审核通过
+                log.info("AI审核通过:所有内容安全");
+                return new AuditResult(true, null);
+            }
+
+        } catch (Exception e) {
+            log.error("解析审核结果异常", e);
+            return new AuditResult(false, "审核异常");
+        }
+    }
+}

+ 17 - 42
alien-store/src/main/java/shop/alien/store/controller/CommonRatingController.java

@@ -147,47 +147,22 @@ public class CommonRatingController {
         return R.fail("删除评价失败");
     }
 
-//
-//    @ApiOperation("更新评价")
-//    @PutMapping("/update")
-//    public R<Boolean> update(@RequestBody CommonRating commonRating) {
-//        log.info("CommonRatingController.update?commonRating={}", commonRating);
-//        return R.data(commonRatingService.updateById(commonRating));
-//    }
-//
-//    @ApiOperation("删除评价")
-//    @ApiImplicitParam(name = "id", value = "评价ID", dataType = "Long", paramType = "path", required = true)
-//    @DeleteMapping("/delete/{id}")
-//    public R<Boolean> delete(@PathVariable Long id) {
-//        log.info("CommonRatingController.delete?id={}", id);
-//        return R.data(commonRatingService.removeById(id));
-//    }
-//
-//    @ApiOperation("获取平均评分")
-//    @ApiImplicitParams({
-//            @ApiImplicitParam(name = "businessType", value = "业务类型", dataType = "Integer", paramType = "query", required = true),
-//            @ApiImplicitParam(name = "businessId", value = "业务ID", dataType = "Long", paramType = "query", required = true)
-//    })
-//    @GetMapping("/getAverageScore")
-//    public R<Double> getAverageScore(
-//            @RequestParam Integer businessType,
-//            @RequestParam Long businessId) {
-//        log.info("CommonRatingController.getAverageScore?businessType={}&businessId={}", businessType, businessId);
-//        return R.data(commonRatingService.getAverageScore(businessType, businessId));
-//    }
-//
-//    @ApiOperation("获取评价数量")
-//    @ApiImplicitParams({
-//            @ApiImplicitParam(name = "businessType", value = "业务类型", dataType = "Integer", paramType = "query", required = true),
-//            @ApiImplicitParam(name = "businessId", value = "业务ID", dataType = "Long", paramType = "query", required = true)
-//    })
-//    @GetMapping("/getRatingCount")
-//    public R<Long> getRatingCount(
-//            @RequestParam Integer businessType,
-//            @RequestParam Long businessId) {
-//        log.info("CommonRatingController.getRatingCount?businessType={}&businessId={}", businessType, businessId);
-//        return R.data(commonRatingService.getRatingCount(businessType, businessId));
-//    }
+    // 查询我自己的评价
+    @ApiOperation("查询我的评价")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "当前页码", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "每页数量", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "businessType", value = "业务类型", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "Long", paramType = "query", required = true),
+            @ApiImplicitParam(name = "auditStatus", value = "审核状态", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/getMyRatingList")
+    public R getMyRatingList(@RequestParam(defaultValue = "1") Integer pageNum,
+                             @RequestParam(defaultValue = "10") Integer pageSize,
+                             @RequestParam Integer businessType,
+                             @RequestParam Long userId,
+                             @RequestParam(required = false) Integer auditStatus) {
+        log.info("CommonRatingController.getMyRatingList?pageNum={}&pageSize={}&businessType={}&userId={}&auditStatus={}", pageNum, pageSize, businessType, userId, auditStatus);
+        return commonRatingService.getMyRatingList(pageNum, pageSize, businessType, userId, auditStatus);
+    }
 
 }
-

+ 11 - 0
alien-store/src/main/java/shop/alien/store/service/CommonRatingService.java

@@ -61,6 +61,17 @@ public interface CommonRatingService extends IService<CommonRating> {
      */
     void updateStoreScoreAfterDelete(Integer businessId);
 
+    /**
+     * 查询我的评价
+     * @param pageNum
+     * @param pageSize
+     * @param businessType
+     * @param userId
+     * @param auditStatus
+     * @return
+     */
+    R getMyRatingList(Integer pageNum, Integer pageSize, Integer businessType, Long userId, Integer auditStatus);
+
 
   /*  /**
      * 根据业务类型和业务ID获取平均评分

+ 18 - 1
alien-store/src/main/java/shop/alien/store/service/impl/CommonRatingServiceImpl.java

@@ -323,7 +323,8 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
                 wrapper.eq(CommonRating::getId, -1);
             }
         }
-
+        // 只查询审核通过的评价
+        wrapper.eq(CommonRating::getAuditStatus, 1);
         wrapper.eq(CommonRating::getIsShow, 1);
         wrapper.orderByDesc(CommonRating::getId);
         IPage<CommonRating> page1 = this.page(page, wrapper);
@@ -778,6 +779,22 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
         }
     }
 
+    @Override
+    public R getMyRatingList(Integer pageNum, Integer pageSize, Integer businessType, Long userId, Integer auditStatus) {
+        Page<CommonRating> page = new Page<>(pageNum, pageSize);
+        // 改成queryWrapper
+        QueryWrapper<CommonRating> wrapper = new QueryWrapper<>();
+        wrapper.eq(businessType!=null,"cr.business_type", businessType)
+                .eq(userId != null, "cr.user_id", userId)
+                .eq("cr.delete_flag", 0)
+                .eq(auditStatus != null,"cr.audit_status", auditStatus);
+        wrapper.eq("cr.is_show", 1);
+        wrapper.orderByDesc("cr.created_time");
+        IPage<CommonRating> page1 = commonRatingMapper.getMyRatingList(page, wrapper);
+        R<IPage<CommonRatingVo>> iPageR = doListBusinessWithType(page1, businessType, userId, null);
+        return iPageR;
+    }
+
 /*
     @Override
     public Double getAverageScore(Integer businessType, Long businessId) {