Эх сурвалжийг харах

feat(ai): 集成AI审核任务功能

- 新增AI任务工具类AiTaskUtils,支持获取访问令牌和创建审核任务
- 实现用户违规举报的AI审核任务创建与结果查询功能
- 在商品审核流程中集成第二轮AI审核机制
- 添加定时任务goodsCheckJob和AiUserViolationJob处理AI审核结果
- 支持聊天记录文本提取与格式化用于AI分析
- 实现商品图片与描述的AI审核任务调度逻辑
- 提供完整的AI服务认证与任务状态管理功能
fcw 1 долоо хоног өмнө
parent
commit
f4ae954134

+ 1 - 1
alien-gateway/src/main/resources/bootstrap.yml

@@ -1,3 +1,3 @@
 spring:
   profiles:
-    active: dev
+    active: test

+ 4 - 0
alien-job/src/main/java/shop/alien/job/feign/SecondGoodsFeign.java

@@ -3,6 +3,7 @@ package shop.alien.job.feign;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestParam;
+import shop.alien.entity.second.SecondGoods;
 
 @FeignClient(name = "alien-second", url = "${feign.alienSecond.url}")
 public interface SecondGoodsFeign {
@@ -24,4 +25,7 @@ public interface SecondGoodsFeign {
      */
     @GetMapping("/video/moderation/processTask")
     boolean processTask(@RequestParam("taskId") String taskId);
+
+    @GetMapping("/secondGoods/approveAndListGoods")
+    boolean approveAndListGoods(SecondGoods goods);
 }

+ 180 - 0
alien-job/src/main/java/shop/alien/job/second/AiUserViolationJob.java

@@ -0,0 +1,180 @@
+package shop.alien.job.second;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+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 shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.store.LifeUserViolation;
+import shop.alien.entity.store.SecondAiTask;
+import shop.alien.mapper.LifeUserViolationMapper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AiUserViolationJob {
+
+    private final RestTemplate restTemplate;
+    private final LifeUserViolationMapper lifeUserViolationMapper;
+
+    @Value("${third-party-user-name.base-url}")
+    private String userName;
+
+    @Value("${third-party-pass-word.base-url}")
+    private String passWord;
+
+    private String loginUrl = "http://192.168.2.78:9000/ai/user-auth-core/api/v1/auth/login";
+
+    private String aiUserViolationCheckUrl = "http://192.168.2.78:9000/ai/auto-review/api/v1/user_complaint_record/result";
+
+    @XxlJob("getAiGoodsCheckResult")
+    public R<String> getAiUserViolationResult() {
+        String accessToken = fetchAiServiceToken();
+        if (!StringUtils.hasText(accessToken)) {
+            return R.fail("调用AI举报用户辅助系统 登录接口失败");
+        }
+        return getAiUserViolationCheck(accessToken);
+    }
+
+    private String fetchAiServiceToken() {
+        log.info("登录Ai服务获取token...{}", loginUrl);
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username", userName);
+        formData.add("password", passWord);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
+
+        try {
+            ResponseEntity<String> response = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+            if (response != null && response.getStatusCodeValue() == 200 && response.getBody() != null) {
+                JSONObject jsonObject = JSONObject.parseObject(response.getBody());
+                JSONObject dataJson = jsonObject.getJSONObject("data");
+                return dataJson != null ? dataJson.getString("access_token") : null;
+            }
+            log.error("请求差评申诉辅助系统 登录接口失败 http状态:{}", response != null ? response.getStatusCode() : null);
+        } catch (Exception e) {
+            log.error("调用差评申诉辅助系统登录接口异常", e);
+        }
+        return null;
+    }
+
+    private R<String> getAiUserViolationCheck(String accessToken) {
+
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + accessToken);
+        // 查询所有状态为处理中的申诉
+        List<LifeUserViolation> pendingTasks = lifeUserViolationMapper.selectList(
+                new QueryWrapper<LifeUserViolation>()
+                        .eq("status", "0")
+        );
+
+        // 循环调用查询结果接口
+        for (LifeUserViolation task : pendingTasks) {
+            String completedUrl = buildCompletedUrl(task.getAiTaskId());
+
+            ResponseEntity<String> analyzeResp;
+
+            RestTemplate restTemplateWithAuth = new RestTemplate();
+            List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
+            interceptors.add((request, body, execution) -> {
+                request.getHeaders().set("Authorization", "Bearer " + accessToken);
+                return execution.execute(request, body);
+            });
+            restTemplateWithAuth.setInterceptors(interceptors);
+
+            ResponseEntity<String> response = null;
+
+            try {
+                analyzeResp = restTemplateWithAuth.getForEntity(completedUrl, String.class);
+
+                if (analyzeResp != null && analyzeResp.getStatusCodeValue() == 200) {
+                    String analyzeBody = analyzeResp.getBody();
+                    log.info("AI举报用户审核提交成功, 返回: {}", analyzeBody);
+
+                    JSONObject analyzeJson = JSONObject.parseObject(analyzeBody);
+                    JSONObject dataJsonObj = analyzeJson.getJSONObject("data");
+
+                    if (dataJsonObj == null) {
+                        log.error("AI举报用户审核返回数据为空");
+                        R.fail("AI举报用户审核返回数据为空");
+                        continue;
+                    }
+
+                    // 获取task_id用于后续查询
+                    String taskId = dataJsonObj.getString("task_id");
+                    if (taskId == null) {
+                        log.error("AI举报用户审核返回task_id为空");
+                        R.fail("AI举报用户审核返回task_id为空");
+                        continue;
+                    }
+
+                    LifeUserViolation aiTask = new LifeUserViolation();
+                    aiTask.setAiTaskId(taskId);
+                    if (dataJsonObj.getString("status").equals("pending")) {
+                        R.fail("审核未结束");
+                        continue;
+                    }
+
+                    if (dataJsonObj.getString("status").equals("completed")) {
+                        if (dataJsonObj.getString("is_valid").isEmpty() && dataJsonObj.getString("is_valid").equals("true")) {
+                            aiTask.setProcessingStatus("1");
+                        } else {
+                            aiTask.setProcessingStatus("0");
+                        }
+                    }
+
+                    QueryWrapper<LifeUserViolation> queryWrapper = new QueryWrapper<>();
+                    queryWrapper.eq("ai_task_id", taskId);
+                    LifeUserViolation lifeUserViolation = lifeUserViolationMapper.selectOne(queryWrapper);
+                    if (lifeUserViolation == null) {
+                        log.error("AI举报用户不存在");
+                        R.fail("AI举报用户不存在");
+                        continue;
+                    }
+                    lifeUserViolationMapper.update(aiTask, queryWrapper);
+
+                } else {
+                    if (analyzeResp != null) {
+                        log.error("调用AI举报用户审核接口失败, http状态: {}", analyzeResp.getStatusCode());
+                        R.fail("调用AI举报用户审核接口失败, http状态: " + analyzeResp.getStatusCode());
+                    }
+                }
+            } catch (Exception e) {
+                log.error("调用差评申述查询结果接口异常", e);
+            }
+        }
+        return R.success("调用AI举报用户审核结果接口完成");
+    }
+
+    private String buildCompletedUrl(String recordId) {
+        String baseUrl = aiUserViolationCheckUrl;
+        if (!StringUtils.hasText(baseUrl)) {
+            throw new IllegalStateException("ai举报用户接口地址未配置");
+        }
+        if (baseUrl.endsWith("/")) {
+            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
+        }
+        // 构建新的URL格式: /api/v1/audit_task/getResult?task_id={recordId}
+        return baseUrl + "?task_id=" + recordId;
+    }
+}

+ 192 - 0
alien-job/src/main/java/shop/alien/job/second/goodsCheckJob.java

