欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > TTS-Web-Vue系列:API调用业务分层与存储整合优化

TTS-Web-Vue系列:API调用业务分层与存储整合优化

2025/5/21 2:52:05 来源:https://blog.csdn.net/hujian401387616/article/details/147995008  浏览:    关键词:TTS-Web-Vue系列:API调用业务分层与存储整合优化

🔄 本文是TTS-Web-Vue系列的重要技术更新,重点介绍了项目中API调用的业务分层设计和local-tts-store功能整合到play.ts的优化过程。通过这次重构,我们实现了更清晰的代码分层、更高的复用性和更易于维护的代码结构。

📖 系列文章导航

欢迎查看主页

🔍 重构背景与动机

随着TTS-Web-Vue项目的功能不断扩展,我们遇到了以下挑战:

  1. API调用逻辑分散:语音合成API调用逻辑散布在多个组件和函数中,导致代码冗余和维护困难
  2. 业务逻辑与数据访问混杂:业务处理逻辑与底层API调用未明确分离
  3. 错误处理不一致:不同地方的错误处理逻辑不统一,用户体验不一致
  4. 状态管理碎片化:local-tts-store与其他状态管理代码分离,增加了代码理解和维护的复杂度

为了解决这些问题,我们决定实施以下重构:

  1. 建立API调用的清晰分层:将API调用分为数据访问层、业务逻辑层和表现层
  2. 整合local-tts-store到play.ts:将相关功能合并到统一的文件中,减少代码分散
  3. 统一错误处理:实现一致的错误处理和恢复策略
  4. 提高代码复用性:减少重复代码,提高模块化程度

💡 API调用的业务分层设计

分层架构概述

我们采用了经典的三层架构设计模式,并结合Vue.js的特点进行了调整:

  1. 数据访问层(DAL):直接与外部API交互,负责数据的获取和发送
  2. 业务逻辑层(BLL):处理业务规则、数据转换和验证逻辑
  3. 表现层(PL):处理用户界面和交互,调用业务逻辑层

这种分层设计带来的好处是:

  • 关注点分离:每层只关注自己的职责
  • 可测试性提高:各层可独立测试
  • 复用性增强:逻辑可以被多个组件共享
  • 维护性改善:修改一层不会影响其他层

实际实现架构

在项目中,我们的实现方式如下:

src/
├── api/                     # 数据访问层
│   ├── tts.ts               # TTS API调用实现
│   └── local-tts.ts         # 本地TTS服务接口
├── store/                   # 业务逻辑层
│   ├── play.ts              # TTS业务逻辑和状态管理(整合了local-tts-store)
│   └── store.ts             # 全局状态管理
└── components/              # 表现层└── main/└── Main.vue         # 用户界面组件

🧩 数据访问层实现

数据访问层的主要职责是与外部API交互,处理HTTP请求和响应。我们在tts.ts中实现了这一层:

// src/api/tts.ts
export async function callTTSApi(params: TTSParams): Promise<TTSResponse> {try {const { api, voiceData, speechKey, region, thirdPartyApi, tts88Key } = params;// 根据不同的 API 类型构建不同的请求 URL 和认证头let apiUrl = '';let headers: Record<string, string> = {'Content-Type': 'application/ssml+xml','X-Microsoft-OutputFormat': 'audio-16khz-128kbitrate-mono-mp3',};if (api === 4) {apiUrl = thirdPartyApi;// TTS88 API 使用 tts88Keyif (tts88Key) {headers['Authorization'] = `Bearer ${tts88Key}`;}} else if (api === 5) {// 导入本地TTS服务相关功能try {const { useFreeTTSstore } = await import('@/store/play');const localTTSStore = useFreeTTSstore();// 获取配置const config = localTTSStore.fullConfig;// 准备API请求的URL和参数apiUrl = `${config.baseUrl}/api/v1/free-tts-stream`;// 获取本地TTS所需的参数const isSSML = voiceData.activeIndex === "1"; // 判断是否为SSML内容// 内容处理逻辑...// 发送请求const response = await axios.post(apiUrl,requestBody,{headers,responseType: 'arraybuffer',timeout: 30000});// 返回二进制音频数据return {buffer: response.data};} catch (localError: any) {return {error: `FreeTTS服务错误: ${localError.message}`,errorCode: "LOCAL_TTS_ERROR"};}} else {// Azure APIapiUrl = `https://${region}.tts.speech.microsoft.com/cognitiveservices/v1`;headers['Ocp-Apim-Subscription-Key'] = speechKey;// 发送请求和处理响应...}// 其他错误处理和返回...} catch (error: any) {// 全局错误处理return {audioContent: '',error: error.message || '获取语音数据失败',errorCode: "GLOBAL_ERROR"};}
}

数据访问层的特点:

  1. 只关注API交互:不包含业务判断逻辑
  2. 统一的错误返回格式:各种错误情况都返回标准化的错误对象
  3. 清晰的接口定义:输入和输出类型明确定义

🔄 业务逻辑层实现

业务逻辑层负责处理业务规则、参数验证和错误处理。我们在play.ts中实现了这一层,并整合了原来的local-tts-store功能:

// src/store/play.ts
import { createBatchTask, getBatchTaskStatus, deleteBatchTask, callTTSApi } from '@/api/tts';
import * as Pinia from 'pinia';
import { LocalTTSConfig, DEFAULT_LOCAL_TTS_CONFIG, checkServerConnection,getFreeLimitInfo,
} from '@/api/local-tts';// 定义错误类型
export enum FreeTTSErrorType {NONE = 0,QUOTA_EXCEEDED = 402,   // 额度用完RATE_LIMITED = 429,     // 请求频率限制BANNED = 403,           // 被封禁SERVER_ERROR = 500,     // 服务器错误CONNECTION_ERROR = -1   // 连接错误
}// 定义freeTTS服务状态和管理 - 整合了原来的local-tts-store
export const useFreeTTSstore = Pinia.defineStore('localTTSStore', {state: () => {return {// 配置config: {enabled: store.get('localTTS.enabled') ?? true,baseUrl: store.get('localTTS.baseUrl') ?? DEFAULT_LOCAL_TTS_CONFIG.baseUrl,defaultVoice: store.get('localTTS.defaultVoice') ?? DEFAULT_LOCAL_TTS_CONFIG.defaultVoice,defaultLanguage: store.get('localTTS.defaultLanguage') ?? DEFAULT_LOCAL_TTS_CONFIG.defaultLanguage,},// 服务器状态serverStatus: {connected: false,lastChecked: null as number | null,freeLimit: null as any | null,error: null as string | null,errorCode: FreeTTSErrorType.NONE},// 当前音频audio: {buffer: null as ArrayBuffer | null,url: null as string | null,isPlaying: false,error: null as string | null,errorCode: FreeTTSErrorType.NONE}};},getters: {// 获取完整配置fullConfig(): LocalTTSConfig {return {...DEFAULT_LOCAL_TTS_CONFIG,...this.config};},// 其他getter...},actions: {// 保存配置saveConfig() {Object.entries(this.config).forEach(([key, value]) => {store.set(`localTTS.${key}`, value);});},// 检查服务器连接async checkServerConnection() {try {const isConnected = await checkServerConnection(this.fullConfig);this.serverStatus.connected = isConnected;this.serverStatus.lastChecked = Date.now();if (isConnected) {this.serverStatus.error = null;this.serverStatus.errorCode = FreeTTSErrorType.NONE;// 如果连接成功,获取可用额度信息await this.getFreeLimitInfo();} else {this.setErrorState('无法连接到服务器', FreeTTSErrorType.CONNECTION_ERROR);}return isConnected;} catch (error: any) {this.setErrorState(`连接错误: ${error.message}`, FreeTTSErrorType.CONNECTION_ERROR);return false;}},// 其他actions...}
});/*** 获取TTS数据 - 业务逻辑层实现* 负责调用底层API,处理重试逻辑和特定的业务转换*/
async function getTTSData(params: TTSParams): Promise<TTSResponse> {const { api, voiceData } = params;const { activeIndex, retryCount = 3, retryInterval = 1 } = voiceData;// 参数验证 - 全面的参数校验if (!voiceData.ssmlContent && !voiceData.inputContent) {console.error('缺少转换内容');return {error: '没有可转换的内容',errorCode: 'EMPTY_CONTENT'};}// API类型相关验证if (api === 1 || api === 2 || api === 3) {// 验证Azure API必要参数if (!params.speechKey) {console.error('缺少 Azure Speech API Key');return {error: '请先在设置中配置 Azure Speech API Key',errorCode: 'MISSING_AZURE_KEY'};}// 其他验证逻辑...}// 处理特定业务逻辑try {// 根据API类型进行不同的预处理if (api === 3) { // Azureconsole.log("使用Azure API");// 可以在这里添加Azure特定的处理逻辑}else if (api === 4) { // TTS88console.log("使用TTS88 API");// 可以在这里添加TTS88特定的处理逻辑}else if (api === 5) { // 本地TTSconsole.log("使用本地TTS服务");// 获取当前选中的声音和配置const { useTtsStore } = await import('@/store/store');const ttsStore = useTtsStore();const selectedVoice = ttsStore.formConfig.voiceSelect;const speed = ttsStore.formConfig.speed;const pitch = ttsStore.formConfig.pitch;console.log("当前选择的声音:", selectedVoice, "语速:", speed, "音调:", pitch);}// 调用API层的函数,并包含重试逻辑let retry = 0;let lastError;while (retry < retryCount) {try {console.log(`尝试调用TTS API (尝试 ${retry + 1}/${retryCount})`);// 确保参数类型兼容const apiParams = {...params,// 确保必要属性不为undefinedspeechKey: params.speechKey || '',region: params.region || '',thirdPartyApi: params.thirdPartyApi || '',tts88Key: params.tts88Key || ''};const result = await callTTSApi(apiParams);// 检查是否有错误if (result.error) {// 错误增强处理// ...throw new Error(result.error);}// 返回结果return result;} catch (error: any) {console.error(`TTS API调用失败 (尝试 ${retry + 1}/${retryCount}):`, error);lastError = error;console.log(`等待 ${retryInterval} 秒后重试...`);await sleep(retryInterval * 1000);retry++;}}// 达到最大重试次数return {error: lastError?.message || "达到最大重试次数,请求失败",errorCode: "MAX_RETRY_EXCEEDED"};} catch (error: any) {console.error("TTS转换失败:", error);return {error: error.message || "TTS转换失败",errorCode: "GENERAL_ERROR"};}
}// 导出供组件使用的函数
export { getTTSData };

业务逻辑层的特点:

  1. 职责清晰:负责业务规则和参数验证,而不是API调用细节
  2. 错误增强:提供更友好、更具体的错误信息
  3. 重试机制:实现了自动重试逻辑
  4. 状态管理:整合了之前分散的状态管理功能

🔀 整合local-tts-store到play.ts

在重构过程中,我们将原来独立的local-tts-store.ts文件整合到了play.ts中,这样做的好处是:

  1. 减少文件数量:相关功能集中在一个文件中
  2. 避免循环依赖:解决了之前可能存在的循环引用问题
  3. 逻辑集中:所有与TTS播放相关的逻辑都在同一个地方

整合过程的主要工作包括:

  1. local-tts-store.ts中的类型定义、状态和方法移动到play.ts
  2. 重新组织和优化代码结构,确保逻辑流程清晰
  3. 更新所有引用local-tts-store.ts的地方,指向新的位置
  4. 优化错误处理和状态管理逻辑

🌟 表现层实现

表现层(即组件层)通过调用业务逻辑层的函数来完成功能,而不直接与API交互:

// 在组件中使用getTTSData函数
import { getTTSData } from '@/store/play';// 在组件方法中
async function startBtn() {// 准备参数const params = {api: formConfig.value.api,voiceData: {activeIndex: tabsValue.value,ssmlContent: ssmlContent.value,inputContent: inputs.value.content},speechKey: config.value.key,region: config.value.region,thirdPartyApi: config.value.thirdPartyApi,tts88Key: config.value.tts88Key};// 调用业务逻辑层函数const result = await getTTSData(params);// 处理结果if (result.error) {// 显示错误信息ElMessage.error(result.error);} else {// 处理成功结果handleAudioBlob(result.buffer);}
}

表现层的特点:

  1. 专注于用户交互:只关注UI渲染和事件处理
  2. 调用业务逻辑层:不直接进行API调用
  3. 处理显示逻辑:负责向用户展示结果或错误信息

📊 分层架构的优势

通过实施API调用业务分层,我们获得了以下优势:

  1. 代码组织更清晰:每一层都有明确的职责
  2. 复用性提高:业务逻辑可以被多个组件重用
  3. 测试变得简单:可以独立测试每一层
  4. 错误处理更一致:统一的错误处理策略
  5. 易于扩展:添加新功能时可以只修改相关层
  6. 维护成本降低:修改一个层的实现不会影响其他层

🔍 代码质量提升

错误处理统一化

重构后,我们实现了更统一的错误处理机制:

// 错误类型定义
export enum FreeTTSErrorType {NONE = 0,QUOTA_EXCEEDED = 402,   // 额度用完RATE_LIMITED = 429,     // 请求频率限制BANNED = 403,           // 被封禁SERVER_ERROR = 500,     // 服务器错误CONNECTION_ERROR = -1   // 连接错误
}// 错误处理函数
function getErrorCodeFromResponse(error) {if (!error || !error.response) {return FreeTTSErrorType.CONNECTION_ERROR;}const status = error.response.status;if (status === 402 || status === 403) {return FreeTTSErrorType.QUOTA_EXCEEDED;} else if (status === 429) {return FreeTTSErrorType.RATE_LIMITED;} else if (status >= 500) {return FreeTTSErrorType.SERVER_ERROR;}return FreeTTSErrorType.SERVER_ERROR;
}

代码复用性提高

通过将业务逻辑集中在getTTSData函数中,我们减少了代码重复:

// 任何需要TTS功能的组件都可以直接调用此函数
import { getTTSData } from '@/store/play';// 在不同组件中使用相同的逻辑
const result = await getTTSData(params);

🚀 性能优化

动态导入优化

为了避免不必要的依赖加载,我们使用了动态导入:

// 只在需要时动态导入
if (api === 5) { // 本地TTSconst { useTtsStore } = await import('@/store/store');const ttsStore = useTtsStore();// 使用ttsStore...
}

缓存与重用

我们增加了结果缓存机制,避免重复的API调用:

// 简单的结果缓存实现
const resultCache = new Map();function getCacheKey(params) {// 生成缓存键...
}async function getTTSDataWithCache(params) {const cacheKey = getCacheKey(params);// 检查缓存if (resultCache.has(cacheKey)) {return resultCache.get(cacheKey);}// 调用API获取结果const result = await getTTSData(params);// 缓存结果if (!result.error) {resultCache.set(cacheKey, result);}return result;
}

🔮 未来展望

基于当前的分层架构,我们计划在未来实施以下优化:

  1. 更细粒度的分层:将业务逻辑层进一步拆分为多个专门的服务
  2. 更完善的错误恢复机制:自动尝试不同的API和参数组合
  3. 服务质量监控:添加性能和可用性监控
  4. 缓存优化:实现更智能的缓存策略
  5. 接口标准化:统一所有API的请求和响应格式

🎯 总结

通过实施API调用业务分层和整合local-tts-store到play.ts,我们实现了代码结构的显著优化:

  1. 关注点分离:清晰的分层架构使每部分代码都有明确的职责
  2. 代码复用性提高:业务逻辑可以被多个组件共享和重用
  3. 错误处理更一致:统一的错误处理机制提供了更好的用户体验
  4. 代码可维护性增强:修改一层的实现不会影响其他层
  5. 状态管理统一:整合相关功能减少了状态管理的复杂度

这些优化不仅提升了当前项目的代码质量,也为未来的功能扩展和维护提供了坚实的基础。我们推荐在类似的前端项目中采用这种分层架构设计,尤其是当项目规模增长到一定程度时。

🔗 相关链接

  • Vue.js官方文档
  • Pinia状态管理
  • TypeScript文档
  • TTS-Web-Vue项目主页
  • 在线演示

注意:本文介绍的架构设计思路仅供学习和参考,具体实践时应根据项目特点进行调整。如有问题或建议,欢迎在评论区讨论!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词