音频模型调整

This commit is contained in:
spllzh
2025-10-21 09:23:23 +08:00
parent a029d57e7d
commit 9aba49a817
25 changed files with 254 additions and 4 deletions

View File

@@ -180,6 +180,8 @@ public class PasswordUtil {

View File

@@ -164,6 +164,8 @@ public class ServiceManager {

View File

@@ -141,6 +141,8 @@ public class AliyunConfig {

View File

@@ -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);

View File

@@ -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,
@@ -54,6 +56,9 @@ public class TtsRequestLogController {
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()));
}
}
}

View File

@@ -402,6 +402,8 @@ public class MenuController {

View File

@@ -372,6 +372,8 @@ public class RoleController {

View File

@@ -378,6 +378,8 @@ public class UserRoleController {

View File

@@ -144,6 +144,8 @@ public class DifyWorkflowResponseDto {

View File

@@ -87,6 +87,8 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper<CustomerProfil

View File

@@ -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();
}

View File

@@ -108,6 +108,8 @@ public interface ICustomerProfileAnalysisService extends IService<CustomerProfil

View File

@@ -143,6 +143,8 @@ public class CustomerProfileAnalysisServiceImpl extends ServiceImpl<CustomerProf

View File

@@ -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();
}
}

View File

@@ -115,6 +115,8 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IM

View File

@@ -115,6 +115,8 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IR

View File

@@ -115,6 +115,8 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i

View File

@@ -124,6 +124,8 @@ spring:

View File

@@ -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:

View File

@@ -300,6 +300,8 @@

View File

@@ -238,6 +238,8 @@ public class FaceDetectImageCountTest {

View File

@@ -179,6 +179,8 @@ public class TtsRequestLogShortUrlTest {

View File

@@ -157,6 +157,8 @@ public class VideoSynthesisTempUrlTest {

View File

@@ -132,6 +132,8 @@ public class VideoSynthesisVideoNameTest {