音频模型调整
This commit is contained in:
@@ -180,6 +180,8 @@ public class PasswordUtil {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -164,6 +164,8 @@ public class ServiceManager {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -141,6 +141,8 @@ public class AliyunConfig {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -419,7 +419,7 @@ public class SiliconFlowTtsController {
|
||||
String fileExtension = originalFilename != null && originalFilename.contains(".")
|
||||
? originalFilename.substring(originalFilename.lastIndexOf("."))
|
||||
: ".mp3";
|
||||
String uniqueFileName = "audio/" + UUID.randomUUID().toString() + fileExtension;
|
||||
String uniqueFileName = "/" + UUID.randomUUID().toString() + fileExtension;
|
||||
|
||||
// 上传文件到MinIO
|
||||
String minioUrl = minIOService.uploadFileWithName(file, uniqueFileName);
|
||||
|
||||
@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.rj.entity.TtsRequestLog;
|
||||
import com.rj.mapper.TtsRequestLogMapper;
|
||||
import com.rj.service.ITtsRequestLogService;
|
||||
import com.rj.dto.AsrRequest;
|
||||
import com.rj.dto.AsrResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -13,7 +15,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -40,6 +41,7 @@ public class TtsRequestLogController {
|
||||
public ResponseEntity<Map<String, Object>> getTtsRequestLogs(
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@Parameter(description = "每页大小") @RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@Parameter(description = "音乐名称") @RequestParam(required = false) String audioName,
|
||||
@Parameter(description = "状态") @RequestParam(required = false) String status,
|
||||
@Parameter(description = "模型") @RequestParam(required = false) String model,
|
||||
@Parameter(description = "创建人姓名") @RequestParam(required = false) String creatorName,
|
||||
@@ -48,12 +50,15 @@ public class TtsRequestLogController {
|
||||
@Parameter(description = "结束时间") @RequestParam(required = false) String endTime) {
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
|
||||
try {
|
||||
Page<TtsRequestLog> page = new Page<>(pageNum, pageSize);
|
||||
QueryWrapper<TtsRequestLog> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
// 添加查询条件
|
||||
if (audioName != null && !audioName.trim().isEmpty()) {
|
||||
queryWrapper.like("audio_name", audioName);
|
||||
}
|
||||
if (status != null && !status.trim().isEmpty()) {
|
||||
queryWrapper.eq("status", status);
|
||||
}
|
||||
@@ -149,4 +154,28 @@ public class TtsRequestLogController {
|
||||
return ResponseEntity.status(500).body(result);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/speech-to-text")
|
||||
@Operation(summary = "语音转文本", description = "将语音文件转换为文本")
|
||||
public ResponseEntity<AsrResponse> speechToText(
|
||||
@Parameter(description = "语音转文本请求参数") @RequestBody AsrRequest request) {
|
||||
|
||||
try {
|
||||
if (!ttsRequestLogService.isAsrServiceAvailable()) {
|
||||
return ResponseEntity.status(500).body(AsrResponse.error("ASR服务不可用,请检查配置"));
|
||||
}
|
||||
|
||||
AsrResponse response = ttsRequestLogService.speechToText(request);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
return ResponseEntity.ok(response);
|
||||
} else {
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("语音转文本失败: {}", e.getMessage(), e);
|
||||
return ResponseEntity.status(500).body(AsrResponse.error("语音转文本失败: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,6 +402,8 @@ public class MenuController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -372,6 +372,8 @@ public class RoleController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -378,6 +378,8 @@ public class UserRoleController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -144,6 +144,8 @@ public class DifyWorkflowResponseDto {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -87,6 +87,8 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper<CustomerProfil
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.rj.service;
|
||||
|
||||
import com.rj.entity.TtsRequestLog;
|
||||
import com.rj.utils.MinIOUrlGenerator;
|
||||
import com.rj.dto.AsrRequest;
|
||||
import com.rj.dto.AsrResponse;
|
||||
|
||||
/**
|
||||
* TTS请求日志服务接口
|
||||
@@ -41,4 +43,19 @@ public interface ITtsRequestLogService {
|
||||
* @return 是否设置成功
|
||||
*/
|
||||
boolean setShortUrlByAudioName(String audioName);
|
||||
|
||||
/**
|
||||
* 语音转文本
|
||||
*
|
||||
* @param request ASR请求
|
||||
* @return ASR响应
|
||||
*/
|
||||
AsrResponse speechToText(AsrRequest request);
|
||||
|
||||
/**
|
||||
* 检查ASR服务是否可用
|
||||
*
|
||||
* @return 是否可用
|
||||
*/
|
||||
boolean isAsrServiceAvailable();
|
||||
}
|
||||
|
||||
@@ -108,6 +108,8 @@ public interface ICustomerProfileAnalysisService extends IService<CustomerProfil
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -143,6 +143,8 @@ public class CustomerProfileAnalysisServiceImpl extends ServiceImpl<CustomerProf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
package com.rj.service.impl;
|
||||
|
||||
import com.alibaba.dashscope.audio.asr.transcription.*;
|
||||
import com.rj.entity.TtsRequestLog;
|
||||
import com.rj.mapper.TtsRequestLogMapper;
|
||||
import com.rj.service.ITtsRequestLogService;
|
||||
import com.rj.utils.MinIOUrlGenerator;
|
||||
import com.rj.dto.AsrRequest;
|
||||
import com.rj.dto.AsrResponse;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TTS请求日志服务实现类
|
||||
@@ -27,6 +34,15 @@ public class TtsRequestLogServiceImpl implements ITtsRequestLogService {
|
||||
@Autowired
|
||||
private MinIOUrlGenerator urlGenerator;
|
||||
|
||||
@Value("${dashscope.api.key}")
|
||||
private String apiKey;
|
||||
|
||||
@Value("${asr.enabled:true}")
|
||||
private boolean asrEnabled;
|
||||
|
||||
@Value("${asr.default-model:paraformer-v2}")
|
||||
private String defaultAsrModel;
|
||||
|
||||
@Override
|
||||
public boolean saveTtsRequestLog(TtsRequestLog requestLog) {
|
||||
try {
|
||||
@@ -52,7 +68,7 @@ public class TtsRequestLogServiceImpl implements ITtsRequestLogService {
|
||||
@Override
|
||||
public boolean setShortUrlByAudioName(MinIOUrlGenerator.UrlInfo urlInfo ) {
|
||||
try {
|
||||
if (urlInfo == null || urlInfo.getFileName().trim().isEmpty()) {
|
||||
if (urlInfo == null || urlInfo.getFileName() == null || urlInfo.getFileName().trim().isEmpty()) {
|
||||
log.error("音频名称不能为空");
|
||||
return false;
|
||||
}
|
||||
@@ -92,4 +108,131 @@ public class TtsRequestLogServiceImpl implements ITtsRequestLogService {
|
||||
public boolean setShortUrlByAudioName(String audioName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsrResponse speechToText(AsrRequest request) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
if (!isAsrServiceAvailable()) {
|
||||
return AsrResponse.error("ASR服务不可用,请检查配置");
|
||||
}
|
||||
|
||||
if (request.getAudioUrl() == null || request.getAudioUrl().trim().isEmpty()) {
|
||||
return AsrResponse.error("音频文件URL不能为空");
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if (request.getModel() == null || request.getModel().trim().isEmpty()) {
|
||||
request.setModel(defaultAsrModel);
|
||||
}
|
||||
if (request.getFormat() == null || request.getFormat().trim().isEmpty()) {
|
||||
request.setFormat("wav");
|
||||
}
|
||||
if (request.getSampleRate() == null) {
|
||||
request.setSampleRate(16000);
|
||||
}
|
||||
if (request.getEnablePunctuation() == null) {
|
||||
request.setEnablePunctuation(true);
|
||||
}
|
||||
if (request.getEnableNumberConversion() == null) {
|
||||
request.setEnableNumberConversion(true);
|
||||
}
|
||||
if (request.getEnableSpeakerDiarization() == null) {
|
||||
request.setEnableSpeakerDiarization(false);
|
||||
}
|
||||
if (request.getSpeakerCount() == null) {
|
||||
request.setSpeakerCount(1);
|
||||
}
|
||||
|
||||
log.info("开始语音识别 - 音频URL: {}, 模型: {}, 格式: {}, 采样率: {}",
|
||||
request.getAudioUrl(), request.getModel(), request.getFormat(), request.getSampleRate());
|
||||
|
||||
// 创建转写请求参数
|
||||
TranscriptionParam param = TranscriptionParam.builder()
|
||||
.apiKey(apiKey)
|
||||
.model(request.getModel())
|
||||
.fileUrls(Arrays.asList(request.getAudioUrl()))
|
||||
.build();
|
||||
|
||||
// 创建转写对象
|
||||
Transcription transcription = new Transcription();
|
||||
|
||||
// 提交转写请求
|
||||
TranscriptionResult result = transcription.asyncCall(param);
|
||||
String taskId = result.getTaskId();
|
||||
String requestId = result.getRequestId();
|
||||
|
||||
log.info("ASR任务已提交 - TaskId: {}, RequestId: {}", taskId, requestId);
|
||||
|
||||
// 等待任务完成
|
||||
TranscriptionQueryParam queryParam = TranscriptionQueryParam.FromTranscriptionParam(param, taskId);
|
||||
result = transcription.wait(queryParam);
|
||||
log.info("语音识别完成 - result: {} ", result);
|
||||
long processingTime = System.currentTimeMillis() - startTime;
|
||||
|
||||
if (result.getOutput() != null) {
|
||||
// 解析识别结果 - 从JSON中提取文本
|
||||
String fullText = "";
|
||||
Double duration = 0.0;
|
||||
|
||||
try {
|
||||
// 从输出中提取文本和时长信息
|
||||
if (result.getOutput().toString().contains("\"text\"")) {
|
||||
// 简单解析JSON获取文本
|
||||
String outputStr = result.getOutput().toString();
|
||||
int textStart = outputStr.indexOf("\"text\":\"") + 8;
|
||||
int textEnd = outputStr.indexOf("\"", textStart);
|
||||
if (textStart > 7 && textEnd > textStart) {
|
||||
fullText = outputStr.substring(textStart, textEnd);
|
||||
}
|
||||
}
|
||||
|
||||
// 提取时长信息
|
||||
if (result.getOutput().toString().contains("\"duration\"")) {
|
||||
String outputStr = result.getOutput().toString();
|
||||
int durationStart = outputStr.indexOf("\"duration\":") + 11;
|
||||
int durationEnd = outputStr.indexOf(",", durationStart);
|
||||
if (durationEnd == -1) {
|
||||
durationEnd = outputStr.indexOf("}", durationStart);
|
||||
}
|
||||
if (durationStart > 10 && durationEnd > durationStart) {
|
||||
try {
|
||||
duration = Double.parseDouble(outputStr.substring(durationStart, durationEnd).trim());
|
||||
} catch (NumberFormatException e) {
|
||||
duration = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析ASR结果时出错: {}", e.getMessage());
|
||||
}
|
||||
|
||||
List<AsrResponse.AsrResult> asrResults = new ArrayList<>();
|
||||
|
||||
// 创建简单的结果对象
|
||||
AsrResponse.AsrResult asrResult = new AsrResponse.AsrResult();
|
||||
asrResult.setText(fullText);
|
||||
asrResult.setStartTime(0.0);
|
||||
asrResult.setEndTime(duration);
|
||||
asrResults.add(asrResult);
|
||||
|
||||
log.info("语音识别完成 - 识别文本长度: {}, 处理时间: {}ms", fullText.length(), processingTime);
|
||||
|
||||
return AsrResponse.success(fullText, asrResults, duration, processingTime, taskId, requestId);
|
||||
} else {
|
||||
log.warn("ASR识别结果为空 - TaskId: {}", taskId);
|
||||
return AsrResponse.error("语音识别结果为空");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("语音识别失败: {}", e.getMessage(), e);
|
||||
return AsrResponse.error("语音识别失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAsrServiceAvailable() {
|
||||
return asrEnabled && apiKey != null && !apiKey.trim().isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,8 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IM
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -115,6 +115,8 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IR
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -115,6 +115,8 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -124,6 +124,8 @@ spring:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -125,6 +125,27 @@ aliyun:
|
||||
# 是否启用阿里云服务
|
||||
enabled: true
|
||||
|
||||
# ASR语音识别配置
|
||||
asr:
|
||||
# 是否启用ASR服务
|
||||
enabled: true
|
||||
# 默认ASR模型
|
||||
default-model: paraformer-v2
|
||||
# 请求超时时间(毫秒)
|
||||
timeout: 60000
|
||||
# 是否启用标点符号
|
||||
enable-punctuation: true
|
||||
# 是否启用数字转换
|
||||
enable-number-conversion: true
|
||||
# 是否启用说话人分离
|
||||
enable-speaker-diarization: false
|
||||
# 默认说话人数量
|
||||
default-speaker-count: 1
|
||||
# 默认音频格式
|
||||
default-format: wav
|
||||
# 默认采样率
|
||||
default-sample-rate: 16000
|
||||
|
||||
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
|
||||
@@ -101,6 +101,8 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -300,6 +300,8 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -238,6 +238,8 @@ public class FaceDetectImageCountTest {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -179,6 +179,8 @@ public class TtsRequestLogShortUrlTest {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -157,6 +157,8 @@ public class VideoSynthesisTempUrlTest {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -132,6 +132,8 @@ public class VideoSynthesisVideoNameTest {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user