wjj пре 1 недеља
родитељ
комит
5d5aa4b5b3

+ 18 - 0
fs-admin/src/main/java/com/fs/his/task/Task.java

@@ -300,6 +300,12 @@ public class Task {
     @Autowired
     private ICompanySmsService companySmsService;
 
+    @Autowired
+    private IFsProjectService projectService;
+
+    @Autowired
+    private FsProjectMapper projectMapper;
+
     public static final String SOP_TEMP_VOICE_KEY = "sop:tempVoice";
 
     // sop升单客户类型
@@ -2510,4 +2516,16 @@ public class Task {
         boolean result = companySmsService.rechargeSms(2L, Integer.parseInt(number));
     }
 
+    /**
+     * 理疗生成语音
+     */
+    public void generateSound(){
+        List<FsProject> projects = projectMapper.selectFsProjecNoSoundtList();
+        if (CollectionUtils.isNotEmpty(projects)) {
+            for (FsProject project : projects) {
+                projectService.generateSound(project);
+            }
+        }
+    }
+
 }

+ 5 - 0
fs-service/pom.xml

@@ -21,6 +21,11 @@
     </properties>
 
     <dependencies>
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>1.17.2</version>
+        </dependency>
         <dependency>
             <groupId>cn.jpush.api</groupId>
             <artifactId>jpush-client</artifactId>

+ 19 - 0
fs-service/src/main/java/com/fs/aiSoundReplication/VoiceCloneController.java

@@ -54,6 +54,25 @@ public class VoiceCloneController {
         return ttsService.textToSpeech(request);
     }
 
+    @PostMapping("/textToSound")
+    @ApiOperation("文本转语音")
+    public AudioVO textToSound(
+            @ApiParam(value = "要合成的文本", required = true)
+            @RequestParam String text,
+            @ApiParam(value = "音频格式")
+            @RequestParam(required = false, defaultValue = "mp3") String format,
+            @ApiParam(value = "语速 (0-15)")
+            @RequestParam(required = false, defaultValue = "1") Integer speed
+    ) {
+
+        TtsRequest request = new TtsRequest(
+                "", "", null, null); // AppID和Token会在Service中设置
+        request.setReqId(UUID.randomUUID().toString());
+        request.setFormat(format);
+        request.setSpeed(speed);
+        return ttsService.textToSound(request);
+    }
+
 //    @PostMapping("/synthesize-and-download")
 //    @ApiOperation("文本转语音并下载")
 //    public R synthesizeAndDownload(

+ 3 - 0
fs-service/src/main/java/com/fs/aiSoundReplication/config/TtsConfig.java

