|
@@ -2,16 +2,19 @@ package com.takai.web.controller.droneai;
|
|
|
|
|
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
import cn.hutool.crypto.digest.DigestUtil;
|
|
import cn.hutool.crypto.digest.DigestUtil;
|
|
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
|
-import com.takai.ai.domain.dto.drone.ProjectDetectionParam;
|
|
|
|
|
-import com.takai.ai.domain.dto.drone.SafeWarning;
|
|
|
|
|
|
|
+import com.takai.ai.domain.PictureRagRes;
|
|
|
|
|
+import com.takai.ai.domain.dto.drone.*;
|
|
|
import com.takai.ai.domain.entity.*;
|
|
import com.takai.ai.domain.entity.*;
|
|
|
|
|
+import com.takai.ai.service.IPictureRagResService;
|
|
|
import com.takai.ai.service.ITakaiAiService;
|
|
import com.takai.ai.service.ITakaiAiService;
|
|
|
|
|
+import com.takai.ai.service.impl.PictureRagResServiceImpl;
|
|
|
import com.takai.common.core.controller.BaseController;
|
|
import com.takai.common.core.controller.BaseController;
|
|
|
import com.takai.common.core.domain.AjaxResult;
|
|
import com.takai.common.core.domain.AjaxResult;
|
|
|
import com.takai.common.enums.QualityTypeEnum;
|
|
import com.takai.common.enums.QualityTypeEnum;
|
|
@@ -21,10 +24,13 @@ import com.takai.system.service.ISysClientService;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.http.HttpStatus;
|
|
import org.springframework.http.HttpStatus;
|
|
|
|
|
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
|
import org.springframework.util.StringUtils;
|
|
import org.springframework.util.StringUtils;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
|
|
|
|
|
|
+import java.io.IOException;
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
@Slf4j
|
|
@Slf4j
|
|
|
@RestController
|
|
@RestController
|
|
@@ -37,6 +43,12 @@ public class DroneAiController extends BaseController {
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private ITakaiAiService takaiAisService;
|
|
private ITakaiAiService takaiAisService;
|
|
|
|
|
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private IPictureRagResService pictureRagResService;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
|
|
|
|
+
|
|
|
// 静态ObjectMapper实例(线程安全,可复用)
|
|
// 静态ObjectMapper实例(线程安全,可复用)
|
|
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
|
|
|
|
|
|
@@ -49,7 +61,7 @@ public class DroneAiController extends BaseController {
|
|
|
@PostMapping("/ai_check")
|
|
@PostMapping("/ai_check")
|
|
|
public AjaxResult aiCheck(@RequestBody ProjectDetectionParam requestBody) throws Exception
|
|
public AjaxResult aiCheck(@RequestBody ProjectDetectionParam requestBody) throws Exception
|
|
|
{
|
|
{
|
|
|
- log.info("登录请求参数:{}", requestBody.toString());
|
|
|
|
|
|
|
+ log.info("图片RAG检查接口请求参数:{}", requestBody.toString());
|
|
|
AjaxResult ajax = AjaxResult.success();
|
|
AjaxResult ajax = AjaxResult.success();
|
|
|
|
|
|
|
|
if(
|
|
if(
|
|
@@ -69,6 +81,7 @@ public class DroneAiController extends BaseController {
|
|
|
)
|
|
)
|
|
|
)
|
|
)
|
|
|
) {
|
|
) {
|
|
|
|
|
+ log.info("请求参数错误");
|
|
|
return AjaxResult.error(org.springframework.http.HttpStatus.BAD_REQUEST.value(),"参数错误");
|
|
return AjaxResult.error(org.springframework.http.HttpStatus.BAD_REQUEST.value(),"参数错误");
|
|
|
}
|
|
}
|
|
|
String clientId = requestBody.getClient().getClientId();
|
|
String clientId = requestBody.getClient().getClientId();
|
|
@@ -80,6 +93,7 @@ public class DroneAiController extends BaseController {
|
|
|
//检查ClientId是否存在
|
|
//检查ClientId是否存在
|
|
|
SysClient client = clientService.selectSysClientById(clientId);
|
|
SysClient client = clientService.selectSysClientById(clientId);
|
|
|
if(client == null) {
|
|
if(client == null) {
|
|
|
|
|
+ log.info("无效的client_id");
|
|
|
return AjaxResult.error(org.springframework.http.HttpStatus.BAD_REQUEST.value(),"无效的client_id");
|
|
return AjaxResult.error(org.springframework.http.HttpStatus.BAD_REQUEST.value(),"无效的client_id");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -88,15 +102,18 @@ public class DroneAiController extends BaseController {
|
|
|
// Step 3: Check if the difference is greater than 5 minutes
|
|
// Step 3: Check if the difference is greater than 5 minutes
|
|
|
boolean isMoreThanFiveMinutes = btwTime > 5 * 1000 * 60;
|
|
boolean isMoreThanFiveMinutes = btwTime > 5 * 1000 * 60;
|
|
|
if (isMoreThanFiveMinutes) {
|
|
if (isMoreThanFiveMinutes) {
|
|
|
|
|
+ log.info("请求已过期,请重新发起请求");
|
|
|
return AjaxResult.error(org.springframework.http.HttpStatus.BAD_REQUEST.value(),"请求已过期,请重新发起请求");
|
|
return AjaxResult.error(org.springframework.http.HttpStatus.BAD_REQUEST.value(),"请求已过期,请重新发起请求");
|
|
|
}
|
|
}
|
|
|
if (sign != null) {
|
|
if (sign != null) {
|
|
|
String dataInput = clientId+timestamp+type+imageId;
|
|
String dataInput = clientId+timestamp+type+imageId;
|
|
|
String generatedHash = DigestUtil.sha256Hex(dataInput.toUpperCase()+client.getClientSecret());
|
|
String generatedHash = DigestUtil.sha256Hex(dataInput.toUpperCase()+client.getClientSecret());
|
|
|
if(!StrUtil.equals(generatedHash, sign)) {
|
|
if(!StrUtil.equals(generatedHash, sign)) {
|
|
|
|
|
+ log.info("签名验证失败");
|
|
|
return AjaxResult.error(org.springframework.http.HttpStatus.UNAUTHORIZED.value(),"签名验证失败");
|
|
return AjaxResult.error(org.springframework.http.HttpStatus.UNAUTHORIZED.value(),"签名验证失败");
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
|
|
+ log.info("无效的签名");
|
|
|
return AjaxResult.error(HttpStatus.UNAUTHORIZED.value(),"无效的签名");
|
|
return AjaxResult.error(HttpStatus.UNAUTHORIZED.value(),"无效的签名");
|
|
|
}
|
|
}
|
|
|
//处理请求报文
|
|
//处理请求报文
|
|
@@ -110,10 +127,15 @@ public class DroneAiController extends BaseController {
|
|
|
String rootArrayPath = null;
|
|
String rootArrayPath = null;
|
|
|
if (VisionTypeEnum.SAFE.getCode().equals(type)) {
|
|
if (VisionTypeEnum.SAFE.getCode().equals(type)) {
|
|
|
//检查label是否为空
|
|
//检查label是否为空
|
|
|
- int size = requestBody.getSafe().getWarning().size();
|
|
|
|
|
- for(int i = 0; i < size; i++) {
|
|
|
|
|
|
|
+ List<SafeWarning> swList = requestBody.getSafe().getWarning();
|
|
|
|
|
+ if (swList == null || swList.isEmpty()) {
|
|
|
|
|
+ log.info("safe不能为空");
|
|
|
|
|
+ return AjaxResult.error(HttpStatus.BAD_REQUEST.value(),"safe不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ for(int i = 0; i < swList.size(); i++) {
|
|
|
SafeWarning sw = requestBody.getSafe().getWarning().get(i);
|
|
SafeWarning sw = requestBody.getSafe().getWarning().get(i);
|
|
|
if(!StringUtils.hasText(sw.getLabel())) {
|
|
if(!StringUtils.hasText(sw.getLabel())) {
|
|
|
|
|
+ log.info("label不能为空");
|
|
|
return AjaxResult.error(HttpStatus.BAD_REQUEST.value(),"label不能为空");
|
|
return AjaxResult.error(HttpStatus.BAD_REQUEST.value(),"label不能为空");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -124,14 +146,27 @@ public class DroneAiController extends BaseController {
|
|
|
requestParams.setAppId("3010574988024877056");
|
|
requestParams.setAppId("3010574988024877056");
|
|
|
|
|
|
|
|
} else if (VisionTypeEnum.QUALITY.getCode().equals(type)) {
|
|
} else if (VisionTypeEnum.QUALITY.getCode().equals(type)) {
|
|
|
|
|
+
|
|
|
if (QualityTypeEnum.WARNING.getCode().equalsIgnoreCase(requestBody.getQuality().getSubType())) {
|
|
if (QualityTypeEnum.WARNING.getCode().equalsIgnoreCase(requestBody.getQuality().getSubType())) {
|
|
|
|
|
+ List<QualityWarning> qwList = requestBody.getQuality().getWarning();
|
|
|
|
|
+ if (qwList == null || qwList.isEmpty()) {
|
|
|
|
|
+ log.info("quality不能为空");
|
|
|
|
|
+ return AjaxResult.error(HttpStatus.BAD_REQUEST.value(),"quality不能为空");
|
|
|
|
|
+ }
|
|
|
extractFields = Arrays.asList("label", "judgment");
|
|
extractFields = Arrays.asList("label", "judgment");
|
|
|
rootArrayPath = "quality.warning";
|
|
rootArrayPath = "quality.warning";
|
|
|
} else if (QualityTypeEnum.REBAR_INFO.getCode().equalsIgnoreCase(requestBody.getQuality().getSubType())) {
|
|
} else if (QualityTypeEnum.REBAR_INFO.getCode().equalsIgnoreCase(requestBody.getQuality().getSubType())) {
|
|
|
|
|
+ List<RebarInfo> rebarList = requestBody.getQuality().getRebarInfo();
|
|
|
|
|
+ if (rebarList == null || rebarList.isEmpty()) {
|
|
|
|
|
+ log.info("quality不能为空");
|
|
|
|
|
+ return AjaxResult.error(HttpStatus.BAD_REQUEST.value(),"quality不能为空");
|
|
|
|
|
+ }
|
|
|
extractFields = Collections.singletonList("remark");
|
|
extractFields = Collections.singletonList("remark");
|
|
|
rootArrayPath = "quality.rebarInfo";
|
|
rootArrayPath = "quality.rebarInfo";
|
|
|
}
|
|
}
|
|
|
requestParams.setAppId("3010568863451844608");
|
|
requestParams.setAppId("3010568863451844608");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return ajax;
|
|
|
}
|
|
}
|
|
|
String requestJson = JSONObject.toJSONString(requestBody);
|
|
String requestJson = JSONObject.toJSONString(requestBody);
|
|
|
String simpleJson = extractFields(requestJson, rootArrayPath, extractFields);
|
|
String simpleJson = extractFields(requestJson, rootArrayPath, extractFields);
|
|
@@ -146,17 +181,156 @@ public class DroneAiController extends BaseController {
|
|
|
requestParams.setMessages(messages);
|
|
requestParams.setMessages(messages);
|
|
|
String result = takaiAisService.syncInvoke(requestParams);
|
|
String result = takaiAisService.syncInvoke(requestParams);
|
|
|
if(StringUtils.hasText(result)) {
|
|
if(StringUtils.hasText(result)) {
|
|
|
|
|
+ //保存RAG返回信息
|
|
|
|
|
+ PictureRagRes pictureRagRes = getPictureRagRes(requestBody,result);
|
|
|
|
|
+ pictureRagResService.insertPictureRagRes(pictureRagRes);
|
|
|
//调用安卓后端接口
|
|
//调用安卓后端接口
|
|
|
|
|
+// threadPoolTaskExecutor.execute(() -> excuteAppReport(requestBody,result));
|
|
|
} else {
|
|
} else {
|
|
|
ajax = AjaxResult.error("调用知识库异常");
|
|
ajax = AjaxResult.error("调用知识库异常");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-// ObjectMapper objectMapper = new ObjectMapper();
|
|
|
|
|
-// Map<String, Object> map = null;
|
|
|
|
|
-// ajax.put("data", map);
|
|
|
|
|
return ajax;
|
|
return ajax;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private PictureRagRes getPictureRagRes(ProjectDetectionParam requestBody,String ragRes) {
|
|
|
|
|
+ PictureRagRes pictureRagRes = new PictureRagRes();
|
|
|
|
|
+ pictureRagRes.setPhaseId(requestBody.getPhaseId());
|
|
|
|
|
+ pictureRagRes.setProjectId(requestBody.getProjectId());
|
|
|
|
|
+ pictureRagRes.setResImageId(requestBody.getResImageId());
|
|
|
|
|
+ pictureRagRes.setType(requestBody.getType());
|
|
|
|
|
+ pictureRagRes.setReqJson(JSONObject.toJSONString(requestBody));
|
|
|
|
|
+ pictureRagRes.setRagRes(ragRes);
|
|
|
|
|
+ return pictureRagRes;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 图片RAG结果查询接口
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param requestBody 查询
|
|
|
|
|
+ * @return 结果
|
|
|
|
|
+ */
|
|
|
|
|
+ @PostMapping("/ai_result")
|
|
|
|
|
+ public AjaxResult aiResult(@RequestBody PictureRagResParam requestBody) throws Exception
|
|
|
|
|
+ {
|
|
|
|
|
+ log.info("图片RAG结果查询接口请求参数:{}", requestBody.toString());
|
|
|
|
|
+ AjaxResult ajax = AjaxResult.success();
|
|
|
|
|
+
|
|
|
|
|
+ if(
|
|
|
|
|
+ requestBody.getClient() == null
|
|
|
|
|
+ || requestBody.getClient().getClientId() == null
|
|
|
|
|
+ || requestBody.getClient().getTimestamp() == null
|
|
|
|
|
+ || requestBody.getClient().getSignature() == null
|
|
|
|
|
+ ||!StringUtils.hasText(requestBody.getPhaseId())
|
|
|
|
|
+ ) {
|
|
|
|
|
+ log.info("请求参数错误");
|
|
|
|
|
+ return AjaxResult.error(org.springframework.http.HttpStatus.BAD_REQUEST.value(),"参数错误");
|
|
|
|
|
+ }
|
|
|
|
|
+ String clientId = requestBody.getClient().getClientId();
|
|
|
|
|
+ String timestamp = requestBody.getClient().getTimestamp();
|
|
|
|
|
+ String phaseId = requestBody.getPhaseId();
|
|
|
|
|
+ String sign = requestBody.getClient().getSignature();
|
|
|
|
|
+
|
|
|
|
|
+ //检查ClientId是否存在
|
|
|
|
|
+ SysClient client = clientService.selectSysClientById(clientId);
|
|
|
|
|
+ if(client == null) {
|
|
|
|
|
+ log.info("无效的client_id");
|
|
|
|
|
+ return AjaxResult.error(org.springframework.http.HttpStatus.BAD_REQUEST.value(),"无效的client_id");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ long btwTime = System.currentTimeMillis() - Long.parseLong(timestamp);
|
|
|
|
|
+
|
|
|
|
|
+ // Step 3: Check if the difference is greater than 5 minutes
|
|
|
|
|
+ boolean isMoreThanFiveMinutes = btwTime > 5 * 1000 * 60;
|
|
|
|
|
+ if (isMoreThanFiveMinutes) {
|
|
|
|
|
+ log.info("请求已过期,请重新发起请求");
|
|
|
|
|
+ return AjaxResult.error(org.springframework.http.HttpStatus.BAD_REQUEST.value(),"请求已过期,请重新发起请求");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (sign != null) {
|
|
|
|
|
+ String dataInput = clientId+timestamp+phaseId;
|
|
|
|
|
+ String generatedHash = DigestUtil.sha256Hex(dataInput.toUpperCase()+client.getClientSecret());
|
|
|
|
|
+ if(!StrUtil.equals(generatedHash, sign)) {
|
|
|
|
|
+ log.info("签名验证失败");
|
|
|
|
|
+ return AjaxResult.error(org.springframework.http.HttpStatus.UNAUTHORIZED.value(),"签名验证失败");
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.info("无效的签名");
|
|
|
|
|
+ return AjaxResult.error(HttpStatus.UNAUTHORIZED.value(),"无效的签名");
|
|
|
|
|
+ }
|
|
|
|
|
+ //处理请求报文
|
|
|
|
|
+ PictureRagRes pictureRagRes = new PictureRagRes();
|
|
|
|
|
+ pictureRagRes.setPhaseId(phaseId);
|
|
|
|
|
+ List<PictureRagRes> ragResList = pictureRagResService.selectPictureRagResList(pictureRagRes);
|
|
|
|
|
+
|
|
|
|
|
+ List<Map<String, Object>> resultData = convertResultData(ragResList);
|
|
|
|
|
+ return AjaxResult.success(resultData);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<Map<String, Object>> convertResultData (List<PictureRagRes> ragResList) {
|
|
|
|
|
+ // 2. 核心转换:直接转成目标JSON的Map结构(无需实体类)
|
|
|
|
|
+ List<Map<String, Object>> finalJsonMap = ragResList.stream()
|
|
|
|
|
+ // 按phaseId分组
|
|
|
|
|
+ .collect(Collectors.groupingBy(PictureRagRes::getPhaseId))
|
|
|
|
|
+ .entrySet().stream()
|
|
|
|
|
+ .map(phaseEntry -> {
|
|
|
|
|
+ Map<String, Object> phaseMap = new HashMap<>(2);
|
|
|
|
|
+ phaseMap.put("phaseId", phaseEntry.getKey());
|
|
|
|
|
+
|
|
|
|
|
+ // 同phase下,按resImageId分组,合并safe/quality
|
|
|
|
|
+ List<Map<String, Object>> ragResListMap = phaseEntry.getValue().stream()
|
|
|
|
|
+ .collect(Collectors.groupingBy(PictureRagRes::getResImageId))
|
|
|
|
|
+ .entrySet().stream()
|
|
|
|
|
+ .map(imageEntry -> {
|
|
|
|
|
+ Map<String, Object> imageMap = new HashMap<>(3);
|
|
|
|
|
+ imageMap.put("resImageId", imageEntry.getKey());
|
|
|
|
|
+ // 遍历赋值safe/quality
|
|
|
|
|
+ imageEntry.getValue().forEach(item -> {
|
|
|
|
|
+ if ("safe".equals(item.getType())) {
|
|
|
|
|
+ imageMap.put("safe", item.getRagRes());
|
|
|
|
|
+ } else if ("quality".equals(item.getType())) {
|
|
|
|
|
+ imageMap.put("quality", item.getRagRes());
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ return imageMap;
|
|
|
|
|
+ }).collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ phaseMap.put("ragRes", ragResListMap);
|
|
|
|
|
+ return phaseMap;
|
|
|
|
|
+ }).collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ return finalJsonMap;
|
|
|
|
|
+ // 3. 转成你需要的JSON字符串(直接返回给前端即可)
|
|
|
|
|
+ //String targetJson = JSONUtil.toJsonStr(finalJsonMap);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ private void excuteAppReport(ProjectDetectionParam requestBody, String reportStr) {
|
|
|
|
|
+ final int MAX_RETRY = 3; // 最多重试3次
|
|
|
|
|
+ boolean success = false;
|
|
|
|
|
+ int retryNum = 0; // 重试次数
|
|
|
|
|
+
|
|
|
|
|
+ do {
|
|
|
|
|
+ try {
|
|
|
|
|
+ success = takaiAisService.reportToApp(requestBody, reportStr);
|
|
|
|
|
+ if (success) {
|
|
|
|
|
+ return; // 调用成功,直接结束方法
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("调用App结果通知接口失败,第{}次重试", retryNum + 1, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ retryNum++;
|
|
|
|
|
+ // 重试间隔递增:第1次等1s,第2次等2s,第3次等3s (递增核心逻辑,极简一行)
|
|
|
|
|
+ if (retryNum <= MAX_RETRY) {
|
|
|
|
|
+ try { Thread.sleep(5000L * retryNum); } catch (InterruptedException ignored) {}
|
|
|
|
|
+ }
|
|
|
|
|
+ } while (!success && retryNum <= MAX_RETRY);
|
|
|
|
|
+
|
|
|
|
|
+ // 执行到这=重试3次后还是失败,打印最终错误日志
|
|
|
|
|
+ log.error("调用App结果通知接口失败,已重试{}次,放弃重试", MAX_RETRY);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 通用JSON字段提取方法
|
|
* 通用JSON字段提取方法
|
|
|
* @param originalJson 原始JSON字符串
|
|
* @param originalJson 原始JSON字符串
|