import '../../../core/models/api_response.dart'; import '../../../core/services/enhanced_api_service.dart'; import '../models/speaking_scenario.dart'; import '../models/conversation.dart'; import '../models/speaking_stats.dart'; import '../models/pronunciation_assessment.dart'; import '../models/pronunciation_item.dart'; /// 口语训练服务 class SpeakingService { static final SpeakingService _instance = SpeakingService._internal(); factory SpeakingService() => _instance; SpeakingService._internal(); final EnhancedApiService _enhancedApiService = EnhancedApiService(); // 缓存时长配置 static const Duration _shortCacheDuration = Duration(minutes: 5); static const Duration _longCacheDuration = Duration(hours: 1); /// 获取口语场景列表 Future>> getSpeakingScenarios({ SpeakingScenario? scenario, SpeakingDifficulty? difficulty, int page = 1, int limit = 20, }) async { try { final response = await _enhancedApiService.get>( '/speaking/scenarios', queryParameters: { 'page': page, 'page_size': limit, if (scenario != null) 'category': scenario.name, if (difficulty != null) 'level': difficulty.name, }, cacheDuration: _shortCacheDuration, fromJson: (data) { final scenarios = data['scenarios'] as List?; if (scenarios == null) return []; return scenarios.map((json) => SpeakingTask.fromJson(json)).toList(); }, ); if (response.success && response.data != null) { return ApiResponse.success(message: '获取成功', data: response.data!); } else { return ApiResponse.error(message: response.message); } } catch (e) { return ApiResponse.error(message: '获取口语场景失败: $e'); } } /// 获取单个口语场景详情 Future> getSpeakingScenario(String scenarioId) async { try { final response = await _enhancedApiService.get( '/speaking/scenarios/$scenarioId', cacheDuration: _longCacheDuration, fromJson: (data) => SpeakingTask.fromJson(data['data'] ?? data), ); if (response.success && response.data != null) { return ApiResponse.success(message: '获取成功', data: response.data!); } else { return ApiResponse.error(message: response.message); } } catch (e) { return ApiResponse.error(message: '获取口语场景详情失败: $e'); } } /// 开始口语对话 Future> startConversation(String scenarioId) async { try { final response = await _enhancedApiService.post( '/speaking/records', data: {'scenario_id': scenarioId}, fromJson: (data) => Conversation.fromJson(data['data'] ?? data), ); if (response.success && response.data != null) { return ApiResponse.success(message: '对话开始成功', data: response.data!); } else { return ApiResponse.error(message: response.message); } } catch (e) { return ApiResponse.error(message: '开始对话失败: $e'); } } /// 获取用户口语历史 Future>> getUserSpeakingHistory({ int page = 1, int limit = 20, }) async { try { final response = await _enhancedApiService.get>( '/speaking/records', queryParameters: { 'page': page, 'page_size': limit, }, cacheDuration: _shortCacheDuration, fromJson: (data) { final records = data['records'] as List?; if (records == null) return []; return records.map((json) => Conversation.fromJson(json)).toList(); }, ); if (response.success && response.data != null) { return ApiResponse.success(message: '获取历史记录成功', data: response.data!); } else { return ApiResponse.error(message: response.message); } } catch (e) { return ApiResponse.error(message: '获取历史记录失败: $e'); } } /// 获取用户口语统计 Future> getUserSpeakingStatistics() async { try { final response = await _enhancedApiService.get( '/speaking/stats', cacheDuration: _shortCacheDuration, fromJson: (data) => SpeakingStats.fromJson(data['data'] ?? data), ); if (response.success && response.data != null) { return ApiResponse.success(message: '获取统计数据成功', data: response.data!); } // API失败时返回默认统计数据 final stats = SpeakingStats( totalSessions: 45, totalMinutes: 320, averageScore: 85.5, scenarioStats: { SpeakingScenario.dailyConversation: 8, SpeakingScenario.businessMeeting: 5, SpeakingScenario.jobInterview: 3, SpeakingScenario.shopping: 4, SpeakingScenario.restaurant: 3, SpeakingScenario.travel: 2, SpeakingScenario.academic: 2, SpeakingScenario.socializing: 1, }, difficultyStats: { SpeakingDifficulty.beginner: 10, SpeakingDifficulty.intermediate: 15, SpeakingDifficulty.advanced: 8, }, progressData: [], skillAnalysis: SpeakingSkillAnalysis( criteriaScores: { PronunciationCriteria.accuracy: 85.0, PronunciationCriteria.fluency: 82.0, PronunciationCriteria.completeness: 88.0, PronunciationCriteria.prosody: 80.0, }, commonErrors: {}, strengths: ['发音准确'], weaknesses: ['语调需要改进'], recommendations: ['多练习语调'], improvementRate: 0.05, lastAnalyzed: DateTime.now(), ), lastUpdated: DateTime.now(), ); return ApiResponse.success(message: '获取统计数据成功', data: stats); } catch (e) { return ApiResponse.error(message: '获取统计数据失败: $e'); } } /// 获取推荐任务 Future>> getRecommendedTasks() async { try { final response = await _enhancedApiService.get>( '/speaking/scenarios/recommendations', cacheDuration: _shortCacheDuration, fromJson: (data) { final scenarios = data['scenarios'] as List?; if (scenarios == null) return []; return scenarios.map((json) => SpeakingTask.fromJson(json)).toList(); }, ); if (response.success && response.data != null) { return ApiResponse.success(message: '获取推荐任务成功', data: response.data!); } else { return ApiResponse.error(message: response.message); } } catch (e) { return ApiResponse.error(message: '获取推荐任务失败: $e'); } } /// 获取热门任务 Future>> getPopularTasks() async { try { // 热门任务可以通过获取场景列表并按某种排序获得 final response = await _enhancedApiService.get>( '/speaking/scenarios', queryParameters: { 'page': 1, 'page_size': 10, 'sort': 'popular', // 如果后端支持排序 }, cacheDuration: _shortCacheDuration, fromJson: (data) { final scenarios = data['scenarios'] as List?; if (scenarios == null) return []; return scenarios.map((json) => SpeakingTask.fromJson(json)).toList(); }, ); if (response.success && response.data != null) { return ApiResponse.success(message: '获取热门任务成功', data: response.data!); } else { return ApiResponse.error(message: response.message); } } catch (e) { return ApiResponse.error(message: '获取热门任务失败: $e'); } } /// 获取发音练习项目(通过词汇后端数据映射) Future>> getPronunciationItems( PronunciationType type, { int limit = 50, }) async { try { final response = await _enhancedApiService.get>( '/vocabulary/study/today', queryParameters: { 'limit': limit, }, cacheDuration: _shortCacheDuration, fromJson: (data) { dynamic root = data; if (root is Map && root.containsKey('data')) { root = root['data']; } List list = const []; if (root is List) { list = root; } else if (root is Map) { final words = root['words']; if (words is List) { list = words; } } final items = []; for (final w in list) { final id = w['id'].toString(); final level = (w['level'] ?? '').toString(); final audioUrl = (w['audio_url'] ?? w['audio_us_url'] ?? w['audio_uk_url'] ?? '').toString(); final phonetic = (w['phonetic'] ?? w['phonetic_us'] ?? w['phonetic_uk'] ?? '').toString(); final createdAtStr = (w['created_at'] ?? DateTime.now().toIso8601String()).toString(); final createdAt = DateTime.tryParse(createdAtStr) ?? DateTime.now(); DifficultyLevel mapLevel(String l) { switch (l) { case 'beginner': case 'elementary': return DifficultyLevel.beginner; case 'intermediate': return DifficultyLevel.intermediate; case 'advanced': case 'expert': return DifficultyLevel.advanced; default: return DifficultyLevel.intermediate; } } if (type == PronunciationType.word) { items.add(PronunciationItem( id: id, text: (w['word'] ?? '').toString(), phonetic: phonetic, audioUrl: audioUrl, type: PronunciationType.word, difficulty: mapLevel(level), category: level.isEmpty ? '词汇练习' : level, tips: const [], createdAt: createdAt, )); } else if (type == PronunciationType.sentence) { final examples = (w['examples'] as List?) ?? []; for (var i = 0; i < examples.length; i++) { final ex = examples[i] as Map; final exAudio = (ex['audio_url'] ?? '').toString(); final exCreated = DateTime.tryParse((ex['created_at'] ?? createdAtStr).toString()) ?? createdAt; items.add(PronunciationItem( id: '${id}_ex_$i', text: (ex['sentence'] ?? ex['sentence_en'] ?? '').toString(), phonetic: '', audioUrl: exAudio, type: PronunciationType.sentence, difficulty: mapLevel(level), category: '例句', tips: const [], createdAt: exCreated, )); } } } return items; }, ); if (response.success && response.data != null) { return ApiResponse.success(message: '获取发音练习成功', data: response.data!); } else { return ApiResponse.error(message: response.message); } } catch (e) { return ApiResponse.error(message: '获取发音练习失败: $e'); } } }