Преглед изворни кода

♻️ refactor(stt): 重构STT管理器授权和常量处理逻辑

- 提取常量到stt-constants.ts,包括SUB_BOT_UID、PUB_BOT_UID和EXPERIENCE_DURATION
- 创建token-utils.ts,封装token生成和授权逻辑,添加缓存机制
- 重构授权生成方法,从_genBasicAuthorization重命名为_genAuthorization并使用工具函数
- 优化bot token生成逻辑,支持动态添加到RTC配置

💄 style(scss): 优化多个组件的SCSS格式

- 移除caption-item组件中的空行
- 修复menu组件中borderLeft选择器的格式问题
- 清理dialogue-record相关组件中的多余空行和空格
- 修复record-content中flex属性的格式问题
yourname пре 2 месеци
родитељ
комит
04d49408ea

+ 30 - 39
packages/stt-sdk-core/src/managers/stt-manager-adapter.ts

@@ -8,6 +8,8 @@ import type {
   SttEventMap,
   ILanguageItem,
 } from '../types'
+import { SUB_BOT_UID, PUB_BOT_UID, EXPERIENCE_DURATION } from '../utils/stt-constants'
+import { generateBotTokens, generateAuthorization } from '../utils/token-utils'
 
 interface ApiSTTStartOptions {
   uid: string | number
@@ -50,7 +52,6 @@ export class SttManagerAdapter extends AGEventEmitter<SttEventMap> implements IS
   private _appId: string = ''
   private _certificate: string = ''
   private _gatewayAddress = 'https://api.agora.io'
-  private _baseUrl = 'https://service.agora.io/toolbox-overseas'
 
   constructor(rtmManager: any, appId: string, certificate: string) {
     super()
@@ -146,7 +147,7 @@ export class SttManagerAdapter extends AGEventEmitter<SttEventMap> implements IS
               taskId,
               token,
               startTime: Date.now(),
-              duration: 3600000, // 1小时
+              duration: EXPERIENCE_DURATION,
             }),
           ])
         }
@@ -375,7 +376,7 @@ export class SttManagerAdapter extends AGEventEmitter<SttEventMap> implements IS
       method: 'POST',
       headers: {
         'Content-Type': 'application/json',
-        Authorization: await this._genBasicAuthorization({ uid, channel }),
+        Authorization: await this._genAuthorization({ uid, channel }),
       },
       body: JSON.stringify(data),
     })
@@ -394,16 +395,36 @@ export class SttManagerAdapter extends AGEventEmitter<SttEventMap> implements IS
     const { channel, languages, token, uid } = options
     const url = `${this._gatewayAddress}/v1/projects/${this._appId}/rtsc/speech-to-text/tasks?builderToken=${token}`
 
+    // 为bot生成token
+    let subBotToken: string | null = null
+    let pubBotToken: string | null = null
+
+    if (this._certificate) {
+      const botTokens = await generateBotTokens({
+        appId: this._appId,
+        appCertificate: this._certificate,
+        channel,
+      })
+      subBotToken = botTokens.subBotToken
+      pubBotToken = botTokens.pubBotToken
+    }
+
     const body: any = {
       languages: languages.map((item) => item.source),
       maxIdleTime: 60,
       rtcConfig: {
         channelName: channel,
-        subBotUid: '1000',
-        pubBotUid: '2000',
+        subBotUid: SUB_BOT_UID,
+        pubBotUid: PUB_BOT_UID,
       },
     }
 
