Procházet zdrojové kódy

feat: add AiGateway.transcribe + /api/ai/transcribe endpoint

Xiaogang Liao před 2 měsíci
rodič
revize
8520ab988c

+ 19 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/ai/AIChatController.java

@@ -476,4 +476,23 @@ public class AIChatController {
         return Result.success(result);
     }
 
+    @Operation(summary = "语音转文字")
+    @PostMapping("/transcribe")
+    public Result<Map<String, Object>> transcribe(
+            @RequestAttribute("userId") Long userId,
+            @RequestParam("file") org.springframework.web.multipart.MultipartFile file) {
+        if (file == null || file.isEmpty()) {
+            return Result.error(400, "音频文件为空");
+        }
+        try {
+            Map<String, Object> result = aiGateway.transcribe(file.getBytes(), file.getOriginalFilename());
+            if (result != null) {
+                return Result.success(result);
+            }
+            return Result.error(500, "语音转写失败");
+        } catch (java.io.IOException e) {
+            return Result.error(500, "读取音频文件失败");
+        }
+    }
+
 }

+ 30 - 1
cfc-backend/src/main/java/com/etotem/cfc/service/AiGateway.java

@@ -351,5 +351,34 @@ public class AiGateway {
         }
     }
 
-    
+    public Map<String, Object> transcribe(byte[] audioData, String filename) {
+        if (!enabled || isCircuitOpen()) return null;
+        try {
+            String url = baseUrl + "/api/v1/audio/transcribe";
+            org.springframework.util.LinkedMultiValueMap<String, Object> parts = new org.springframework.util.LinkedMultiValueMap<>();
+            parts.add("file", new org.springframework.core.io.ByteArrayResource(audioData) {
+                @Override
+                public String getFilename() { return filename; }
+            });
+            org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
+            headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA);
+            HttpEntity<org.springframework.util.LinkedMultiValueMap<String, Object>> entity = new HttpEntity<>(parts, headers);
+            ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
+            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
+                JsonNode root = objectMapper.readTree(response.getBody());
+                consecutiveFailures.set(0);
+                Map<String, Object> result = new LinkedHashMap<>();
+                result.put("text", root.has("data") && root.get("data").has("text") ? root.get("data").get("text").asText() : "");
+                result.put("language", root.has("data") && root.get("data").has("language") ? root.get("data").get("language").asText() : "zh");
+                result.put("duration", root.has("data") && root.get("data").has("duration") ? root.get("data").get("duration").asDouble() : 0.0);
+                return result;
+            }
+            return null;
+        } catch (Exception e) {
+            log.warn("AiGateway transcribe 调用失败: {}", e.getMessage());
+            recordFailure();
+            return null;
+        }
+    }
+
 }