From 17cda288a35b015bd71af990deecc036f3ec2b58 Mon Sep 17 00:00:00 2001 From: wangxiangshun Date: Fri, 7 Nov 2025 09:18:47 +0800 Subject: [PATCH] =?UTF-8?q?TTS=20key=20=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vetti/socket/ChatWebSocketHandler.java | 51 ++++++++++++++----- .../web/controller/ai/AiCommonController.java | 3 +- .../src/main/resources/application-druid.yml | 4 +- .../target/classes/application-druid.yml | 4 +- 4 files changed, 45 insertions(+), 17 deletions(-) diff --git a/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketHandler.java b/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketHandler.java index fc4e322..b410fb0 100644 --- a/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketHandler.java +++ b/vetti-admin/src/main/java/com/vetti/socket/ChatWebSocketHandler.java @@ -177,7 +177,7 @@ public class ChatWebSocketHandler { OpenAiStreamClient aiStreamClient = SpringUtils.getBean(OpenAiStreamClient.class); log.info("AI提示词为:{}", promptJson); //先获取回答的评分,是否符合要求 - Boolean isEndFlag = getInterviewScore(promptJson, session, ""); + Boolean isEndFlag = getInterviewScore(clientId,promptJson, session, ""); if(isEndFlag){ log.info("面试回答符合条件规则,继续追问啦!!!!!"); aiStreamClient.streamChat(promptJson, new OpenAiStreamListenerService() { @@ -190,20 +190,22 @@ public class ChatWebSocketHandler { content = ""; } resultNum = resultNum+1; - String contentData = content.replaceAll("\n", ""); + log.info("提问的问题:{}",content); +// String contentData = content.replaceAll("\n", ""); //返回是追问的问题 //获取的是追问的问题 - if (StrUtil.isNotEmpty(contentData)) { + if (StrUtil.isNotEmpty(content)) { //对问题进行数据缓存 - cacheQuestionResult.put(session.getId(), contentData); + cacheQuestionResult.put(session.getId(), content); //开始进行语音输出-流式持续输出 - sendTTSBuffer(clientId, contentData, session); + sendTTSBuffer(clientId, content, session); // 实时输出内容 try { //把文本也给前端返回去 Map dataText = new HashMap<>(); dataText.put("type", "question"); - dataText.put("content", contentData); + dataText.put("content", content); + log.info("提问的问题文本发送啦:{}",JSONUtil.toJsonStr(dataText)); session.getBasicRemote().sendText(JSONUtil.toJsonStr(dataText)); } catch (Exception e) { e.printStackTrace(); @@ -245,7 +247,7 @@ public class ChatWebSocketHandler { } } else if ("end".equals(resultFlag)) { log.info("面试结束啦!!!!!"); - handleInterviewEnd(session,""); + handleInterviewEnd(clientId,session,""); } } } catch (Exception e) { @@ -329,6 +331,7 @@ public class ChatWebSocketHandler { ElevenLabsClient elevenLabsClient = SpringUtils.getBean(ElevenLabsClient.class); elevenLabsClient.handleTextToVoice(content, resultPathUrl); //持续返回数据流给客户端 + log.info("发送语音流成功啦!!!!!!!"); sendVoiceBuffer(resultPathUrl, session); } @@ -340,6 +343,7 @@ public class ChatWebSocketHandler { */ private void initializationQuestion(String clientId, Session session) { try { + log.info("开始获取到clientid :{}",clientId); //自我介绍结束后马上返回一个Good //发送初始化面试官语音流 String openingPathUrl = RuoYiConfig.getProfile() + VOICE_SYSTEM_DIR + "good.wav"; @@ -355,6 +359,7 @@ public class ChatWebSocketHandler { HotakeProblemBaseInfo queryPro = new HotakeProblemBaseInfo(); queryPro.setUserId(Long.valueOf(clientId)); List baseInfoList = problemBaseInfoService.selectHotakeProblemBaseInfoList(queryPro); + log.info("准备进行第一个问题的提问:{}",JSONUtil.toJsonStr(baseInfoList)); if (CollectionUtil.isNotEmpty(baseInfoList)) { HotakeProblemBaseInfo baseInfo = baseInfoList.get(0); if (StrUtil.isNotEmpty(baseInfo.getContents())) { @@ -366,7 +371,9 @@ public class ChatWebSocketHandler { mapEntityQ.put("role", "user"); mapEntityQ.put("content", "Question:" + question + "\\nCandidate Answer:{}"); list.add(mapEntityQ); + log.info("开始提问啦:{}",JSONUtil.toJsonStr(list)); //直接对该问题进行转换处理返回语音流 + log.info("第一个问题为:{}",question); sendTTSBuffer(clientId, question, session); } } @@ -384,7 +391,7 @@ public class ChatWebSocketHandler { * @param session 客户端会话 * @param position 职位 */ - private void handleInterviewEnd(Session session,String position) { + private void handleInterviewEnd(String clientId,Session session,String position) { //暂时的业务逻辑 //发送面试官结束语音流 String openingPathUrl = RuoYiConfig.getProfile() + VOICE_SYSTEM_DIR + "end.wav"; @@ -422,7 +429,13 @@ public class ChatWebSocketHandler { resultEntity.put("content", resultMsg); resultEntity.put("type", "score"); try{ + //返回最终的评分结构 + log.info("返回最终的评分结构:{}",JSONUtil.toJsonStr(resultEntity)); session.getBasicRemote().sendText(JSONUtil.toJsonStr(resultEntity)); + + //返回评分语音 + sendTTSBuffer(clientId,resultMsg,session); + }catch (Exception e){ e.printStackTrace(); } @@ -441,11 +454,13 @@ public class ChatWebSocketHandler { */ private Boolean handleScoreRecord(String content, Session session) { Map scoreRecordMap = cacheScoreResult.get(session.getId()); + log.info("获取评分结果:{}",content); //对评分进行处理 if (StrUtil.isNotEmpty(content)) { String[] strs = content.split("/"); //取第一个数就是对应的评分 - BigDecimal score = new BigDecimal(strs[0]); + log.info("获取的数据为:{}",strs[0]); + BigDecimal score = new BigDecimal(strs[0].trim()); //记录Key为1 if (BigDecimal.ZERO.compareTo(score) <= 0 && BigDecimal.ONE.compareTo(score) >= 0) { Integer n1 = scoreRecordMap.get("0-1") + 1; @@ -489,7 +504,7 @@ public class ChatWebSocketHandler { * @param session 客户端会话 * @param position 职位 */ - private Boolean getInterviewScore(String promptJson, Session session, String position) { + private Boolean getInterviewScore(String clientId,String promptJson, Session session, String position) { //返回文本评分 //获取缓存记录 String msgMapData = cacheMsgMapData.get(session.getId()); @@ -515,18 +530,30 @@ public class ChatWebSocketHandler { String resultMsg = gptClient.handleAiChat(promptJson, "QA"); //评论格式为: Score: 3/5\nAssessment: Basically correct answer but lacks detail String resultScore = ""; + String scoreText = resultMsg; if (StrUtil.isNotEmpty(resultMsg)) { - String[] resultMsgs = resultMsg.split("\\\\n"); + resultMsg = resultMsg.replaceAll("\n","#AA#"); + String[] resultMsgs = resultMsg.split("#AA#"); resultScore = resultMsgs[0].replaceAll(SCORE_FLAG, ""); } //校验面试是否结束 Boolean flag = handleScoreRecord(resultScore, session); try { if (!flag) { + //发送面试官结束语音流 + String openingPathUrl = RuoYiConfig.getProfile() + VOICE_SYSTEM_DIR + "end.wav"; + sendVoiceBuffer(openingPathUrl, session); + Map resultEntity = new HashMap<>(); - resultEntity.put("content", resultMsg); + resultEntity.put("content", scoreText); resultEntity.put("type", "score"); + //返回评分结果 + log.info("返回最终的评分结果:{}",JSONUtil.toJsonStr(resultEntity)); session.getBasicRemote().sendText(JSONUtil.toJsonStr(resultEntity)); + + //返回评分语音 + sendTTSBuffer(clientId,scoreText,session); + } } catch (Exception e) { e.printStackTrace(); diff --git a/vetti-admin/src/main/java/com/vetti/web/controller/ai/AiCommonController.java b/vetti-admin/src/main/java/com/vetti/web/controller/ai/AiCommonController.java index 30de200..1386cd4 100644 --- a/vetti-admin/src/main/java/com/vetti/web/controller/ai/AiCommonController.java +++ b/vetti-admin/src/main/java/com/vetti/web/controller/ai/AiCommonController.java @@ -51,8 +51,9 @@ public class AiCommonController extends BaseController public AjaxResult handleTextToVice() { //你好,我是本次的面试官Vetti,请点击开始按钮后,做一段自我介绍. + //你好,我是本次的面试官Vetti,请在三秒后,开始做一段自我介绍. //本轮面试结束,谢谢您的配合,面试结果将稍后通知 - elevenLabsClient.handleTextToVoice("Good","/Users/wangxiangshun/Desktop/临时文件/good.wav"); + elevenLabsClient.handleTextToVoice("Hello, I am Vetti, the interviewer for this interview. Please begin a self introduction in three seconds","/Users/wangxiangshun/Desktop/临时文件/opening1.wav"); return success(); } diff --git a/vetti-admin/src/main/resources/application-druid.yml b/vetti-admin/src/main/resources/application-druid.yml index 8cae58d..2a13167 100644 --- a/vetti-admin/src/main/resources/application-druid.yml +++ b/vetti-admin/src/main/resources/application-druid.yml @@ -152,8 +152,8 @@ verification: # 文本转语音 elevenLabs: baseUrl: https://api.elevenlabs.io/v1 -# apiKey: sk_5240d8f56cb1eb5225fffcf903f62479884d1af5b3de6812 - apiKey: sk_88f5a560e1bbde0e5b8b6b6eb1812163a98bfb98554acbec + apiKey: sk_5240d8f56cb1eb5225fffcf903f62479884d1af5b3de6812 +# apiKey: sk_88f5a560e1bbde0e5b8b6b6eb1812163a98bfb98554acbec modelId: eleven_turbo_v2_5 # 语音转文本 diff --git a/vetti-admin/target/classes/application-druid.yml b/vetti-admin/target/classes/application-druid.yml index 8cae58d..2a13167 100644 --- a/vetti-admin/target/classes/application-druid.yml +++ b/vetti-admin/target/classes/application-druid.yml @@ -152,8 +152,8 @@ verification: # 文本转语音 elevenLabs: baseUrl: https://api.elevenlabs.io/v1 -# apiKey: sk_5240d8f56cb1eb5225fffcf903f62479884d1af5b3de6812 - apiKey: sk_88f5a560e1bbde0e5b8b6b6eb1812163a98bfb98554acbec + apiKey: sk_5240d8f56cb1eb5225fffcf903f62479884d1af5b3de6812 +# apiKey: sk_88f5a560e1bbde0e5b8b6b6eb1812163a98bfb98554acbec modelId: eleven_turbo_v2_5 # 语音转文本