+    // 如果成功生成了bot token,添加到配置中
+    if (subBotToken && pubBotToken) {
+      body.rtcConfig.subBotToken = subBotToken
+      body.rtcConfig.pubBotToken = pubBotToken
+    }
+
     if (languages.find((item) => item.target?.length)) {
       body.translateConfig = {
         forceTranslateInterval: 2,
@@ -496,42 +517,12 @@ export class SttManagerAdapter extends AGEventEmitter<SttEventMap> implements IS
     uid: string | number
     channel: string
   }): Promise<string> {
-    // 使用基础的Agora token(与主应用保持一致)
-    return await this._genBasicAuthorization(config)
-  }
-
-  private async _genBasicAuthorization(config: {
-    uid: string | number
-    channel: string
-  }): Promise<string> {
-    // 使用基础的Agora token生成逻辑
-    const { uid, channel } = config
-    const url = `${this._baseUrl}/v2/token/generate`
-
-    const data = {
+    // 使用工具函数生成Authorization头(带缓存机制)
+    return await generateAuthorization({
       appId: this._appId,
       appCertificate: this._certificate,
-      channelName: channel,
-      expire: 7200,
-      src: 'web',
-      types: [1, 2],
-      uid: uid.toString(),
-    }
-
-    const response = await fetch(url, {
-      method: 'POST',
-      headers: {
-        'Content-Type': 'application/json',
-      },
-      body: JSON.stringify(data),
+      uid: config.uid,
+      channel: config.channel,
     })
-
-    if (response.ok) {
-      const result = await response.json()
-      const token = result?.data?.token || ''
-      return `agora token="${token}"`
-    }
-
-    return ''
   }
 }

+ 9 - 0
packages/stt-sdk-core/src/utils/stt-constants.ts

@@ -0,0 +1,9 @@
+// STT 相关常量定义
+export const SUB_BOT_UID = '1000'
+export const PUB_BOT_UID = '2000'
+
+export const EXPERIENCE_DURATION = 60 * 60 * 1000 // 1小时
+
+// 默认网关地址
+export const DEFAULT_GATEWAY_ADDRESS = 'https://api.agora.io'
+export const DEFAULT_BASE_URL = 'https://service.agora.io/toolbox-overseas'

+ 108 - 0
packages/stt-sdk-core/src/utils/token-utils.ts

@@ -0,0 +1,108 @@
+import { DEFAULT_BASE_URL } from './stt-constants'
+
+// Token缓存管理
+let agoraToken = ''
+let genTokenTime = 0
+
+/**
+ * 生成基础的Agora token
+ */
+export async function generateAgoraToken(config: {
+  appId: string
+  appCertificate: string
+  uid: string | number
+  channel: string
+}): Promise<string | null> {
+  const { appId, appCertificate, uid, channel } = config
+
+  if (!appCertificate) {
+    return null
+  }
+
+  const url = `${DEFAULT_BASE_URL}/v2/token/generate`
+  const data = {
+    appId,
+    appCertificate,
+    channelName: channel,
+    expire: 7200,
+    src: 'web',
+    types: [1, 2],
+    uid: uid.toString(),
+  }
+
+  const response = await fetch(url, {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    body: JSON.stringify(data),
+  })
+
+  if (response.ok) {
+    const result = await response.json()
+    return result?.data?.token || ''
+  }
+
+  return null
+}
+
+/**
+ * 生成Authorization头(带缓存机制)
+ */
+export async function generateAuthorization(config: {
+  appId: string
+  appCertificate: string
+  uid: string | number
+  channel: string
+}): Promise<string> {
+  // 检查缓存token是否有效(1小时有效期)
+  if (agoraToken) {
+    const curTime = Date.now()
+    if (curTime - genTokenTime < 1000 * 60 * 60) {
+      return `agora token="${agoraToken}"`
+    }
+  }
+
+  // 重新生成token
+  agoraToken = (await generateAgoraToken(config)) || ''
+  genTokenTime = Date.now()
+
+  return `agora token="${agoraToken}"`
+}
+
+/**
+ * 为bot生成token
+ */
+export async function generateBotTokens(config: {
+  appId: string
+  appCertificate: string
+  channel: string
+}): Promise<{ subBotToken: string | null; pubBotToken: string | null }> {
+  const { appId, appCertificate, channel } = config
+
+  if (!appCertificate) {
+    return { subBotToken: null, pubBotToken: null }
+  }
+
+  try {
+    const [subBotToken, pubBotToken] = await Promise.all([
+      generateAgoraToken({
+        appId,
+        appCertificate,
+        uid: '1000', // SUB_BOT_UID
+        channel,
+      }),
+      generateAgoraToken({
+        appId,
+        appCertificate,
+        uid: '2000', // PUB_BOT_UID
+        channel,
+      }),
+    ])
+
+    return { subBotToken, pubBotToken }
+  } catch (error) {
+    console.error('Failed to generate bot tokens:', error)
+    return { subBotToken: null, pubBotToken: null }
+  }
+}

+ 0 - 3
src/components/caption/caption-item/index.module.scss

@@ -30,7 +30,4 @@
     direction: rtl;
     text-align: left;
   }
-
 }
-
-

+ 1 - 3
src/components/menu/index.module.scss

@@ -8,7 +8,7 @@
   border-left: 1px solid #eaecf0;
   box-sizing: border-box;
 
-  .borderLeft{
+  .borderLeft {
     position: absolute;
     top: 0;
     left: 0;
@@ -18,6 +18,4 @@
     box-sizing: border-box;
     cursor: col-resize;
   }
-
-
 }

+ 0 - 2
src/components/menu/menu-content/dialogue-record/index.module.scss

@@ -9,7 +9,6 @@
   bottom: 0;
   overflow: hidden;
 
-
   .btnWrapper {
     position: absolute;
     right: 20px;
@@ -33,5 +32,4 @@
       cursor: pointer;
     }
   }
-
 }

+ 4 - 7
src/components/menu/menu-content/dialogue-record/record-content/index.module.scss

@@ -7,8 +7,7 @@
   overflow-y: auto;
   box-sizing: border-box;
 
-
-  .item+.item {
+  .item + .item {
     margin-top: 12px;
   }
 
@@ -20,11 +19,11 @@
     box-sizing: border-box;
 
     .left {
-      flex:  0 0 auto;
+      flex: 0 0 auto;
     }
 
     .right {
-      flex:  1 1 auto;
+      flex: 1 1 auto;
       margin-left: 12px;
       display: flex;
       flex-direction: column;
@@ -77,12 +76,10 @@
             font-weight: 600;
           }
 
-          .arabic{
+          .arabic {
             direction: rtl;
           }
-
         }
-
       }
     }
   }

+ 2 - 4
src/components/menu/menu-content/dialogue-record/record-header/index.module.scss

@@ -52,11 +52,10 @@
       }
     }
 
-    .setting {}
-
+    .setting {
+    }
   }
 
-
   .conversation {
     margin-top: 12px;
     margin-bottom: 12px;
@@ -68,5 +67,4 @@
     font-weight: 400;
     line-height: 18px;
   }
-
 }