Explorar el Código

优化阿里云tts配置常量信息
整理tts命名

liyanbo hace 2 semanas
padre
commit
8ee294d852

+ 23 - 23
byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/framework/ai/config/AiAutoConfiguration.java

@@ -37,7 +37,7 @@ import org.springframework.context.annotation.Configuration;
  * @author fansili
  */
 @Configuration
-@EnableConfigurationProperties({ YudaoAiProperties.class,
+@EnableConfigurationProperties({ ByzsAiProperties.class,
         QdrantVectorStoreProperties.class, // 解析 Qdrant 配置
         RedisVectorStoreProperties.class, // 解析 Redis 配置
         MilvusVectorStoreProperties.class, MilvusServiceClientProperties.class // 解析 Milvus 配置
@@ -54,12 +54,12 @@ public class AiAutoConfiguration {
 
     @Bean
     @ConditionalOnProperty(value = "byzs.ai.deepseek.enable", havingValue = "true")
-    public DeepSeekChatModel deepSeekChatModel(YudaoAiProperties yudaoAiProperties) {
-        YudaoAiProperties.DeepSeekProperties properties = yudaoAiProperties.getDeepseek();
+    public DeepSeekChatModel deepSeekChatModel(ByzsAiProperties byzsAiProperties) {
+        ByzsAiProperties.DeepSeekProperties properties = byzsAiProperties.getDeepseek();
         return buildDeepSeekChatModel(properties);
     }
 
-    public DeepSeekChatModel buildDeepSeekChatModel(YudaoAiProperties.DeepSeekProperties properties) {
+    public DeepSeekChatModel buildDeepSeekChatModel(ByzsAiProperties.DeepSeekProperties properties) {
         if (StrUtil.isEmpty(properties.getModel())) {
             properties.setModel(DeepSeekChatModel.MODEL_DEFAULT);
         }
@@ -81,12 +81,12 @@ public class AiAutoConfiguration {
 
     @Bean
     @ConditionalOnProperty(value = "byzs.ai.doubao.enable", havingValue = "true")
-    public DouBaoChatModel douBaoChatClient(YudaoAiProperties yudaoAiProperties) {
-        YudaoAiProperties.DouBaoProperties properties = yudaoAiProperties.getDoubao();
+    public DouBaoChatModel douBaoChatClient(ByzsAiProperties byzsAiProperties) {
+        ByzsAiProperties.DouBaoProperties properties = byzsAiProperties.getDoubao();
         return buildDouBaoChatClient(properties);
     }
 
-    public DouBaoChatModel buildDouBaoChatClient(YudaoAiProperties.DouBaoProperties properties) {
+    public DouBaoChatModel buildDouBaoChatClient(ByzsAiProperties.DouBaoProperties properties) {
         if (StrUtil.isEmpty(properties.getModel())) {
             properties.setModel(DouBaoChatModel.MODEL_DEFAULT);
         }
@@ -108,12 +108,12 @@ public class AiAutoConfiguration {
 
     @Bean
     @ConditionalOnProperty(value = "byzs.ai.siliconflow.enable", havingValue = "true")
-    public SiliconFlowChatModel siliconFlowChatClient(YudaoAiProperties yudaoAiProperties) {
-        YudaoAiProperties.SiliconFlowProperties properties = yudaoAiProperties.getSiliconflow();
+    public SiliconFlowChatModel siliconFlowChatClient(ByzsAiProperties byzsAiProperties) {
+        ByzsAiProperties.SiliconFlowProperties properties = byzsAiProperties.getSiliconflow();
         return buildSiliconFlowChatClient(properties);
     }
 
-    public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlowProperties properties) {
+    public SiliconFlowChatModel buildSiliconFlowChatClient(ByzsAiProperties.SiliconFlowProperties properties) {
         if (StrUtil.isEmpty(properties.getModel())) {
             properties.setModel(SiliconFlowApiConstants.MODEL_DEFAULT);
         }
@@ -135,12 +135,12 @@ public class AiAutoConfiguration {
 
     @Bean
     @ConditionalOnProperty(value = "byzs.ai.hunyuan.enable", havingValue = "true")
-    public HunYuanChatModel hunYuanChatClient(YudaoAiProperties yudaoAiProperties) {
-        YudaoAiProperties.HunYuanProperties properties = yudaoAiProperties.getHunyuan();
+    public HunYuanChatModel hunYuanChatClient(ByzsAiProperties byzsAiProperties) {
+        ByzsAiProperties.HunYuanProperties properties = byzsAiProperties.getHunyuan();
         return buildHunYuanChatClient(properties);
     }
 
-    public HunYuanChatModel buildHunYuanChatClient(YudaoAiProperties.HunYuanProperties properties) {
+    public HunYuanChatModel buildHunYuanChatClient(ByzsAiProperties.HunYuanProperties properties) {
         if (StrUtil.isEmpty(properties.getModel())) {
             properties.setModel(HunYuanChatModel.MODEL_DEFAULT);
         }
@@ -169,12 +169,12 @@ public class AiAutoConfiguration {
 
     @Bean
     @ConditionalOnProperty(value = "byzs.ai.xinghuo.enable", havingValue = "true")
-    public XingHuoChatModel xingHuoChatClient(YudaoAiProperties yudaoAiProperties) {
-        YudaoAiProperties.XingHuoProperties properties = yudaoAiProperties.getXinghuo();
+    public XingHuoChatModel xingHuoChatClient(ByzsAiProperties byzsAiProperties) {
+        ByzsAiProperties.XingHuoProperties properties = byzsAiProperties.getXinghuo();
         return buildXingHuoChatClient(properties);
     }
 
-    public XingHuoChatModel buildXingHuoChatClient(YudaoAiProperties.XingHuoProperties properties) {
+    public XingHuoChatModel buildXingHuoChatClient(ByzsAiProperties.XingHuoProperties properties) {
         if (StrUtil.isEmpty(properties.getModel())) {
             properties.setModel(XingHuoChatModel.MODEL_DEFAULT);
         }
@@ -196,12 +196,12 @@ public class AiAutoConfiguration {
 
     @Bean
     @ConditionalOnProperty(value = "byzs.ai.baichuan.enable", havingValue = "true")
-    public BaiChuanChatModel baiChuanChatClient(YudaoAiProperties yudaoAiProperties) {
-        YudaoAiProperties.BaiChuanProperties properties = yudaoAiProperties.getBaichuan();
+    public BaiChuanChatModel baiChuanChatClient(ByzsAiProperties byzsAiProperties) {
+        ByzsAiProperties.BaiChuanProperties properties = byzsAiProperties.getBaichuan();
         return buildBaiChuanChatClient(properties);
     }
 
-    public BaiChuanChatModel buildBaiChuanChatClient(YudaoAiProperties.BaiChuanProperties properties) {
+    public BaiChuanChatModel buildBaiChuanChatClient(ByzsAiProperties.BaiChuanProperties properties) {
         if (StrUtil.isEmpty(properties.getModel())) {
             properties.setModel(BaiChuanChatModel.MODEL_DEFAULT);
         }
@@ -223,15 +223,15 @@ public class AiAutoConfiguration {
 
     @Bean
     @ConditionalOnProperty(value = "byzs.ai.midjourney.enable", havingValue = "true")
-    public MidjourneyApi midjourneyApi(YudaoAiProperties yudaoAiProperties) {
-        YudaoAiProperties.MidjourneyProperties config = yudaoAiProperties.getMidjourney();
+    public MidjourneyApi midjourneyApi(ByzsAiProperties byzsAiProperties) {
+        ByzsAiProperties.MidjourneyProperties config = byzsAiProperties.getMidjourney();
         return new MidjourneyApi(config.getBaseUrl(), config.getApiKey(), config.getNotifyUrl());
     }
 
     @Bean
     @ConditionalOnProperty(value = "byzs.ai.suno.enable", havingValue = "true")
-    public SunoApi sunoApi(YudaoAiProperties yudaoAiProperties) {
-        return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl());
+    public SunoApi sunoApi(ByzsAiProperties byzsAiProperties) {
+        return new SunoApi(byzsAiProperties.getSuno().getBaseUrl());
     }
 
     // ========== RAG 相关 ==========

+ 27 - 1
byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/framework/ai/config/YudaoAiProperties.java → byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/framework/ai/config/ByzsAiProperties.java

@@ -11,7 +11,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
  */
 @ConfigurationProperties(prefix = "byzs.ai")
 @Data
-public class YudaoAiProperties {
+public class ByzsAiProperties {
 
     /**
      * DeepSeek
@@ -180,4 +180,30 @@ public class YudaoAiProperties {
 
     }
 
+    /**
+     * 阿里云
+     */
+    private AliyunProperties aliyun;
+
+    @Data
+    public static class AliyunProperties {
+
+        private String enable;
+
+        /**
+         * TTS配置
+         */
+        private TtsProperties tts;
+
+        @Data
+        public static class TtsProperties {
+            private String appKey;
+            private String token;
+            private String url;
+            private String aliyunAkId;
+            private String aliyunAkSecret;
+        }
+
+    }
+
 }

+ 14 - 14
byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/framework/ai/core/AiModelFactoryImpl.java

@@ -9,7 +9,7 @@ import cn.hutool.core.util.RuntimeUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.spring.SpringUtil;
 import cn.iocoder.byzs.module.ai.framework.ai.config.AiAutoConfiguration;
-import cn.iocoder.byzs.module.ai.framework.ai.config.YudaoAiProperties;
+import cn.iocoder.byzs.module.ai.framework.ai.config.ByzsAiProperties;
 import cn.iocoder.byzs.module.ai.enums.model.AiPlatformEnum;
 import cn.iocoder.byzs.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;
 import cn.iocoder.byzs.module.ai.framework.ai.core.model.deepseek.DeepSeekChatModel;
@@ -253,7 +253,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
         String cacheKey = buildClientCacheKey(MidjourneyApi.class, AiPlatformEnum.MIDJOURNEY.getPlatform(), apiKey,
                 url);
         return Singleton.get(cacheKey, (Func0<MidjourneyApi>) () -> {
-            YudaoAiProperties.MidjourneyProperties properties = SpringUtil.getBean(YudaoAiProperties.class)
+            ByzsAiProperties.MidjourneyProperties properties = SpringUtil.getBean(ByzsAiProperties.class)
                     .getMidjourney();
             return new MidjourneyApi(url, apiKey, properties.getNotifyUrl());
         });
@@ -372,37 +372,37 @@ public class AiModelFactoryImpl implements AiModelFactory {
 
 
     /**
-     * 可参考 {@link AiAutoConfiguration#deepSeekChatModel(YudaoAiProperties)}
+     * 可参考 {@link AiAutoConfiguration#deepSeekChatModel(ByzsAiProperties)}
      */
     private static DeepSeekChatModel buildDeepSeekChatModel(String apiKey) {
-        YudaoAiProperties.DeepSeekProperties properties = new YudaoAiProperties.DeepSeekProperties()
+        ByzsAiProperties.DeepSeekProperties properties = new ByzsAiProperties.DeepSeekProperties()
                 .setApiKey(apiKey);
         return new AiAutoConfiguration().buildDeepSeekChatModel(properties);
     }
 
     /**
-     * 可参考 {@link AiAutoConfiguration#douBaoChatClient(YudaoAiProperties)}
+     * 可参考 {@link AiAutoConfiguration#douBaoChatClient(ByzsAiProperties)}
      */
     private ChatModel buildDouBaoChatModel(String apiKey) {
-        YudaoAiProperties.DouBaoProperties properties = new YudaoAiProperties.DouBaoProperties()
+        ByzsAiProperties.DouBaoProperties properties = new ByzsAiProperties.DouBaoProperties()
                 .setApiKey(apiKey);
         return new AiAutoConfiguration().buildDouBaoChatClient(properties);
     }
 
     /**
-     * 可参考 {@link AiAutoConfiguration#hunYuanChatClient(YudaoAiProperties)}
+     * 可参考 {@link AiAutoConfiguration#hunYuanChatClient(ByzsAiProperties)}
      */
     private ChatModel buildHunYuanChatModel(String apiKey, String url) {
-        YudaoAiProperties.HunYuanProperties properties = new YudaoAiProperties.HunYuanProperties()
+        ByzsAiProperties.HunYuanProperties properties = new ByzsAiProperties.HunYuanProperties()
                 .setBaseUrl(url).setApiKey(apiKey);
         return new AiAutoConfiguration().buildHunYuanChatClient(properties);
     }
 
     /**
-     * 可参考 {@link AiAutoConfiguration#siliconFlowChatClient(YudaoAiProperties)}
+     * 可参考 {@link AiAutoConfiguration#siliconFlowChatClient(ByzsAiProperties)}
      */
     private ChatModel buildSiliconFlowChatModel(String apiKey) {
-        YudaoAiProperties.SiliconFlowProperties properties = new YudaoAiProperties.SiliconFlowProperties()
+        ByzsAiProperties.SiliconFlowProperties properties = new ByzsAiProperties.SiliconFlowProperties()
                 .setApiKey(apiKey);
         return new AiAutoConfiguration().buildSiliconFlowChatClient(properties);
     }
@@ -447,21 +447,21 @@ public class AiModelFactoryImpl implements AiModelFactory {
     }
 
     /**
-     * 可参考 {@link AiAutoConfiguration#xingHuoChatClient(YudaoAiProperties)}
+     * 可参考 {@link AiAutoConfiguration#xingHuoChatClient(ByzsAiProperties)}
      */
     private static XingHuoChatModel buildXingHuoChatModel(String key) {
         List<String> keys = StrUtil.split(key, '|');
         Assert.equals(keys.size(), 2, "XingHuoChatClient 的密钥需要 (appKey|secretKey) 格式");
-        YudaoAiProperties.XingHuoProperties properties = new YudaoAiProperties.XingHuoProperties()
+        ByzsAiProperties.XingHuoProperties properties = new ByzsAiProperties.XingHuoProperties()
                 .setAppKey(keys.get(0)).setSecretKey(keys.get(1));
         return new AiAutoConfiguration().buildXingHuoChatClient(properties);
     }
 
     /**
-     * 可参考 {@link AiAutoConfiguration#baiChuanChatClient(YudaoAiProperties)}
+     * 可参考 {@link AiAutoConfiguration#baiChuanChatClient(ByzsAiProperties)}
      */
     private BaiChuanChatModel buildBaiChuanChatModel(String apiKey) {
-        YudaoAiProperties.BaiChuanProperties properties = new YudaoAiProperties.BaiChuanProperties()
+        ByzsAiProperties.BaiChuanProperties properties = new ByzsAiProperties.BaiChuanProperties()
                 .setApiKey(apiKey);
         return new AiAutoConfiguration().buildBaiChuanChatClient(properties);
     }

+ 30 - 30
byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/service/chat/AiChatMessageServiceImpl.java

@@ -31,7 +31,7 @@ import cn.iocoder.byzs.module.ai.service.model.AiModelService;
 import cn.iocoder.byzs.module.ai.service.model.AiToolService;
 import cn.iocoder.byzs.module.ai.service.tts.DouBaoTtsService;
 import cn.iocoder.byzs.module.ai.util.AiUtils;
-import cn.iocoder.byzs.module.ai.util.tts.StreamTtsService;
+import cn.iocoder.byzs.module.ai.util.tts.StreamingAliyunTtsService;
 import cn.iocoder.byzs.module.ai.util.tts.StreamingDouBaoTtsService;
 import cn.iocoder.byzs.module.ai.util.tts.WavHeader;
 import com.alibaba.nls.client.protocol.SampleRateEnum;
@@ -108,10 +108,10 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
     @Resource
     private AiToolService toolService;
 //    @Resource
-//    private StreamTtsService streamTtsService;  // 注入TTS服务
+//    private StreamingAliyunTtsService streamTtsService;  // 注入TTS服务
     // 在AiChatMessageServiceImpl中注入
     @Resource
-    private ObjectProvider<StreamTtsService> streamTtsServiceProvider;  // 使用ObjectProvider获取原型bean
+    private ObjectProvider<StreamingAliyunTtsService> streamTtsServiceProvider;  // 使用ObjectProvider获取原型bean
 
     @Resource
     private DouBaoTtsService douBaoTtsService;
@@ -231,7 +231,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
 
         // 4.3 初始化TTS服务 - 创建新的实例而非使用共享实例
         // 4.3 初始化TTS服务 - 统一处理阿里云和豆包TTS
-        StreamTtsService streamTtsService;
+        StreamingAliyunTtsService streamingAliyunTtsService;
         ScheduledExecutorService scheduler;
         AtomicReference<ScheduledFuture<?>> ttsTask;
         StringBuffer contentTTSBuffer;
@@ -248,7 +248,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
             ttsTask = new AtomicReference<>();
             
             if (isDouBaoTts) {
-                streamTtsService = null;
+                streamingAliyunTtsService = null;
                 // 豆包TTS服务 - 同步合成,模拟流式处理
                 log.info("使用豆包TTS服务");
                 // 为豆包TTS创建音频流,用于发送同步合成的音频数据
@@ -264,12 +264,12 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
             } else {
                 // 阿里云TTS服务 - 流式合成
                 log.info("使用阿里云TTS服务");
-                streamTtsService = streamTtsServiceProvider.getObject();
+                streamingAliyunTtsService = streamTtsServiceProvider.getObject();
 
                 // 先创建音频流并设置回调,再启动TTS服务
                 audioStream = Flux.create(sink2 -> {
                     AtomicBoolean isFirstChunk = new AtomicBoolean(true); // 首包标志位
-                    streamTtsService.setAudioDataCallback(audioBytes -> {
+                    streamingAliyunTtsService.setAudioDataCallback(audioBytes -> {
                         try {
                             byte[] processedAudio;
                             if (isFirstChunk.getAndSet(false)) {
@@ -290,14 +290,14 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
                             sink2.error(new RuntimeException("TTS音频处理失败: " + e.getMessage(), e));
                         }
                     });
-                    streamTtsService.setOnCompleteCallback(sink2::complete);
+                    streamingAliyunTtsService.setOnCompleteCallback(sink2::complete);
                 });
 
                 // 启动阿里云TTS服务
-                streamTtsService.startTts(finalAiTtsDO);
+                streamingAliyunTtsService.startTts(finalAiTtsDO);
             }
         } else {
-            streamTtsService = null;
+            streamingAliyunTtsService = null;
             sentencePattern = null;
             scheduler = null;
             contentTTSBuffer = null;
@@ -344,13 +344,13 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
                         if (isDouBaoTts) {
                             processCompleteSentence(finalAiTtsDO, contentTTSBuffer, matcher);
                         } else {
-                            processCompleteSentence(streamTtsService, contentTTSBuffer, matcher);
+                            processCompleteSentence(streamingAliyunTtsService, contentTTSBuffer, matcher);
                         }
                     } else if (contentTTSBuffer.length() > 50) { // 最长50字未结束也处理
                         if (isDouBaoTts) {
                             processCompleteSentence(finalAiTtsDO, contentTTSBuffer, contentTTSBuffer.length());
                         } else {
-                            processCompleteSentence(streamTtsService, contentTTSBuffer, contentTTSBuffer.length());
+                            processCompleteSentence(streamingAliyunTtsService, contentTTSBuffer, contentTTSBuffer.length());
                         }
                     }
                 }, 500, TimeUnit.MILLISECONDS));
@@ -371,7 +371,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
                 if (isDouBaoTts) {
                     processRemainingText(finalAiTtsDO, contentTTSBuffer); // 处理豆包TTS剩余文本
                 } else {
-                    processRemainingText(streamTtsService, contentTTSBuffer); // 处理阿里云TTS剩余文本
+                    processRemainingText(streamingAliyunTtsService, contentTTSBuffer); // 处理阿里云TTS剩余文本
                 }
 
                 if (ttsTask.get() != null) {
@@ -382,7 +382,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
                 // 通知TTS服务文本发送完成
                 try {
                     if (!isDouBaoTts) {
-                        streamTtsService.stopTts();
+                        streamingAliyunTtsService.stopTts();
                     }
                     // 豆包TTS服务在任务完成时统一停止
                 } catch (Exception e) {
@@ -406,8 +406,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
                     if (isDouBaoTts) {
                         // 豆包TTS服务在任务完成时统一停止
                     } else {
-                        if (streamTtsService != null) {
-                            streamTtsService.stopTts();
+                        if (streamingAliyunTtsService != null) {
+                            streamingAliyunTtsService.stopTts();
                         }
                     }
                 } catch (Exception e) {
@@ -427,8 +427,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
                         // 重置豆包TTS的首次音频标记
                         isFirstDouBaoAudio.set(true);
                     } else {
-                        if (streamTtsService != null) {
-                            streamTtsService.stopTts();
+                        if (streamingAliyunTtsService != null) {
+                            streamingAliyunTtsService.stopTts();
                         }
                     }
                 } catch (Exception e) {
@@ -452,9 +452,9 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
                             // 豆包TTS音频流由任务计数器管理,在所有任务完成后自动关闭
                             // 重置豆包TTS的首次音频标记
                             isFirstDouBaoAudio.set(true);
-                        } else if (streamTtsService != null) {
-                            streamTtsService.setAudioDataCallback(null);
-                            streamTtsService.setOnCompleteCallback(null);
+                        } else if (streamingAliyunTtsService != null) {
+                            streamingAliyunTtsService.setAudioDataCallback(null);
+                            streamingAliyunTtsService.setOnCompleteCallback(null);
                         }
                     }
                     // 确保调度器被关闭
@@ -465,14 +465,14 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
     }
 
     // 处理完整句子 - 阿里云TTS
-    private void processCompleteSentence(StreamTtsService streamTtsService, StringBuffer buffer, Matcher matcher) {
-        if (streamTtsService == null || buffer == null || matcher == null) {
+    private void processCompleteSentence(StreamingAliyunTtsService streamingAliyunTtsService, StringBuffer buffer, Matcher matcher) {
+        if (streamingAliyunTtsService == null || buffer == null || matcher == null) {
             return;
         }
 
         String sentence = buffer.substring(0, matcher.end());
         log.debug("[处理完整句子][buffer: {}", sentence);
-        streamTtsService.sendText(sentence);
+        streamingAliyunTtsService.sendText(sentence);
         buffer.delete(0, matcher.end());
         log.info("TTS合成完整句: {}", sentence);
     }
@@ -491,14 +491,14 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
     }
 
     // 处理指定长度文本 - 阿里云TTS
-    private void processCompleteSentence(StreamTtsService streamTtsService, StringBuffer buffer, int length) {
-        if (streamTtsService == null || buffer == null || length <= 0) {
+    private void processCompleteSentence(StreamingAliyunTtsService streamingAliyunTtsService, StringBuffer buffer, int length) {
+        if (streamingAliyunTtsService == null || buffer == null || length <= 0) {
             return;
         }
 
         String sentence = buffer.substring(0, length);
         log.debug("[处理指定长度文本][buffer: {}", sentence);
-        streamTtsService.sendText(sentence);
+        streamingAliyunTtsService.sendText(sentence);
         buffer.delete(0, length);
         log.info("TTS合成长文本: {}", sentence);
     }
@@ -517,13 +517,13 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
     }
 
     // 处理剩余文本
-    private void processRemainingText(StreamTtsService streamTtsService, StringBuffer buffer) {
-        if (streamTtsService == null || buffer == null || buffer.isEmpty()) {
+    private void processRemainingText(StreamingAliyunTtsService streamingAliyunTtsService, StringBuffer buffer) {
+        if (streamingAliyunTtsService == null || buffer == null || buffer.isEmpty()) {
             return;
         }
 
         log.info("TTS合成剩余文本: {}", buffer);
-        streamTtsService.sendText(buffer.toString());
+        streamingAliyunTtsService.sendText(buffer.toString());
         buffer.setLength(0);
     }
 

+ 9 - 9
byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/service/tts/AliyunTtsService.java

@@ -1,7 +1,7 @@
 package cn.iocoder.byzs.module.ai.service.tts;
 
 import cn.iocoder.byzs.module.ai.dal.dataobject.tts.AiTtsDO;
-import cn.iocoder.byzs.module.ai.util.tts.StreamTtsService;
+import cn.iocoder.byzs.module.ai.util.tts.StreamingAliyunTtsService;
 import com.alibaba.nls.client.protocol.OutputFormatEnum;
 import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.stereotype.Service;
@@ -13,15 +13,15 @@ import java.io.ByteArrayOutputStream;
 public class AliyunTtsService {
 
     @Resource
-    private ObjectProvider<StreamTtsService> streamTtsServiceProvider;
+    private ObjectProvider<StreamingAliyunTtsService> streamTtsServiceProvider;
 
     public byte[] convertTextToSpeech(AiTtsDO aiTtsDO, String content) {
-        StreamTtsService streamTtsService = streamTtsServiceProvider.getObject();
+        StreamingAliyunTtsService streamingAliyunTtsService = streamTtsServiceProvider.getObject();
         try {
             // 创建音频数据缓冲区
             ByteArrayOutputStream audioOutputStream = new ByteArrayOutputStream();
             // 设置音频数据回调
-            streamTtsService.setAudioDataCallback(audioData -> {
+            streamingAliyunTtsService.setAudioDataCallback(audioData -> {
                 try {
                     audioOutputStream.write(audioData);
                 } catch (Exception e) {
@@ -30,19 +30,19 @@ public class AliyunTtsService {
             });
 
             // 开始TTS语音合成
-            streamTtsService.startTts(aiTtsDO, OutputFormatEnum.MP3);
+            streamingAliyunTtsService.startTts(aiTtsDO, OutputFormatEnum.MP3);
             // 发送文本
-            streamTtsService.sendText(content);
+            streamingAliyunTtsService.sendText(content);
             // 停止TTS
-            streamTtsService.stopTts();
+            streamingAliyunTtsService.stopTts();
 
             return audioOutputStream.toByteArray();
         } catch (Exception e) {
             throw new RuntimeException("文本转语音失败", e);
         } finally {
             // 确保资源被释放
-            if (streamTtsService != null) {
-                streamTtsService.stopTts();
+            if (streamingAliyunTtsService != null) {
+                streamingAliyunTtsService.stopTts();
             }
         }
     }

+ 4 - 4
byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/service/tts/DouBaoTtsService.java

@@ -1,7 +1,7 @@
 package cn.iocoder.byzs.module.ai.service.tts;
 
 import cn.iocoder.byzs.module.ai.dal.dataobject.tts.AiTtsDO;
-import cn.iocoder.byzs.module.ai.framework.ai.config.YudaoAiProperties;
+import cn.iocoder.byzs.module.ai.framework.ai.config.ByzsAiProperties;
 import com.alibaba.nls.client.protocol.OutputFormatEnum;
 import com.alibaba.nls.client.protocol.SampleRateEnum;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -25,7 +25,7 @@ public class DouBaoTtsService {
     private static final Logger logger = LoggerFactory.getLogger(DouBaoTtsService.class);
 
     @Resource
-    private YudaoAiProperties yudaoAiProperties;
+    private ByzsAiProperties byzsAiProperties;
 
     /**
      * 语音合成,返回完整音频数据
@@ -147,12 +147,12 @@ public class DouBaoTtsService {
      */
     private Request buildTtsRequest(AiTtsDO aiTtsDO, String content, String command, OutputFormatEnum format) throws IOException {
         // 获取配置
-        YudaoAiProperties.DouBaoProperties doubaoProperties = yudaoAiProperties.getDoubao();
+        ByzsAiProperties.DouBaoProperties doubaoProperties = byzsAiProperties.getDoubao();
         if (doubaoProperties == null) {
             throw new IllegalArgumentException("豆包配置未设置");
         }
         
-        YudaoAiProperties.DouBaoProperties.TtsProperties doubaoTtsProperties = doubaoProperties.getTts();
+        ByzsAiProperties.DouBaoProperties.TtsProperties doubaoTtsProperties = doubaoProperties.getTts();
         if (doubaoTtsProperties == null) {
             throw new IllegalArgumentException("豆包TTS配置未设置");
         }

+ 23 - 11
byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/util/tts/StreamTtsService.java → byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/util/tts/StreamingAliyunTtsService.java

@@ -1,6 +1,7 @@
 package cn.iocoder.byzs.module.ai.util.tts;
 
 import cn.iocoder.byzs.module.ai.dal.dataobject.tts.AiTtsDO;
+import cn.iocoder.byzs.module.ai.framework.ai.config.ByzsAiProperties;
 import com.alibaba.nls.client.AccessToken;
 import com.alibaba.nls.client.protocol.NlsClient;
 import com.alibaba.nls.client.protocol.OutputFormatEnum;
@@ -11,10 +12,10 @@ import com.alibaba.nls.client.protocol.tts.StreamInputTtsResponse;
 import jakarta.annotation.PostConstruct;
 import jakarta.annotation.PreDestroy;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Service;
 
+import javax.annotation.Resource;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.function.Consumer;
@@ -22,21 +23,15 @@ import java.util.function.Consumer;
 @Service
 @Slf4j
 @Scope("prototype")  // 原型作用域,每次注入都会创建新实例
-public class StreamTtsService {
+public class StreamingAliyunTtsService {
 
-    @Value("${ai.tts.appKey:4SUOF4LfaU7FekyW}")
-    private String appKey;
+    @Resource
+    private ByzsAiProperties byzsAiProperties;
 
-    @Value("${ai.tts.token:20aa8ae2577b414db6c3cbbfcdc287be}")
+    private String appKey;
     private String token;
-
-    @Value("${ai.tts.url:wss://nls-gateway-cn-beijing.aliyuncs.com/ws/v1}")
     private String url;
-
-
-    @Value("${ai.tts.ALIYUN_AK_ID:LTAI5tQhMPLXtSgXiPiWbw6D}")
     private String ALIYUN_AK_ID;
-    @Value("${ai.tts.ALIYUN_AK_SECRET:HCXpFYjl4swk0qwfIKa9s2bXx0AWcG}")
     private String ALIYUN_AK_SECRET;
 
     private NlsClient client;
@@ -49,6 +44,23 @@ public class StreamTtsService {
 
     @PostConstruct
     public void init() {
+        // 从配置中加载阿里云TTS配置
+        ByzsAiProperties.AliyunProperties aliyunProperties = byzsAiProperties.getAliyun();
+        if (aliyunProperties == null) {
+            throw new IllegalArgumentException("阿里云配置未设置");
+        }
+        
+        ByzsAiProperties.AliyunProperties.TtsProperties aliyunTtsProperties = aliyunProperties.getTts();
+        if (aliyunTtsProperties == null) {
+            throw new IllegalArgumentException("阿里云TTS配置未设置");
+        }
+
+        this.appKey = aliyunTtsProperties.getAppKey() != null ? aliyunTtsProperties.getAppKey() : "4SUOF4LfaU7FekyW";
+        this.token = aliyunTtsProperties.getToken() != null ? aliyunTtsProperties.getToken() : "20aa8ae2577b414db6c3cbbfcdc287be";
+        this.url = aliyunTtsProperties.getUrl() != null ? aliyunTtsProperties.getUrl() : "wss://nls-gateway-cn-beijing.aliyuncs.com/ws/v1";
+        this.ALIYUN_AK_ID = aliyunTtsProperties.getAliyunAkId() != null ? aliyunTtsProperties.getAliyunAkId() : "LTAI5tQhMPLXtSgXiPiWbw6D";
+        this.ALIYUN_AK_SECRET = aliyunTtsProperties.getAliyunAkSecret() != null ? aliyunTtsProperties.getAliyunAkSecret() : "HCXpFYjl4swk0qwfIKa9s2bXx0AWcG";
+
         getToken();
         // 初始化NlsClient
         if (client == null) {

+ 40 - 40
byzs-web/src/main/java/cn/iocoder/byzs/module/web/service/ai/WebAiServiceImpl.java

@@ -13,7 +13,7 @@ import cn.iocoder.byzs.module.ai.dal.mysql.tts.AiTtsMapper;
 import cn.iocoder.byzs.module.ai.enums.ErrorCodeConstants;
 import cn.iocoder.byzs.module.ai.service.chat.AiChatConversationService;
 import cn.iocoder.byzs.module.ai.service.model.AiChatRoleService;
-import cn.iocoder.byzs.module.ai.util.tts.StreamTtsService;
+import cn.iocoder.byzs.module.ai.util.tts.StreamingAliyunTtsService;
 import cn.iocoder.byzs.module.ai.util.tts.WavHeader;
 import com.alibaba.nls.client.protocol.SampleRateEnum;
 import jakarta.annotation.Resource;
@@ -60,7 +60,7 @@ public class WebAiServiceImpl {
     private AiTtsMapper ttsMapper;
 
     @Resource
-    private ObjectProvider<StreamTtsService> streamTtsServiceProvider;
+    private ObjectProvider<StreamingAliyunTtsService> streamTtsServiceProvider;
 
     /**
      * 发送指定回答的SSE流式响应
@@ -82,16 +82,16 @@ public class WebAiServiceImpl {
         log.info("开始处理文本内容: {}", contentAnswer);
 
         // 4. 创建TTS服务实例
-        StreamTtsService streamTtsService = streamTtsServiceProvider.getObject();
+        StreamingAliyunTtsService streamingAliyunTtsService = streamTtsServiceProvider.getObject();
 
         try {
             // 5. 初始化TTS服务
-            streamTtsService.startTts(aiTtsDO);
-            return createSseFlux(sendReqVO, userId, conversation, contentAnswer, streamTtsService);
+            streamingAliyunTtsService.startTts(aiTtsDO);
+            return createSseFlux(sendReqVO, userId, conversation, contentAnswer, streamingAliyunTtsService);
         } catch (Exception e) {
             log.error("发送指定回答失败", e);
             AtomicBoolean tempTtsStopped = new AtomicBoolean(false);
-            cleanupTtsResources(streamTtsService, tempTtsStopped);
+            cleanupTtsResources(streamingAliyunTtsService, tempTtsStopped);
             // 即使发生异常,也要返回文本数据,确保前端至少能收到文本
             return Flux.just(createFallbackTextResponse(sendReqVO, userId, conversation, contentAnswer));
         }
@@ -117,7 +117,7 @@ public class WebAiServiceImpl {
      */
     private Flux<CommonResult<AiChatMessageSendRespVO>> createSseFlux(
             AiChatMessageSendReqVO sendReqVO, Long userId, AiChatConversationDO conversation,
-            String contentAnswer, StreamTtsService streamTtsService) {
+            String contentAnswer, StreamingAliyunTtsService streamingAliyunTtsService) {
         return Flux.<CommonResult<AiChatMessageSendRespVO>>create(sink -> {
                     // 初始化句子处理相关组件
                     ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
@@ -135,18 +135,18 @@ public class WebAiServiceImpl {
                         sendTextData(sink, sendReqVO, userId, conversation, contentAnswer);
 
                         // 创建音频流并订阅
-                        createAndSubscribeToAudioStream(sink, streamTtsService, scheduler, ttsTask, ttsStopped);
+                        createAndSubscribeToAudioStream(sink, streamingAliyunTtsService, scheduler, ttsTask, ttsStopped);
 
                         // 开始处理文本分段并发送到TTS
                         Pattern sentencePattern = Pattern.compile("[。!?;\n\r]");
-                        processTextSegments(streamTtsService, contentTTSBuffer, sentencePattern,
+                        processTextSegments(streamingAliyunTtsService, contentTTSBuffer, sentencePattern,
                                 scheduler, ttsTask, ttsStopped, allTextProcessed, sink);
 
                         // 添加超时检测(60秒)
                         ScheduledFuture<?> timeoutTask = scheduler.schedule(() -> {
                             if (!ttsStopped.get()) {
                                 log.warn("TTS处理超时,强制终止SSE流");
-                                cleanupResources(streamTtsService, scheduler, ttsTask, ttsStopped, sink);
+                                cleanupResources(streamingAliyunTtsService, scheduler, ttsTask, ttsStopped, sink);
                             }
                         }, 60, TimeUnit.SECONDS);
 
@@ -156,12 +156,12 @@ public class WebAiServiceImpl {
                             if (timeoutTask != null) {
                                 timeoutTask.cancel(false);
                             }
-                            cleanupResources(streamTtsService, scheduler, ttsTask, ttsStopped, sink);
+                            cleanupResources(streamingAliyunTtsService, scheduler, ttsTask, ttsStopped, sink);
                         });
 
                     } catch (Exception e) {
                         log.error("创建SSE流异常", e);
-                        cleanupResources(streamTtsService, scheduler, ttsTask, ttsStopped, sink);
+                        cleanupResources(streamingAliyunTtsService, scheduler, ttsTask, ttsStopped, sink);
                         sink.error(e);
                     }
                 }).subscribeOn(Schedulers.boundedElastic())
@@ -201,7 +201,7 @@ public class WebAiServiceImpl {
      * 创建并订阅音频流
      */
     private void createAndSubscribeToAudioStream(FluxSink<CommonResult<AiChatMessageSendRespVO>> mainSink,
-                                                 StreamTtsService streamTtsService,
+                                                 StreamingAliyunTtsService streamingAliyunTtsService,
                                                  ScheduledExecutorService scheduler,
                                                  AtomicReference<ScheduledFuture<?>> ttsTask,
                                                  AtomicBoolean ttsStopped) {
@@ -209,7 +209,7 @@ public class WebAiServiceImpl {
             AtomicBoolean isFirstChunk = new AtomicBoolean(true); // 首包标志位
 
             // 设置音频数据回调
-            streamTtsService.setAudioDataCallback(audioBytes -> {
+            streamingAliyunTtsService.setAudioDataCallback(audioBytes -> {
                 try {
                     byte[] processedAudio = processAudioData(audioBytes, isFirstChunk);
                     String base64Audio = java.util.Base64.getEncoder().encodeToString(processedAudio);
@@ -225,14 +225,14 @@ public class WebAiServiceImpl {
             });
 
             // 设置完成回调
-            streamTtsService.setOnCompleteCallback(() -> {
+            streamingAliyunTtsService.setOnCompleteCallback(() -> {
                 log.info("TTS转换完成,准备终止SSE流");
 
                 // 确保只停止一次TTS服务
                 if (ttsStopped.compareAndSet(false, true)) {
                     try {
                         log.info("TTS完成回调中调用stopTts()");
-                        streamTtsService.stopTts();
+                        streamingAliyunTtsService.stopTts();
                         log.info("TTS完成回调中stopTts()调用完成");
                     } catch (Exception e) {
                         log.error("停止TTS服务异常", e);
@@ -280,7 +280,7 @@ public class WebAiServiceImpl {
                     } catch (Exception e) {
                         log.error("主SSE流设置错误异常", e);
                     }
-                    cleanupResources(streamTtsService, scheduler, ttsTask, ttsStopped, mainSink);
+                    cleanupResources(streamingAliyunTtsService, scheduler, ttsTask, ttsStopped, mainSink);
                 },
                 () -> {
                     log.info("音频流处理完成,准备终止主SSE流");
@@ -316,41 +316,41 @@ public class WebAiServiceImpl {
     /**
      * 处理文本分段
      */
-    private void processTextSegments(StreamTtsService streamTtsService, StringBuilder buffer,
+    private void processTextSegments(StreamingAliyunTtsService streamingAliyunTtsService, StringBuilder buffer,
                                      Pattern sentencePattern, ScheduledExecutorService scheduler,
                                      AtomicReference<ScheduledFuture<?>> ttsTask, AtomicBoolean ttsStopped,
                                      AtomicBoolean allTextProcessed, FluxSink<CommonResult<AiChatMessageSendRespVO>> sink) {
         if (buffer.isEmpty()) {
             log.info("文本为空,无需处理");
-            handleTextComplete(streamTtsService, scheduler, ttsStopped, allTextProcessed, sink);
+            handleTextComplete(streamingAliyunTtsService, scheduler, ttsStopped, allTextProcessed, sink);
             return;
         }
 
         // 立即处理文本
         Matcher matcher = sentencePattern.matcher(buffer);
         if (matcher.find()) {
-            processCompleteSentence(streamTtsService, buffer, matcher);
+            processCompleteSentence(streamingAliyunTtsService, buffer, matcher);
             // 继续调度处理剩余文本
-            scheduleNextProcessing(streamTtsService, buffer, sentencePattern, scheduler,
+            scheduleNextProcessing(streamingAliyunTtsService, buffer, sentencePattern, scheduler,
                     ttsTask, ttsStopped, allTextProcessed, sink);
         } else if (buffer.length() > 50) { // 最长50字未结束也处理
-            processCompleteSentence(streamTtsService, buffer, buffer.length());
+            processCompleteSentence(streamingAliyunTtsService, buffer, buffer.length());
             // 继续调度处理剩余文本
-            scheduleNextProcessing(streamTtsService, buffer, sentencePattern, scheduler,
+            scheduleNextProcessing(streamingAliyunTtsService, buffer, sentencePattern, scheduler,
                     ttsTask, ttsStopped, allTextProcessed, sink);
         } else {
             // 文本较短且未结束,直接处理全部
             log.info("TTS合成短文本: {}", buffer.toString());
-            streamTtsService.sendText(buffer.toString());
+            streamingAliyunTtsService.sendText(buffer.toString());
             buffer.setLength(0);
-            handleTextComplete(streamTtsService, scheduler, ttsStopped, allTextProcessed, sink);
+            handleTextComplete(streamingAliyunTtsService, scheduler, ttsStopped, allTextProcessed, sink);
         }
     }
 
     /**
      * 调度下一次文本处理
      */
-    private void scheduleNextProcessing(StreamTtsService streamTtsService, StringBuilder buffer,
+    private void scheduleNextProcessing(StreamingAliyunTtsService streamingAliyunTtsService, StringBuilder buffer,
                                         Pattern sentencePattern, ScheduledExecutorService scheduler,
                                         AtomicReference<ScheduledFuture<?>> ttsTask, AtomicBoolean ttsStopped,
                                         AtomicBoolean allTextProcessed, FluxSink<CommonResult<AiChatMessageSendRespVO>> sink) {
@@ -360,19 +360,19 @@ public class WebAiServiceImpl {
                 ttsTask.get().cancel(false); // 取消之前的延迟任务
             }
             ttsTask.set(scheduler.schedule(() -> {
-                processTextSegments(streamTtsService, buffer, sentencePattern, scheduler,
+                processTextSegments(streamingAliyunTtsService, buffer, sentencePattern, scheduler,
                         ttsTask, ttsStopped, allTextProcessed, sink);
             }, 200, TimeUnit.MILLISECONDS));
         } else {
             // 所有文本处理完毕
-            handleTextComplete(streamTtsService, scheduler, ttsStopped, allTextProcessed, sink);
+            handleTextComplete(streamingAliyunTtsService, scheduler, ttsStopped, allTextProcessed, sink);
         }
     }
 
     /**
      * 处理文本完成后的逻辑
      */
-    private void handleTextComplete(StreamTtsService streamTtsService, ScheduledExecutorService scheduler,
+    private void handleTextComplete(StreamingAliyunTtsService streamingAliyunTtsService, ScheduledExecutorService scheduler,
                                     AtomicBoolean ttsStopped, AtomicBoolean allTextProcessed,
                                     FluxSink<CommonResult<AiChatMessageSendRespVO>> sink) {
         allTextProcessed.set(true);
@@ -383,7 +383,7 @@ public class WebAiServiceImpl {
             // 设置标志位
             ttsStopped.set(true);
             // 调用stopTts()明确通知TTS服务文本已全部发送
-            streamTtsService.stopTts();
+            streamingAliyunTtsService.stopTts();
             log.info("已通知TTS服务文本发送完毕,等待TTS合成完成回调");
         } catch (Exception e) {
             log.error("通知TTS服务文本发送完毕异常: {}", e.getMessage());
@@ -398,7 +398,7 @@ public class WebAiServiceImpl {
                     ttsStopped.set(true);
                     // 尝试停止TTS服务
                     try {
-                        streamTtsService.stopTts();
+                        streamingAliyunTtsService.stopTts();
                         log.info("超时后主动停止TTS服务完成");
                     } catch (Exception e) {
                         log.error("停止TTS服务异常: {}", e.getMessage());
@@ -421,9 +421,9 @@ public class WebAiServiceImpl {
     /**
      * 处理完整句子
      */
-    private void processCompleteSentence(StreamTtsService streamTtsService, StringBuilder buffer, Matcher matcher) {
+    private void processCompleteSentence(StreamingAliyunTtsService streamingAliyunTtsService, StringBuilder buffer, Matcher matcher) {
         String sentence = buffer.substring(0, matcher.end());
-        streamTtsService.sendText(sentence);
+        streamingAliyunTtsService.sendText(sentence);
         buffer.delete(0, matcher.end());
         log.info("TTS合成完整句: {}", sentence);
     }
@@ -431,9 +431,9 @@ public class WebAiServiceImpl {
     /**
      * 处理指定长度文本
      */
-    private void processCompleteSentence(StreamTtsService streamTtsService, StringBuilder buffer, int length) {
+    private void processCompleteSentence(StreamingAliyunTtsService streamingAliyunTtsService, StringBuilder buffer, int length) {
         String sentence = buffer.substring(0, length);
-        streamTtsService.sendText(sentence);
+        streamingAliyunTtsService.sendText(sentence);
         buffer.delete(0, length);
         log.info("TTS合成长文本: {}", sentence);
     }
@@ -441,7 +441,7 @@ public class WebAiServiceImpl {
     /**
      * 清理所有资源
      */
-    private void cleanupResources(StreamTtsService streamTtsService,
+    private void cleanupResources(StreamingAliyunTtsService streamingAliyunTtsService,
                                   ScheduledExecutorService scheduler,
                                   AtomicReference<ScheduledFuture<?>> ttsTask,
                                   AtomicBoolean ttsStopped,
@@ -452,7 +452,7 @@ public class WebAiServiceImpl {
         if (scheduler != null && !scheduler.isShutdown()) {
             scheduler.shutdownNow();
         }
-        cleanupTtsResources(streamTtsService, ttsStopped);
+        cleanupTtsResources(streamingAliyunTtsService, ttsStopped);
         ttsStopped.set(true);
 
         // 确保SSE流终止
@@ -470,16 +470,16 @@ public class WebAiServiceImpl {
     /**
      * 清理TTS服务资源
      */
-    private void cleanupTtsResources(StreamTtsService streamTtsService, AtomicBoolean ttsStopped) {
+    private void cleanupTtsResources(StreamingAliyunTtsService streamingAliyunTtsService, AtomicBoolean ttsStopped) {
         try {
-            if (streamTtsService != null) {
+            if (streamingAliyunTtsService != null) {
                 log.info("开始清理TTS服务资源");
 
                 // 检查ttsStopped标志位,避免重复停止TTS服务
                 if (!ttsStopped.get()) {
                     try {
                         log.info("调用streamTtsService.stopTts()");
-                        streamTtsService.stopTts();
+                        streamingAliyunTtsService.stopTts();
                         log.info("streamTtsService.stopTts()调用完成");
                     } catch (Exception e) {
                         log.error("停止TTS服务异常", e);