@@ -0,0 +1,192 @@
+package shop.alien.job.second;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+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 shop.alien.entity.result.R;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.store.SecondAiTask;
+import shop.alien.entity.store.StoreComment;
+import shop.alien.entity.store.StoreCommentAppeal;
+import shop.alien.job.feign.SecondGoodsFeign;
+import shop.alien.mapper.SecondAiTaskMapper;
+import shop.alien.mapper.second.SecondGoodsMapper;
+import shop.alien.second.service.SecondGoodsAuditService;
+import shop.alien.second.service.impl.SecondGoodsAuditServiceImpl;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class goodsCheckJob {
+
+    private final RestTemplate restTemplate;
+
+    private final SecondAiTaskMapper secondAiTaskMapper;
+    private final SecondGoodsMapper secondGoodsMapper;
+
+    private final SecondGoodsFeign secondGoodsFeign;
+
+    private String loginUrl = "http://192.168.2.78:9000/ai/user-auth-core/api/v1/auth/login";
+
+    private String goodsCheckUrl = "http://192.168.2.78:9000/ai/auto-review/api/v1/audit_task/getResult";
+
+    @Value("${third-party-user-name.base-url}")
+    private String userName;
+
+    @Value("${third-party-pass-word.base-url}")
+    private String passWord;
+
+    @XxlJob("getAiGoodsCheckResult")
+    public R<String> getAiGoodsCheckResult() {
+        String accessToken = fetchAiServiceToken();
+        if (!StringUtils.hasText(accessToken)) {
+            return R.fail("调用差评申诉辅助系统 登录接口失败");
+        }
+        return getGoodsCheck(accessToken);
+    }
+
+
+    private String fetchAiServiceToken() {
+        log.info("登录Ai服务获取token...{}", loginUrl);
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username", userName);
+        formData.add("password", passWord);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
+
+        try {
+            ResponseEntity<String> response = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+            if (response != null && response.getStatusCodeValue() == 200 && response.getBody() != null) {
+                JSONObject jsonObject = JSONObject.parseObject(response.getBody());
+                JSONObject dataJson = jsonObject.getJSONObject("data");
+                return dataJson != null ? dataJson.getString("access_token") : null;
+            }
+            log.error("请求差评申诉辅助系统 登录接口失败 http状态:{}", response != null ? response.getStatusCode() : null);
+        } catch (Exception e) {
+            log.error("调用差评申诉辅助系统登录接口异常", e);
+        }
+        return null;
+    }
+
+
+    private R<String> getGoodsCheck(String accessToken) {
+
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + accessToken);
+        // 查询所有状态为处理中的申诉
+        List<SecondAiTask> pendingTasks = secondAiTaskMapper.selectList(
+                new QueryWrapper<SecondAiTask>()
+                        .eq("status", "PROCESSING")
+        );
+
+        // 循环调用查询结果接口
+        for (SecondAiTask task : pendingTasks) {
+            String completedUrl = buildCompletedUrl(task.getTaskId());
+
+            ResponseEntity<String> analyzeResp;
+
+            RestTemplate restTemplateWithAuth = new RestTemplate();
+            List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
+            interceptors.add((request, body, execution) -> {
+                request.getHeaders().set("Authorization", "Bearer " + accessToken);
+                return execution.execute(request, body);
+            });
+            restTemplateWithAuth.setInterceptors(interceptors);
+
+            ResponseEntity<String> response = null;
+
+            try {
+                analyzeResp = restTemplateWithAuth.getForEntity(completedUrl, String.class);
+
+                if (analyzeResp != null && analyzeResp.getStatusCodeValue() == 200) {
+                    String analyzeBody = analyzeResp.getBody();
+                    log.info("商品审核提交成功, 返回: {}", analyzeBody);
+
+                    JSONObject analyzeJson = JSONObject.parseObject(analyzeBody);
+                    JSONObject dataJsonObj = analyzeJson.getJSONObject("data");
+
+                    if (dataJsonObj == null) {
+                        log.error("商品审核返回数据为空");
+                        R.fail("商品审核返回数据为空");
+                        continue;
+                    }
+
+                    // 获取task_id用于后续查询
+                    String taskId = dataJsonObj.getString("task_id");
+                    if (taskId == null) {
+                        log.error("商品审核返回task_id为空");
+                        R.fail("商品审核返回task_id为空");
+                        continue;
+                    }
+
+                    SecondAiTask aiTask = new SecondAiTask();
+                    aiTask.setTaskId(taskId);
+                    if (dataJsonObj.getString("status").equals("pending")) {
+                        R.fail("审核未结束");
+                        continue;
+                    }
+
+                    if (dataJsonObj.getString("status").equals("done")) {
+                        aiTask.setStatus("SUCCESS");
+                    }
+
+                    aiTask.setResult(dataJsonObj.toJSONString());
+
+                    QueryWrapper<SecondGoods> queryWrapper = new QueryWrapper<>();
+                    queryWrapper.eq("ai_task_id", taskId);
+                    SecondGoods goods = secondGoodsMapper.selectOne(queryWrapper);
+                    if (goods == null) {
+                        log.error("商品不存在");
+                        R.fail("商品不存在");
+                        continue;
+                    }
+                    secondGoodsFeign.approveAndListGoods(goods);
+
+                } else {
+                    if (analyzeResp != null) {
+                        log.error("调用商品审核接口失败, http状态: {}", analyzeResp.getStatusCode());
+                        R.fail("调用商品审核接口失败, http状态: " + analyzeResp.getStatusCode());
+                    }
+                }
+            } catch (Exception e) {
+                log.error("调用差评申述查询结果接口异常", e);
+            }
+        }
+        return R.success("调用商品审核结果接口完成");
+    }
+
+
+    private String buildCompletedUrl(String recordId) {
+        String baseUrl = goodsCheckUrl;
+        if (!StringUtils.hasText(baseUrl)) {
+            throw new IllegalStateException("差评申述分析接口地址未配置");
+        }
+        if (baseUrl.endsWith("/")) {
+            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
+        }
+        // 构建新的URL格式: /api/v1/audit_task/getResult?task_id={recordId}
+        return baseUrl + "?task_id=" + recordId;
+    }
+}

+ 1 - 1
alien-job/src/main/resources/bootstrap.yml

@@ -1,3 +1,3 @@
 spring:
   profiles:
-    active: dev
+    active: test

+ 12 - 0
alien-second/src/main/java/shop/alien/second/service/SecondAiTaskService.java

@@ -0,0 +1,12 @@
+package shop.alien.second.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.SecondAiTask;
+
+/**
+ * second_ai_task 服务接口
+ */
+public interface SecondAiTaskService extends IService<SecondAiTask> {
+}
+
+

+ 84 - 0
alien-second/src/main/java/shop/alien/second/service/SecondGoodsAuditService.java

