Преглед изворни кода

视觉数据大模型总结接口

huiqi пре 5 дана
родитељ
комит
34e2246363

+ 182 - 8
takai-admin/src/main/java/com/takai/web/controller/droneai/DroneAiController.java

@@ -2,16 +2,19 @@ package com.takai.web.controller.droneai;
 
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.crypto.digest.DigestUtil;
+import cn.hutool.json.JSONUtil;
 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.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.service.IPictureRagResService;
 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.domain.AjaxResult;
 import com.takai.common.enums.QualityTypeEnum;
@@ -21,10 +24,13 @@ 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.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 
+import java.io.IOException;
 import java.util.*;
+import java.util.stream.Collectors;
 
 @Slf4j
 @RestController
@@ -37,6 +43,12 @@ public class DroneAiController extends BaseController {
     @Autowired
     private ITakaiAiService takaiAisService;
 
+    @Autowired
+    private IPictureRagResService pictureRagResService;
+
+    @Autowired
+    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
+
     // 静态ObjectMapper实例(线程安全,可复用)
     private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
 
@@ -49,7 +61,7 @@ public class DroneAiController extends BaseController {
     @PostMapping("/ai_check")
     public AjaxResult aiCheck(@RequestBody ProjectDetectionParam requestBody) throws Exception
     {
-        log.info("登录请求参数:{}", requestBody.toString());
+        log.info("图片RAG检查接口请求参数:{}", requestBody.toString());
         AjaxResult ajax = AjaxResult.success();
 
         if(
@@ -69,6 +81,7 @@ public class DroneAiController extends BaseController {
                         )
                 )
         ) {
+            log.info("请求参数错误");
             return AjaxResult.error(org.springframework.http.HttpStatus.BAD_REQUEST.value(),"参数错误");
         }
         String clientId = requestBody.getClient().getClientId();
@@ -80,6 +93,7 @@ public class DroneAiController extends BaseController {
         //检查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");
         }
 
@@ -88,15 +102,18 @@ public class DroneAiController extends BaseController {
         // 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+type+imageId;
             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(),"无效的签名");
         }
         //处理请求报文
@@ -110,10 +127,15 @@ public class DroneAiController extends BaseController {
             String rootArrayPath = null;
             if (VisionTypeEnum.SAFE.getCode().equals(type)) {
                 //检查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);
                     if(!StringUtils.hasText(sw.getLabel())) {
+                        log.info("label不能为空");
                         return AjaxResult.error(HttpStatus.BAD_REQUEST.value(),"label不能为空");
                     }
                 }
@@ -124,14 +146,27 @@ public class DroneAiController extends BaseController {
                 requestParams.setAppId("3010574988024877056");
 
             } else if (VisionTypeEnum.QUALITY.getCode().equals(type)) {
+
                 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");
                     rootArrayPath = "quality.warning";
                 } 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");
                     rootArrayPath = "quality.rebarInfo";
                 }
                 requestParams.setAppId("3010568863451844608");
+            } else {
+                return ajax;
             }
             String requestJson = JSONObject.toJSONString(requestBody);
             String simpleJson = extractFields(requestJson, rootArrayPath, extractFields);
@@ -146,17 +181,156 @@ public class DroneAiController extends BaseController {
             requestParams.setMessages(messages);
             String result =  takaiAisService.syncInvoke(requestParams);
             if(StringUtils.hasText(result)) {
+                //保存RAG返回信息
+                PictureRagRes pictureRagRes = getPictureRagRes(requestBody,result);
+                pictureRagResService.insertPictureRagRes(pictureRagRes);
                 //调用安卓后端接口
+//                threadPoolTaskExecutor.execute(() -> excuteAppReport(requestBody,result));
             } else {
                 ajax = AjaxResult.error("调用知识库异常");
             }
         }
-//        ObjectMapper objectMapper = new ObjectMapper();
-//        Map<String, Object> map  = null;
-//        ajax.put("data", map);
         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字段提取方法
      * @param originalJson 原始JSON字符串