@@ -19,6 +19,9 @@ public class TtsConfig {
     private Integer defaultPitch = 10;
     private String defaultCluster = "volcano_icl";
 
+    //合成业务集群
+    private String ttsCluster = "volcano_tts";
+
     // 文本长度限制
     private Integer maxTextLength = 500; // 最大文本长度
 

+ 5 - 0
fs-service/src/main/java/com/fs/aiSoundReplication/service/TtsService.java

@@ -14,6 +14,11 @@ public interface TtsService {
      */
     AudioVO textToSpeech(TtsRequest request);
 
+    /**
+     * 文本转语音(合成)
+     */
+    AudioVO textToSound(TtsRequest request);
+
     /**
      * 简化版文本转语音
      * @param text 要合成的文本

+ 156 - 0
fs-service/src/main/java/com/fs/aiSoundReplication/service/impl/TtsServiceImpl.java

@@ -16,11 +16,17 @@ import com.fs.fastGpt.domain.FastgptEventLogTotal;
 import com.fs.fastGpt.mapper.FastGptRoleMapper;
 import com.fs.fastGpt.service.impl.FastgptEventLogTotalServiceImpl;
 import com.fs.fastgptApi.vo.AudioVO;
+import com.fs.his.domain.FsProject;
+import com.fs.his.mapper.FsProjectMapper;
+import com.fs.huifuPay.sdk.opps.core.utils.StringUtil;
 import com.fs.qw.domain.QwUser;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 import lombok.extern.slf4j.Slf4j;
 import okhttp3.*;
+import org.jsoup.Jsoup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -44,6 +50,8 @@ import static com.fs.fastgptApi.util.AudioUtils.transferAudioSilk;
 @Slf4j
 public class TtsServiceImpl implements TtsService  {
 
+    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
     @Autowired
     private TtsConfig ttsConfig;
 
@@ -67,6 +75,9 @@ public class TtsServiceImpl implements TtsService  {
     @Autowired
     private FastgptEventLogTotalServiceImpl fastgptEventLogTotalServiceImpl;
 
+    @Autowired
+    private FsProjectMapper fsProjectMapper;
+
     @Override
     public AudioVO textToSpeech(TtsRequest request) {
         try {
@@ -126,6 +137,65 @@ public class TtsServiceImpl implements TtsService  {
         }
     }
 
+    @Override
+    public AudioVO textToSound(TtsRequest request) {
+        try {
+            // 1. 参数校验
+            validateTtsHcRequest(request);
+
+            // 2. 设置默认值
+            setTtsDefaultValues(request);
+
+            // 3. 构建请求体
+            String requestBody = buildTtsRequestBody(request);
+
+            // 4. 构建HTTP请求
+            Request httpRequest = buildHttpRequest(requestBody);
+
+            // 5. 发送请求(带重试)
+            byte[] bytes = executeTtsRequest(httpRequest);
+
+            // 6. 检查音频数据
+            if (bytes == null || bytes.length == 0) {
+                log.error("音频数据为空,VoiceType: {}", request.getVoiceType());
+                return null;
+            }
+
+            // 7. 自动保存音频文件
+            // 创建临时文件
+            File tempFile = File.createTempFile("tts_", ".wav");
+            try (FileOutputStream fos = new FileOutputStream(tempFile)) {
+                fos.write(bytes);
+            }
+            // 上传到OSS
+            try (FileInputStream fileInputStream = new FileInputStream(tempFile)) {
+                //直接转silk然后传桶,返回url     优化-需要wav格式
+                CloudStorageService storage = OSSFactory.build();
+                String wavUrl = storage.uploadSuffix(fileInputStream, ".wav");
+//                AudioVO audioVO = AudioUtils.transferAudioSilkFromUrl(wavUrl, false);
+                Integer durations = getDurations(tempFile.getParent()+"\\"+tempFile.getName());
+                String silkUrl = transferAudioSilk(tempFile.getParent()+"\\", tempFile.getName(), false);
+                AudioVO audioVO = new AudioVO();
+                audioVO.setDuration(durations);
+                audioVO.setUrl(silkUrl);
+                audioVO.setWavUrl(wavUrl);
+                audioVO.setVoiceTxt(request.getText());
+                log.info("音频文件上传OSS成功: {}", audioVO.getUrl());
+                return audioVO;
+            } finally {
+                // 删除临时文件
+                tempFile.delete();
+            }
+
+        } catch (Exception e) {
+            log.error("TTS合成失败,reqId: {}, 错误: {}",
+                    request.getReqId(), e.getMessage());
+            throw e instanceof VoiceCloneException ?
+                    (VoiceCloneException) e :
+                    new VoiceCloneException("TTS合成失败", e);
+        }
+    }
+
     @Override
     public void ttsChargeByCount(VcCompanyUser vcCompanyUser, AudioVO audioVO, QwUser user) {
         try {
@@ -318,6 +388,25 @@ public class TtsServiceImpl implements TtsService  {
         }
     }
 
+    private void validateTtsHcRequest(TtsRequest request) {
+        if (request == null) {
+            throw new VoiceCloneException(1001, "请求参数不能为空");
+        }
+
+        if (request.getText() == null || request.getText().trim().isEmpty()) {
+            throw new VoiceCloneException(1001, "文本内容不能为空");
+        }
+
+        if (request.getText().length() > ttsConfig.getMaxTextLength()) {
+            throw new VoiceCloneException(1001,
+                    String.format("文本长度超过限制(%d字符)", ttsConfig.getMaxTextLength()));
+        }
+
+        if (request.getReqId() == null || request.getReqId().trim().isEmpty()) {
+            request.setReqId(UUID.randomUUID().toString());
+        }
+    }
+
     private void setDefaultValues(TtsRequest request) {
         if (request.getAppId() == null || request.getAppId().equals("")) {
             request.setAppId(voiceCloneConfig.getAppId());
@@ -352,6 +441,41 @@ public class TtsServiceImpl implements TtsService  {
         }
     }
 
+    private void setTtsDefaultValues(TtsRequest request) {
+        if (request.getAppId() == null || request.getAppId().equals("")) {
+            request.setAppId(voiceCloneConfig.getAppId());
+        }
+
+        if (request.getToken() == null || request.getToken().equals("")) {
+            request.setToken(voiceCloneConfig.getAccessToken());
+        }
+
+        request.setCluster(ttsConfig.getTtsCluster());
+
+        if (request.getFormat() == null) {
+            request.setFormat(ttsConfig.getDefaultFormat());
+        }
+
+        if (request.getSampleRate() == null) {
+            request.setSampleRate(ttsConfig.getDefaultSampleRate());
+        }
+
+        if (request.getSpeed() == null) {
+            request.setSpeed(ttsConfig.getDefaultSpeed());
+        }
+
+        if (request.getVolume() == null) {
+            request.setVolume(ttsConfig.getDefaultVolume());
+        }
+
+        if (request.getPitch() == null) {
+            request.setPitch(ttsConfig.getDefaultPitch());
+        }
+        if (request.getVoiceType() == null) {
+            request.setVoiceType("zh_male_M392_conversation_wvae_bigtts");
+        }
+    }
+
     private String buildRequestBody(TtsRequest request) throws IOException {
         Map<String, Object> requestBody = new HashMap<>();
 
@@ -430,6 +554,38 @@ public class TtsServiceImpl implements TtsService  {
         return objectMapper.writeValueAsString(requestBody);
     }
 
+    private String buildTtsRequestBody(TtsRequest request) throws IOException {
+        Map<String, Object> requestBody = new HashMap<>();
+
+        // 必填参数
+        HashMap<String, Object> app = new HashMap<String, Object>() {{
+            put("appid", request.getAppId());
+            put("token", request.getToken());
+            put("cluster", request.getCluster());
+        }};
+        HashMap<String, Object> user = new HashMap<String, Object>() {{
+            put("uid","001");
+        }};
+        HashMap<String, Object> audio = new HashMap<String, Object>() {{
+            put("voice_type", request.getVoiceType());
+        }};
+        if (request.getFormat() != null)audio.put("encoding", request.getFormat());
+        if (request.getSpeed() != null)audio.put("speed_ratio", request.getSpeed());
+
+        HashMap<String, Object> requestMap = new HashMap<String, Object>() {{
+            put("reqid", request.getReqId());
+            put("text", request.getText());
+            put("operation","query");
+        }};
+
+        requestBody.put("app",app);
+        requestBody.put("user", user);
+        requestBody.put("audio", audio);
+        requestBody.put("request", requestMap);
+
+        return objectMapper.writeValueAsString(requestBody);
+    }
+
     private Request buildHttpRequest(String requestBody) {
         RequestBody body = RequestBody.create(JSON,requestBody );