Browse Source

Ai语音识别(文件上传)

panzhilin 4 ngày trước cách đây
mục cha
commit
dc8dd52253

+ 58 - 0
alien-store/src/main/java/shop/alien/store/controller/AiUploadController.java

@@ -0,0 +1,58 @@
+package shop.alien.store.controller;
+
+import com.alibaba.fastjson2.JSONObject;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiOperationSupport;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.store.util.ai.AiFeedbackAssignUtils;
+
+@Slf4j
+@Api(tags = {"ai语音识别"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/aiUp")
+@RequiredArgsConstructor
+public class AiUploadController {
+    private final AiFeedbackAssignUtils aiFeedbackAssignUtils;
+    @ApiOperation("AI语音识别")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/GetAIUpload")
+    public R<JSONObject> getAiUpload(MultipartFile file, String language, String use_itn, String merge_vad ) {
+        // 检查文件是否为空
+        if (file == null || file.isEmpty()) {
+            return R.fail("音频文件不能为空");
+        }
+        
+        String accessToken = aiFeedbackAssignUtils.getAccessToken();
+        if (!StringUtils.hasText(accessToken)) {
+          return  R.fail("调用AI语音识别接口 登录接口失败");
+        }
+        
+        try {
+            String speechRecognitionResult = aiFeedbackAssignUtils.getSpeechRecognition1(file, accessToken, language, use_itn, merge_vad);
+            if (speechRecognitionResult == null) {
+                return R.fail("语音识别失败");
+            }
+            // 将返回的JSON字符串解析为JSONObject
+            JSONObject result = JSONObject.parseObject(speechRecognitionResult);
+            return R.data(result);
+        } catch (RuntimeException e) {
+            log.error("语音识别失败", e);
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("语音识别异常", e);
+            return R.fail("语音识别失败: " + e.getMessage());
+        }
+    }
+}

+ 35 - 0
alien-store/src/main/java/shop/alien/store/feign/AlienAIFeign.java

@@ -0,0 +1,35 @@
+package shop.alien.store.feign;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.http.MediaType;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.*;
+
+@FeignClient(url = "${feign.alienAI.url}", name = "alien-AI")
+public interface AlienAIFeign {
+
+    /**
+     * 登录接口 - 获取access_token
+     *
+     *
+     *
+     * @return JsonNode 响应体,包含access_token
+     */
+    @PostMapping(value = "/ai/user-auth-core/api/v1/auth/login",
+            consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    JsonNode login(@RequestBody MultiValueMap<String, String> formData);
+
+    /**
+     * 使用 JsonNode 灵活调用接口
+     *
+     * @param authorization Bearer token,格式为 "Bearer {access_token}"
+     * @return JsonNode 响应体,可以灵活解析
+     */
+    @PostMapping(value = "/asr/upload",
+            consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    JsonNode getSpeechRecognition (@RequestHeader("Authorization") String authorization, @RequestBody MultiValueMap<String, Object> formData);
+}
+

+ 275 - 0
alien-store/src/main/java/shop/alien/store/util/ai/AiFeedbackAssignUtils.java

@@ -0,0 +1,275 @@
+package shop.alien.store.util.ai;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.http.*;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.store.LifeFeedback;
+import shop.alien.store.feign.AlienAIFeign;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AiFeedbackAssignUtils {
+
+    private final RestTemplate restTemplate;
+
+    @Value("${ai.service.login-url}")
+    private String loginUrl;
+
+    @Value("${ai.service.username:UdUser}")
+    private String userName;
+
+    @Value("${ai.service.password:123456}")
+    private String passWord;
+
+    @Value("${ai.service.assign-staff-url}")
+    private String assignStaffUrl;
+
+    private  final AlienAIFeign alienAIFeign;
+
+    // 语音识别接口地址(从配置中读取,如果没有配置则使用默认值)
+    @Value("${feign.alienAI.url}")
+    private String aiServiceBaseUrl;
+
+    /**登录的语音识别文件登录
+     * 登录 AI 服务,获取 token
+     *
+     * @return accessToken
+     */
+    public String getSpeechRecognition1(MultipartFile file,String accessToken,String language,String use_itn,String merge_vad) {
+        log.info("调用AI语音识别接口...");
+        try {
+            // 使用 RestTemplate 直接发送 multipart/form-data 请求
+            MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
+            
+            // 添加音频文件
+            if (file != null && !file.isEmpty()) {
+                try {
+                    formData.add("file", new ByteArrayResource(file.getBytes()) {
+                        @Override
+                        public String getFilename() {
+                            String originalFilename = file.getOriginalFilename();
+                            return originalFilename != null ? originalFilename : "audio_file";
+                        }
+                    });
+                } catch (IOException e) {
+                    log.error("读取音频文件失败", e);
+                    throw new RuntimeException("读取音频文件失败", e);
+                }
+            } else {
+                log.warn("音频文件为空,无法进行语音识别");
+                throw new RuntimeException("音频文件不能为空");
+            }
+            
+            // 添加识别语言参数(默认 auto)
+            if (StringUtils.hasText(language)) {
+                formData.add("language", language);
+            } else {
+                formData.add("language", "auto");
+            }
+            
+            // 添加是否开启逆文本标准化参数(默认 true)
+            // 支持传入 "true"/"false" 字符串或 boolean 值
+            if (StringUtils.hasText(use_itn)) {
+                // 确保传递的是有效的 boolean 字符串值
+                String useItnValue = use_itn.trim().toLowerCase();
+                if ("true".equals(useItnValue) || "false".equals(useItnValue)) {
+                    formData.add("use_itn", useItnValue);
+                } else {
+                    formData.add("use_itn", "true"); // 默认值
+                }
+            } else {
+                formData.add("use_itn", "true"); // 默认值
+            }
+            
+            // 添加是否合并VAD参数(默认 true)
+            // 支持传入 "true"/"false" 字符串或 boolean 值
+            if (StringUtils.hasText(merge_vad)) {
+                // 确保传递的是有效的 boolean 字符串值
+                String mergeVadValue = merge_vad.trim().toLowerCase();
+                if ("true".equals(mergeVadValue) || "false".equals(mergeVadValue)) {
+                    formData.add("merge_vad", mergeVadValue);
+                } else {
+                    formData.add("merge_vad", "true"); // 默认值
+                }
+            } else {
+                formData.add("merge_vad", "true"); // 默认值
+            }
+
+            // 设置请求头
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+            // 添加 Authorization header(如果需要)
+            if (StringUtils.hasText(accessToken)) {
+                headers.set("Authorization", "Bearer " + accessToken);
+            }
+
+            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(formData, headers);
+
+            // 构建完整的URL
+            String url = aiServiceBaseUrl + "/asr/upload";
+            log.info("调用AI语音识别接口,URL: {}", url);
+
+            // 使用 RestTemplate 发送请求
+            ResponseEntity<String> response = restTemplate.postForEntity(url, requestEntity, String.class);
+            
+            // 处理响应
+            if (response != null && response.getStatusCode() == HttpStatus.OK) {
+                String responseBody = response.getBody();
+                log.info("AI语音识别接口响应:{}", responseBody);
+                return responseBody;
+            } else {
+                log.error("AI语音识别接口调用失败,HTTP状态码: {}", 
+                        response != null ? response.getStatusCode() : "null");
+                throw new RuntimeException("AI语音识别接口调用失败,HTTP状态码: " + 
+                        (response != null ? response.getStatusCode() : "null"));
+            }
+        } catch (org.springframework.web.client.HttpClientErrorException e) {
+            log.error("调用AI语音识别接口失败,HTTP错误: {}, 响应体: {}", 
+                    e.getStatusCode(), e.getResponseBodyAsString(), e);
+            throw new RuntimeException("调用AI语音识别接口失败: " + e.getStatusCode() + 
+                    ", 响应: " + e.getResponseBodyAsString(), e);
+        } catch (Exception e) {
+            log.error("调用AI语音识别接口失败", e);
+            throw new RuntimeException("调用AI语音识别接口失败: " + e.getMessage(), e);
+        }
+    }
+
+    public String getAccessToken() {
+        log.info("登录AI服务获取token...{}", loginUrl);
+        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
+        formData.add("username","admin");
+        formData.add("password", "123456");
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
+
+        ResponseEntity<String> response;
+        try {
+            log.info("请求AI服务登录接口===================>");
+            response = restTemplate.postForEntity(loginUrl, requestEntity, String.class);
+        } catch (Exception e) {
+            log.error("请求AI服务登录接口失败", e);
+            return null;
+        }
+
+        if (response != null && response.getStatusCode() == HttpStatus.OK) {
+            String body = response.getBody();
+            log.info("请求AI服务登录成功 postForEntity.getBody()\t{}", body);
+            if (StringUtils.hasText(body)) {
+                JSONObject jsonObject = JSONObject.parseObject(body);
+                if (jsonObject != null) {
+                    JSONObject dataJson = jsonObject.getJSONObject("data");
+                    if (dataJson != null) {
+                        return dataJson.getString("access_token");
+                    }
+                }
+            }
+            log.warn("AI服务登录响应解析失败 body: {}", body);
+            return null;
+        }
+
+        log.error("请求AI服务登录接口失败 http状态:{}", response != null ? response.getStatusCode() : null);
+        return null;
+    }
+
+    /**
+     * 调用AI接口分配跟踪人员
+     *
+     * @param feedback 反馈记录
+     * @param imageUrls 图片URL列表
+     * @param videoUrls 视频URL列表
+     * @return 分配的跟踪人员ID,失败返回null
+     */
+    public Integer assignStaffByAI(LifeFeedback feedback, List<String> imageUrls, List<String> videoUrls) {
+        try {
+            // 1. 获取访问令牌
+            String accessToken = getAccessToken();
+            if (accessToken == null) {
+                log.error("获取AI访问令牌失败,无法分配跟踪人员");
+                return null;
+            }
+
+            // 2. 构建请求参数
+            Map<String, Object> requestBody = new HashMap<>();
+            requestBody.put("feedback_id", feedback.getId());
+            requestBody.put("feedback_content", feedback.getContent());
+            requestBody.put("feedback_type", feedback.getFeedbackType());
+            requestBody.put("feedback_source", feedback.getFeedbackSource());
+            requestBody.put("feedback_way", feedback.getFeedbackWay());
+            requestBody.put("contact_way", feedback.getContactWay());
+            requestBody.put("user_id", feedback.getUserId());
+            if (imageUrls != null && !imageUrls.isEmpty()) {
+                requestBody.put("image_urls", imageUrls);
+            }
+            if (videoUrls != null && !videoUrls.isEmpty()) {
+                requestBody.put("video_urls", videoUrls);
+            }
+
+            // 3. 设置请求头
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_JSON);
+            headers.set("Authorization", "Bearer " + accessToken);
+
+            HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
+
+            // 4. 调用AI接口
+            log.info("调用AI分配跟踪人员接口,feedbackId={}, url={}", feedback.getId(), assignStaffUrl);
+            ResponseEntity<String> response = restTemplate.postForEntity(assignStaffUrl, requestEntity, String.class);
+
+            // 5. 处理响应
+            if (response != null && response.getStatusCodeValue() == 200) {
+                String responseBody = response.getBody();
+                log.info("AI分配跟踪人员接口响应:{}", responseBody);
+
+                if (StringUtils.hasText(responseBody)) {
+                    JSONObject responseJson = JSONObject.parseObject(responseBody);
+                    Integer code = responseJson.getInteger("code");
+                    
+                    if (code != null && code == 200) {
+                        JSONObject data = responseJson.getJSONObject("data");
+                        if (data != null) {
+                            Integer staffId = data.getInteger("staff_id");
+                            if (staffId != null) {
+                                log.info("AI分配跟踪人员成功,feedbackId={}, staffId={}, staffName={}", 
+                                        feedback.getId(), staffId, data.getString("staff_name"));
+                                return staffId;
+                            }
+                        }
+                    } else {
+                        String message = responseJson.getString("message");
+                        log.error("AI分配跟踪人员失败,feedbackId={}, code={}, message={}", 
+                                feedback.getId(), code, message);
+                    }
+                }
+            } else {
+                log.error("AI分配跟踪人员接口调用失败,feedbackId={}, http状态={}", 
+                        feedback.getId(), response != null ? response.getStatusCode() : null);
+            }
+
+            return null;
+        } catch (org.springframework.web.client.HttpServerErrorException.ServiceUnavailable e) {
+            log.error("AI分配跟踪人员接口返回503 Service Unavailable错误: {}", e.getResponseBodyAsString());
+            return null;
+        } catch (Exception e) {
+            log.error("调用AI分配跟踪人员接口异常,feedbackId={}", feedback.getId(), e);
+            return null;
+        }
+    }
+}