浏览代码

提交测试接口

ligao 3 周之前
父节点
当前提交
f0d961cd6b

+ 30 - 1
emoon-extend/emoon-tongue/src/main/java/com/emoon/tongue/controller/ForwardRobotController.java

@@ -20,7 +20,7 @@ import java.util.Map;
 @Slf4j
 @RestController
 @RequestMapping("/api/v1/forward/diagnosis/robot")
-@CrossOrigin(origins = "*", maxAge = 3600)
+@CrossOrigin(origins = "*", maxAge = 3600, allowedHeaders = "*")
 @RequiredArgsConstructor
 public class ForwardRobotController {
 
@@ -41,6 +41,25 @@ public class ForwardRobotController {
         return forwardRobotService.forwardCheckImage(file, patientId, projectId, timestamp, sign, version, bizParams);
     }
 
+    /**
+     * 转发:测试舌象图片校验(仅本地落盘)
+     * 原接口:POST /api/v1/diagnosis/testtongue/check-image
+     */
+    @PostMapping("/testtongue/check-image")
+    public ResponseEntity<String> checkTestTongueImage(@RequestParam("file") MultipartFile file,
+                                                        @RequestHeader(value = "Authorization", required = false) String authorization) {
+        return forwardRobotService.forwardTesttongueCheckImage(file, authorization);
+    }
+
+    /**
+     * 转发:舌象测试页登录(JSON)
+     * 原接口:POST /api/v1/diagnosis/testtongue/login
+     */
+    @PostMapping("/testtongue/login")
+    public ResponseEntity<String> testtongueLogin(@RequestBody Map<String, Object> body) {
+        return forwardRobotService.forwardJsonPost("/api/v1/diagnosis/testtongue/login", body);
+    }
+
     /**
      * 转发:获取舌诊详情
      * 原接口:GET /api/v1/diagnosis/robot/detail
@@ -85,6 +104,16 @@ public class ForwardRobotController {
         return forwardRobotService.forwardJsonPost("/api/v1/diagnosis/robot/generate-sign", params);
     }
 
+    @GetMapping("/mask-config")
+    public ResponseEntity<String> getMaskConfig() {
+        return forwardRobotService.forwardGet("/api/v1/diagnosis/robot/mask-config");
+    }
+
+    @PostMapping("/mask-config")
+    public ResponseEntity<String> setMaskConfig(@RequestBody Map<String, Object> body) {
+        return forwardRobotService.forwardJsonPost("/api/v1/diagnosis/robot/mask-config", body);
+    }
+
     /**
      * 快速健康检查:便于确认转发目标是否可达
      */

+ 100 - 0
emoon-extend/emoon-tongue/src/main/java/com/emoon/tongue/service/ForwardRobotService.java

@@ -218,6 +218,85 @@ public class ForwardRobotService {
         }
     }
 
