Files
qiming/qiming-mobile/utils/mockApiService.uts

465 lines
12 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 模拟后端API服务
* 用于语音转文字功能的模拟接口实现
*/
// API响应接口
export interface ApiResponse<T = any> {
success: boolean
data?: T
error?: {
code: string
message: string
}
timestamp: number
}
// 语音转文字请求参数
export interface VoiceToTextRequest {
audio: any // 音频文件
format: string // 音频格式
duration: number // 音频时长(秒)
language?: string // 语言类型
userId?: string // 用户ID
}
// 语音转文字响应数据
export interface VoiceToTextResponse {
text: string // 转换后的文本
confidence: number // 识别置信度 (0-1)
duration: number // 音频时长
segments?: Array<{ // 分段结果
text: string
start: number
end: number
confidence: number
}>
}
/**
* 模拟API服务类
*/
export class MockApiService {
private static instance: MockApiService
private requestDelay: number = 1500 // 模拟网络延迟
private successRate: number = 0.95 // 成功率
// 预设的模拟文本库
private mockTexts = {
short: [
"你好",
"谢谢",
"再见",
"是的",
"不是",
"好的",
"没问题",
"可以"
],
medium: [
"这是一段测试语音",
"语音识别功能正常",
"请检查录音质量",
"系统运行正常",
"欢迎使用语音功能",
"录音测试成功",
"语音转文字完成"
],
long: [
"这是一段较长的语音转文字测试内容,用于验证系统的识别能力",
"人工智能语音识别技术在近年来取得了重大突破,准确率不断提升",
"语音交互将成为未来人机交互的重要方式,应用前景非常广阔",
"通过深度学习和神经网络技术,语音识别系统能够更好地理解人类语言",
"语音转文字功能在智能客服、会议记录、语音助手等场景中发挥重要作用"
]
}
// 错误类型库
private errorTypes = [
{
code: 'NETWORK_ERROR',
message: '网络连接异常,请检查网络设置'
},
{
code: 'AUDIO_QUALITY_LOW',
message: '音频质量较低,识别结果可能不准确'
},
{
code: 'SERVER_BUSY',
message: '服务器繁忙,请稍后重试'
},
{
code: 'FORMAT_NOT_SUPPORTED',
message: '音频格式不支持,请使用标准格式'
},
{
code: 'DURATION_TOO_SHORT',
message: '音频时长过短,无法识别'
},
{
code: 'LANGUAGE_NOT_SUPPORTED',
message: '不支持的语言类型'
}
]
private constructor() {}
/**
* 获取单例实例
*/
static getInstance(): MockApiService {
if (!MockApiService.instance) {
MockApiService.instance = new MockApiService()
}
return MockApiService.instance
}
/**
* 语音转文字接口
*/
async voiceToText(request: VoiceToTextRequest): Promise<ApiResponse<VoiceToTextResponse>> {
// 参数验证
const validationError = this.validateRequest(request)
if (validationError) {
return this.createErrorResponse(validationError.code, validationError.message)
}
// 模拟网络延迟
await this.delay(this.requestDelay + Math.random() * 1000)
// 模拟请求失败
if (Math.random() > this.successRate) {
const randomError = this.errorTypes[Math.floor(Math.random() * this.errorTypes.length)]
return this.createErrorResponse(randomError.code, randomError.message)
}
// 生成模拟结果
const result = this.generateMockResult(request)
return this.createSuccessResponse(result)
}
/**
* 验证请求参数
*/
private validateRequest(request: VoiceToTextRequest): { code: string, message: string } | null {
if (!request.audio) {
return { code: 'MISSING_AUDIO', message: '音频文件不能为空' }
}
if (request.duration < 0.5) {
return { code: 'DURATION_TOO_SHORT', message: '音频时长过短至少需要0.5秒' }
}
if (request.duration > 600) {
return { code: 'DURATION_TOO_LONG', message: '音频时长过长最多支持10分钟' }
}
const supportedFormats = ['mp3', 'wav', 'aac', 'PCM', 'webm']
if (!supportedFormats.includes(request.format?.toLowerCase())) {
return { code: 'FORMAT_NOT_SUPPORTED', message: '不支持的音频格式' }
}
return null
}
/**
* 生成模拟识别结果
*/
private generateMockResult(request: VoiceToTextRequest): VoiceToTextResponse {
const duration = request.duration
let selectedText: string
// 根据音频时长选择合适的文本
if (duration < 2) {
selectedText = this.mockTexts.short[Math.floor(Math.random() * this.mockTexts.short.length)]
} else if (duration < 8) {
selectedText = this.mockTexts.medium[Math.floor(Math.random() * this.mockTexts.medium.length)]
} else {
selectedText = this.mockTexts.long[Math.floor(Math.random() * this.mockTexts.long.length)]
}
// 根据语言类型调整文本
if (request.language === 'en-US') {
selectedText = this.translateToEnglish(selectedText)
}
// 生成置信度 (时长越长,置信度可能越低)
let baseConfidence = 0.95
if (duration > 30) {
baseConfidence = 0.85
} else if (duration > 60) {
baseConfidence = 0.80
}
const confidence = baseConfidence - Math.random() * 0.15
return {
text: selectedText,
confidence: Math.max(0.60, confidence),
duration: duration,
segments: this.generateSegments(selectedText, duration)
}
}
/**
* 简单的中英文翻译(模拟)
*/
private translateToEnglish(chineseText: string): string {
const translations: Record<string, string> = {
'你好': 'Hello',
'谢谢': 'Thank you',
'再见': 'Goodbye',
'是的': 'Yes',
'不是': 'No',
'好的': 'OK',
'没问题': 'No problem',
'可以': 'Sure',
'这是一段测试语音': 'This is a test voice',
'语音识别功能正常': 'Voice recognition is working',
'请检查录音质量': 'Please check recording quality',
'系统运行正常': 'System is running normally',
'欢迎使用语音功能': 'Welcome to use voice function',
'录音测试成功': 'Recording test successful',
'语音转文字完成': 'Speech to text completed'
}
return translations[chineseText] || 'This is a voice recognition test'
}
/**
* 生成分段结果
*/
private generateSegments(text: string, duration: number): Array<{
text: string
start: number
end: number
confidence: number
}> {
const segments = []
const words = text.split('')
const avgTimePerChar = duration / words.length
let currentTime = 0
let currentSegment = ''
const segmentLength = Math.max(1, Math.floor(words.length / Math.max(1, Math.floor(duration / 3))))
for (let i = 0; i < words.length; i++) {
currentSegment += words[i]
if (currentSegment.length >= segmentLength || i === words.length - 1) {
const segmentDuration = currentSegment.length * avgTimePerChar
segments.push({
text: currentSegment,
start: currentTime,
end: currentTime + segmentDuration,
confidence: 0.70 + Math.random() * 0.30
})
currentTime += segmentDuration
currentSegment = ''
}
}
return segments
}
/**
* 创建成功响应
*/
private createSuccessResponse<T>(data: T): ApiResponse<T> {
return {
success: true,
data,
timestamp: Date.now()
}
}
/**
* 创建错误响应
*/
private createErrorResponse(code: string, message: string): ApiResponse {
return {
success: false,
error: {
code,
message
},
timestamp: Date.now()
}
}
/**
* 延迟函数
*/
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}
/**
* 设置请求延迟
*/
setRequestDelay(delay: number): void {
this.requestDelay = Math.max(0, delay)
}
/**
* 设置成功率
*/
setSuccessRate(rate: number): void {
this.successRate = Math.max(0, Math.min(1, rate))
}
/**
* 添加自定义文本
*/
addMockText(category: 'short' | 'medium' | 'long', text: string): void {
this.mockTexts[category].push(text)
}
/**
* 添加自定义错误类型
*/
addErrorType(code: string, message: string): void {
this.errorTypes.push({ code, message })
}
}
/**
* API客户端类
* 用于统一管理API调用
*/
export class ApiClient {
private baseUrl: string = ''
private timeout: number = 30000
private mockService: MockApiService
constructor(baseUrl: string = '', useMock: boolean = true) {
this.baseUrl = baseUrl
this.mockService = MockApiService.getInstance()
// 如果没有提供baseUrl或者明确使用模拟服务则使用模拟服务
if (!baseUrl || useMock) {
console.log('使用模拟API服务')
}
}
/**
* 语音转文字接口调用
*/
async voiceToText(audioFile: any, options: {
format: string
duration: number
language?: string
userId?: string
}): Promise<ApiResponse<VoiceToTextResponse>> {
// 如果没有配置真实API地址使用模拟服务
if (!this.baseUrl) {
return this.mockService.voiceToText({
audio: audioFile,
format: options.format,
duration: options.duration,
language: options.language || 'zh-CN',
userId: options.userId
})
}
// 真实API调用实现
return this.realApiCall(audioFile, options)
}
/**
* 真实API调用
*/
private async realApiCall(audioFile: any, options: any): Promise<ApiResponse<VoiceToTextResponse>> {
try {
const response = await uni.request({
url: `${this.baseUrl}/api/voice-to-text`,
method: 'POST',
data: {
audio: audioFile,
format: options.format,
duration: options.duration,
language: options.language || 'zh-CN',
userId: options.userId
},
timeout: this.timeout,
header: {
'Content-Type': 'application/json'
}
})
if (response.statusCode === 200) {
return response.data as ApiResponse<VoiceToTextResponse>
} else {
throw new Error(`API调用失败状态码: ${response.statusCode}`)
}
} catch (error: any) {
return {
success: false,
error: {
code: 'API_ERROR',
message: error.message || 'API调用失败'
},
timestamp: Date.now()
}
}
}
/**
* 设置超时时间
*/
setTimeout(timeout: number): void {
this.timeout = timeout
}
/**
* 获取模拟服务实例
*/
getMockService(): MockApiService {
return this.mockService
}
}
/**
* 创建API客户端实例
*/
export function createApiClient(baseUrl?: string, useMock: boolean = true): ApiClient {
return new ApiClient(baseUrl, useMock)
}
/**
* 默认API客户端实例
*/
export const defaultApiClient = createApiClient()
/**
* 便捷的语音转文字函数
*/
export async function convertVoiceToText(
audioFile: any,
options: {
format: string
duration: number
language?: string
userId?: string
}
): Promise<string> {
try {
const response = await defaultApiClient.voiceToText(audioFile, options)
if (response.success && response.data) {
return response.data.text
} else {
throw new Error(response.error?.message || '语音转换失败')
}
} catch (error: any) {
console.error('语音转文字失败:', error)
throw error
}
}
// 导出模拟服务实例
export const mockApiService = MockApiService.getInstance()