|
|
@@ -0,0 +1,214 @@
|
|
|
+package com.takai.web.controller.droneai;
|
|
|
+
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import cn.hutool.crypto.digest.DigestUtil;
|
|
|
+import com.alibaba.fastjson2.JSONObject;
|
|
|
+import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
+import com.fasterxml.jackson.databind.JsonNode;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
|
+import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
|
+import com.takai.ai.domain.dto.drone.ProjectDetectionParam;
|
|
|
+import com.takai.ai.domain.entity.*;
|
|
|
+import com.takai.ai.service.ITakaiAiService;
|
|
|
+import com.takai.common.core.controller.BaseController;
|
|
|
+import com.takai.common.core.domain.AjaxResult;
|
|
|
+import com.takai.common.enums.QualityTypeEnum;
|
|
|
+import com.takai.common.enums.VisionTypeEnum;
|
|
|
+import com.takai.system.domain.SysClient;
|
|
|
+import com.takai.system.service.ISysClientService;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.http.HttpStatus;
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
+import org.springframework.web.bind.annotation.*;
|
|
|
+
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@RestController
|
|
|
+@RequestMapping("/vision")
|
|
|
+public class DroneAiController extends BaseController {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ISysClientService clientService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ITakaiAiService takaiAisService;
|
|
|
+
|
|
|
+ // 静态ObjectMapper实例(线程安全,可复用)
|
|
|
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 视觉数据大模型总结
|
|
|
+ *
|
|
|
+ * @param requestBody 登录信息
|
|
|
+ * @return 结果
|
|
|
+ */
|
|
|
+ @PostMapping("/ai_check")
|
|
|
+ public AjaxResult aiCheck(@RequestBody ProjectDetectionParam requestBody) throws Exception
|
|
|
+ {
|
|
|
+ log.info("登录请求参数:{}", 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.getType())
|
|
|
+ || (VisionTypeEnum.SAFE.getCode().equals(requestBody.getType()) && requestBody.getSafe() == null)
|
|
|
+ || (VisionTypeEnum.QUALITY.getCode().equals(requestBody.getType()) && requestBody.getQuality() == null)
|
|
|
+ || (VisionTypeEnum.QUALITY.getCode().equals(requestBody.getType()) &&
|
|
|
+ (!StringUtils.hasText(requestBody.getQuality().getSubType())
|
|
|
+ || (QualityTypeEnum.WARNING.getCode().equalsIgnoreCase(requestBody.getQuality().getSubType()) && requestBody.getQuality().getWarning() == null)
|
|
|
+ || (QualityTypeEnum.THICKNESS.getCode().equalsIgnoreCase(requestBody.getQuality().getSubType()) && requestBody.getQuality().getThickness() == null)
|
|
|
+ || (QualityTypeEnum.REBAR_INFO.getCode().equalsIgnoreCase(requestBody.getQuality().getSubType()) && requestBody.getQuality().getRebarInfo() == null)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ ) {
|
|
|
+ return AjaxResult.error(org.springframework.http.HttpStatus.BAD_REQUEST.value(),"参数错误");
|
|
|
+ }
|
|
|
+ String clientId = requestBody.getClient().getClientId();
|
|
|
+ String timestamp = requestBody.getClient().getTimestamp();
|
|
|
+ String type = requestBody.getType();
|
|
|
+ String imageId = requestBody.getResImageId();
|
|
|
+ String sign = requestBody.getClient().getSignature();
|
|
|
+
|
|
|
+ //检查ClientId是否存在
|
|
|
+ SysClient client = clientService.selectSysClientById(clientId);
|
|
|
+ if(client == null) {
|
|
|
+ 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) {
|
|
|
+ return AjaxResult.error(org.springframework.http.HttpStatus.BAD_REQUEST.value(),"请求已过期,请重新发起请求");
|
|
|
+ }
|
|
|
+ if (sign != null) {
|
|
|
+ String dataInput = clientId+timestamp+type+imageId;
|
|
|
+ String generatedHash = DigestUtil.sha256Hex(dataInput.toUpperCase()+client.getClientSecret());
|
|
|
+ if(!StrUtil.equals(generatedHash, sign)) {
|
|
|
+ return AjaxResult.error(org.springframework.http.HttpStatus.UNAUTHORIZED.value(),"签名验证失败");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return AjaxResult.error(HttpStatus.UNAUTHORIZED.value(),"无效的签名");
|
|
|
+ }
|
|
|
+ //处理请求报文
|
|
|
+ TakaiSseInfoParams requestParams = new TakaiSseInfoParams();
|
|
|
+ //如果是浇筑厚度,不处理,直接调研APP后端接口
|
|
|
+ if(QualityTypeEnum.THICKNESS.getCode().equalsIgnoreCase(requestBody.getQuality().getSubType())) {
|
|
|
+
|
|
|
+ } else {
|
|
|
+ List<String> extractFields = null;
|
|
|
+ String rootArrayPath = null;
|
|
|
+ if (VisionTypeEnum.SAFE.getCode().equals(type)) {
|
|
|
+ // 调用通用方法:提取safe.warning下的label和judgment字段
|
|
|
+ extractFields = Arrays.asList("label", "judgment");
|
|
|
+ rootArrayPath = "safe.warning";
|
|
|
+ requestParams.setAppId("3010574988024877056");
|
|
|
+
|
|
|
+ } else if (VisionTypeEnum.QUALITY.getCode().equals(type)) {
|
|
|
+ if (QualityTypeEnum.WARNING.getCode().equalsIgnoreCase(requestBody.getQuality().getSubType())) {
|
|
|
+ extractFields = Arrays.asList("label", "judgment");
|
|
|
+ rootArrayPath = "quality.warning";
|
|
|
+ } else if (QualityTypeEnum.REBAR_INFO.getCode().equalsIgnoreCase(requestBody.getQuality().getSubType())) {
|
|
|
+ extractFields = Collections.singletonList("remark");
|
|
|
+ rootArrayPath = "quality.rebarInfo";
|
|
|
+ }
|
|
|
+ requestParams.setAppId("3010568863451844608");
|
|
|
+ }
|
|
|
+ String requestJson = JSONObject.toJSONString(requestBody);
|
|
|
+ String simpleJson = extractFields(requestJson, rootArrayPath, extractFields);
|
|
|
+ //调用大模型接口
|
|
|
+
|
|
|
+ //messages示例:[{'role': 'user', 'content': '你是谁?'}, {'role': 'assistant', 'content': '我是小智'}, {'role': 'user', 'content': '你能做什么?'}]
|
|
|
+ List<TakaiPromptInfo> messages = new ArrayList<>();
|
|
|
+ TakaiPromptInfo message = new TakaiPromptInfo();
|
|
|
+ message.setRole("user");
|
|
|
+ message.setContent(simpleJson);
|
|
|
+ messages.add(message);
|
|
|
+ requestParams.setMessages(messages);
|
|
|
+ String result = takaiAisService.syncInvoke(requestParams);
|
|
|
+ if(StringUtils.hasText(result)) {
|
|
|
+ //调用安卓后端接口
|
|
|
+ } else {
|
|
|
+ ajax = AjaxResult.error("调用知识库异常");
|
|
|
+ }
|
|
|
+ }
|
|
|
+// ObjectMapper objectMapper = new ObjectMapper();
|
|
|
+// Map<String, Object> map = null;
|
|
|
+// ajax.put("data", map);
|
|
|
+ return ajax;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通用JSON字段提取方法
|
|
|
+ * @param originalJson 原始JSON字符串
|
|
|
+ * @param rootArrayPath 要提取的数组根路径(如safe.warning)
|
|
|
+ * @param fieldPaths 要提取的字段路径列表(如label、judgment,基于rootArrayPath的相对路径)
|
|
|
+ * @return 简化后的JSON字符串(格式化输出)
|
|
|
+ * @throws JsonProcessingException JSON解析/生成异常
|
|
|
+ */
|
|
|
+ public String extractFields(String originalJson, String rootArrayPath, List<String> fieldPaths)
|
|
|
+ throws JsonProcessingException {
|
|
|
+ // 1. 解析原始JSON为树形结构
|
|
|
+ JsonNode rootNode = OBJECT_MAPPER.readTree(originalJson);
|
|
|
+
|
|
|
+ // 2. 定位到目标数组节点(解析rootArrayPath,如safe.warning)
|
|
|
+ JsonNode targetArrayNode = getNodeByPath(rootNode, rootArrayPath);
|
|
|
+ if (targetArrayNode == null || !targetArrayNode.isArray()) {
|
|
|
+ throw new IllegalArgumentException("路径[" + rootArrayPath + "]不是有效的JSON数组");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 创建简化后的数组节点
|
|
|
+ ArrayNode simplifiedArray = OBJECT_MAPPER.createArrayNode();
|
|
|
+
|
|
|
+ // 4. 遍历数组,提取指定字段
|
|
|
+ Iterator<JsonNode> arrayIterator = targetArrayNode.iterator();
|
|
|
+ while (arrayIterator.hasNext()) {
|
|
|
+ JsonNode itemNode = arrayIterator.next();
|
|
|
+ ObjectNode simplifiedItem = OBJECT_MAPPER.createObjectNode();
|
|
|
+
|
|
|
+ // 遍历需要提取的字段,逐个赋值
|
|
|
+ for (String fieldPath : fieldPaths) {
|
|
|
+ JsonNode fieldNode = getNodeByPath(itemNode, fieldPath);
|
|
|
+ String fieldValue = (fieldNode == null) ? "" : fieldNode.asText("");
|
|
|
+ simplifiedItem.put(fieldPath, fieldValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ simplifiedArray.add(simplifiedItem);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 转换为格式化的JSON字符串返回
|
|
|
+ return OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(simplifiedArray);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 辅助方法:根据路径(如a.b.c)从JsonNode中获取对应节点
|
|
|
+ * @param startNode 起始节点
|
|
|
+ * @param path 字段路径(点分隔,如safe.warning)
|
|
|
+ * @return 目标节点(null表示路径不存在)
|
|
|
+ */
|
|
|
+ private static JsonNode getNodeByPath(JsonNode startNode, String path) {
|
|
|
+ if (startNode == null || path == null || path.isEmpty()) {
|
|
|
+ return startNode;
|
|
|
+ }
|
|
|
+
|
|
|
+ String[] pathSegments = path.split("\\.");
|
|
|
+ JsonNode currentNode = startNode;
|
|
|
+ for (String segment : pathSegments) {
|
|
|
+ currentNode = currentNode.path(segment);
|
|
|
+ // 如果路径中某一级不存在,直接返回null
|
|
|
+ if (currentNode.isMissingNode()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return currentNode;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|