+    /**
+     * 转发:测试舌象图片校验(multipart/form-data)
+     * 目标接口:POST /api/v1/diagnosis/testtongue/check-image
+     */
+    public ResponseEntity<String> forwardTesttongueCheckImage(MultipartFile file, String authorization) {
+        String url = joinUrl(targetBaseUrl, "/api/v1/diagnosis/testtongue/check-image");
+
+        try {
+            long start = System.currentTimeMillis();
+            long fileSize = (file != null ? file.getSize() : 0L);
+            String ct = (file != null ? file.getContentType() : null);
+            log.info("转发 testtongue check-image 开始,url={}, fileSize={} bytes, contentType={}", url, fileSize, ct);
+
+            final String boundary = "----EmoonForwardBoundary" + System.currentTimeMillis();
+            final byte[] CRLF = "\r\n".getBytes(StandardCharsets.UTF_8);
+
+            String filename = (file != null && file.getOriginalFilename() != null) ? file.getOriginalFilename() : "file";
+            String fileContentType = (file != null && file.getContentType() != null && !file.getContentType().isBlank())
+                ? file.getContentType()
+                : MediaType.APPLICATION_OCTET_STREAM_VALUE;
+            byte[] fileBytes = (file != null) ? file.getBytes() : new byte[0];
+
+            ByteArrayOutputStream bos = new ByteArrayOutputStream(fileBytes.length + 2048);
+
+            // file field
+            bos.write(("--" + boundary).getBytes(StandardCharsets.UTF_8));
+            bos.write(CRLF);
+            bos.write(("Content-Disposition: form-data; name=\"file\"; filename=\"" + filename + "\"").getBytes(StandardCharsets.UTF_8));
+            bos.write(CRLF);
+            bos.write(("Content-Type: " + fileContentType).getBytes(StandardCharsets.UTF_8));
+            bos.write(CRLF);
+            bos.write(CRLF);
+            bos.write(fileBytes);
+            bos.write(CRLF);
+
+            // end
+            bos.write(("--" + boundary + "--").getBytes(StandardCharsets.UTF_8));
+            bos.write(CRLF);
+
+            byte[] multipartBody = bos.toByteArray();
+
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.parseMediaType("multipart/form-data; boundary=" + boundary));
+            headers.setContentLength(multipartBody.length);
+            headers.setAccept(java.util.List.of(MediaType.ALL));
+            if (userAgent != null && !userAgent.isBlank()) {
+                headers.set(HttpHeaders.USER_AGENT, userAgent.trim());
+            }
+            if (targetHostHeader != null && !targetHostHeader.isBlank()) {
+                headers.set(HttpHeaders.HOST, targetHostHeader.trim());
+            }
+            if (forceConnectionClose) {
+                headers.setConnection("close");
+            }
+            if (authorization != null && !authorization.isBlank()) {
+                headers.set(HttpHeaders.AUTHORIZATION, authorization.trim());
+            }
+
+            HttpEntity<byte[]> req = new HttpEntity<>(multipartBody, headers);
+            ResponseEntity<String> resp;
+            try {
+                resp = buildRestTemplate().exchange(url, HttpMethod.POST, req, String.class);
+            } catch (ResourceAccessException e) {
+                log.warn("转发 testtongue check-image JDK HttpClient 失败,尝试降级为 HttpURLConnection 重试,url={}, reason={}",
+                    url, e.getMessage());
+                resp = buildSimpleRestTemplate().exchange(url, HttpMethod.POST, req, String.class);
+            }
+
+            log.info("转发 testtongue check-image 完成,url={}, status={}, costMs={}", url,
+                resp != null ? resp.getStatusCode() : null, (System.currentTimeMillis() - start));
+            return toJsonResponse(resp);
+        } catch (Exception e) {
+            log.error("转发 testtongue check-image 失败,url={}", url, e);
+            return ResponseEntity.status(HttpStatus.BAD_GATEWAY)
+                .contentType(MediaType.APPLICATION_JSON)
+                .body(JSONUtil.toJsonStr(R.fail("转发失败:" + e.getMessage())));
+        }
+    }
+
     public ResponseEntity<String> forwardDetail(String patientId,
                                                 String projectId,
                                                 Long timestamp,
@@ -281,6 +360,27 @@ public class ForwardRobotService {
         }
     }
 
+    /**
+     * 通用:转发 GET(用于 /mask-config 等简单查询接口)
+     */
+    public ResponseEntity<String> forwardGet(String path) {
+        String url = joinUrl(targetBaseUrl, path);
+        try {
+            HttpHeaders headers = new HttpHeaders();
+            headers.setAccept(java.util.List.of(MediaType.APPLICATION_JSON, MediaType.ALL));
+            if (targetHostHeader != null && !targetHostHeader.isBlank()) {
+                headers.set(HttpHeaders.HOST, targetHostHeader.trim());
+            }
+            ResponseEntity<String> resp = buildRestTemplate().exchange(url, HttpMethod.GET, new HttpEntity<>(headers), String.class);
+            return toJsonResponse(resp);
+        } catch (Exception e) {
+            log.error("转发 GET 失败(机器人端),url={}", url, e);
+            return ResponseEntity.status(HttpStatus.BAD_GATEWAY)
+                .contentType(MediaType.APPLICATION_JSON)
+                .body(JSONUtil.toJsonStr(R.fail("转发失败:" + e.getMessage())));
+        }
+    }
+
     /**
      * 通用:转发 JSON POST(用于 /llm-call、/generate-sign 等)
      */

