refactor(service): 用 RestClient 重写 ElevenLabs 调用并替换 UUID 工具类
- 将手写 HttpURLConnection 改为 Spring RestClient,精简 64 行冗余代码 - 引入 RestClientConfig 统一配置 - 统一使用 Hutool 的 IdUtil 生成文件名称
This commit is contained in:
25
src/main/java/com/yolo/keyborad/config/RestClientConfig.java
Normal file
25
src/main/java/com/yolo/keyborad/config/RestClientConfig.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package com.yolo.keyborad.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||||
|
import org.springframework.web.client.RestClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RestClient 配置类
|
||||||
|
* 提供连接池复用,优化 HTTP 请求性能
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class RestClientConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RestClient restClient() {
|
||||||
|
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||||
|
factory.setConnectTimeout(30000);
|
||||||
|
factory.setReadTimeout(60000);
|
||||||
|
|
||||||
|
return RestClient.builder()
|
||||||
|
.requestFactory(factory)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -481,7 +481,7 @@ public class ChatServiceImpl implements ChatService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
byte[] audioBytes = Base64.getDecoder().decode(audioBase64);
|
byte[] audioBytes = Base64.getDecoder().decode(audioBase64);
|
||||||
String fileName = UUID.randomUUID() + ".mp3";
|
String fileName = IdUtil.fastSimpleUUID() + ".mp3";
|
||||||
|
|
||||||
FileInfo fileInfo = fileStorageService.of(new ByteArrayInputStream(audioBytes))
|
FileInfo fileInfo = fileStorageService.of(new ByteArrayInputStream(audioBytes))
|
||||||
.setPath(userId + "/")
|
.setPath(userId + "/")
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.yolo.keyborad.service.impl;
|
package com.yolo.keyborad.service.impl;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.alibaba.fastjson.JSON;
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.yolo.keyborad.common.ErrorCode;
|
import com.yolo.keyborad.common.ErrorCode;
|
||||||
import com.yolo.keyborad.config.ElevenLabsProperties;
|
import com.yolo.keyborad.config.ElevenLabsProperties;
|
||||||
@@ -10,14 +9,10 @@ import com.yolo.keyborad.model.vo.TextToSpeechVO;
|
|||||||
import com.yolo.keyborad.service.ElevenLabsService;
|
import com.yolo.keyborad.service.ElevenLabsService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.client.RestClient;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -34,6 +29,9 @@ public class ElevenLabsServiceImpl implements ElevenLabsService {
|
|||||||
@Resource
|
@Resource
|
||||||
private ElevenLabsProperties elevenLabsProperties;
|
private ElevenLabsProperties elevenLabsProperties;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RestClient restClient;
|
||||||
|
|
||||||
private static final int MAX_TEXT_LENGTH = 5000;
|
private static final int MAX_TEXT_LENGTH = 5000;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -43,7 +41,6 @@ public class ElevenLabsServiceImpl implements ElevenLabsService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TextToSpeechVO textToSpeechWithTimestamps(String text, String voiceId) {
|
public TextToSpeechVO textToSpeechWithTimestamps(String text, String voiceId) {
|
||||||
// 1. 参数验证
|
|
||||||
if (StrUtil.isBlank(text)) {
|
if (StrUtil.isBlank(text)) {
|
||||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "文本内容不能为空");
|
throw new BusinessException(ErrorCode.PARAMS_ERROR, "文本内容不能为空");
|
||||||
}
|
}
|
||||||
@@ -57,111 +54,50 @@ public class ElevenLabsServiceImpl implements ElevenLabsService {
|
|||||||
voiceId = elevenLabsProperties.getVoiceId();
|
voiceId = elevenLabsProperties.getVoiceId();
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpURLConnection connection = null;
|
String requestUrl = buildRequestUrl(voiceId);
|
||||||
|
Map<String, Object> requestBody = buildRequestBody(text);
|
||||||
|
|
||||||
|
log.info("调用 ElevenLabs TTS API, voiceId: {}, 文本长度: {}", voiceId, text.length());
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 2. 构建请求 URL
|
String responseJson = restClient.post()
|
||||||
String requestUrl = buildRequestUrl(voiceId);
|
.uri(requestUrl)
|
||||||
URL url = new URL(requestUrl);
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.header("xi-api-key", elevenLabsProperties.getApiKey())
|
||||||
|
.body(requestBody)
|
||||||
|
.retrieve()
|
||||||
|
.body(String.class);
|
||||||
|
|
||||||
// 3. 创建连接
|
|
||||||
connection = (HttpURLConnection) url.openConnection();
|
|
||||||
connection.setRequestMethod("POST");
|
|
||||||
connection.setDoOutput(true);
|
|
||||||
connection.setDoInput(true);
|
|
||||||
connection.setConnectTimeout(30000);
|
|
||||||
connection.setReadTimeout(60000);
|
|
||||||
|
|
||||||
// 4. 设置请求头
|
|
||||||
connection.setRequestProperty("Content-Type", "application/json");
|
|
||||||
connection.setRequestProperty("xi-api-key", elevenLabsProperties.getApiKey());
|
|
||||||
|
|
||||||
// 5. 构建请求体
|
|
||||||
Map<String, Object> requestBody = buildRequestBody(text);
|
|
||||||
String jsonBody = JSON.toJSONString(requestBody);
|
|
||||||
|
|
||||||
log.info("调用 ElevenLabs TTS API, voiceId: {}, 文本长度: {}", voiceId, text.length());
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// 6. 发送请求
|
|
||||||
try (OutputStream os = connection.getOutputStream()) {
|
|
||||||
byte[] input = jsonBody.getBytes(StandardCharsets.UTF_8);
|
|
||||||
os.write(input, 0, input.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. 获取响应
|
|
||||||
int responseCode = connection.getResponseCode();
|
|
||||||
long duration = System.currentTimeMillis() - startTime;
|
long duration = System.currentTimeMillis() - startTime;
|
||||||
log.info("ElevenLabs TTS API 响应码: {}, 耗时: {}ms", responseCode, duration);
|
log.info("ElevenLabs TTS API 响应成功, 耗时: {}ms", duration);
|
||||||
|
|
||||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
JSONObject jsonResponse = JSONObject.parseObject(responseJson);
|
||||||
// 读取响应 JSON
|
String audioBase64 = jsonResponse.getString("audio_base64");
|
||||||
try (InputStream is = connection.getInputStream();
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
|
||||||
byte[] buffer = new byte[8192];
|
|
||||||
int bytesRead;
|
|
||||||
while ((bytesRead = is.read(buffer)) != -1) {
|
|
||||||
baos.write(buffer, 0, bytesRead);
|
|
||||||
}
|
|
||||||
String responseJson = baos.toString(StandardCharsets.UTF_8);
|
|
||||||
JSONObject jsonResponse = JSON.parseObject(responseJson);
|
|
||||||
|
|
||||||
String audioBase64 = jsonResponse.getString("audio_base64");
|
log.info("语音合成成功,Base64长度: {}", audioBase64.length());
|
||||||
|
|
||||||
log.info("语音合成成功,Base64长度: {}", audioBase64.length());
|
return TextToSpeechVO.builder()
|
||||||
|
.audioBase64(audioBase64)
|
||||||
|
.build();
|
||||||
|
|
||||||
return TextToSpeechVO.builder()
|
|
||||||
.audioBase64(audioBase64)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 读取错误信息
|
|
||||||
String errorMsg = "";
|
|
||||||
try (InputStream es = connection.getErrorStream()) {
|
|
||||||
if (es != null) {
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
byte[] buffer = new byte[1024];
|
|
||||||
int bytesRead;
|
|
||||||
while ((bytesRead = es.read(buffer)) != -1) {
|
|
||||||
baos.write(buffer, 0, bytesRead);
|
|
||||||
}
|
|
||||||
errorMsg = baos.toString(StandardCharsets.UTF_8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.error("ElevenLabs TTS API 调用失败, 状态码: {}, 错误信息: {}", responseCode, errorMsg);
|
|
||||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "语音合成服务异常: " + responseCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (BusinessException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("调用 ElevenLabs TTS API 发生异常", e);
|
log.error("调用 ElevenLabs TTS API 发生异常", e);
|
||||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "语音合成服务异常: " + e.getMessage());
|
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "语音合成服务异常: " + e.getMessage());
|
||||||
} finally {
|
|
||||||
if (connection != null) {
|
|
||||||
connection.disconnect();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建 ElevenLabs TTS API 请求 URL(带时间戳)
|
|
||||||
*/
|
|
||||||
private String buildRequestUrl(String voiceId) {
|
private String buildRequestUrl(String voiceId) {
|
||||||
StringBuilder url = new StringBuilder(elevenLabsProperties.getBaseUrl());
|
return elevenLabsProperties.getBaseUrl() +
|
||||||
url.append("/text-to-speech/").append(voiceId).append("/with-timestamps");
|
"/text-to-speech/" + voiceId + "/with-timestamps" +
|
||||||
url.append("?output_format=").append(elevenLabsProperties.getOutputFormat());
|
"?output_format=" + elevenLabsProperties.getOutputFormat();
|
||||||
return url.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建请求体
|
|
||||||
*/
|
|
||||||
private Map<String, Object> buildRequestBody(String text) {
|
private Map<String, Object> buildRequestBody(String text) {
|
||||||
Map<String, Object> requestBody = new HashMap<>();
|
Map<String, Object> requestBody = new HashMap<>();
|
||||||
requestBody.put("text", text);
|
requestBody.put("text", text);
|
||||||
requestBody.put("model_id", elevenLabsProperties.getModelId());
|
requestBody.put("model_id", elevenLabsProperties.getModelId());
|
||||||
|
|
||||||
// 设置语音参数
|
|
||||||
Map<String, Object> voiceSettings = new HashMap<>();
|
Map<String, Object> voiceSettings = new HashMap<>();
|
||||||
voiceSettings.put("stability", elevenLabsProperties.getStability());
|
voiceSettings.put("stability", elevenLabsProperties.getStability());
|
||||||
voiceSettings.put("similarity_boost", elevenLabsProperties.getSimilarityBoost());
|
voiceSettings.put("similarity_boost", elevenLabsProperties.getSimilarityBoost());
|
||||||
|
|||||||
Reference in New Issue
Block a user