Files
smartDriveEE/src/main/java/com/rj/controller/SiliconFlowTtsController.java
2025-10-21 09:23:23 +08:00

501 lines
23 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.rj.controller;
import com.rj.dto.SiliconFlowTtsRequest;
import com.rj.dto.SiliconFlowTtsResponse;
import com.rj.entity.TtsRequestLog;
import com.rj.service.ITtsRequestLogService;
import com.rj.service.MinIOService;
import com.rj.service.SiliconFlowTtsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* SiliconFlow TTS 控制器
*
* @author rj
* @date 2025-01-02
*/
@Slf4j
@RestController
@RequestMapping("/api/tts")
@Tag(name = "文本转语音TTS", description = "SiliconFlow文本转语音接口")
public class SiliconFlowTtsController {
@Autowired
private SiliconFlowTtsService ttsService;
@Autowired
private MinIOService minIOService;
@Autowired
private ITtsRequestLogService ttsRequestLogService;
@PostMapping("/speech")
@Operation(summary = "文本转语音", description = "将文本转换为语音音频")
public ResponseEntity<Map<String, Object>> textToSpeech(
@Parameter(description = "TTS请求参数") @RequestBody SiliconFlowTtsRequest request) {
Map<String, Object> result = new HashMap<>();
try {
if (!ttsService.isServiceAvailable()) {
result.put("success", false);
result.put("message", "TTS服务不可用请检查配置");
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(result);
}
SiliconFlowTtsResponse response = ttsService.textToSpeech(request);
if (response.isSuccess()) {
result.put("success", true);
result.put("message", "文本转语音成功");
result.put("audio", response.getAudio());
result.put("format", response.getFormat());
result.put("sampleRate", response.getSampleRate());
result.put("duration", response.getDuration());
result.put("requestId", response.getRequestId());
result.put("minioUrl", response.getMinioUrl()); // 添加MinIO访问URL
result.put("shortUrl", response.getShortUrl()); // 添加短链URL
result.put("shortUrlExpireTime", response.getShortUrlExpireTime()); // 添加短链过期时间
// 添加音频数据长度信息
if (response.getAudio() != null) {
result.put("audioLength", response.getAudio().length());
log.info("TTS转换成功音频格式: {}, 采样率: {}, 时长: {:.2f}秒, Base64长度: {}, MinIO URL: {}, 临时URL: {}",
response.getFormat(), response.getSampleRate(), response.getDuration(),
response.getAudio().length(), response.getMinioUrl(), response.getShortUrl());
} else {
log.warn("TTS转换成功但音频数据为空");
}
return ResponseEntity.ok(result);
} else {
result.put("success", false);
result.put("message", response.getMessage());
result.put("error", response.getError());
result.put("errorCode", response.getErrorCode());
log.error("TTS转换失败: {}", response.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
} catch (Exception e) {
log.error("TTS请求异常: {}", e.getMessage(), e);
result.put("success", false);
result.put("message", "TTS请求异常: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}
@PostMapping("/speech/simple")
@Operation(summary = "简单文本转语音", description = "使用默认模型将文本转换为语音")
public ResponseEntity<Map<String, Object>> simpleTextToSpeech(
@Parameter(description = "要转换的文本") @RequestParam String text) {
Map<String, Object> result = new HashMap<>();
try {
if (text == null || text.trim().isEmpty()) {
result.put("success", false);
result.put("message", "输入文本不能为空");
return ResponseEntity.badRequest().body(result);
}
if (!ttsService.isServiceAvailable()) {
result.put("success", false);
result.put("message", "TTS服务不可用请检查配置");
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(result);
}
SiliconFlowTtsResponse response = ttsService.textToSpeech(text);
if (response.isSuccess()) {
result.put("success", true);
result.put("message", "文本转语音成功");
result.put("audio", response.getAudio());
result.put("format", response.getFormat());
result.put("sampleRate", response.getSampleRate());
result.put("duration", response.getDuration());
result.put("inputText", text);
log.info("简单TTS转换成功输入文本长度: {}, 音频格式: {}", text.length(), response.getFormat());
return ResponseEntity.ok(result);
} else {
result.put("success", false);
result.put("message", response.getMessage());
result.put("error", response.getError());
result.put("errorCode", response.getErrorCode());
log.error("简单TTS转换失败: {}", response.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
} catch (Exception e) {
log.error("简单TTS请求异常: {}", e.getMessage(), e);
result.put("success", false);
result.put("message", "TTS请求异常: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}
@PostMapping("/speech/download")
@Operation(summary = "文本转语音并下载", description = "将文本转换为语音并直接下载音频文件")
public ResponseEntity<byte[]> textToSpeechDownload(
@Parameter(description = "TTS请求参数") @RequestBody SiliconFlowTtsRequest request) {
try {
if (!ttsService.isServiceAvailable()) {
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build();
}
byte[] audioBytes = ttsService.textToSpeechBytes(request);
if (audioBytes != null && audioBytes.length > 0) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "speech.wav");
headers.setContentLength(audioBytes.length);
log.info("TTS音频下载成功文件大小: {} bytes", audioBytes.length);
return ResponseEntity.ok()
.headers(headers)
.body(audioBytes);
} else {
log.error("TTS音频生成失败");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
} catch (Exception e) {
log.error("TTS音频下载异常: {}", e.getMessage(), e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@PostMapping("/speech/download/simple")
@Operation(summary = "简单文本转语音并下载", description = "使用默认模型将文本转换为语音并直接下载")
public ResponseEntity<byte[]> simpleTextToSpeechDownload(
@Parameter(description = "要转换的文本") @RequestParam String text) {
try {
if (text == null || text.trim().isEmpty()) {
return ResponseEntity.badRequest().build();
}
if (!ttsService.isServiceAvailable()) {
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build();
}
byte[] audioBytes = ttsService.textToSpeechBytes(text);
if (audioBytes != null && audioBytes.length > 0) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "speech.wav");
headers.setContentLength(audioBytes.length);
log.info("简单TTS音频下载成功输入文本长度: {}, 文件大小: {} bytes", text.length(), audioBytes.length);
return ResponseEntity.ok()
.headers(headers)
.body(audioBytes);
} else {
log.error("简单TTS音频生成失败");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
} catch (Exception e) {
log.error("简单TTS音频下载异常: {}", e.getMessage(), e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@GetMapping("/status")
@Operation(summary = "检查TTS服务状态", description = "检查SiliconFlow TTS服务是否可用")
public ResponseEntity<Map<String, Object>> checkServiceStatus() {
Map<String, Object> result = new HashMap<>();
try {
boolean available = ttsService.isServiceAvailable();
result.put("success", true);
result.put("available", available);
result.put("message", available ? "TTS服务可用" : "TTS服务不可用");
return ResponseEntity.ok(result);
} catch (Exception e) {
log.error("检查TTS服务状态异常: {}", e.getMessage(), e);
result.put("success", false);
result.put("available", false);
result.put("message", "检查服务状态异常: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}
@PostMapping("/speech/preview")
@Operation(summary = "文本转语音预览", description = "将文本转换为语音并返回可直接播放的音频URL")
public ResponseEntity<Map<String, Object>> textToSpeechPreview(
@Parameter(description = "要转换的文本") @RequestParam String text) {
Map<String, Object> result = new HashMap<>();
try {
if (text == null || text.trim().isEmpty()) {
result.put("success", false);
result.put("message", "输入文本不能为空");
return ResponseEntity.badRequest().body(result);
}
if (!ttsService.isServiceAvailable()) {
result.put("success", false);
result.put("message", "TTS服务不可用请检查配置");
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(result);
}
SiliconFlowTtsResponse response = ttsService.textToSpeech(text);
if (response.isSuccess()) {
result.put("success", true);
result.put("message", "文本转语音成功");
result.put("audioBase64", response.getAudio());
result.put("format", response.getFormat());
result.put("sampleRate", response.getSampleRate());
result.put("duration", response.getDuration());
result.put("inputText", text);
// 生成音频播放URL
String audioUrl = "/api/tts/audio/play?text=" + java.net.URLEncoder.encode(text, "UTF-8");
result.put("audioUrl", audioUrl);
result.put("playUrl", "data:audio/mp3;base64," + response.getAudio());
// 同时提供解码后的字节数组长度信息
if (response.getAudio() != null) {
try {
byte[] audioBytes = Base64.getDecoder().decode(response.getAudio());
result.put("audioSizeBytes", audioBytes.length);
} catch (Exception e) {
log.warn("Base64解码失败: {}", e.getMessage());
}
}
log.info("TTS预览转换成功输入文本长度: {}, 音频格式: {}, 时长: {:.2f}秒",
text.length(), response.getFormat(), response.getDuration());
return ResponseEntity.ok(result);
} else {
result.put("success", false);
result.put("message", response.getMessage());
result.put("error", response.getError());
result.put("errorCode", response.getErrorCode());
log.error("TTS预览转换失败: {}", response.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
} catch (Exception e) {
log.error("TTS预览请求异常: {}", e.getMessage(), e);
result.put("success", false);
result.put("message", "TTS请求异常: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}
@GetMapping("/audio/info")
@Operation(summary = "获取音频信息", description = "获取音频的详细信息")
public ResponseEntity<Map<String, Object>> getAudioInfo(
@Parameter(description = "要转换的文本") @RequestParam String text) {
Map<String, Object> result = new HashMap<>();
try {
if (text == null || text.trim().isEmpty()) {
result.put("success", false);
result.put("message", "输入文本不能为空");
return ResponseEntity.badRequest().body(result);
}
if (!ttsService.isServiceAvailable()) {
result.put("success", false);
result.put("message", "TTS服务不可用请检查配置");
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(result);
}
SiliconFlowTtsResponse response = ttsService.textToSpeech(text);
if (response.isSuccess()) {
result.put("success", true);
result.put("message", "音频信息获取成功");
result.put("format", response.getFormat());
result.put("sampleRate", response.getSampleRate());
result.put("duration", response.getDuration());
result.put("inputText", text);
result.put("inputLength", text.length());
if (response.getAudio() != null) {
byte[] audioBytes = Base64.getDecoder().decode(response.getAudio());
result.put("audioSizeBytes", audioBytes.length);
result.put("audioSizeKB", String.format("%.2f", audioBytes.length / 1024.0));
result.put("audioSizeMB", String.format("%.2f", audioBytes.length / (1024.0 * 1024.0)));
}
// 提供播放链接
result.put("playUrl", "/api/tts/audio/play?text=" + java.net.URLEncoder.encode(text, "UTF-8"));
result.put("downloadUrl", "/api/tts/speech/download/simple?text=" + java.net.URLEncoder.encode(text, "UTF-8"));
log.info("音频信息获取成功,输入文本长度: {}, 音频格式: {}, 时长: {:.2f}秒",
text.length(), response.getFormat(), response.getDuration());
return ResponseEntity.ok(result);
} else {
result.put("success", false);
result.put("message", response.getMessage());
result.put("error", response.getError());
result.put("errorCode", response.getErrorCode());
log.error("音频信息获取失败: {}", response.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
} catch (Exception e) {
log.error("音频信息获取异常: {}", e.getMessage(), e);
result.put("success", false);
result.put("message", "获取音频信息异常: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}
@PostMapping(value = "/upload-audio", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "音频文件上传", description = "上传音频文件到MinIO并生成临时访问链接")
public ResponseEntity<Map<String, Object>> uploadAudio(
@Parameter(description = "音频文件") @RequestParam("file") MultipartFile file,
@Parameter(description = "音频名称") @RequestParam("audioName") String audioName,
@Parameter(description = "创建人姓名") @RequestParam("creatorName") String creatorName,
@Parameter(description = "创建人电话") @RequestParam("creatorPhone") String creatorPhone) {
Map<String, Object> result = new HashMap<>();
try {
log.info("开始上传音频文件: {}, 创建人: {}, 电话: {}", audioName, creatorName, creatorPhone);
// 验证文件
if (file.isEmpty()) {
log.error("上传文件为空");
result.put("success", false);
result.put("message", "上传文件为空");
return ResponseEntity.badRequest().body(result);
}
// 验证文件类型
String contentType = file.getContentType();
if (contentType == null || !contentType.startsWith("audio/")) {
log.error("文件类型不是音频文件: {}", contentType);
result.put("success", false);
result.put("message", "文件类型不是音频文件,请上传音频文件");
return ResponseEntity.badRequest().body(result);
}
// 生成唯一文件名
String originalFilename = file.getOriginalFilename();
String fileExtension = originalFilename != null && originalFilename.contains(".")
? originalFilename.substring(originalFilename.lastIndexOf("."))
: ".mp3";
String uniqueFileName = "/" + UUID.randomUUID().toString() + fileExtension;
// 上传文件到MinIO
String minioUrl = minIOService.uploadFileWithName(file, uniqueFileName);
if (minioUrl == null) {
log.error("文件上传到MinIO失败");
result.put("success", false);
result.put("message", "文件上传到MinIO失败");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
log.info("文件上传成功MinIO URL: {}", minioUrl);
// 生成7天临时访问链接
int expiresInSeconds = 7 * 24 * 60 * 60; // 7天
String shortUrl = minIOService.getPresignedUrl(uniqueFileName, expiresInSeconds);
if (shortUrl == null) {
log.error("生成临时访问链接失败");
result.put("success", false);
result.put("message", "生成临时访问链接失败");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
LocalDateTime expireTime = LocalDateTime.now().plusSeconds(expiresInSeconds);
log.info("生成临时访问链接成功,过期时间: {}", expireTime);
// 创建TTS请求日志记录
TtsRequestLog ttsLog = new TtsRequestLog();
ttsLog.setId(UUID.randomUUID().toString());
ttsLog.setRequestTime(LocalDateTime.now());
ttsLog.setModel("AUDIO_UPLOAD");
ttsLog.setInputText("音频文件上传");
ttsLog.setInputLength(0);
ttsLog.setStatus("SUCCESS");
ttsLog.setAudioSizeBytes(file.getSize());
ttsLog.setMinioUrl(minioUrl);
ttsLog.setAudioName(audioName);
ttsLog.setShortUrl(shortUrl);
ttsLog.setShortUrlExpireTime(expireTime);
ttsLog.setCreatorName(creatorName);
ttsLog.setCreatorPhone(creatorPhone);
ttsLog.setCreateTime(LocalDateTime.now());
ttsLog.setUpdateTime(LocalDateTime.now());
// 保存到数据库
boolean saveResult = ttsRequestLogService.saveTtsRequestLog(ttsLog);
if (!saveResult) {
log.error("保存TTS请求日志失败");
result.put("success", false);
result.put("message", "保存TTS请求日志失败");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
// 构建成功响应
result.put("success", true);
result.put("message", "音频文件上传成功");
result.put("id", ttsLog.getId());
result.put("audioName", audioName);
result.put("minioUrl", minioUrl);
result.put("shortUrl", shortUrl);
result.put("shortUrlExpireTime", expireTime);
result.put("creatorName", creatorName);
result.put("creatorPhone", creatorPhone);
result.put("audioSizeBytes", file.getSize());
result.put("status", "SUCCESS");
result.put("createTime", LocalDateTime.now());
log.info("音频文件上传完成记录ID: {}, MinIO URL: {}, 临时URL: {}",
ttsLog.getId(), minioUrl, shortUrl);
return ResponseEntity.ok(result);
} catch (Exception e) {
log.error("音频文件上传异常: {}", e.getMessage(), e);
result.put("success", false);
result.put("message", "音频文件上传异常: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}
}