+ 28 - 3
emoon-extend/emoon-tongue/src/main/java/com/emoon/tongue/service/impl/TongueDiagnosisServiceImpl.java

@@ -8,9 +8,9 @@ import com.emoon.tongue.domain.entity.TongueDiagnosis;
 import com.emoon.tongue.domain.vo.*;
 import com.emoon.tongue.mapper.TongueDiagnosisMapper;
 import com.emoon.tongue.service.ITongueDiagnosisService;
+import com.emoon.tongue.service.MinioService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 
 import java.time.LocalDateTime;
@@ -31,9 +31,11 @@ import java.util.stream.Collectors;
 public class TongueDiagnosisServiceImpl implements ITongueDiagnosisService {
 
     @Autowired
-    @Qualifier("tongueDiagnosisMapper")
     private TongueDiagnosisMapper baseMapper;
 
+    @Autowired
+    private MinioService minioService;
+
     /**
      * 根据患者ID和医院编码查询舌诊任务详情(按创建时间倒序取最新一条)
      *
@@ -256,6 +258,8 @@ public class TongueDiagnosisServiceImpl implements ITongueDiagnosisService {
      */
     private TongueDiagnosisDetailVo convertToDetailVo(TongueDiagnosis entity) {
         TongueDiagnosisDetailVo detailVo = new TongueDiagnosisDetailVo();
+        log.info("开始组装舌诊详情VO,id={}, patientId={}, projectId={}, uploadStatus={}",
+            entity.getId(), entity.getPatientId(), entity.getProjectId(), entity.getUploadStatus());
 
         // 主键ID与创建时间
         detailVo.setId(entity.getId());
@@ -283,10 +287,13 @@ public class TongueDiagnosisServiceImpl implements ITongueDiagnosisService {
         if (entity.getUploadStatus() >= 1
             && (StringUtils.isNotEmpty(entity.getImageUrl()) || StringUtils.isNotEmpty(entity.getImagePath()))) {
             TongueImageInfo tongueImage = new TongueImageInfo();
-            tongueImage.setUrl(entity.getImageUrl());
+            String refreshedUrl = resolveFreshImageUrl(entity.getImageUrl());
+            tongueImage.setUrl(refreshedUrl);
             tongueImage.setPath(entity.getImagePath());
             tongueImage.setUploadTime(entity.getUploadTime());
             detailVo.setTongueImage(tongueImage);
+            log.info("舌诊详情VO图片信息已设置,id={}, originalUrl={}, refreshedUrl={}, imagePath={}",
+                entity.getId(), entity.getImageUrl(), refreshedUrl, entity.getImagePath());
         }
 
         // 如果诊断完成,则设置诊断结果
@@ -294,6 +301,24 @@ public class TongueDiagnosisServiceImpl implements ITongueDiagnosisService {
             detailVo.setDiagnosisInfo(entity.getTongueDiagnosisInfo());
         }
 
+        log.info("舌诊详情VO组装完成,id={}, hasTongueImage={}, hasDiagnosisInfo={}",
+            detailVo.getId(), detailVo.getTongueImage() != null, detailVo.getDiagnosisInfo() != null);
         return detailVo;
     }
+
+    private String resolveFreshImageUrl(String imageUrl) {
+        if (StringUtils.isEmpty(imageUrl)) {
+            log.info("刷新舌象图片URL跳过:imageUrl为空");
+            return imageUrl;
+        }
+        try {
+            log.info("开始刷新舌象图片URL,originalUrl={}", imageUrl);
+            String refreshedUrl = minioService.refreshFileUrl(imageUrl);
+            log.info("刷新舌象图片URL完成,originalUrl={}, refreshedUrl={}", imageUrl, refreshedUrl);
+            return refreshedUrl;
+        } catch (Exception e) {
+            log.warn("刷新舌象图片URL失败,沿用数据库原值: {}", imageUrl, e);
+            return imageUrl;
+        }
+    }
 }