+ 6 - 0
takai-admin/src/main/resources/application.yml

@@ -205,6 +205,12 @@ deepseek:
   #图片检测接口url
   #pictureCheckUrl: http://xia0miduo.gicp.net:6001/rag/chat/sync
   pictureCheckUrl: http://10.1.27.4:18071/rag/chat/sync
+  #图片检测下游APP后端url
+  pictureAppUrl: https://ocr.jkec.info:8095/ocr/siteData/report
+  #图片检测下游clientId
+  pictureAppclientId: 86E92B25EFD14C7287351D396FE645BB
+  #图片检测下游secretKey
+  pictureAppSecretKey: 886177ed7459271354797265430b793dca640176997230ffdbb2c23a3f0dc3f2
 jk:
   #Appid
   iamAppid: e971e84b574c40b2

+ 104 - 0
takai-ai/src/main/java/com/takai/ai/controller/PictureRagResController.java

@@ -0,0 +1,104 @@
+package com.takai.ai.controller;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.takai.common.annotation.Log;
+import com.takai.common.core.controller.BaseController;
+import com.takai.common.core.domain.AjaxResult;
+import com.takai.common.enums.BusinessType;
+import com.takai.ai.domain.PictureRagRes;
+import com.takai.ai.service.IPictureRagResService;
+import com.takai.common.utils.poi.ExcelUtil;
+import com.takai.common.core.page.TableDataInfo;
+
+/**
+ * 图片检查结果Controller
+ * 
+ * @author takai
+ * @date 2026-01-16
+ */
+@RestController
+@RequestMapping("/takai-ai/res")
+public class PictureRagResController extends BaseController
+{
+    @Autowired
+    private IPictureRagResService pictureRagResService;
+
+    /**
+     * 查询图片检查结果列表
+     */
+    @PreAuthorize("@ss.hasPermi('takai-ai:res:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(PictureRagRes pictureRagRes)
+    {
+        startPage();
+        List<PictureRagRes> list = pictureRagResService.selectPictureRagResList(pictureRagRes);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出图片检查结果列表
+     */
+    @PreAuthorize("@ss.hasPermi('takai-ai:res:export')")
+    @Log(title = "图片检查结果", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, PictureRagRes pictureRagRes)
+    {
+        List<PictureRagRes> list = pictureRagResService.selectPictureRagResList(pictureRagRes);
+        ExcelUtil<PictureRagRes> util = new ExcelUtil<PictureRagRes>(PictureRagRes.class);
+        util.exportExcel(response, list, "图片检查结果数据");
+    }
+
+    /**
+     * 获取图片检查结果详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('takai-ai:res:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return success(pictureRagResService.selectPictureRagResById(id));
+    }
+
+    /**
+     * 新增图片检查结果
+     */
+    @PreAuthorize("@ss.hasPermi('takai-ai:res:add')")
+    @Log(title = "图片检查结果", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody PictureRagRes pictureRagRes)
+    {
+        return toAjax(pictureRagResService.insertPictureRagRes(pictureRagRes));
+    }
+
+    /**
+     * 修改图片检查结果
+     */
+    @PreAuthorize("@ss.hasPermi('takai-ai:res:edit')")
+    @Log(title = "图片检查结果", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody PictureRagRes pictureRagRes)
+    {
+        return toAjax(pictureRagResService.updatePictureRagRes(pictureRagRes));
+    }
+
+    /**
+     * 删除图片检查结果
+     */
+    @PreAuthorize("@ss.hasPermi('takai-ai:res:remove')")
+    @Log(title = "图片检查结果", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(pictureRagResService.deletePictureRagResByIds(ids));
+    }
+}

+ 113 - 0
takai-ai/src/main/java/com/takai/ai/domain/PictureRagRes.java

@@ -0,0 +1,113 @@
+package com.takai.ai.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.takai.common.annotation.Excel;
+import com.takai.common.core.domain.BaseEntity;
+
+/**
+ * 图片检查结果对象 picture_rag_res
+ *
+ * @author takai
+ * @date 2026-01-16
+ */
+public class PictureRagRes extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+
+    private Long id;
+
+    private String projectId;
+
+    private String phaseId;
+
+    private String resImageId;
+
+    private String type;
+
+    private String ragRes;
+
+    private String reqJson;
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
+    public Long getId()
+    {
+        return id;
+    }
+    public void setProjectId(String projectId)
+    {
+        this.projectId = projectId;
+    }
+
+    public String getProjectId()
+    {
+        return projectId;
+    }
+    public void setPhaseId(String phaseId)
+    {
+        this.phaseId = phaseId;
+    }
+
+    public String getPhaseId()
+    {
+        return phaseId;
+    }
+    public void setResImageId(String resImageId)
+    {
+        this.resImageId = resImageId;
+    }
+
+    public String getResImageId()
+    {
+        return resImageId;
+    }
+    public void setType(String type)
+    {
+        this.type = type;
+    }
+
+    public String getType()
+    {
+        return type;
+    }
+    public void setRagRes(String ragRes)
+    {
+        this.ragRes = ragRes;
+    }
+
+    public String getRagRes()
+    {
+        return ragRes;
+    }
+    public void setReqJson(String reqJson)
+    {
+        this.reqJson = reqJson;
+    }
+
+    public String getReqJson()
+    {
+        return reqJson;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("id", getId())
+            .append("projectId", getProjectId())
+            .append("phaseId", getPhaseId())
+            .append("resImageId", getResImageId())
+            .append("type", getType())
+            .append("ragRes", getRagRes())
+            .append("reqJson", getReqJson())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .toString();
+    }
+}

+ 20 - 0
takai-ai/src/main/java/com/takai/ai/domain/dto/drone/PictureRagResParam.java

@@ -0,0 +1,20 @@
+package com.takai.ai.domain.dto.drone;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 工程检测数据主入参类
+ * 对应JSON根节点的所有字段
+ */
+@Data
+public class PictureRagResParam {
+
+    private ClientInfo client;
+
+    /**
+     * 阶段ID
+     */
+    private String phaseId;
+}

+ 61 - 0
takai-ai/src/main/java/com/takai/ai/mapper/PictureRagResMapper.java

@@ -0,0 +1,61 @@
+package com.takai.ai.mapper;
+
+import java.util.List;
+import com.takai.ai.domain.PictureRagRes;
+
+/**
+ * 图片检查结果Mapper接口
+ * 
+ * @author takai
+ * @date 2026-01-16
+ */
+public interface PictureRagResMapper 
+{
+    /**
+     * 查询图片检查结果
+     * 
+     * @param id 图片检查结果主键
+     * @return 图片检查结果
+     */
+    public PictureRagRes selectPictureRagResById(Long id);
+
+    /**
+     * 查询图片检查结果列表
+     * 
+     * @param pictureRagRes 图片检查结果
+     * @return 图片检查结果集合
+     */
+    public List<PictureRagRes> selectPictureRagResList(PictureRagRes pictureRagRes);
+
+    /**
+     * 新增图片检查结果
+     * 
+     * @param pictureRagRes 图片检查结果
+     * @return 结果
+     */
+    public int insertPictureRagRes(PictureRagRes pictureRagRes);
+
+    /**
+     * 修改图片检查结果
+     * 
+     * @param pictureRagRes 图片检查结果
+     * @return 结果
+     */
+    public int updatePictureRagRes(PictureRagRes pictureRagRes);
+
+    /**
+     * 删除图片检查结果
+     * 
+     * @param id 图片检查结果主键
+     * @return 结果
+     */
+    public int deletePictureRagResById(Long id);
+
+    /**
+     * 批量删除图片检查结果
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deletePictureRagResByIds(Long[] ids);
+}

+ 61 - 0
takai-ai/src/main/java/com/takai/ai/service/IPictureRagResService.java

@@ -0,0 +1,61 @@
+package com.takai.ai.service;
+
+import java.util.List;
+import com.takai.ai.domain.PictureRagRes;
+
+/**
+ * 图片检查结果Service接口
+ * 
+ * @author takai
+ * @date 2026-01-16
+ */
+public interface IPictureRagResService 
+{
+    /**
+     * 查询图片检查结果
+     * 
+     * @param id 图片检查结果主键
+     * @return 图片检查结果
+     */
+    public PictureRagRes selectPictureRagResById(Long id);
+
+    /**
+     * 查询图片检查结果列表
+     * 
+     * @param pictureRagRes 图片检查结果
+     * @return 图片检查结果集合
+     */
+    public List<PictureRagRes> selectPictureRagResList(PictureRagRes pictureRagRes);
+
+    /**
+     * 新增图片检查结果
+     * 
+     * @param pictureRagRes 图片检查结果
+     * @return 结果
+     */
+    public int insertPictureRagRes(PictureRagRes pictureRagRes);
+
+    /**
+     * 修改图片检查结果
+     * 
+     * @param pictureRagRes 图片检查结果
+     * @return 结果
+     */
+    public int updatePictureRagRes(PictureRagRes pictureRagRes);
+
+    /**
+     * 批量删除图片检查结果
+     * 
+     * @param ids 需要删除的图片检查结果主键集合
+     * @return 结果
+     */
+    public int deletePictureRagResByIds(Long[] ids);
+
+    /**
+     * 删除图片检查结果信息
+     * 
+     * @param id 图片检查结果主键
+     * @return 结果
+     */
+    public int deletePictureRagResById(Long id);
+}

+ 3 - 0
takai-ai/src/main/java/com/takai/ai/service/ITakaiAiService.java

@@ -3,6 +3,7 @@ package com.takai.ai.service;
 import com.alibaba.fastjson2.JSONObject;
 import com.takai.ai.domain.TakaiSysOss;
 import com.takai.ai.domain.dto.*;
+import com.takai.ai.domain.dto.drone.ProjectDetectionParam;
 import com.takai.ai.domain.entity.*;
 import com.takai.common.core.domain.entity.SysDictData;
 import org.springframework.web.multipart.MultipartFile;
@@ -129,4 +130,6 @@ public interface ITakaiAiService
     List<TakaiApplicationResult> getChatAppList(List<TakaiApplicationResult> list, String userId);
 
     List<Object> setDialogList(List<TakaiDialogRespDTO> list);
+
+    boolean reportToApp(ProjectDetectionParam requestBody, String reportStr);
 }

+ 96 - 0
takai-ai/src/main/java/com/takai/ai/service/impl/PictureRagResServiceImpl.java

@@ -0,0 +1,96 @@
+package com.takai.ai.service.impl;
+
+import java.util.List;
+import com.takai.common.utils.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.takai.ai.mapper.PictureRagResMapper;
+import com.takai.ai.domain.PictureRagRes;
+import com.takai.ai.service.IPictureRagResService;
+
+/**
+ * 图片检查结果Service业务层处理
+ * 
+ * @author takai
+ * @date 2026-01-16
+ */
+@Service
+public class PictureRagResServiceImpl implements IPictureRagResService 
+{
+    @Autowired
+    private PictureRagResMapper pictureRagResMapper;
+
+    /**
+     * 查询图片检查结果
+     * 
+     * @param id 图片检查结果主键
+     * @return 图片检查结果
+     */
+    @Override
+    public PictureRagRes selectPictureRagResById(Long id)
+    {
+        return pictureRagResMapper.selectPictureRagResById(id);
+    }
+
+    /**
+     * 查询图片检查结果列表
+     * 
+     * @param pictureRagRes 图片检查结果
+     * @return 图片检查结果
+     */
+    @Override
+    public List<PictureRagRes> selectPictureRagResList(PictureRagRes pictureRagRes)
+    {
+        return pictureRagResMapper.selectPictureRagResList(pictureRagRes);
+    }
+
+    /**
+     * 新增图片检查结果
+     * 
+     * @param pictureRagRes 图片检查结果
+     * @return 结果
+     */
+    @Override
+    public int insertPictureRagRes(PictureRagRes pictureRagRes)
+    {
+        pictureRagRes.setCreateTime(DateUtils.getNowDate());
+        return pictureRagResMapper.insertPictureRagRes(pictureRagRes);
+    }
+
+    /**
+     * 修改图片检查结果
+     * 
+     * @param pictureRagRes 图片检查结果
+     * @return 结果
+     */
+    @Override
+    public int updatePictureRagRes(PictureRagRes pictureRagRes)
+    {
+        pictureRagRes.setUpdateTime(DateUtils.getNowDate());
+        return pictureRagResMapper.updatePictureRagRes(pictureRagRes);
+    }
+
+    /**
+     * 批量删除图片检查结果
+     * 
+     * @param ids 需要删除的图片检查结果主键
+     * @return 结果
+     */
+    @Override
+    public int deletePictureRagResByIds(Long[] ids)
+    {
+        return pictureRagResMapper.deletePictureRagResByIds(ids);
+    }
+
+    /**
+     * 删除图片检查结果信息
+     * 
+     * @param id 图片检查结果主键
+     * @return 结果
+     */
+    @Override
+    public int deletePictureRagResById(Long id)
+    {
+        return pictureRagResMapper.deletePictureRagResById(id);
+    }
+}

+ 78 - 3
takai-ai/src/main/java/com/takai/ai/service/impl/TakaiAiServiceImpl.java

@@ -1,11 +1,13 @@
 package com.takai.ai.service.impl;
 
+import cn.hutool.crypto.digest.DigestUtil;
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import com.github.pagehelper.PageHelper;
 import com.takai.ai.domain.TakaiSysOss;
 import com.takai.ai.domain.dto.*;
+import com.takai.ai.domain.dto.drone.ProjectDetectionParam;
 import com.takai.ai.domain.entity.*;
 import com.takai.ai.mapper.*;
 import com.takai.ai.service.IMinioConfigService;
@@ -48,6 +50,7 @@ import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
@@ -420,9 +423,10 @@ public class TakaiAiServiceImpl implements ITakaiAiService {
                     json.put("enable_think", false);
                 }
 
-                RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8"), json.toJSONString());
-                Request request = buildPostRequest(url, requestBody);
+//                RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8"), json.toJSONString());
+//                Request request = buildPostRequest(url, requestBody);
                 OkHttpClient client = buildOkHttpClient();
+                Request request = createOkHttpRequest(url,json);
                 Response response = null;
                 try {
                     response = client.newCall(request).execute();
@@ -436,10 +440,11 @@ public class TakaiAiServiceImpl implements ITakaiAiService {
                         Integer code = obj.getInteger("code");
                         if (code == 200) {
                             logger.info("图片检测结果调用python接口成功,返回内容:{}", body);
+                            return obj.getJSONObject("data").getString("answer");
                         } else {
                             logger.info("图片检测结果调用python接口失败,返回内容:{}", body);
+                            return null;
                         }
-                        return obj.getJSONObject("data").getString("answer");
                     } else {
                         logger.error("图片检测结果调用python接口失败返回code:{}", response.code());
                         return null;
@@ -453,6 +458,76 @@ public class TakaiAiServiceImpl implements ITakaiAiService {
         return null;
     }
 
+    private Request createOkHttpRequest(String url, JSONObject requestJson) {
+        RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8"), requestJson.toJSONString());
+        return buildPostRequest(url, requestBody);
+    }
+
+    @Override
+    public boolean reportToApp(ProjectDetectionParam requestBody, String reportStr) {
+        JSONObject requestJson = getRequestJson(requestBody, reportStr);
+        OkHttpClient client = buildOkHttpClient();
+        Request request = createOkHttpRequest(deepseekConfig.getPictureAppUrl(),requestJson);
+        Response response = null;
+        try {
+            response = client.newCall(request).execute();
+            if (response.isSuccessful()) {
+                String body = response.body().string();
+                JSONObject obj = JSON.parseObject(body);
+                Integer code = obj.getInteger("code");
+                if (code == 200) {
+                    logger.info("图片检测结果调用App接口成功,返回内容:{}", body);
+                    return true;
+                } else {
+                    logger.info("图片检测结果调用App接口失败,返回内容:{}", body);
+                }
+            } else {
+                logger.error("图片检测结果调用App接口失败返回code:{}", response.code());
+            }
+        } catch (IOException e) {
+            logger.error("上传文档调用App接口失败", e.getMessage());
+        }
+        return false;
+    }
+
+    private JSONObject getRequestJson(ProjectDetectionParam requestBody, String reportStr) {
+        // ============ 1. 定义所有字段的【动态变量值】,可以从接口/数据库/配置文件等任意地方取值 ============
+        // client子对象的动态值
+        String clientId = deepseekConfig.getPictureAppclientId();
+        long timestamp = new Date().getTime(); // 时间戳建议用long类型
+
+        // 一级对象的动态值
+        String deviceId = requestBody.getDeviceId();
+        String projectId = requestBody.getProjectId();
+        String phaseId = requestBody.getPhaseId();
+        String type = requestBody.getType();
+        String time = requestBody.getTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+        String resImageId = requestBody.getResImageId();
+
+        //签名
+        String dataInput  = clientId+timestamp+type+resImageId;
+        String signature = DigestUtil.sha256Hex(dataInput.toUpperCase()+deepseekConfig.getPictureAppSecretKey());
+
+        // ============ 2. 动态构建嵌套的client子JSON对象 ============
+        JSONObject clientJson = new JSONObject();
+        clientJson.put("clientId", clientId);   // 动态赋值
+        clientJson.put("timestamp", timestamp); // 动态赋值
+        clientJson.put("signature", signature); // 动态赋值
+
+        // ============ 3. 动态构建最外层的主JSON对象 ============
+        JSONObject mainJson = new JSONObject();
+        mainJson.put("client", clientJson);     // 放入嵌套的子对象
+        mainJson.put("deviceId", deviceId);
+        mainJson.put("projectId", projectId);
+        mainJson.put("phaseId", phaseId);
+        mainJson.put("type", type);
+        mainJson.put("time", time);
+        mainJson.put("resImageId", resImageId);
+        mainJson.put("ragRes", reportStr);
+
+        return mainJson;
+    }
+
     private void decodeMessage(List<TakaiPromptInfo> messages) {
         if(messages == null || messages.isEmpty()) {
             return;

+ 97 - 0
takai-ai/src/main/resources/mapper/takaiai/PictureRagResMapper.xml

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.takai.ai.mapper.PictureRagResMapper">
+    
+    <resultMap type="PictureRagRes" id="PictureRagResResult">
+        <result property="id"    column="id"    />
+        <result property="projectId"    column="projectId"    />
+        <result property="phaseId"    column="phaseId"    />
+        <result property="resImageId"    column="resImageId"    />
+        <result property="type"    column="type"    />
+        <result property="ragRes"    column="ragRes"    />
+        <result property="reqJson"    column="reqJson"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectPictureRagResVo">
+        select id, projectId, phaseId, resImageId, type, ragRes, reqJson, create_by, create_time, update_by, update_time from picture_rag_res
+    </sql>
+
+    <select id="selectPictureRagResList" parameterType="PictureRagRes" resultMap="PictureRagResResult">
+        <include refid="selectPictureRagResVo"/>
+        <where>  
+            <if test="projectId != null  and projectId != ''"> and projectId = #{projectId}</if>
+            <if test="phaseId != null  and phaseId != ''"> and phaseId = #{phaseId}</if>
+            <if test="resImageId != null  and resImageId != ''"> and resImageId = #{resImageId}</if>
+            <if test="type != null  and type != ''"> and type = #{type}</if>
+            <if test="ragRes != null  and ragRes != ''"> and ragRes = #{ragRes}</if>
+            <if test="reqJson != null  and reqJson != ''"> and reqJson = #{reqJson}</if>
+        </where>
+    </select>
+    
+    <select id="selectPictureRagResById" parameterType="Long" resultMap="PictureRagResResult">
+        <include refid="selectPictureRagResVo"/>
+        where id = #{id}
+    </select>
+        
+    <insert id="insertPictureRagRes" parameterType="PictureRagRes" useGeneratedKeys="true" keyProperty="id">
+        insert into picture_rag_res
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="projectId != null">projectId,</if>
+            <if test="phaseId != null and phaseId != ''">phaseId,</if>
+            <if test="resImageId != null and resImageId != ''">resImageId,</if>
+            <if test="type != null and type != ''">type,</if>
+            <if test="ragRes != null">ragRes,</if>
+            <if test="reqJson != null">reqJson,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateTime != null">update_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="projectId != null">#{projectId},</if>
+            <if test="phaseId != null and phaseId != ''">#{phaseId},</if>
+            <if test="resImageId != null and resImageId != ''">#{resImageId},</if>
+            <if test="type != null and type != ''">#{type},</if>
+            <if test="ragRes != null">#{ragRes},</if>
+            <if test="reqJson != null">#{reqJson},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updatePictureRagRes" parameterType="PictureRagRes">
+        update picture_rag_res
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="projectId != null">projectId = #{projectId},</if>
+            <if test="phaseId != null and phaseId != ''">phaseId = #{phaseId},</if>
+            <if test="resImageId != null and resImageId != ''">resImageId = #{resImageId},</if>
+            <if test="type != null and type != ''">type = #{type},</if>
+            <if test="ragRes != null">ragRes = #{ragRes},</if>
+            <if test="reqJson != null">reqJson = #{reqJson},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deletePictureRagResById" parameterType="Long">
+        delete from picture_rag_res where id = #{id}
+    </delete>
+
+    <delete id="deletePictureRagResByIds" parameterType="String">
+        delete from picture_rag_res where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 6 - 0
takai-common/src/main/java/com/takai/common/config/DeepseekConfig.java

@@ -48,4 +48,10 @@ public class DeepseekConfig {
 
     private String pictureCheckUrl;
 
+    private String pictureAppUrl;
+
+    private String pictureAppclientId;
+
+    private String pictureAppSecretKey;
+
 }

+ 1 - 1
takai-framework/src/main/java/com/takai/framework/config/SecurityConfig.java

@@ -111,7 +111,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 // 过滤请求
                 .authorizeRequests()
                 // 对于登录login 注册register 验证码captchaImage 允许匿名访问
-                .antMatchers("/login", "/jk_code_login","/frame_login","/vision/ai_check","/register", "/captchaImage","/getToken","/**/sse-invoke","/**/application/list","/bigmodel/api/dialog/**",
+                .antMatchers("/login", "/jk_code_login","/frame_login","/vision/**","/register", "/captchaImage","/getToken","/**/sse-invoke","/**/application/list","/bigmodel/api/dialog/**",
                         "/**/completions", "/**/slice_info/**", "/**/async_result/**", "/**/assistant/**", "/getInfo",
                         "/checkToken/**", "/**/presets/**", "/**/index/**", "/**/createApplaction/**", "/**/createKnowledge/**",
                         "/**/updateKnowledge/**", "/**/detailKnowledge/**", "/**/delKnowledge/**", "/**/knowledgeList/**",