@@ -0,0 +1,84 @@
+package shop.alien.second.service;
+
+import org.springframework.stereotype.Service;
+import shop.alien.entity.SecondVideoTask;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+
+import java.util.List;
+
+/**
+ * 二手商品审核服务接口
+ * 负责商品的图片、文本、视频审核相关业务逻辑
+ */
+public interface SecondGoodsAuditService {
+
+    /**
+     * 执行内容审核(图片、文本和视频)
+     * @param goodsDTO 商品信息DTO
+     * @param goods 商品实体
+     * @throws Exception 审核过程中可能抛出的异常
+     */
+    void performContentReview(SecondGoodsVo goodsDTO, SecondGoods goods) throws Exception;
+
+    /**
+     * 执行图片审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果,true表示通过,false表示不通过
+     * @throws Exception 审核过程中可能抛出的异常
+     */
+    boolean performImageReviews(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception;
+
+    /**
+     * 执行文本审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果,true表示通过,false表示不通过
+     * @throws Exception 审核过程中可能抛出的异常
+     */
+    boolean performTextReview(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception;
+
+    /**
+     * 执行视频审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 视频审核任务ID列表
+     */
+    List<String> performVideoReviews(SecondGoods goods, SecondGoodsVo goodsDTO);
+
+    /**
+     * 处理视频审核结果
+     * @param task 视频审核任务
+     */
+    void processVideoModerationResult(SecondVideoTask task);
+
+    /**
+     * 审核通过后上架商品
+     * @param goods 商品信息
+     */
+    void approveAndListGoods(SecondGoods goods);
+
+    /**
+     * 创建商品审核记录
+     * @param goods 商品信息
+     * @param failReason 审核失败原因
+     * @param goodsStatus 商品状态
+     */
+    void createGoodsAudit(SecondGoods goods, String failReason, Integer goodsStatus);
+
+    /**
+     * 从图片URL列表中提取视频URL
+     * @param imageUrls 图片URL列表
+     * @return 视频URL列表
+     */
+    List<String> extractVideoUrls(List<String> imageUrls);
+
+    /**
+     * 判断URL是否为视频地址
+     * @param url 输入URL
+     * @return 是否为视频地址
+     */
+    boolean isVideoUrl(String url);
+}
+

+ 546 - 0
alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsAuditServiceImpl.java

@@ -0,0 +1,546 @@
+package shop.alien.second.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.google.common.collect.Lists;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.SecondVideoTask;
+import shop.alien.entity.second.SecondGoods;
+import shop.alien.entity.second.SecondGoodsAudit;
+import shop.alien.entity.second.SecondGoodsRecord;
+import shop.alien.entity.second.enums.SecondGoodsStatusEnum;
+import shop.alien.entity.second.vo.SecondGoodsVo;
+import shop.alien.entity.store.SecondAiTask;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.mapper.SecondAiTaskMapper;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.mapper.second.SecondGoodsAuditMapper;
+import shop.alien.mapper.second.SecondGoodsMapper;
+import shop.alien.mapper.second.SecondGoodsRecordMapper;
+import shop.alien.second.service.*;
+import shop.alien.second.util.AiTaskUtils;
+import shop.alien.util.common.Constants;
+import shop.alien.util.common.safe.ImageModerationResultVO;
+import shop.alien.util.common.safe.ImageModerationUtil;
+import shop.alien.util.common.safe.ImageReviewServiceEnum;
+import shop.alien.util.common.safe.TextModerationResultVO;
+import shop.alien.util.common.safe.TextModerationUtil;
+import shop.alien.util.common.safe.TextReviewServiceEnum;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 二手商品审核服务实现类
+ * 负责商品的图片、文本、视频审核相关业务逻辑
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SecondGoodsAuditServiceImpl implements SecondGoodsAuditService {
+
+    private final SecondAiTaskService secondAiTaskService;
+    private final SecondAiTaskMapper secondAiTaskMapper;
+    private final StoreImgMapper storeImgMapper;
+    /**
+     * 视频审核功能是否启用的配置项
+     */
+    @Value("${video.moderation.enabled}")
+    private boolean videoModerationEnabled;
+
+    /**
+     * 视频审核失败时是否阻塞商品发布的配置项
+     */
+    @Value("${video.moderation.block-on-failure}")
+    private boolean videoModerationBlockOnFailure;
+
+    /**
+     * 视频审核服务,用于处理商品中视频内容的审核
+     */
+    private final VideoModerationService videoModerationService;
+
+    /**
+     * 文本审核工具,用于审核商品标题、描述等文本内容
+     */
+    private final TextModerationUtil textModerationUtil;
+
+    /**
+     * 图片审核工具,用于审核商品图片内容
+     */
+    private final ImageModerationUtil imageModerationUtil;
+
+    /**
+     * 二手商品Mapper
+     */
+    private final SecondGoodsMapper secondGoodsMapper;
+
+    /**
+     * 二手商品审核Mapper
+     */
+    private final SecondGoodsAuditMapper secondGoodsAuditMapper;
+
+    /**
+     * 商品操作历史记录Mapper
+     */
+    private final SecondGoodsRecordMapper secondGoodsRecordMapper;
+
+    /**
+     * 消息通知服务
+     */
+    private final SecondGoodsNotificationService notificationService;
+
+    /**
+     * 操作历史记录服务
+     */
+    private final SecondGoodsOperationRecordService operationRecordService;
+
+    private final AiTaskUtils aiTaskUtil;
+
+    /**
+     * 执行内容审核
+     * @param goodsDTO 商品信息
+     * @param goods 商品实体
+     */
+    @Override
+    public void performContentReview(SecondGoodsVo goodsDTO, SecondGoods goods) throws Exception {
+        // 图片审核
+        boolean imageAuditResult = performImageReviews(goods, goodsDTO);
+        
+        // 审核失败,直接返回
+        if (!imageAuditResult) {
+            // 图片审核不通过,记录操作历史
+            operationRecordService.recordGoodsOperation(goods, "图片审核失败");
+            return;
+        }
+
+        // 文本审核
+        boolean textAuditResult = performTextReview(goods, goodsDTO);
+        
+        // 审核失败,直接返回
+        if (!textAuditResult) {
+            // 文本审核不通过,记录操作历史
+            operationRecordService.recordGoodsOperation(goods, "文本审核失败");
+            return;
+        }
+        
+        // 视频审核
+        List<String> taskIds = performVideoReviews(goods, goodsDTO);
+
+        // 如果成功提交了视频审核任务,设置商品状态为审核中
+        if (!taskIds.isEmpty()) {
+            goods.setGoodsStatus(SecondGoodsStatusEnum.UNDER_REVIEW.getCode()); // 审核中
+            goods.setVideoTaskId(taskIds.get(0)); // 保存第一个任务ID到商品表
+            goods.setFailedReason("");
+            secondGoodsMapper.updateById(goods);
+            // 审核中审核记录
+            createGoodsAudit(goods, "", Constants.AuditStatus.UNDER_REVIEW);
+        } else {
+            // 第二轮AI审核
+            boolean b = performSecondRoundReview(goods, goodsDTO);
+            if (!b) {
+                // 第二轮审核失败,记录操作历史
+                operationRecordService.recordGoodsOperation(goods, "第二轮审核失败");
+                goods.setGoodsStatus(2);
+                goods.setFailedReason("调用AI接口失败,未获取到task_id");
+                secondGoodsMapper.updateById(goods);
+            }
+        }
+        
+        // 审核通过后上架商品
+//        approveAndListGoods(goods);
+    }
+
+    // 第二轮审核(AI)
+    private boolean performSecondRoundReview(SecondGoods goods, SecondGoodsVo goodsDTO) {
+        try {
+            // 参数校验
+            if (goodsDTO == null || CollectionUtil.isEmpty(goodsDTO.getImgUrl())) {
+                log.warn("Second round review skipped: empty image URLs for goods id={}",
+                        goods != null ? goods.getId() : "unknown");
+                return false;
+            }
+
+            // 获取访问令牌
+            String accessToken = aiTaskUtil.getAccessToken();
+            if (StringUtils.isEmpty(accessToken)) {
+                log.error("Failed to obtain access token for second round review, goods id={}", goods.getId());
+                return false;
+            }
+
+            // 创建AI任务
+            String taskId = aiTaskUtil.createTask(accessToken, goods.getDescription(), goodsDTO.getImgUrl());
+            if (StringUtils.isEmpty(taskId)) {
+                log.warn("Failed to create AI task for second round review, goods id={}", goods.getId());
+                return false;
+            }
+            goods.setAiTaskId(taskId);
+            goods.setGoodsStatus(SecondGoodsStatusEnum.UNDER_REVIEW.getCode());
+            secondGoodsMapper.updateById(goods);
+
+            // 保存任务信息
+            SecondAiTask secondAiTask = new SecondAiTask();
+            secondAiTask.setTaskId(taskId);
+            secondAiTask.setStatus("PROCESSING");
+            Date currentTime = new Date();
+            secondAiTask.setCreateTime(currentTime);
+            secondAiTask.setUpdateTime(currentTime);
+            secondAiTaskMapper.insert(secondAiTask);
+
+            log.info("Successfully created second round AI review task, taskId={}, goodsId={}",
+                    taskId, goods.getId());
+            return true;
+        } catch (Exception e) {
+            log.error("Error during second round review for goods id={}",
+                    goods != null ? goods.getId() : "unknown", e);
+            return false;
+        }
+    }
+
+
+    /**
+     * 执行图片审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    @Override
+    public boolean performImageReviews(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception {
+        // 图片审核(循环处理)
+        List<String> imageUrls = goodsDTO.getImgUrl();
+        // 根据imageUrls过滤不是图片的url
+        imageUrls = imageUrls.stream()
+                .filter(url -> url.toLowerCase().endsWith(".jpg") 
+                        || url.toLowerCase().endsWith(".png") 
+                        || url.toLowerCase().endsWith(".jpeg") 
+                        || url.toLowerCase().endsWith(".gif"))
+                .collect(Collectors.toList());
+        
+        // 图片审核
+        if (imageUrls != null && !imageUrls.isEmpty()) {
+            for (String imageUrl : imageUrls) {
+                List<String> imgServicesList = Lists.newArrayList();
+                // 内容治理检测 + AIGC图片风险检测
+                imgServicesList.add(ImageReviewServiceEnum.TONALITY_IMPROVE.getService());
+                imgServicesList.add(ImageReviewServiceEnum.AIGC_CHECK.getService());
+                
+                ImageModerationResultVO response = imageModerationUtil.productPublishCheck(imageUrl, imgServicesList);
+                if ("high".equals(response.getRiskLevel())) {
+                    // 图片审核不通过或存在高风险
+                    goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode()); // 审核失败
+                    String failReason = "图片审核不通过:图片中包含" + 
+                            (response.getDescriptions() != null ? response.getDescriptions() : "高风险内容");
+                    goods.setFailedReason(failReason);
+                    // 插入审核记录
+                    createGoodsAudit(goods, failReason, Constants.AuditStatus.FAILED);
+                    // 发送审核失败消息
+                    notificationService.sendFailedMsg(goods);
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 执行文本审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    @Override
+    public boolean performTextReview(SecondGoods goods, SecondGoodsVo goodsDTO) throws Exception {
+        List<String> servicesList = Lists.newArrayList();
+        servicesList.add(TextReviewServiceEnum.AD_COMPLIANCE_DETECTION_PRO.getService());
+        servicesList.add(TextReviewServiceEnum.LLM_QUERY_MODERATION.getService());
+        
+        // 使用商品发布场景的审核服务
+        String test = goodsDTO.getDescription() + goodsDTO.getTitle() + goods.getLabel() + goods.getTopic();
+        TextModerationResultVO textCheckResult = textModerationUtil.invokeFunction(test, servicesList);
+
+        if ("high".equals(textCheckResult.getRiskLevel())) {
+            // 文本审核不通过或存在高风险
+            goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode()); // 审核失败
+            String failReason = "文本审核不通过:" + 
+                    (textCheckResult.getRiskWords() != null ? textCheckResult.getRiskWords() : "存在高风险内容");
+            goods.setFailedReason(failReason);
+            // 插入审核记录
+            createGoodsAudit(goods, failReason, Constants.AuditStatus.FAILED);
+            // 发送审核失败消息
+            notificationService.sendFailedMsg(goods);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 执行视频审核
+     * @param goods 商品信息
+     * @param goodsDTO 商品DTO信息
+     * @return 审核结果
+     */
+    @Override
+    public List<String> performVideoReviews(SecondGoods goods, SecondGoodsVo goodsDTO) {
+        List<String> videoUrls = extractVideoUrls(goodsDTO.getImgUrl());
+        List<String> taskIds = new ArrayList<>();
+        
+        // 视频审核
+        if (videoModerationEnabled) {
+            if (!videoUrls.isEmpty()) {
+                // 提交视频审核任务
+                for (String videoUrl : videoUrls) {
+                    try {
+                        String taskId = videoModerationService.submitVideoModerationTask(videoUrl);
+                        taskIds.add(taskId);
+                    } catch (Exception e) {
+                        log.error("提交视频审核任务失败,视频URL: {}", videoUrl, e);
+                        if (videoModerationBlockOnFailure) {
+                            // 视频审核提交失败,设置为审核失败状态
+                            goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode());
+                            goods.setFailedReason("视频审核提交失败: " + e.getMessage());
+                            createGoodsAudit(goods, "视频审核提交失败", Constants.AuditStatus.FAILED);
+                            notificationService.sendFailedMsg(goods);
+                        }
+                    }
+                }
+            }
+        }
+        return taskIds;
+    }
+
+    /**
+     * 处理视频审核结果
+     * @param task 视频审核任务
+     */
+    @Override
+    public void processVideoModerationResult(SecondVideoTask task) {
+        try {
+            // 查找关联的商品
+            QueryWrapper<SecondGoods> queryWrapper = new QueryWrapper<>();
+            queryWrapper.eq("video_task_id", task.getTaskId());
+            SecondGoods goods = secondGoodsMapper.selectOne(queryWrapper);
+            
+            if (goods == null) {
+                log.warn("未找到关联的商品,任务ID: {}", task.getTaskId());
+                return;
+            }
+
+
+            // 根据审核结果更新商品状态
+            if ("none".equals(task.getRiskLevel())) {
+                QueryWrapper<StoreImg> imgQueryWrapper = new QueryWrapper<>();
+                imgQueryWrapper.eq("store_id", goods.getId());
+                imgQueryWrapper.eq("img_type", 19);
+                List<StoreImg> storeImgs = storeImgMapper.selectList(imgQueryWrapper);
+                List<String> imgUrls = storeImgs.stream()
+                        .map(StoreImg::getImgUrl)
+                        .filter(imgUrl -> StringUtils.hasText(imgUrl))
+                        .collect(Collectors.toList());
+
+                SecondGoodsVo goodsDTO = new SecondGoodsVo();
+                goodsDTO.setImgUrl(imgUrls);
+
+                // 开始第二轮审核
+                boolean b = performSecondRoundReview(goods, goodsDTO);
+
+                // 审核通过
+//                approveAndListGoods(goods);
+            } else {
+                // 审核不通过
+                goods.setGoodsStatus(SecondGoodsStatusEnum.REVIEW_FAILED.getCode());
+                
+                // 解析审核结果,生成具体的失败原因
+                String failedReason = parseVideoModerationFailureReason(task);
+                goods.setFailedReason(failedReason);
+                secondGoodsMapper.updateById(goods);
+                
+                // 更新审核记录
+                createGoodsAudit(goods, failedReason, Constants.AuditStatus.FAILED);
+
+                // 记录操作历史
+                operationRecordService.recordGoodsOperation(goods, "视频审核失败");
+                // 发送审核失败消息
+                notificationService.sendFailedMsg(goods);
+            }
+        } catch (Exception e) {
+            log.error("处理视频审核结果时发生异常,任务ID: {}", task.getTaskId(), e);
+        }
+    }
+
+    /**
+     * 审核通过后上架商品
+     * @param goods 商品信息
+     */
+    @Override
+    public void approveAndListGoods(SecondGoods goods) {
+        boolean isNotified = false;
+        try {
+            // 如果所有审核都通过,设置为上架状态
+            goods.setGoodsStatus(SecondGoodsStatusEnum.LISTED.getCode()); // 上架
+            goods.setFailedReason("");
+            goods.setReleaseTime(new Date()); // 上架时间
+            secondGoodsMapper.updateById(goods);
+            // 插入审核记录
+            createGoodsAudit(goods, "", Constants.AuditStatus.PASSED);
+            // 发送审核成功消息
+            notificationService.sendMessage(goods);
+            isNotified = true; // 标记通知已发送
+            
+            // 上架 记录商品操作历史
+            String operationName = "";
+            QueryWrapper<SecondGoodsRecord> queryWrapper = new QueryWrapper<>();
+            queryWrapper.eq("goods_id", goods.getId());
+            List<SecondGoodsRecord> recordList = secondGoodsRecordMapper.selectList(queryWrapper);
+            if (CollectionUtil.isNotEmpty(recordList)) {
+                operationName = "重新发布";
+            } else {
+                operationName = "首次发布";
+            }
+            operationRecordService.recordGoodsOperation(goods, operationName);
+        } catch (Exception e) {
+            log.error("商品上架过程中发生异常,执行回滚", e);
+            // 如果通知已发送但后续操作失败,需要补偿
+            if (isNotified) {
+                // 发送补偿消息,比如撤销通知或标记为异常状态
+            }
+            throw e;
+        }
+    }
+
+    /**
+     * 创建商品审核记录
+     * @param goods 商品信息
+     * @param failReason 审核失败原因
+     * @param goodsStatus 商品状态
+     */
+    @Override
+    public void createGoodsAudit(SecondGoods goods, String failReason, Integer goodsStatus) {
+        // 保存审核结果
+        secondGoodsMapper.updateById(goods);
+        // 插入审核记录
+        SecondGoodsAudit auditRecord = new SecondGoodsAudit();
+        auditRecord.setGoodsId(goods.getId());
+        auditRecord.setGoodsStatus(goodsStatus); // 审核状态
+        if (Constants.AuditStatus.FAILED.equals(goodsStatus)) {
+            auditRecord.setFailedReason(failReason);
+        }
+        auditRecord.setCreatedUserId(goods.getUserId());
+        auditRecord.setUpdatedUserId(goods.getUserId());
+        auditRecord.setCreatedTime(new Date());
+        auditRecord.setUpdatedTime(new Date());
+        secondGoodsAuditMapper.insert(auditRecord);
+        goods.setAuditRecordId(auditRecord.getId());
+    }
+
+    /**
+     * 从图片URL列表中提取视频URL
+     * @param imageUrls 图片URL列表
+     * @return 视频URL列表
+     */
+    @Override
+    public List<String> extractVideoUrls(List<String> imageUrls) {
+        if (CollectionUtil.isEmpty(imageUrls)) {
+            return Collections.emptyList();
+        }
+        
+        List<String> videoUrls = new ArrayList<>();
+        for (String url : imageUrls) {
+            if (isVideoUrl(url)) {
+                videoUrls.add(url);
+            }
+        }
+        return videoUrls;
+    }
+
+    /**
+     * 判断URL是否为视频地址
+     * @param url 输入URL
+     * @return 是否为视频地址
+     */
+    @Override
+    public boolean isVideoUrl(String url) {
+        if (url == null) return false;
+        url = url.toLowerCase();
+        return url.endsWith(".mp4") ||
+                url.endsWith(".avi") ||
+                url.endsWith(".mov") ||
+                url.endsWith(".flv") ||
+                url.endsWith(".wmv") ||
+                url.endsWith(".mkv");
+    }
+
+    /**
+     * 解析视频审核失败原因
+     * @param task 视频审核任务
+     * @return 失败原因
+     */
+    private String parseVideoModerationFailureReason(SecondVideoTask task) {
+        StringBuilder reasonBuilder = new StringBuilder("视频审核不通过,风险等级: " + task.getRiskLevel());
+        
+        try {
+            // 解析审核结果JSON
+            JSONObject resultJson = JSON.parseObject(task.getResult());
+            if (resultJson != null && resultJson.containsKey("data")) {
+                JSONObject data = resultJson.getJSONObject("data");
+                
+                // 处理帧结果(视频画面)
+                if (data.containsKey("FrameResult")) {
+                    JSONObject frameResult = data.getJSONObject("FrameResult");
+                    if (frameResult != null && frameResult.containsKey("FrameSummarys")) {
+                        JSONArray frameSummarys = frameResult.getJSONArray("FrameSummarys");
+                        if (frameSummarys != null && !frameSummarys.isEmpty()) {
+                            reasonBuilder.append("。检测到违规内容:");
+                            for (int i = 0; i < frameSummarys.size(); i++) {
+                                JSONObject summary = frameSummarys.getJSONObject(i);
+                                String label = summary.getString("Label");
+                                String description = summary.getString("Description");
+                                Integer labelSum = summary.getInteger("LabelSum");
+                                
+                                if (label != null && !label.isEmpty()) {
+                                    reasonBuilder.append("[").append(description != null ? description : label)
+                                            .append("]出现").append(labelSum != null ? labelSum : 1).append("次;");
+                                }
+                            }
+                        }
+                    }
+                }
+                
+                // 处理音频结果
+                if (data.containsKey("AudioResult")) {
+                    JSONObject audioResult = data.getJSONObject("AudioResult");
+                    if (audioResult != null && audioResult.containsKey("AudioSummarys")) {
+                        JSONArray audioSummarys = audioResult.getJSONArray("AudioSummarys");
+                        if (audioSummarys != null && !audioSummarys.isEmpty()) {
+                            reasonBuilder.append("。检测到违规音频:");
+                            for (int i = 0; i < audioSummarys.size(); i++) {
+                                JSONObject summary = audioSummarys.getJSONObject(i);
+                                String label = summary.getString("Label");
+                                String description = summary.getString("Description");
+                                Integer labelSum = summary.getInteger("LabelSum");
+                                
+                                if (label != null && !label.isEmpty()) {
+                                    reasonBuilder.append("[").append(description != null ? description : label)
+                                            .append("]出现").append(labelSum != null ? labelSum : 1).append("次;");
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.warn("解析视频审核结果失败,使用默认原因,任务ID: {}", task.getTaskId(), e);
+        }
+        
+        return reasonBuilder.toString();
+    }
+}
+

+ 146 - 0
alien-second/src/main/java/shop/alien/second/util/AiTaskUtils.java

@@ -0,0 +1,146 @@
+package shop.alien.second.util;
+
+import com.alibaba.fastjson2.JSONObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.map.HashedMap;
+import org.springframework.beans.factory.annotation.Value;
+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 shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreCommentAppeal;
+
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AiTaskUtils {
+
+    private final RestTemplate restTemplate;
+
+//    @Value("${third-party-login.base-url:http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login}")
+    private String loginUrl = "http://192.168.2.78:9000/ai/user-auth-core/api/v1/auth/login";
+
+    @Value("${third-party-user-name.base-url:UdUser}")
+    private String userName;
+
+    @Value("${third-party-pass-word.base-url:123456}")
+    private String passWord;
+
+    private String auditTaskUrl = "http://192.168.2.78:9000/ai/auto-review/api/v1/audit_task/product";
+
+
+
+    private String auditTaskResultUrl = "http://192.168.2.250:9000/ai/task-core/api/v1/audit_task/getResult";
+
+    /**
+     * 登录 AI 服务,获取 token
+     *
+     * @return accessToken
+     */
+    public String getAccessToken() {
+        log.info("登录Ai服务获取token...{}", loginUrl);
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username", userName);
+        formData.add("password", passWord);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
+
+        ResponseEntity<String> response;
+        try {
+            log.info("请求Ai服务登录接口===================>");
+            response = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+        } catch (Exception e) {
+            log.error("请求AI服务登录接口失败", e);
+            return null;
+        }
+
+        if (response != null && response.getStatusCode() == HttpStatus.OK) {
+            String body = response.getBody();
+            log.info("请求Ai服务登录成功 postForEntity.getBody()\t{}", body);
+            if (StringUtils.hasText(body)) {
+                JSONObject jsonObject = JSONObject.parseObject(body);
+                if (jsonObject != null) {
+                    JSONObject dataJson = jsonObject.getJSONObject("data");
+                    if (dataJson != null) {
+                        return dataJson.getString("access_token");
+                    }
+                }
+            }
+            log.warn("AI服务登录响应解析失败 body: {}", body);
+            return null;
+        }
+
+        log.error("请求AI服务 登录接口失败 http状态:{}", response != null ? response.getStatusCode() : null);
+        return null;
+    }
+
+
+    /**
+     * 调用AI服务创建任务
+     *
+     * @return accessToken
+     */
+    public String createTask(String accessToken, String text, List<String> img_urls) {
+        log.info("创建Ai服务任务...{}", auditTaskUrl);
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + accessToken);
+
+        Map<String, Object> analyzeRequest = new HashedMap<>();
+        analyzeRequest.put("text", StringUtils.hasText(text) ? text : "");
+        analyzeRequest.put("img_urls", img_urls);
+
+        HttpEntity<Map<String, Object>> analyzeEntity = new HttpEntity<>(analyzeRequest, analyzeHeaders);
+
+        ResponseEntity<String> analyzeResp = null;
+        try {
+            analyzeResp = restTemplate.postForEntity(auditTaskUrl, analyzeEntity, String.class);
+        } catch (org.springframework.web.client.HttpServerErrorException.ServiceUnavailable e) {
+            log.error("调用提交商品审核任务接口返回503 Service Unavailable错误: {}", e.getResponseBodyAsString());
+            return  null;
+        } catch (Exception e) {
+            log.error("调用提交商品审核任务接口异常", e);
+            return  null;
+        }
+
+        if (analyzeResp != null && analyzeResp.getStatusCodeValue() == 200) {
+            String analyzeBody = analyzeResp.getBody();
+            log.info("提交商品审核任务提交成功, 返回: {}", analyzeBody);
+
+            JSONObject analyzeJson = JSONObject.parseObject(analyzeBody);
+            JSONObject dataJsonObj = analyzeJson.getJSONObject("data");
+
+            if (dataJsonObj == null) {
+                log.error("提交商品审核任务返回数据为空");
+                R.fail("提交商品审核任务返回数据为空");
+                return  null;
+            }
+
+            // 获取record_id用于后续查询
+            String taskId = dataJsonObj.getString("task_id");
+            if (taskId == null) {
+                log.error("提交商品审核任务返回record_id为空");
+                R.fail("提交商品审核任务返回record_id为空");
+                return  null;
+            }
+            return taskId;
+        } else {
+            if (analyzeResp != null) {
+                log.error("调用提交商品审核任务接口失败, http状态: {}", analyzeResp.getStatusCode());
+                R.fail("调用提交商品审核任务接口失败, http状态: " + analyzeResp.getStatusCode());
+                return  null;
+            }
+        }
+        return  null;
+    }
+}

+ 1 - 1
alien-second/src/main/resources/bootstrap.yml

@@ -1,3 +1,3 @@
 spring:
   profiles:
-    active: dev
+    active: test

+ 100 - 81
alien-store/src/main/java/shop/alien/store/service/impl/LifeUserViolationServiceImpl.java

@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Lists;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
@@ -17,6 +18,7 @@ import org.springframework.util.CollectionUtils;
 import shop.alien.entity.second.SecondGoodsRecord;
 import shop.alien.entity.store.*;
 import shop.alien.entity.store.dto.LifeUserViolationDto;
+import shop.alien.entity.store.excelVo.LifeUserOrderExcelVo;
 import shop.alien.entity.store.excelVo.LifeUserViolationExcelVO;
 import shop.alien.entity.store.excelVo.util.ExcelGenerator;
 import shop.alien.entity.store.vo.LifeUserViolationVo;
@@ -25,19 +27,24 @@ import shop.alien.mapper.*;
 import shop.alien.mapper.second.SecondGoodsRecordMapper;
 import shop.alien.store.config.WebSocketProcess;
 import shop.alien.store.service.*;
+import shop.alien.store.util.AiUserViolationUtils;
 import shop.alien.store.util.FunctionMagic;
 import shop.alien.util.ali.AliOSSUtil;
 import shop.alien.util.common.EnumUtil;
+import shop.alien.util.common.JwtUtil;
 
 import java.io.File;
 import java.io.IOException;
 import java.text.SimpleDateFormat;
+import java.time.Instant;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
+import static shop.alien.util.common.constant.Constant.*;
+
 
 /**
  * <p>
@@ -81,6 +88,8 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
     private final SecondGoodsRecordMapper secondGoodsRecordMapper;
     private final StoreImgService storeImgService;
 
+    private final AiUserViolationUtils aiUserViolationUtils;
+
     @Value("${spring.web.resources.excel-path}")
     private String excelPath;
 
@@ -101,34 +110,47 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
             }
             int result = lifeUserViolationMapper.insert(lifeuserViolation);
             if (result > 0) {
+                // AI审核
+                //登录获取token
+                String token = aiUserViolationUtils.getAccessToken();
+                //调用AI接口
+                String taskId = aiUserViolationUtils.createTask(token, lifeuserViolation);
+                if (org.springframework.util.StringUtils.isEmpty(taskId)) {
+                    log.warn("Failed to create AI task for second round review, lifeuserViolation id={}", lifeuserViolation.getId());
+                    return 0;
+                }
+                lifeuserViolation.setAiTaskId(taskId);
+                lifeUserViolationMapper.updateById(lifeuserViolation);
+
+
                 //String phoneId = Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getString("userType") + "_" + JwtUtil.getCurrentUserInfo().getString("phone");
-                // 举报人消息
-                LifeNotice lifeNotice = getLifeNotice(lifeuserViolation);
-                lifeNoticeMapper.insert(lifeNotice);
-                WebSocketVo websocketVo = new WebSocketVo();
-                websocketVo.setSenderId("system");
-                websocketVo.setReceiverId(lifeNotice.getReceiverId());
-                websocketVo.setCategory("notice");
-                websocketVo.setNoticeType("1");
-                websocketVo.setIsRead(0);
-                websocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeNotice).toJSONString());
-                webSocketProcess.sendMessage(lifeNotice.getReceiverId(), com.alibaba.fastjson2.JSONObject.from(websocketVo).toJSONString());
-
-                // 被举报人消息
-                if (StringUtils.isNotEmpty(lifeuserViolation.getReportContextType()) && "1,2,3".contains(lifeuserViolation.getReportContextType())) {
-                    LifeNotice lifeNoticeReported = getLifeReportedNotice(lifeuserViolation);
-                    if (lifeNoticeReported != null) {
-                        lifeNoticeMapper.insert(lifeNoticeReported);
-                        WebSocketVo websocketVoReported = new WebSocketVo();
-                        websocketVoReported.setSenderId("system");
-                        websocketVoReported.setReceiverId(lifeNoticeReported.getReceiverId());
-                        websocketVoReported.setCategory("notice");
-                        websocketVoReported.setNoticeType("1");
-                        websocketVoReported.setIsRead(0);
-                        websocketVoReported.setText(com.alibaba.fastjson2.JSONObject.from(lifeNoticeReported).toJSONString());
-                        webSocketProcess.sendMessage(lifeNoticeReported.getReceiverId(), com.alibaba.fastjson2.JSONObject.from(websocketVoReported).toJSONString());
+                    // 举报人消息
+                    LifeNotice lifeNotice = getLifeNotice(lifeuserViolation);
+                    lifeNoticeMapper.insert(lifeNotice);
+                    WebSocketVo websocketVo = new WebSocketVo();
+                    websocketVo.setSenderId("system");
+                    websocketVo.setReceiverId(lifeNotice.getReceiverId());
+                    websocketVo.setCategory("notice");
+                    websocketVo.setNoticeType("1");
+                    websocketVo.setIsRead(0);
+                    websocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeNotice).toJSONString());
+                    webSocketProcess.sendMessage(lifeNotice.getReceiverId(), com.alibaba.fastjson2.JSONObject.from(websocketVo).toJSONString());
+
+                    // 被举报人消息
+                    if(StringUtils.isNotEmpty(lifeuserViolation.getReportContextType()) && "1,2,3".contains(lifeuserViolation.getReportContextType())){
+                        LifeNotice lifeNoticeReported = getLifeReportedNotice(lifeuserViolation);
+                        if (lifeNoticeReported != null) {
+                            lifeNoticeMapper.insert(lifeNoticeReported);
+                            WebSocketVo websocketVoReported = new WebSocketVo();
+                            websocketVoReported.setSenderId("system");
+                            websocketVoReported.setReceiverId(lifeNoticeReported.getReceiverId());
+                            websocketVoReported.setCategory("notice");
+                            websocketVoReported.setNoticeType("1");
+                            websocketVoReported.setIsRead(0);
+                            websocketVoReported.setText(com.alibaba.fastjson2.JSONObject.from(lifeNoticeReported).toJSONString());
+                            webSocketProcess.sendMessage(lifeNoticeReported.getReceiverId(), com.alibaba.fastjson2.JSONObject.from(websocketVoReported).toJSONString());
+                        }
                     }
-                }
                 return result;
             }
         } catch (Exception e) {
@@ -153,16 +175,16 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
 
         if ("1".equals(lifeuserViolation.getReportingUserType())) {
             StoreUser storeUsers = storeUserMapper.selectById(reportUserId);
-            phoneId = "store_" + storeUsers.getPhone();
+            phoneId = "store_"+storeUsers.getPhone();
         } else {
             LifeUser lifeUsers = lifeUserMapper.selectById(reportUserId);
-            phoneId = "user_" + lifeUsers.getUserPhone();
+            phoneId = "user_"+lifeUsers.getUserPhone();
         }
 
         lifeNotice.setReceiverId(phoneId);
 
-        if (StringUtils.isNotEmpty(reportContextType) && "1,2,3".contains(reportContextType)) {
-            String violationType = StringUtils.isNotEmpty(lifeuserViolation.getViolationType()) ? lifeuserViolation.getViolationType() : "13";
+        if(StringUtils.isNotEmpty(reportContextType) && "1,2,3".contains(reportContextType)){
+            String violationType = StringUtils.isNotEmpty(lifeuserViolation.getViolationType())?lifeuserViolation.getViolationType():"13";
             String violationText = EnumUtil.getStatusValue(Integer.parseInt(violationType));
 
             String storeOrUserName = "";
@@ -182,25 +204,25 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
 
             switch (reportContextType) {
                 case "1":
-                    message = "您在" + storeDate + "举报用户“" + storeOrUserName + "”,涉嫌" + violationText + ",已提交至平台审核,1-3个工作日会将审核结果发送到您应用内的消息-通知中,请注意查收。";
+                    message = "您在" + storeDate + "举报用户“" + storeOrUserName + "”,涉嫌"+violationText+",已提交至平台审核,1-3个工作日会将审核结果发送到您应用内的消息-通知中,请注意查收。";
                     break;
                 case "2":
                     String dynamicsId = lifeuserViolation.getDynamicsId();
                     String dynamicsDate = simpleDateFormat.format(new Date());
-                    if (StringUtils.isNotEmpty(dynamicsId)) {
+                    if(StringUtils.isNotEmpty(dynamicsId)){
                         LifeUserDynamics lifeUserDynamics = lifeUserDynamicsMapper.selectById(dynamicsId);
                         dynamicsDate = simpleDateFormats.format(lifeUserDynamics.getCreatedTime());
                     }
-                    message = "您在" + storeDate + "举报用户“" + storeOrUserName + "”在" + dynamicsDate + "发布的动态,涉嫌" + violationText + ",已提交至平台审核,1-3个工作日会将审核结果发送到您应用内的消息-通知中,请注意查收。";
+                    message = "您在" + storeDate + "举报用户“" + storeOrUserName + "”在"+dynamicsDate+"发布的动态,涉嫌"+violationText+",已提交至平台审核,1-3个工作日会将审核结果发送到您应用内的消息-通知中,请注意查收。";
                     break;
                 case "3":
                     String commonId = lifeuserViolation.getCommentId();
                     String commonDate = simpleDateFormat.format(new Date());
-                    if (StringUtils.isNotEmpty(commonId)) {
-                        StoreComment storeComment = storeCommentMapper.selectById(commonId);
+                    if(StringUtils.isNotEmpty(commonId)){
+                        StoreComment storeComment  = storeCommentMapper.selectById(commonId);
                         commonDate = simpleDateFormats.format(storeComment.getCreatedTime());
                     }
-                    message = "您在" + storeDate + "举报用户“" + storeOrUserName + "”在" + commonDate + "发布的评论,涉嫌" + violationText + ",已提交至平台审核,1-3个工作日会将审核结果发送到您应用内的消息-通知中,请注意查收。";
+                    message = "您在" + storeDate + "举报用户“" + storeOrUserName + "”在"+commonDate+"发布的评论,涉嫌"+violationText+",已提交至平台审核,1-3个工作日会将审核结果发送到您应用内的消息-通知中,请注意查收。";
                     break;
             }
         }
@@ -229,7 +251,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
             lifeNotice.setReceiverId("user_" + phoneId);
 
         }
-        if (StringUtils.isEmpty(phoneId)) {
+        if (StringUtils.isEmpty(phoneId)){
             return null;
         }
         String violationText = EnumUtil.getStatusValue(Integer.parseInt(lifeuserViolation.getViolationType()));
@@ -243,8 +265,8 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         String storeDate = simpleDateFormat.format(new Date());
         String reportContextType = lifeuserViolation.getReportContextType();
 
-        if (StringUtils.isNotEmpty(reportContextType) && reportContextType.equals("1")) {
-            message = "您在" + storeDate + "被举报涉嫌" + violationText + ",平台将会进行核实。如确实存在违规行为,平台将禁用您的账号**天,到期后账号可恢复使用,应用内的环境需要我们共同维护。";
+        if(StringUtils.isNotEmpty(reportContextType) && reportContextType.equals("1")){
+            message = "您在" + storeDate + "被举报涉嫌"+violationText+",平台将会进行核实。如确实存在违规行为,平台将禁用您的账号**天,到期后账号可恢复使用,应用内的环境需要我们共同维护。";
         } else {
             return null;
         }
@@ -310,9 +332,8 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         QueryWrapper<LifeUserViolationVo> queryWrapper = new QueryWrapper<>();
 
         // 基础查询条件
-        queryWrapper.eq("luv.delete_flag", 0);
-//                //排除二手的举报
-//                .notIn("luv.report_context_type", Arrays.asList("4", "5"));
+        queryWrapper.eq("luv.delete_flag", 0)
+                .in("luv.report_context_type", Arrays.asList("1", "2", "3"));
 
         // 动态查询条件
         queryWrapper.like(StringUtils.isNotEmpty(nickName), "ui.nick_name", nickName)
@@ -350,7 +371,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
 
     @Override
     public void approve(int id, String processingStatus, String reportResult) {
-        if (id == 0 || StringUtils.isBlank(processingStatus)) {
+        if(id==0 || StringUtils.isBlank(processingStatus)){
             return;
         }
         LifeUserViolation v = lifeUserViolationMapper.selectById(id);
@@ -364,15 +385,15 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         String violationTime = simpleDateFormat.format(v.getCreatedTime());
         // 被举报人信息
         String reportedUserName = "";
-        if (StringUtils.isNotEmpty(v.getReportedUserId())) {
+        if(StringUtils.isNotEmpty(v.getReportedUserId())){
             if (v.getReportedUserType().equals("1")) {
                 StoreUser storeUser = storeUserMapper.selectById(v.getReportedUserId());
-                if (storeUser != null) {
+                if(storeUser != null){
                     reportedUserName = storeUser.getNickName();
                 }
-            } else {
+            } else{
                 LifeUser lifeUser = lifeUserMapper.selectById(v.getReportedUserId());
-                if (lifeUser != null) {
+                if(lifeUser != null) {
                     reportedUserName = lifeUser.getUserName();
                 }
             }
@@ -380,15 +401,15 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         // 被举报动态信息
         String dynamicsId = v.getDynamicsId();
         String dynamicsDate = simpleDateFormats.format(new Date());
-        if (StringUtils.isNotEmpty(dynamicsId)) {
+        if(StringUtils.isNotEmpty(dynamicsId)){
             LifeUserDynamics lifeUserDynamics = lifeUserDynamicsMapper.selectById(dynamicsId);
             dynamicsDate = simpleDateFormats.format(lifeUserDynamics.getCreatedTime());
         }
         // 被举报评论信息
         String commonId = v.getCommentId();
         String commonDate = simpleDateFormats.format(new Date());
-        if (StringUtils.isNotEmpty(commonId)) {
-            StoreComment storeComment = storeCommentMapper.selectById(commonId);
+        if(StringUtils.isNotEmpty(commonId)){
+            StoreComment storeComment  = storeCommentMapper.selectById(commonId);
             commonDate = simpleDateFormats.format(storeComment.getCreatedTime());
         }
 
@@ -401,13 +422,13 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
             // 通过
             if (v.getReportContextType().equals("1")) {
                 // 用户
-                message = "您在" + violationTime + "举报用户“" + reportedUserName + "”,涉嫌违法违规,经核实,确实存在违规行为,平台已将用户禁用,感谢您为此做出的贡献。";
+                message = "您在"+violationTime+"举报用户“"+reportedUserName+"”,涉嫌违法违规,经核实,确实存在违规行为,平台已将用户禁用,感谢您为此做出的贡献。";
                 title = "用户举报成功通知";
             }
             if (v.getReportContextType().equals("2")) {
                 // 动态
-                message = "您在" + violationTime + "举报用户“" + reportedUserName + "”在" + dynamicsDate + "发布的动态,涉嫌违法违规,经核实,确实存在违规行为,平台已将此动态下架,感谢您为此做出的贡献";
-                reportedMessage = "您在" + dynamicsDate + "发布的动态,经核实,确实存在违规行为,平台已将此动态下架,应用内的环境需要我们共同维护";
+                message = "您在"+violationTime+"举报用户“"+reportedUserName+"”在"+dynamicsDate+"发布的动态,涉嫌违法违规,经核实,确实存在违规行为,平台已将此动态下架,感谢您为此做出的贡献";
+                reportedMessage = "您在"+dynamicsDate+"发布的动态,经核实,确实存在违规行为,平台已将此动态下架,应用内的环境需要我们共同维护";
                 lifeUserDynamicsService.removeById(v.getDynamicsId());
                 title = "动态举报成功通知";
             }
@@ -415,29 +436,29 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 // 评论
                 if (v.getReportedUserType().equals("1")) {
                     // 商户
-                    message = "您在" + violationTime + "举报用户“" + reportedUserName + "”在" + commonDate + "发布的评论,涉嫌违法违规,经核实,确实存在违规行为,平台已将此评论下架,感谢您为此做出的贡献。";
+                    message = "您在"+violationTime+"举报用户“"+reportedUserName+"”在"+commonDate+"发布的评论,涉嫌违法违规,经核实,确实存在违规行为,平台已将此评论下架,感谢您为此做出的贡献。";
                     storeCommentService.removeById(v.getCommentId());
                 } else {
                     // 用户
-                    message = "您在" + violationTime + "举报用户“" + reportedUserName + "”在" + commonDate + "发布的评论,涉嫌违法违规,经核实,确实存在违规行为,平台已将此评论下架,感谢您为此做出的贡献。";
+                    message = "您在"+violationTime+"举报用户“"+reportedUserName+"”在"+commonDate+"发布的评论,涉嫌违法违规,经核实,确实存在违规行为,平台已将此评论下架,感谢您为此做出的贡献。";
                     lifeCommentMapper.deleteById(v.getCommentId());
                 }
-                reportedMessage = "您在" + commonDate + "发布的评论,经核实,确实存在违规行为,平台已将此评论下架,应用内的环境需要我们共同维护。";
+                reportedMessage = "您在"+commonDate+"发布的评论,经核实,确实存在违规行为,平台已将此评论下架,应用内的环境需要我们共同维护。";
                 title = "评论举报成功通知";
             }
         } else {
             // 驳回
             switch (v.getReportContextType()) {
                 case "1":
-                    message = "您在" + violationTime + "举报用户“" + reportedUserName + "”,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
+                    message = "您在"+violationTime+"举报用户“"+reportedUserName+"”,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
                     title = "用户举报失败通知";
                     break;
                 case "2":
-                    message = "您在" + violationTime + "举报用户“" + reportedUserName + "”在" + dynamicsDate + "发布的动态,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
+                    message = "您在"+violationTime+"举报用户“"+reportedUserName+"”在"+dynamicsDate+"发布的动态,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
                     title = "动态举报失败通知";
                     break;
                 case "3":
-                    message = "您在" + violationTime + "举报用户“" + reportedUserName + "”在" + commonDate + "发布的评论,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
+                    message = "您在"+violationTime+"举报用户“"+reportedUserName+"”在"+commonDate+"发布的评论,涉嫌违法违规,经核实,不存在违规行为,感谢您为此做出的贡献。";
                     title = "评论举报失败通知";
                     break;
             }
@@ -479,7 +500,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
 
 
         // 被举报通知
-        if (StringUtils.isNotEmpty(reportedMessage)) {
+        if(StringUtils.isNotEmpty(reportedMessage)) {
             LifeNotice reportedLifeMessage = new LifeNotice();
             com.alibaba.fastjson2.JSONObject jsonObjectReported = new com.alibaba.fastjson2.JSONObject();
             jsonObjectReported.put("message", reportedMessage);
@@ -522,12 +543,10 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         if (entity == null) return null;
         LifeUserViolationDto dto = new LifeUserViolationDto();
         BeanUtils.copyProperties(entity, dto);
-        LambdaQueryWrapper<StoreImg> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(StoreImg::getStoreId, entity.getId());
-        queryWrapper.in(StoreImg::getImgType, 19);
-        List<StoreImg> storeImgList = storeImgService.list(queryWrapper);
-        List<String> collect = storeImgList.stream().map(StoreImg::getImgUrl).collect(Collectors.toList());
-        dto.setImageList(collect);
+        if (Objects.nonNull(entity.getReportEvidenceImg())) {
+            List<String> list = Arrays.stream(entity.getReportEvidenceImg().split(",")).map(String::trim).collect(Collectors.toList());
+            dto.setImageList(list);
+        }
         // 处理举报人信息
         FunctionMagic.handleUserInfo(dto.getReportingUserType(), dto.getReportingUserId(), storeId -> storeUserService.getOne(FunctionMagic.idQueryWrapper(storeId)), lifeId -> lifeUserService.getOne(FunctionMagic.idQueryWrapper(lifeId)), user -> {
             if (user instanceof StoreUser) {
@@ -576,7 +595,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         queryWrapper.orderByDesc("luv.updated_time");
 
         List<LifeUserViolationVo> violationList = lifeUserViolationMapper.getViolationList(queryWrapper);
-
+        
         // 如果查询结果为空,返回空列表生成的Excel
         if (CollectionUtils.isEmpty(violationList)) {
             log.warn("导出Excel时查询结果为空,nickName={}, phone={}, processingStatus={}", nickName, phone, processingStatus);
@@ -584,7 +603,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
 
         // 日期格式化器(复用)
         DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
-
+        
         // 使用Stream API进行转换,提高性能和可读性
         AtomicInteger serialNumber = new AtomicInteger(1);
         List<LifeUserViolationExcelVO> excelDataList = violationList.stream()
@@ -594,10 +613,10 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
         // 生成Excel文件
         String fileName = UUID.randomUUID().toString().replace("-", "");
         String filePath = ExcelGenerator.generateExcel(
-                excelPath + excelGeneratePath + fileName + ".xlsx",
-                excelDataList,
+                excelPath + excelGeneratePath + fileName + ".xlsx", 
+                excelDataList, 
                 LifeUserViolationExcelVO.class);
-
+        
         // 上传到OSS并返回URL
         return aliOSSUtil.uploadFile(new File(filePath), "excel/" + fileName + ".xlsx");
     }
@@ -605,18 +624,18 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
     /**
      * 将 LifeUserViolationVo 转换为 LifeUserViolationExcelVO
      *
-     * @param vo           源对象
+     * @param vo 源对象
      * @param serialNumber 序号
-     * @param formatter    日期格式化器
+     * @param formatter 日期格式化器
      * @return Excel VO对象
      */
     private LifeUserViolationExcelVO convertToExcelVO(LifeUserViolationVo vo, int serialNumber, DateTimeFormatter formatter) {
         LifeUserViolationExcelVO excelVO = new LifeUserViolationExcelVO();
         BeanUtils.copyProperties(vo, excelVO);
-
+        
         excelVO.setId(serialNumber);
         excelVO.setNickname(vo.getNickName());
-
+        
         // 处理举报凭证图片(安全分割,避免数组越界)
         if (StringUtils.isNotEmpty(vo.getReportEvidenceImg())) {
             String[] imageParts = vo.getReportEvidenceImg().split(",");
@@ -624,7 +643,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 excelVO.setReportEvidenceImg(imageParts[0].trim());
             }
         }
-
+        
         // 处理举报内容类型
         if (StringUtils.isNotEmpty(vo.getReportContextType())) {
             try {
@@ -634,7 +653,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 excelVO.setReportContextType(vo.getReportContextType());
             }
         }
-
+        
         // 处理举报理由
         if (StringUtils.isNotEmpty(vo.getViolationType())) {
             try {
@@ -644,7 +663,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 excelVO.setViolationType(vo.getViolationType());
             }
         }
-
+        
         // 处理处理状态
         if (StringUtils.isNotEmpty(vo.getProcessingStatus())) {
             try {
@@ -654,7 +673,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 excelVO.setProcessingStatus(vo.getProcessingStatus());
             }
         }
-
+        
         // 格式化创建时间
         if (Objects.nonNull(vo.getCreatedTime())) {
             try {
@@ -667,7 +686,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 log.warn("格式化创建时间失败,createdTime={}, error={}", vo.getCreatedTime(), e.getMessage());
             }
         }
-
+        
         // 格式化处理时间
         if (Objects.nonNull(vo.getProcessingTime())) {
             try {
@@ -680,7 +699,7 @@ public class LifeUserViolationServiceImpl extends ServiceImpl<LifeUserViolationM
                 log.warn("格式化处理时间失败,processingTime={}, error={}", vo.getProcessingTime(), e.getMessage());
             }
         }
-
+        
         return excelVO;
     }
 

+ 245 - 0
alien-store/src/main/java/shop/alien/store/util/AiUserViolationUtils.java

@@ -0,0 +1,245 @@
+package shop.alien.store.util;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.map.HashedMap;
+import org.springframework.beans.factory.annotation.Value;
+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 shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeMessage;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.entity.store.LifeUserViolation;
+import shop.alien.entity.store.vo.LifeUserVo;
+import shop.alien.mapper.LifeMessageMapper;
+import shop.alien.mapper.LifeUserMapper;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AiUserViolationUtils {
+
+    private final RestTemplate restTemplate;
+    private final LifeUserMapper lifeUserMapper;
+    private final LifeMessageMapper lifeMessageMapper;
+
+    //    @Value("${third-party-login.base-url:http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login}")
+    private String loginUrl = "http://192.168.2.78:9000/ai/user-auth-core/api/v1/auth/login";
+
+    @Value("${third-party-user-name.base-url:UdUser}")
+    private String userName;
+
+    @Value("${third-party-pass-word.base-url:123456}")
+    private String passWord;
+
+    private String userComplaintRecordUrl = "http://192.168.2.78:9000/ai/auto-review/api/v1/user_complaint_record/submit";
+
+
+    /**
+     * 登录 AI 服务,获取 token
+     *
+     * @return accessToken
+     */
+    public String getAccessToken() {
+        log.info("登录Ai服务获取token...{}", loginUrl);
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username", userName);
+        formData.add("password", passWord);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
+
+        ResponseEntity<String> response;
+        try {
+            log.info("请求Ai服务登录接口===================>");
+            response = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+        } catch (Exception e) {
+            log.error("请求AI服务登录接口失败", e);
+            return null;
+        }
+
+        if (response != null && response.getStatusCode() == HttpStatus.OK) {
+            String body = response.getBody();
+            log.info("请求Ai服务登录成功 postForEntity.getBody()\t{}", body);
+            if (StringUtils.hasText(body)) {
+                JSONObject jsonObject = JSONObject.parseObject(body);
+                if (jsonObject != null) {
+                    JSONObject dataJson = jsonObject.getJSONObject("data");
+                    if (dataJson != null) {
+                        return dataJson.getString("access_token");
+                    }
+                }
+            }
+            log.warn("AI服务登录响应解析失败 body: {}", body);
+            return null;
+        }
+
+        log.error("请求AI服务 登录接口失败 http状态:{}", response != null ? response.getStatusCode() : null);
+        return null;
+    }
+
+
+    /**
+     * 调用AI服务创建任务
+     *
+     * @return accessToken
+     */
+    public String createTask(String accessToken, LifeUserViolation lifeuserViolation) {
+        log.info("创建Ai服务任务...{}", userComplaintRecordUrl);
+
+        HttpHeaders analyzeHeaders = new HttpHeaders();
+        analyzeHeaders.setContentType(MediaType.APPLICATION_JSON);
+        analyzeHeaders.set("Authorization", "Bearer " + accessToken);
+
+        Map<String, Object> analyzeRequest = new HashedMap<>();
+        // 对应参数
+        analyzeRequest.put("complaint_type", lifeuserViolation.getViolationType());
+        analyzeRequest.put("reporter_user_id", lifeuserViolation.getReportingUserId());
+        analyzeRequest.put("reported_user_id", lifeuserViolation.getReportedUserId());
+
+        //举报人
+        String reporterUserId = lifeuserViolation.getReportingUserId();
+        //被举报人
+        String reportedUserId = lifeuserViolation.getReportedUserId();
+
+        //通过双方userId查询用户信息,查出用户电话,
+        QueryWrapper<LifeUserVo> reporterUserQueryWrapper = new QueryWrapper<>();
+        reporterUserQueryWrapper.eq("id", reporterUserId);
+        LifeUserVo reporterUserById = lifeUserMapper.getUserById(reporterUserQueryWrapper);
+        QueryWrapper<LifeUserVo> reportedUserQueryWrapper = new QueryWrapper<>();
+        reportedUserQueryWrapper.eq("id", reportedUserId);
+        LifeUserVo reportedUserById = lifeUserMapper.getUserById(reportedUserQueryWrapper);
+        // 用user_去拼接,查询life_message表,获取双方两天记录
+        String userReporterUserById = "user_" + reporterUserById.getUserPhone();
+        String userReportedUserById = "user_" + reportedUserById.getUserPhone();
+
+        //将双方两天记录拼接成text,传入analyzeRequest
+        // 获取双方最近的聊天记录
+        List<LifeMessage> chatMessages = getRecentChatMessages(userReporterUserById, userReportedUserById);
+
+        // 将聊天记录转换为文本格式
+        String text = convertMessagesToText(chatMessages);
+        analyzeRequest.put("conversation_context", StringUtils.hasText(text) ? text : "");
+
+//        analyzeRequest.put("text", StringUtils.hasText(text) ? text : "");
+//        analyzeRequest.put("img_urls", img_urls);
+
+        HttpEntity<Map<String, Object>> analyzeEntity = new HttpEntity<>(analyzeRequest, analyzeHeaders);
+
+        ResponseEntity<String> analyzeResp = null;
+        try {
+            analyzeResp = restTemplate.postForEntity(userComplaintRecordUrl, analyzeEntity, String.class);
+        } catch (org.springframework.web.client.HttpServerErrorException.ServiceUnavailable e) {
+            log.error("调用提交用户投诉审核任务接口返回503 Service Unavailable错误: {}", e.getResponseBodyAsString());
+            return  null;
+        } catch (Exception e) {
+            log.error("调用提交用户投诉审核任务接口异常", e);
+            return  null;
+        }
+
+        if (analyzeResp != null && analyzeResp.getStatusCodeValue() == 200) {
+            String analyzeBody = analyzeResp.getBody();
+            log.info("提交用户投诉审核任务提交成功, 返回: {}", analyzeBody);
+
+            JSONObject analyzeJson = JSONObject.parseObject(analyzeBody);
+            JSONObject dataJsonObj = analyzeJson.getJSONObject("data");
+
+            if (dataJsonObj == null) {
+                log.error("提交用户投诉审核任务返回数据为空");
+                R.fail("提交用户投诉审核任务返回数据为空");
+                return  null;
+            }
+
+            // 获取record_id用于后续查询
+            Integer taskId = dataJsonObj.getInteger("task_id");
+            if (taskId == null) {
+                log.error("提交用户投诉审核任务返回record_id为空");
+                R.fail("提交用户投诉审核任务返回record_id为空");
+                return  null;
+            }
+            return taskId.toString();
+        } else {
+            if (analyzeResp != null) {
+                log.error("调用提交用户投诉审核任务接口失败, http状态: {}", analyzeResp.getStatusCode());
+                R.fail("调用提交用户投诉审核任务接口失败, http状态: " + analyzeResp.getStatusCode());
+                return  null;
+            }
+        }
+        return  null;
+    }
+
+    /**
+     * 获取双方用户的最近聊天记录
+     * @param userReporterUserById 举报人ID
+     * @param userReportedUserById 被举报人ID
+     * @return 聊天记录列表
+     */
+    public List<LifeMessage> getRecentChatMessages(String userReporterUserById, String userReportedUserById) {
+        // 构建查询条件
+        QueryWrapper<LifeMessage> queryWrapper = new QueryWrapper<>();
+
+        // 查询双方的聊天记录
+        queryWrapper.and(wrapper -> wrapper
+                        .eq("sender_id", userReporterUserById)
+                        .eq("receiver_id", userReportedUserById))
+                .or(wrapper -> wrapper
+                        .eq("sender_id", userReportedUserById)
+                        .eq("receiver_id", userReporterUserById));
+
+        // 按时间倒序排列
+        queryWrapper.orderByDesc("create_time");
+
+        // 限制50条记录
+        queryWrapper.last("LIMIT 50");
+
+        return lifeMessageMapper.selectList(queryWrapper);
+    }
+
+    /**
+     * 将聊天消息列表转换为文本格式
+     * @param messages 聊天消息列表
+     * @return 格式化后的文本字符串
+     */
+    private String convertMessagesToText(List<LifeMessage> messages) {
+        if (messages == null || messages.isEmpty()) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        // 按时间顺序排列(因为数据库查询是倒序的,这里需要重新排序)
+        for (int i = messages.size() - 1; i >= 0; i--) {
+            LifeMessage message = messages.get(i);
+            // 格式:[时间] 发送者ID: 消息内容
+            sb.append("[").append(formatTime(message.getCreatedTime())).append("] ")
+                    .append(message.getSenderId()).append(": ")
+                    .append(message.getContent()).append("\n");
+        }
+
+        return sb.toString().trim();
+    }
+
+    /**
+     * 格式化时间显示
+     * @param date 时间
+     * @return 格式化后的时间字符串
+     */
+    private String formatTime(Date date) {
+        if (date == null) {
+            return "";
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        return sdf.format(date);
+    }
+}

+ 1 - 1
alien-store/src/main/resources/bootstrap.yml

@@ -1,3 +1,3 @@
 spring:
   profiles:
-    active: dev
+    active: test