|
|
@@ -2,26 +2,23 @@ package shop.alien.job.store;
|
|
|
|
|
|
import com.alibaba.fastjson2.JSONArray;
|
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.xxl.job.core.context.XxlJobHelper;
|
|
|
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.*;
|
|
|
+import org.springframework.http.client.ClientHttpRequestInterceptor;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
import org.springframework.util.CollectionUtils;
|
|
|
import org.springframework.util.LinkedMultiValueMap;
|
|
|
import org.springframework.util.MultiValueMap;
|
|
|
import org.springframework.web.client.RestTemplate;
|
|
|
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.entity.store.vo.AiApproveStoreInfo;
|
|
|
+import shop.alien.mapper.*;
|
|
|
|
|
|
import java.time.LocalDate;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
@@ -49,6 +46,12 @@ public class AiTagJob {
|
|
|
|
|
|
private final StoreCommentSummaryInterestMapper storeCommentSummaryInterestMapper;
|
|
|
|
|
|
+ private final LifeUserDynamicsMapper lifeUserDynamicsMapper;
|
|
|
+
|
|
|
+ private final StoreClockInMapper storeClockInMapper;
|
|
|
+
|
|
|
+ private final StoreInfoMapper storeInfoMapper;
|
|
|
+
|
|
|
// 第三方接口地址 获取所有标签主表信息
|
|
|
@Value("${third-party-tag.base-url}")
|
|
|
private String tagMainUrl;
|
|
|
@@ -73,6 +76,13 @@ public class AiTagJob {
|
|
|
@Value("${third-party-savetag.base-url}")
|
|
|
private String saveTagUrl;
|
|
|
|
|
|
+ // 第三方接口地址 内容合规检测接口
|
|
|
+ @Value("${third-party-contentcheck.base-url}")
|
|
|
+ private String contentComplianceUrl;
|
|
|
+
|
|
|
+ @Value("${third-party-getresult.base-url}")
|
|
|
+ private String getResultUrl;
|
|
|
+
|
|
|
//用户名
|
|
|
@Value("${third-party-user-name.base-url}")
|
|
|
private String userName;
|
|
|
@@ -567,6 +577,430 @@ public class AiTagJob {
|
|
|
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"
|
|
|
+ ));
|
|
|
+
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ HttpHeaders aiHeaders = new HttpHeaders();
|
|
|
+ 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");
|
|
|
+
|
|
|
+ aiHeaders.setContentType(MediaType.APPLICATION_JSON);
|
|
|
+ aiHeaders.set("Authorization", "Bearer " + accessToken);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ List<StoreClockIn> storeClockIns = storeClockInMapper.selectList(new LambdaQueryWrapper<StoreClockIn>()
|
|
|
+ .eq(StoreClockIn::getDeleteFlag, 0).eq(StoreClockIn::getCheckFlag, 0));
|
|
|
+ for (StoreClockIn storeClockIn : storeClockIns) {
|
|
|
+ String imagePath = storeClockIn.getImgUrl() != null ? storeClockIn.getImgUrl() : "";
|
|
|
+ List<String> imageList = new ArrayList<>();
|
|
|
+ List<String> videoList = new ArrayList<>();
|
|
|
+ // 按分隔符拆分路径数组(处理连续分隔符如 ",," 产生的空字符串)
|
|
|
+ String[] allPaths = imagePath.split(",");
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 对每一条动态逐条申请 token 并提交,避免批量失败导致重试逻辑复杂化
|
|
|
+ try {
|
|
|
+ Map<String, Object> jsonBody = new HashMap<>();
|
|
|
+ // text/img/video 三种维度一起传入 AI,便于一次完成合规审查
|
|
|
+ jsonBody.put("text", storeClockIn.getContent());
|
|
|
+ jsonBody.put("image_urls", imageList);
|
|
|
+ jsonBody.put("video_urls", 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());
|
|
|
+ }
|
|
|
+ JSONObject responseNode = JSONObject.parseObject(response.getBody());
|
|
|
+ if (responseNode == null) {
|
|
|
+ log.error("AI接口调用失败,响应内容为空");
|
|
|
+ }
|
|
|
+ Integer code = null;
|
|
|
+ if (responseNode != null) {
|
|
|
+ code = responseNode.getInteger("code");
|
|
|
+ if (code == 200) {
|
|
|
+ JSONObject dataNode = JSONObject.from(responseNode.get("data"));
|
|
|
+ // 审核发起后仅标记 checkFlag=1 并保存任务号,等待回调或后续轮询更新
|
|
|
+ StoreClockIn clockIn = new StoreClockIn();
|
|
|
+ clockIn.setId(storeClockIn.getId());
|
|
|
+ clockIn.setCheckFlag(1);
|
|
|
+ clockIn.setAiTaskId(dataNode.get("task_id").toString());
|
|
|
+ storeClockInMapper.updateById(clockIn);
|
|
|
+ log.info("动态审核成功,AI返回内容: {}", response.getBody());
|
|
|
+ XxlJobHelper.handleSuccess("动态内容审核任务执行成功");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.error("AI接口调用失败,错误码: " + code);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("调用AI内容审核接口失败", e);
|
|
|
+ }
|
|
|
+ } catch (RuntimeException ex) {
|
|
|
+ XxlJobHelper.handleFail("动态内容审核任务执行失败:" + ex.getMessage());
|
|
|
+ return R.fail("动态内容审核任务执行失败:" + ex.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (LifeUserDynamics lifeUserDynamic : lifeUserDynamics) {
|
|
|
+ String imagePath = lifeUserDynamic.getImagePath();
|
|
|
+ List<String> imageList = new ArrayList<>();
|
|
|
+ List<String> videoList = new ArrayList<>();
|
|
|
+ // 按分隔符拆分路径数组(处理连续分隔符如 ",," 产生的空字符串)
|
|
|
+ String[] allPaths = imagePath.split(",");
|
|
|
+ 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 {
|
|
|
+ Map<String, Object> jsonBody = new HashMap<>();
|
|
|
+ // text/img/video 三种维度一起传入 AI,便于一次完成合规审查
|
|
|
+ jsonBody.put("text", lifeUserDynamic.getContext());
|
|
|
+ jsonBody.put("image_urls", imageList);
|
|
|
+ jsonBody.put("video_urls", 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());
|
|
|
+ }
|
|
|
+ JSONObject responseNode = JSONObject.parseObject(response.getBody());
|
|
|
+ if (responseNode == null) {
|
|
|
+ log.error("AI接口调用失败,响应内容为空");
|
|
|
+ }
|
|
|
+ Integer code = null;
|
|
|
+ if (responseNode != null) {
|
|
|
+ code = responseNode.getInteger("code");
|
|
|
+ if (code == 200) {
|
|
|
+ JSONObject dataNode = JSONObject.from(responseNode.get("data"));
|
|
|
+ // 审核发起后仅标记 checkFlag=1 并保存任务号,等待回调或后续轮询更新
|
|
|
+ LifeUserDynamics dynamics = new LifeUserDynamics();
|
|
|
+ dynamics.setId(lifeUserDynamic.getId());
|
|
|
+ dynamics.setCheckFlag(1);
|
|
|
+ dynamics.setAiTaskId(dataNode.get("task_id").toString());
|
|
|
+ lifeUserDynamicsMapper.updateById(dynamics);
|
|
|
+ log.info("动态审核成功,AI返回内容: {}", response.getBody());
|
|
|
+ XxlJobHelper.handleSuccess("动态内容审核任务执行成功");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.error("AI接口调用失败,错误码: " + code);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("调用AI内容审核接口失败", e);
|
|
|
+ }
|
|
|
+ } catch (RuntimeException ex) {
|
|
|
+ XxlJobHelper.handleFail("动态内容审核任务执行失败:" + ex.getMessage());
|
|
|
+ return R.fail("动态内容审核任务执行失败:" + ex.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return R.success("动态内容审核任务执行成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取审核违规内容结果
|
|
|
+ */
|
|
|
+ @XxlJob("getCheckTask")
|
|
|
+ public R<String> getCheckTask() {
|
|
|
+ List<LifeUserDynamics> lifeUserDynamics = lifeUserDynamicsMapper.selectList(new LambdaQueryWrapper<LifeUserDynamics>()
|
|
|
+ .eq(LifeUserDynamics::getCheckFlag, 1).eq(LifeUserDynamics::getDeleteFlag, 0));
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ RestTemplate restTemplateWithAuth = new RestTemplate();
|
|
|
+ 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");
|
|
|
+ List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
|
|
|
+ interceptors.add((request, body, execution) -> {
|
|
|
+ request.getHeaders().set("Authorization", "Bearer " + accessToken);
|
|
|
+ return execution.execute(request, body);
|
|
|
+ });
|
|
|
+ restTemplateWithAuth.setInterceptors(interceptors);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ List<StoreClockIn> storeClockIns = storeClockInMapper.selectList(new LambdaQueryWrapper<StoreClockIn>().eq(StoreClockIn::getCheckFlag, 1));
|
|
|
+ for (StoreClockIn storeClockIn : storeClockIns) {
|
|
|
+ // 针对已提交且未删除的动态轮询查询结果
|
|
|
+ try {
|
|
|
+ ResponseEntity<String> response = null;
|
|
|
+ try {
|
|
|
+ response = restTemplateWithAuth.getForEntity(getResultUrl + "?task_id=" + storeClockIn.getAiTaskId(), String.class);
|
|
|
+ if (response.getStatusCodeValue() != 200) {
|
|
|
+ log.error("AI内容审核结果获取接口调用失败 http状态:" + response.getStatusCode());
|
|
|
+ }
|
|
|
+ JSONObject responseNode = JSONObject.parseObject(response.getBody());
|
|
|
+ if (responseNode == null) {
|
|
|
+ log.error("AI接口调用失败,响应内容为空");
|
|
|
+ }
|
|
|
+ Integer code = null;
|
|
|
+ if (responseNode != null) {
|
|
|
+ code = responseNode.getInteger("code");
|
|
|
+ if (code == 200) {
|
|
|
+ JSONObject dataNode = JSONObject.from(responseNode.get("data"));
|
|
|
+ StoreClockIn clockIn = new StoreClockIn();
|
|
|
+ clockIn.setId(storeClockIn.getId());
|
|
|
+ if ("completed".equals(dataNode.get("status"))) {
|
|
|
+ if (!(boolean) dataNode.get("is_compliant")) {
|
|
|
+ // 只要 AI 判定不合规,立即禁用动态并记录原因
|
|
|
+ clockIn.setDeleteFlag(1);
|
|
|
+ clockIn.setReason(String.valueOf(dataNode.get("failure_reason")));
|
|
|
+ }
|
|
|
+ clockIn.setCheckFlag(2);
|
|
|
+ storeClockInMapper.updateById(clockIn);
|
|
|
+ if (!(boolean) dataNode.get("is_compliant")) {
|
|
|
+ storeClockInMapper.deleteById(clockIn);
|
|
|
+ }
|
|
|
+ log.info("动态审核结果获取成功,AI返回内容: {}", response.getBody());
|
|
|
+ XxlJobHelper.handleSuccess("动态内容审核任务结果获取执行成功");
|
|
|
+ } else {
|
|
|
+ log.info("动态审核未完成,AI返回内容: {}", response.getBody());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.error("AI接口调用失败,错误码: " + code);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("调用AI内容审核结果获取接口失败", e);
|
|
|
+ }
|
|
|
+ } catch (RuntimeException ex) {
|
|
|
+ XxlJobHelper.handleFail("动态内容审核任务结果获取执行失败:" + ex.getMessage());
|
|
|
+ return R.fail("动态内容审核任务结果获取执行失败:" + ex.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (LifeUserDynamics lifeUserDynamic : lifeUserDynamics) {
|
|
|
+ // 针对已提交且未删除的动态轮询查询结果
|
|
|
+ try {
|
|
|
+ ResponseEntity<String> response = null;
|
|
|
+ try {
|
|
|
+ response = restTemplateWithAuth.getForEntity(getResultUrl + "?task_id=" + lifeUserDynamic.getAiTaskId(), String.class);
|
|
|
+ if (response.getStatusCodeValue() != 200) {
|
|
|
+ log.error("AI内容审核结果获取接口调用失败 http状态:" + response.getStatusCode());
|
|
|
+ }
|
|
|
+ JSONObject responseNode = JSONObject.parseObject(response.getBody());
|
|
|
+ if (responseNode == null) {
|
|
|
+ log.error("AI接口调用失败,响应内容为空");
|
|
|
+ }
|
|
|
+ Integer code = null;
|
|
|
+ if (responseNode != null) {
|
|
|
+ code = responseNode.getInteger("code");
|
|
|
+ if (code == 200) {
|
|
|
+ JSONObject dataNode = JSONObject.from(responseNode.get("data"));
|
|
|
+ LifeUserDynamics dynamics = new LifeUserDynamics();
|
|
|
+ dynamics.setId(lifeUserDynamic.getId());
|
|
|
+ if ("completed".equals(dataNode.get("status"))) {
|
|
|
+ if (!(boolean) dataNode.get("is_compliant")) {
|
|
|
+ // 只要 AI 判定不合规,立即禁用动态并记录原因
|
|
|
+ dynamics.setEnableStatus(1);
|
|
|
+ dynamics.setReason(String.valueOf(dataNode.get("failure_reason")));
|
|
|
+ }
|
|
|
+ dynamics.setCheckFlag(2);
|
|
|
+ lifeUserDynamicsMapper.updateById(dynamics);
|
|
|
+ log.info("动态审核结果获取成功,AI返回内容: {}", response.getBody());
|
|
|
+ XxlJobHelper.handleSuccess("动态内容审核任务结果获取执行成功");
|
|
|
+ } else {
|
|
|
+ log.info("动态审核未完成,AI返回内容: {}", response.getBody());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.error("AI接口调用失败,错误码: " + code);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("调用AI内容审核结果获取接口失败", e);
|
|
|
+ }
|
|
|
+ } catch (RuntimeException ex) {
|
|
|
+ XxlJobHelper.handleFail("动态内容审核任务结果获取执行失败:" + ex.getMessage());
|
|
|
+ return R.fail("动态内容审核任务结果获取执行失败:" + ex.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return R.success("动态内容审核任务结果获取执行成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 店铺AI审核任务
|
|
|
+ * <p>
|
|
|
+ * 定时查询 store_info 表中审核状态为 0(待审核)的门店,
|
|
|
+ * 后续可在此处调用 AI 审核接口进行自动审核。
|
|
|
+ * </p>
|
|
|
+ */
|
|
|
+ @XxlJob("aiApproveStoreInfoTask")
|
|
|
+ public R<String> aiApproveStoreInfoTask() {
|
|
|
+ log.info("开始执行店铺AI审核任务...");
|
|
|
+ try {
|
|
|
+ // 1. 查询待审核门店:store_application_status = 0 且 未删除
|
|
|
+ List<StoreInfo> pendingStores = storeInfoMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<StoreInfo>()
|
|
|
+ .eq(StoreInfo::getStoreApplicationStatus, 0)
|
|
|
+ .eq(StoreInfo::getDeleteFlag, 0)
|
|
|
+ );
|
|
|
+
|
|
|
+ if (pendingStores == null || pendingStores.isEmpty()) {
|
|
|
+ log.info("当前无待审核门店,任务结束。");
|
|
|
+ XxlJobHelper.handleSuccess("当前无待审核门店");
|
|
|
+ return R.success("当前无待审核门店");
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("本次待审核门店数量:{}", pendingStores.size());
|
|
|
+
|
|
|
+ // 2. 登录 AI 服务获取 token(如需调用 AI 审核接口,可在此处复用 token)
|
|
|
+ MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
|
|
|
+ formData.add("username", userName);
|
|
|
+ formData.add("password", passWord);
|
|
|
+
|
|
|
+ HttpHeaders loginHeaders = new HttpHeaders();
|
|
|
+ loginHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
|
+ HttpEntity<MultiValueMap<String, String>> loginRequest = new HttpEntity<>(formData, loginHeaders);
|
|
|
+
|
|
|
+ ResponseEntity<String> loginResponse = null;
|
|
|
+ try {
|
|
|
+ log.info("请求Ai服务登录接口===================> {}", loginUrl);
|
|
|
+ loginResponse = restTemplate.postForEntity(loginUrl, loginRequest, String.class);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("请求AI服务登录接口失败", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ String accessToken = null;
|
|
|
+ if (loginResponse != null && loginResponse.getStatusCodeValue() == 200) {
|
|
|
+ log.info("请求Ai服务登录成功 body={}", loginResponse.getBody());
|
|
|
+ String body = loginResponse.getBody();
|
|
|
+ if (body != null) {
|
|
|
+ JSONObject jsonObject = JSONObject.parseObject(body);
|
|
|
+ if (jsonObject != null) {
|
|
|
+ JSONObject dataJson = jsonObject.getJSONObject("data");
|
|
|
+ if (dataJson != null) {
|
|
|
+ accessToken = dataJson.getString("access_token");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (accessToken == null) {
|
|
|
+ log.error("获取AI服务 access_token 失败,终止店铺AI审核任务");
|
|
|
+ XxlJobHelper.handleFail("获取AI服务 access_token 失败");
|
|
|
+ return R.fail("获取AI服务 access_token 失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 遍历待审核门店,构建 AiApproveStoreInfo 并预留调用 AI 审核接口(具体字段后续补充)
|
|
|
+ HttpHeaders aiHeaders = new HttpHeaders();
|
|
|
+ aiHeaders.setContentType(MediaType.APPLICATION_JSON);
|
|
|
+ aiHeaders.set("Authorization", "Bearer " + accessToken);
|
|
|
+
|
|
|
+ for (StoreInfo storeInfo : pendingStores) {
|
|
|
+ AiApproveStoreInfo aiApproveStoreInfo = new AiApproveStoreInfo();
|
|
|
+
|
|
|
+ HttpEntity<AiApproveStoreInfo> request = new HttpEntity<>(aiApproveStoreInfo, aiHeaders);
|
|
|
+ ResponseEntity<String> response = null;
|
|
|
+ try {
|
|
|
+ response = restTemplate.postForEntity("http://192.168.2.250:9000/ai/auto-review/api/v1/merchant-onboarding/applications", request, String.class);
|
|
|
+ if (response.getStatusCodeValue() != 200) {
|
|
|
+ log.error("店铺AI审核接口调用失败 storeId={}, http状态={}", storeInfo.getId(), response.getStatusCode());
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String respBody = response.getBody();
|
|
|
+ log.info("店铺AI审核返回结果 storeId={}, body={}", storeInfo.getId(), respBody);
|
|
|
+ // 如需根据 AI 返回结果更新 store_info 审核状态,可在此处解析 respBody 并更新 DB
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("调用店铺AI审核接口异常, storeId={}", storeInfo.getId(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ XxlJobHelper.handleSuccess("店铺AI审核任务执行完成");
|
|
|
+ return R.success("店铺AI审核任务执行完成");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("店铺AI审核任务执行异常", e);
|
|
|
+ XxlJobHelper.handleFail("店铺AI审核任务执行异常: " + e.getMessage());
|
|
|
+ return R.fail("店铺AI审核任务执行异常: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
class AnalysisRequest {
|
|
|
private String start_time;
|
|
|
private String end_time;
|
|
|
@@ -587,5 +1021,4 @@ public class AiTagJob {
|
|
|
this.end_time = end_time;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
}
|