fix: 讯飞鉴权签名与官方demo对齐+结束帧+文字修改

- ReportEditor: getXfAuthUrl signatureOrigin去掉引号,Base64改用CryptoJS.enc.Base64.stringify与demo完全一致
- ReportEditor: 第一帧增加dwa: wpgs动态修正参数
- ReportEditor: 停止录音时先发送status:2结束帧再关闭WebSocket
- SystemSettings: 标题改为讯飞语音听写Websocket接口配置
This commit is contained in:
2026-04-20 01:43:58 +08:00
parent 4f27edcc92
commit b07bcfaad2
2 changed files with 12 additions and 6 deletions

View File

@@ -897,11 +897,11 @@ export default function ReportEditor() {
async function getXfAuthUrl(apiKey: string, apiSecret: string): Promise<string> {
const host = 'iat-api.xfyun.cn';
const date = new Date().toUTCString();
const signatureOrigin = `host: "${host}"\ndate: "${date}"\nGET /v2/iat HTTP/1.1`;
const signature = CryptoJS.HmacSHA256(signatureOrigin, apiSecret).toString(CryptoJS.enc.Base64);
const signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/iat HTTP/1.1`;
const signature = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(signatureOrigin, apiSecret));
const authorizationOrigin = `api_key="${apiKey}", algorithm="hmac-sha256", headers="host date request-line", signature="${signature}"`;
const authorization = btoa(authorizationOrigin);
return `wss://iat-api.xfyun.cn/v2/iat?authorization=${encodeURIComponent(authorization)}&date=${encodeURIComponent(date)}&host=${encodeURIComponent(host)}`;
const authorization = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(authorizationOrigin));
return `wss://iat-api.xfyun.cn/v2/iat?authorization=${authorization}&date=${date}&host=${host}`;
}
function floatTo16BitPCM(input: Float32Array): ArrayBuffer {
@@ -925,6 +925,12 @@ export default function ReportEditor() {
const toggleListening = async () => {
if (isListening) {
setIsListening(false);
if (xfWsRef.current && xfWsRef.current.readyState === WebSocket.OPEN) {
try {
const endFrame = { data: { status: 2, format: 'audio/L16;rate=16000', encoding: 'raw', audio: '' } };
xfWsRef.current.send(JSON.stringify(endFrame));
} catch {}
}
if (xfWsRef.current) { try { xfWsRef.current.close(); } catch {} xfWsRef.current = null; }
if (xfAudioContextRef.current) { try { xfAudioContextRef.current.close(); } catch {} xfAudioContextRef.current = null; }
if (xfMediaStreamRef.current) { xfMediaStreamRef.current.getTracks().forEach(t => t.stop()); xfMediaStreamRef.current = null; }
@@ -955,7 +961,7 @@ export default function ReportEditor() {
const pcmBuffer = floatTo16BitPCM(inputData);
const base64Audio = arrayBufferToBase64(pcmBuffer);
const frame: any = { data: { status: frameStatus, format: 'audio/L16;rate=16000', encoding: 'raw', audio: base64Audio } };
if (frameStatus === 0) { frame.common = { app_id: xfConfig.appId }; frame.business = { language: 'zh_cn', domain: 'iat', accent: 'mandarin' }; }
if (frameStatus === 0) { frame.common = { app_id: xfConfig.appId }; frame.business = { language: 'zh_cn', domain: 'iat', accent: 'mandarin', dwa: 'wpgs' }; }
ws.send(JSON.stringify(frame));
frameStatus = 1;
};

View File

@@ -429,7 +429,7 @@ export default function SystemSettings() {
<div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-bold text-text-main flex items-center gap-2">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="text-accent"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" x2="12" y1="19" y2="22"/></svg>
Websocket接口配
</h3>
</div>
<div className="space-y-6">