yzx 17 giờ trước cách đây
mục cha
commit
72bca59e3d

+ 105 - 1
ruoyi-admin/src/main/resources/templates/cc/txtts1conf/txtts1conf.html

@@ -11,6 +11,9 @@
 <body>
 <div class="main-content">
     <div class="h4 form-header" th:text="#{switchconf.tts.tx1.header}"></div>
+    <div class="alert alert-info">
+        腾讯云 TTS1 已切换为 WebSocket 协议。电话场景建议使用 `ulaw/alaw + 8000`;离线文件合成请优先使用当前页面可协商出的兼容格式。
+    </div>
     <div id="baseConfigs"></div>
 
     <div class="row"></div>
@@ -25,6 +28,87 @@
 <th:block th:include="include :: layout-latest-js" />
 <script>
     var prefix = ctx + "cc/fsconf";
+
+    function escapeHtml(value) {
+        return $('<div/>').text(value || '').html();
+    }
+
+    function renderInput(config) {
+        var name = config.name;
+        var value = config.value || '';
+        var safeValue = escapeHtml(value);
+
+        if (name === 'format') {
+            return '' +
+                '<select class="config-value form-control" id="' + name + '">' +
+                '<option value="ulaw"' + (value === 'ulaw' ? ' selected' : '') + '>ulaw</option>' +
+                '<option value="alaw"' + (value === 'alaw' ? ' selected' : '') + '>alaw</option>' +
+                '<option value="pcm"' + (value === 'pcm' ? ' selected' : '') + '>pcm</option>' +
+                '<option value="wav"' + (value === 'wav' ? ' selected' : '') + '>wav</option>' +
+                '<option value="mp3"' + (value === 'mp3' ? ' selected' : '') + '>mp3</option>' +
+                '<option value="flac"' + (value === 'flac' ? ' selected' : '') + '>flac</option>' +
+                '<option value="opus"' + (value === 'opus' ? ' selected' : '') + '>opus</option>' +
+                '</select>' +
+                '<span class="help-block m-b-none">FS 实时播报只推荐 `ulaw/alaw/pcm`,最终以握手包协商结果为准。</span>';
+        }
+
+        if (name === 'sample-rate') {
+            return '' +
+                '<select class="config-value form-control" id="' + name + '">' +
+                '<option value="8000"' + (value === '8000' ? ' selected' : '') + '>8000</option>' +
+                '<option value="16000"' + (value === '16000' ? ' selected' : '') + '>16000</option>' +
+                '<option value="22050"' + (value === '22050' ? ' selected' : '') + '>22050</option>' +
+                '<option value="24000"' + (value === '24000' ? ' selected' : '') + '>24000</option>' +
+                '<option value="32000"' + (value === '32000' ? ' selected' : '') + '>32000</option>' +
+                '<option value="44100"' + (value === '44100' ? ' selected' : '') + '>44100</option>' +
+                '<option value="48000"' + (value === '48000' ? ' selected' : '') + '>48000</option>' +
+                '</select>';
+        }
+
+        if (name === 'verify-peer') {
+            return '' +
+                '<select class="config-value form-control" id="' + name + '">' +
+                '<option value="false"' + (value === 'false' ? ' selected' : '') + '>false</option>' +
+                '<option value="true"' + (value === 'true' ? ' selected' : '') + '>true</option>' +
+                '</select>';
+        }
+
+        var inputType = (name === 'secret-key') ? 'password' : 'text';
+        return '<input class="config-value form-control" type="' + inputType + '" id="' + name + '" value="' + safeValue + '" />';
+    }
+
+    function validateConfigs(configs) {
+        var configMap = {};
+        $.each(configs, function(index, item) {
+            configMap[item.name] = item.value;
+        });
+
+        if (!configMap['appid']) {
+            $.modal.alertError('`appid` 不能为空');
+            return false;
+        }
+        if (!configMap['secret-id']) {
+            $.modal.alertError('`secret-id` 不能为空');
+            return false;
+        }
+        if (!configMap['secret-key']) {
+            $.modal.alertError('`secret-key` 不能为空');
+            return false;
+        }
+        if (!configMap['voice-id']) {
+            $.modal.alertError('`voice-id` 不能为空');
+            return false;
+        }
+        if (configMap['timeout-sec']) {
+            var timeoutSec = parseInt(configMap['timeout-sec'], 10);
+            if (isNaN(timeoutSec) || timeoutSec <= 0 || timeoutSec > 120) {
+                $.modal.alertError('`timeout-sec` 必须是 1 到 120 之间的整数');
+                return false;
+            }
+        }
+        return true;
+    }
+
     $(function() {
         $.ajax({
             url: prefix + '/getTxTts1Conf',
@@ -36,7 +120,7 @@
                 $.each(data, function(index, config) {
                     baseConfigsHtml += '<div className="row">';
                     baseConfigsHtml += '<div class="col-sm-12">';
-                    baseConfigsHtml += '<label class="col-sm-2 control-label">' + config.aliasName + '</label><div class="col-sm-10"><input class="config-value form-control" type="text" id="' + config.name + '" value="' + config.value + '" /></div>';
+                    baseConfigsHtml += '<label class="col-sm-2 control-label">' + config.aliasName + '</label><div class="col-sm-10">' + renderInput(config) + '</div>';
                     baseConfigsHtml += '</div>';
                     baseConfigsHtml += '</div>';
                 });
@@ -57,6 +141,26 @@
             };
             configs.push(config);
         });
+        $('select.config-value').each(function() {
+            var exists = false;
+            for (var i = 0; i < configs.length; i++) {
+                if (configs[i].name === $(this).attr('id')) {
+                    exists = true;
+                    break;
+                }
+            }
+            if (!exists) {
+                configs.push({
+                    name: $(this).attr('id'),
+                    value: ($(this).val() || '').trim()
+                });
+            }
+        });
+
+        if (!validateConfigs(configs)) {
+            return;
+        }
+
         $.ajax({
             url: prefix + '/setTxTts1Conf',
             type: 'POST',

+ 17 - 13
sql/v20260616_tx_tts1.sql

@@ -25,38 +25,42 @@ DELETE FROM `cc_params`
 WHERE `param_code` = 'tx-tts1-account-json';
 
 INSERT INTO `cc_params` (`param_name`, `param_code`, `param_value`, `param_type`, `hide_value`)
-VALUES ('腾讯云tts账号参数json', 'tx-tts1-account-json', '{"secret-id":"","secret-key":"","voice-id":"","endpoint":"mps.tencentcloudapi.com","version":"2019-06-12","action":"SyncDubbing","region":"","text-lang":"zh","resource-id":"","sample-rate":"8000","pitch":"0","connect-timeout-ms":"10000","verify-peer":"false"}', 'tts', 1);
+VALUES ('腾讯云tts账号参数json', 'tx-tts1-account-json', '{"appid":"","secret-id":"","secret-key":"","voice-id":"","websocket-host":"mps.cloud.tencent.com","text-lang":"zh","format":"ulaw","sample-rate":"8000","timeout-sec":"60","speed":"0","vol":"0","res-id":"","signature-expire-seconds":"3600","connect-timeout-ms":"10000","verify-peer":"false"}', 'tts', 1);
 
 DELETE FROM `fs_variables`
 WHERE `cat` = 5
   AND `var_field_name` IN (
+    'appid',
     'secret-id',
     'secret-key',
     'voice-id',
-    'endpoint',
-    'version',
-    'action',
-    'region',
+    'websocket-host',
     'text-lang',
-    'resource-id',
+    'format',
     'sample-rate',
-    'pitch',
+    'timeout-sec',
+    'speed',
+    'vol',
+    'res-id',
+    'signature-expire-seconds',
     'connect-timeout-ms',
     'verify-peer'
   );
 
 INSERT INTO `fs_variables` (`cat`, `var_field_name`, `var_field_alias`) VALUES
+(5, 'appid', 'AppId'),
 (5, 'secret-id', 'SecretId'),
 (5, 'secret-key', 'SecretKey'),
 (5, 'voice-id', '音色ID'),
-(5, 'endpoint', '接口域名'),
-(5, 'version', '接口版本'),
-(5, 'action', '接口动作'),
-(5, 'region', '地域'),
+(5, 'websocket-host', 'WebSocket域名'),
 (5, 'text-lang', '文本语言'),
-(5, 'resource-id', '资源ID'),
+(5, 'format', '音频格式'),
 (5, 'sample-rate', '采样率'),
-(5, 'pitch', '音调'),
+(5, 'timeout-sec', '会话超时(秒)'),
+(5, 'speed', '语速'),
+(5, 'vol', '音量'),
+(5, 'res-id', '资源标签'),
+(5, 'signature-expire-seconds', '签名有效期(秒)'),
 (5, 'connect-timeout-ms', '连接超时(毫秒)'),
 (5, 'verify-peer', '校验证书');