|
@@ -2,6 +2,10 @@ package shop.alien.job.store;
|
|
|
|
|
|
|
|
import com.alibaba.fastjson2.JSONArray;
|
|
import com.alibaba.fastjson2.JSONArray;
|
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
|
|
+import com.fasterxml.jackson.databind.JsonNode;
|
|
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
import com.xxl.job.core.context.XxlJobHelper;
|
|
import com.xxl.job.core.context.XxlJobHelper;
|
|
|
import com.xxl.job.core.handler.annotation.XxlJob;
|
|
import com.xxl.job.core.handler.annotation.XxlJob;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
@@ -14,19 +18,14 @@ import org.springframework.util.LinkedMultiValueMap;
|
|
|
import org.springframework.util.MultiValueMap;
|
|
import org.springframework.util.MultiValueMap;
|
|
|
import org.springframework.web.client.RestTemplate;
|
|
import org.springframework.web.client.RestTemplate;
|
|
|
import shop.alien.entity.result.R;
|
|
import shop.alien.entity.result.R;
|
|
|
-import shop.alien.entity.store.StoreCommentSummary;
|
|
|
|
|
-import shop.alien.entity.store.StoreCommentSummaryInterest;
|
|
|
|
|
-import shop.alien.entity.store.TagsMain;
|
|
|
|
|
-import shop.alien.entity.store.TagsSynonym;
|
|
|
|
|
-import shop.alien.mapper.StoreCommentSummaryInterestMapper;
|
|
|
|
|
-import shop.alien.mapper.StoreCommentSummaryMapper;
|
|
|
|
|
-import shop.alien.mapper.TagsMainMapper;
|
|
|
|
|
-import shop.alien.mapper.TagsSynonymMapper;
|
|
|
|
|
|
|
+import shop.alien.entity.store.*;
|
|
|
|
|
+import shop.alien.mapper.*;
|
|
|
|
|
|
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDate;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
import java.time.format.DateTimeFormatter;
|
|
|
-import java.util.ArrayList;
|
|
|
|
|
-import java.util.List;
|
|
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+
|
|
|
|
|
+import static com.alipay.api.internal.util.AlipayUtils.getFileSuffix;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 调用AI标签数据服务类
|
|
* 调用AI标签数据服务类
|
|
@@ -39,6 +38,8 @@ import java.util.List;
|
|
|
@RequiredArgsConstructor
|
|
@RequiredArgsConstructor
|
|
|
public class AiTagJob {
|
|
public class AiTagJob {
|
|
|
|
|
|
|
|
|
|
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
|
|
|
|
+
|
|
|
private final RestTemplate restTemplate;
|
|
private final RestTemplate restTemplate;
|
|
|
|
|
|
|
|
private final TagsMainMapper tagsMainMapper;
|
|
private final TagsMainMapper tagsMainMapper;
|
|
@@ -49,6 +50,8 @@ public class AiTagJob {
|
|
|
|
|
|
|
|
private final StoreCommentSummaryInterestMapper storeCommentSummaryInterestMapper;
|
|
private final StoreCommentSummaryInterestMapper storeCommentSummaryInterestMapper;
|
|
|
|
|
|
|
|
|
|
+ private final LifeUserDynamicsMapper lifeUserDynamicsMapper;
|
|
|
|
|
+
|
|
|
// 第三方接口地址 获取所有标签主表信息
|
|
// 第三方接口地址 获取所有标签主表信息
|
|
|
@Value("${third-party-tag.base-url}")
|
|
@Value("${third-party-tag.base-url}")
|
|
|
private String tagMainUrl;
|
|
private String tagMainUrl;
|
|
@@ -73,6 +76,10 @@ public class AiTagJob {
|
|
|
@Value("${third-party-savetag.base-url}")
|
|
@Value("${third-party-savetag.base-url}")
|
|
|
private String saveTagUrl;
|
|
private String saveTagUrl;
|
|
|
|
|
|
|
|
|
|
+ // 第三方接口地址 内容合规检测接口
|
|
|
|
|
+ @Value("${third-party-content-check.base-url:http://192.168.2.250:9000/ai/auto-review/api/v1/content_compliance/check}")
|
|
|
|
|
+ private String contentComplianceUrl;
|
|
|
|
|
+
|
|
|
//用户名
|
|
//用户名
|
|
|
@Value("${third-party-user-name.base-url}")
|
|
@Value("${third-party-user-name.base-url}")
|
|
|
private String userName;
|
|
private String userName;
|
|
@@ -567,6 +574,117 @@ public class AiTagJob {
|
|
|
return R.success("任务执行失败 状态码" + responseEntity.getStatusCodeValue());
|
|
return R.success("任务执行失败 状态码" + responseEntity.getStatusCodeValue());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 利用AI审核违规内容
|
|
|
|
|
+ */
|
|
|
|
|
+ @XxlJob("contentComplianceCheckTask")
|
|
|
|
|
+ public R<String> contentComplianceCheckTask() {
|
|
|
|
|
+ List<LifeUserDynamics> lifeUserDynamics = lifeUserDynamicsMapper.selectList(new LambdaQueryWrapper<LifeUserDynamics>()
|
|
|
|
|
+ .eq(LifeUserDynamics::getCheckFlag, 0).eq(LifeUserDynamics::getDeleteFlag, 0));
|
|
|
|
|
+
|
|
|
|
|
+ // 常见图片后缀(可按需添加,如 .heic、.svg 等)
|
|
|
|
|
+ HashSet<String> IMAGE_SUFFIXES = new HashSet<>(Arrays.asList(
|
|
|
|
|
+ ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".heic", ".svg", ".tiff"
|
|
|
|
|
+ ));
|
|
|
|
|
+ // 常见视频后缀(可按需添加,如 .avi、.flv 等)
|
|
|
|
|
+ HashSet<String> VIDEO_SUFFIXES = new HashSet<>(Arrays.asList(
|
|
|
|
|
+ ".mp4", ".mov", ".mkv", ".avi", ".flv", ".wmv", ".mpeg", ".mpg", ".webm"
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ for (LifeUserDynamics lifeUserDynamic : lifeUserDynamics) {
|
|
|
|
|
+ String imagePath = lifeUserDynamic.getImagePath();
|
|
|
|
|
+
|
|
|
|
|
+ List<String> imageList = new ArrayList<>();
|
|
|
|
|
+ List<String> videoList = new ArrayList<>();
|
|
|
|
|
+ // 按分隔符拆分路径数组(处理连续分隔符如 ",," 产生的空字符串)
|
|
|
|
|
+ String[] allPaths = imagePath.split(imagePath);
|
|
|
|
|
+ for (String path : allPaths) {
|
|
|
|
|
+ // 去除路径前后空格(避免 " a.jpg " 这类情况)
|
|
|
|
|
+ String trimmedPath = path.trim();
|
|
|
|
|
+ if (trimmedPath.isEmpty()) {
|
|
|
|
|
+ continue; // 跳过空路径
|
|
|
|
|
+ }
|
|
|
|
|
+ // 获取文件后缀(忽略大小写)
|
|
|
|
|
+ // 找到最后一个 "." 的位置
|
|
|
|
|
+ int lastDotIndex = trimmedPath.lastIndexOf('.');
|
|
|
|
|
+ // 截取后缀(从 "." 后一位到结尾)
|
|
|
|
|
+ String suffix = trimmedPath.substring(lastDotIndex + 1);
|
|
|
|
|
+ // 分类添加到对应列表
|
|
|
|
|
+ if (IMAGE_SUFFIXES.contains(suffix)) {
|
|
|
|
|
+ imageList.add(trimmedPath);
|
|
|
|
|
+ } else if (VIDEO_SUFFIXES.contains(suffix)) {
|
|
|
|
|
+ videoList.add(trimmedPath);
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ 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> postForEntity = null;
|
|
|
|
|
+ try {
|
|
|
|
|
+ log.info("请求Ai服务登录接口===================>");
|
|
|
|
|
+ postForEntity = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("请求AI服务登录接口失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (postForEntity != null && postForEntity.getStatusCodeValue() == 200) {
|
|
|
|
|
+ log.info("请求Ai服务登录成功 postForEntity.getBody()\t" + postForEntity.getBody());
|
|
|
|
|
+ String responseBody = postForEntity.getBody();
|
|
|
|
|
+ JSONObject jsonObject = JSONObject.parseObject(responseBody);
|
|
|
|
|
+ if (jsonObject != null) {
|
|
|
|
|
+ JSONObject dataJson = jsonObject.getJSONObject("data");
|
|
|
|
|
+ String accessToken = dataJson.getString("access_token");
|
|
|
|
|
+
|
|
|
|
|
+ HttpHeaders aiHeaders = new HttpHeaders();
|
|
|
|
|
+ aiHeaders.setContentType(MediaType.APPLICATION_JSON);
|
|
|
|
|
+ aiHeaders.set("Authorization", "Bearer " + accessToken);
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> jsonBody = new HashMap<>();
|
|
|
|
|
+ jsonBody.put("text", lifeUserDynamic.getContext());
|
|
|
|
|
+ jsonBody.put("imgUrl", imageList);
|
|
|
|
|
+ jsonBody.put("video", videoList);
|
|
|
|
|
+
|
|
|
|
|
+ HttpEntity<Map<String, Object>> request = new HttpEntity<>(jsonBody, aiHeaders);
|
|
|
|
|
+ ResponseEntity<String> response = null;
|
|
|
|
|
+ try {
|
|
|
|
|
+ response = restTemplate.postForEntity(contentComplianceUrl, request, String.class);
|
|
|
|
|
+ if (response.getStatusCodeValue() != 200) {
|
|
|
|
|
+ log.error("AI内容审核接口调用失败 http状态:" + response.getStatusCode());
|
|
|
|
|
+ }
|
|
|
|
|
+ JsonNode responseNode = OBJECT_MAPPER.readTree(response.getBody());
|
|
|
|
|
+ int code = responseNode.has("code") ? responseNode.get("code").asInt() : 0;
|
|
|
|
|
+ if (code != 200) {
|
|
|
|
|
+ throw new RuntimeException("AI接口调用失败" + code);
|
|
|
|
|
+ }
|
|
|
|
|
+ JsonNode dataNode = responseNode.get("data");
|
|
|
|
|
+
|
|
|
|
|
+ if (dataNode.has("is_compliant")) {
|
|
|
|
|
+ LifeUserDynamics dynamics = new LifeUserDynamics();
|
|
|
|
|
+ dynamics.setId(lifeUserDynamic.getId());
|
|
|
|
|
+ dynamics.setCheckFlag(1);
|
|
|
|
|
+ lifeUserDynamicsMapper.updateById(dynamics);
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("动态审核成功,AI返回内容: {}", response.getBody());
|
|
|
|
|
+ XxlJobHelper.handleSuccess("动态内容审核任务执行成功");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("调用AI内容审核接口失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (RuntimeException ex) {
|
|
|
|
|
+ XxlJobHelper.handleFail("动态内容审核任务执行失败:" + ex.getMessage());
|
|
|
|
|
+ return R.fail("动态内容审核任务执行失败:" + ex.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return R.success("动态内容审核任务执行成功");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
class AnalysisRequest {
|
|
class AnalysisRequest {
|
|
|
private String start_time;
|
|
private String start_time;
|
|
|
private String end_time;
|
|
private String end_time;
|