import 'dart:math'; import '../models/word_model.dart'; import '../models/vocabulary_book_model.dart'; import '../models/study_session_model.dart'; import '../models/daily_stats_model.dart'; import '../../../core/network/api_client.dart'; import '../../../core/services/storage_service.dart'; import '../../../core/services/enhanced_api_service.dart'; import '../../../core/models/api_response.dart'; /// 单词学习服务 class VocabularyService { final ApiClient _apiClient; final StorageService _storageService; final EnhancedApiService _enhancedApiService = EnhancedApiService(); final Random _random = Random(); VocabularyService({ required ApiClient apiClient, required StorageService storageService, }) : _apiClient = apiClient, _storageService = storageService; // 缓存时长配置 static const Duration _shortCacheDuration = Duration(minutes: 5); static const Duration _longCacheDuration = Duration(hours: 1); // ==================== 词汇书相关 ==================== /// 获取系统词汇书列表 Future> getSystemVocabularyBooks({ VocabularyBookDifficulty? difficulty, String? category, int page = 1, int limit = 20, }) async { try { final response = await _enhancedApiService.get>( '/vocabulary/books/system', queryParameters: { if (difficulty != null) 'difficulty': difficulty.name, if (category != null) 'category': category, 'page': page, 'limit': limit, }, cacheDuration: Duration.zero, // 暂时禁用缓存,避免旧数据解析错误 fromJson: (data) { // 处理分页响应结构: data.items final responseData = data['data']; final List list = responseData is Map ? (responseData['items'] ?? []) : (data['data'] ?? []); return list.map((json) => VocabularyBook.fromJson(json)).toList(); }, ); if (response.success && response.data != null) { return response.data!; } else { throw Exception(response.message); } } catch (e) { throw Exception('获取系统词汇书失败: $e'); } } /// 获取词汇书分类列表 Future>> getVocabularyBookCategories() async { try { final response = await _enhancedApiService.get>>( '/vocabulary/books/categories', cacheDuration: _longCacheDuration, fromJson: (data) { final List list = data['data'] ?? []; return list.map((item) => item as Map).toList(); }, ); if (response.success && response.data != null) { return response.data!; } else { throw Exception(response.message); } } catch (e) { throw Exception('获取词汇书分类失败: $e'); } } /// 获取用户词汇书列表 Future> getUserVocabularyBooks() async { try { final response = await _enhancedApiService.get>( '/vocabulary/books/user', cacheDuration: _shortCacheDuration, fromJson: (data) { final List list = data['data'] ?? []; return list.map((json) => VocabularyBook.fromJson(json)).toList(); }, ); if (response.success && response.data != null) { return response.data!; } else { throw Exception(response.message); } } catch (e) { throw Exception('获取用户词汇书失败: $e'); } } /// 获取词汇书详情 Future getVocabularyBookDetail(String bookId) async { try { final response = await _enhancedApiService.get( '/vocabulary/books/$bookId', cacheDuration: _longCacheDuration, fromJson: (data) => VocabularyBook.fromJson(data['data']), ); if (response.success && response.data != null) { return response.data!; } else { throw Exception(response.message); } } catch (e) { throw Exception('获取词汇书详情失败: $e'); } } /// 获取词汇书单词列表 Future> getVocabularyBookWords( String bookId, { int page = 1, int limit = 50, }) async { try { final response = await _enhancedApiService.get>( '/vocabulary/books/$bookId/words', queryParameters: { 'page': page, 'limit': limit, }, cacheDuration: Duration.zero, // 暂时禁用缓存 fromJson: (data) { // 处理分页响应结构: data.items final responseData = data['data']; final List list = responseData is Map ? (responseData['items'] ?? []) : (data['data'] ?? []); return list.map((json) => VocabularyBookWord.fromJson(json)).toList(); }, ); if (response.success && response.data != null) { return response.data!; } else { throw Exception(response.message); } } catch (e) { throw Exception('获取词汇书单词失败: $e'); } } /// 获取用户在某词汇书中的学习进度(后端API优先,失败时兜底为0) Future getVocabularyBookProgress( String bookId, { bool forceRefresh = false, // 是否强制刷新(跳过缓存) }) async { try { final response = await _enhancedApiService.get( '/vocabulary/books/$bookId/progress', cacheDuration: forceRefresh ? Duration.zero : _shortCacheDuration, fromJson: (data) => UserVocabularyBookProgress.fromJson(data['data'] ?? data), ); if (response.success && response.data != null) { return response.data!; } else { throw Exception(response.message); } } catch (e) { // 当后端暂未提供词书进度接口时,返回一个默认的进度对象,避免前端崩溃 final now = DateTime.now(); return UserVocabularyBookProgress( id: 'progress_$bookId', userId: 'current_user', vocabularyBookId: bookId, learnedWords: 0, masteredWords: 0, progressPercentage: 0.0, streakDays: 0, totalStudyDays: 0, averageDailyWords: 0.0, estimatedCompletionDate: null, isCompleted: false, completedAt: null, startedAt: now, lastStudiedAt: now, ); } } /// 添加词汇书到用户库 Future addVocabularyBookToUser(String bookId) async { try { await _apiClient.post('/vocabulary/books/$bookId/add'); } catch (e) { throw Exception('添加词汇书失败: $e'); } } /// 创建自定义词汇书 Future createCustomVocabularyBook({ required String name, String? description, List wordIds = const [], }) async { try { final response = await _apiClient.post( '/vocabulary/books/custom', data: { 'name': name, 'description': description, 'wordIds': wordIds, }, ); return VocabularyBook.fromJson(response.data['data']); } catch (e) { throw Exception('创建词汇书失败: $e'); } } // ==================== 单词相关 ==================== /// 搜索单词 Future> searchWords( String query, { int page = 1, int limit = 20, }) async { try { final response = await _apiClient.get( '/vocabulary/words/search', queryParameters: { 'q': query, 'page': page, 'limit': limit, }, ); final List data = response.data['data'] ?? []; return data.map((json) => Word.fromJson(json)).toList(); } catch (e) { throw Exception('搜索单词失败: $e'); } } /// 获取单词详情 Future getWordDetail(String wordId) async { try { final response = await _apiClient.get('/vocabulary/words/$wordId'); return Word.fromJson(response.data['data']); } catch (e) { throw Exception('获取单词详情失败: $e'); } } /// 获取用户单词学习进度 Future getUserWordProgress(String wordId) async { try { final response = await _apiClient.get('/vocabulary/words/$wordId/progress'); final data = response.data['data']; return data != null ? UserWordProgress.fromJson(data) : null; } catch (e) { throw Exception('获取单词学习进度失败: $e'); } } /// 更新用户单词学习进度 Future updateUserWordProgress({ required String wordId, required LearningStatus status, required bool isCorrect, int responseTime = 0, }) async { try { final response = await _apiClient.put( '/vocabulary/words/$wordId/progress', data: { 'status': status.name, 'isCorrect': isCorrect, 'responseTime': responseTime, }, ); return UserWordProgress.fromJson(response.data['data']); } catch (e) { throw Exception('更新单词学习进度失败: $e'); } } // ==================== 学习会话相关 ==================== /// 开始学习会话 Future startStudySession({ String? vocabularyBookId, required StudyMode mode, required int targetWordCount, }) async { try { final response = await _apiClient.post( '/vocabulary/study/sessions', data: { 'vocabularyBookId': vocabularyBookId, 'mode': mode.name, 'targetWordCount': targetWordCount, }, ); return StudySession.fromJson(response.data['data']); } catch (e) { throw Exception('开始学习会话失败: $e'); } } /// 结束学习会话 Future endStudySession( String sessionId, { required int durationSeconds, required List exercises, }) async { try { final response = await _apiClient.put( '/vocabulary/study/sessions/$sessionId/end', data: { 'durationSeconds': durationSeconds, 'exercises': exercises.map((e) => e.toJson()).toList(), }, ); return StudySession.fromJson(response.data['data']); } catch (e) { throw Exception('结束学习会话失败: $e'); } } /// 获取学习会话历史 Future> getStudySessionHistory({ int page = 1, int limit = 20, DateTime? startDate, DateTime? endDate, }) async { try { final response = await _apiClient.get( '/vocabulary/study/sessions', queryParameters: { 'page': page, 'limit': limit, if (startDate != null) 'startDate': startDate.toIso8601String(), if (endDate != null) 'endDate': endDate.toIso8601String(), }, ); final List data = response.data['data'] ?? []; return data.map((json) => StudySession.fromJson(json)).toList(); } catch (e) { throw Exception('获取学习会话历史失败: $e'); } } // ==================== 学习统计相关 ==================== /// 获取学习统计 Future getStudyStatistics(DateTime date) async { try { final response = await _apiClient.get( '/vocabulary/study/statistics', queryParameters: { 'date': date.toIso8601String().split('T')[0], }, ); return StudyStatistics.fromJson(response.data['data']); } catch (e) { throw Exception('获取学习统计失败: $e'); } } /// 获取每日词汇统计(wordsLearned、studyTimeMinutes) Future getDailyVocabularyStats({required String userId}) async { try { final response = await _enhancedApiService.get( '/vocabulary/daily', queryParameters: { 'user_id': userId, }, useCache: false, fromJson: (data) => DailyStats.fromJson(data['data'] ?? data), ); if (response.success && response.data != null) { return response.data!; } else { throw Exception(response.message); } } catch (e) { throw Exception('获取每日词汇统计失败: $e'); } } /// 获取用户词汇整体统计(total_studied、accuracy_rate、mastery_stats等) Future> getUserVocabularyStats() async { try { final response = await _enhancedApiService.get>( '/vocabulary/stats', fromJson: (data) => Map.from(data['data'] ?? data), ); if (response.success && response.data != null) { return response.data!; } else { throw Exception(response.message); } } catch (e) { throw Exception('获取用户词汇整体统计失败: $e'); } } /// 获取学习统计历史 Future> getStudyStatisticsHistory({ required DateTime startDate, required DateTime endDate, }) async { try { final response = await _apiClient.get( '/vocabulary/study/statistics/history', queryParameters: { 'startDate': startDate.toIso8601String().split('T')[0], 'endDate': endDate.toIso8601String().split('T')[0], }, ); final List data = response.data['data'] ?? []; return data.map((json) => StudyStatistics.fromJson(json)).toList(); } catch (e) { throw Exception('获取学习统计历史失败: $e'); } } // ==================== 智能学习算法 ==================== /// 获取今日需要学习的单词 Future> getTodayStudyWords({ String? vocabularyBookId, int limit = 20, }) async { try { final response = await _apiClient.get( '/vocabulary/study/today', queryParameters: { if (vocabularyBookId != null) 'vocabularyBookId': vocabularyBookId, 'limit': limit, }, ); print('=== getTodayStudyWords API响应 ==='); print('原始响应: ${response.data}'); print('data字段类型: ${response.data['data'].runtimeType}'); // 后端返回的结构是 {"data": {"words": []}} final responseData = response.data['data']; List data; if (responseData is Map) { print('responseData是Map,尝试获取words字段'); final words = responseData['words']; print('words字段类型: ${words.runtimeType}'); data = words is List ? words : []; } else if (responseData is List) { print('responseData是List,直接使用'); data = responseData; } else { print('responseData类型未知: ${responseData.runtimeType}'); data = []; } print('最终数据条数: ${data.length}'); if (data.isNotEmpty) { print('第一条数据: ${data[0]}'); } return data.map((json) => Word.fromJson(json as Map)).toList(); } catch (e, stackTrace) { // API调用失败,返回空列表,让UI显示空状态 print('getTodayStudyWords API调用失败: $e'); print('堆栈跟踪: $stackTrace'); return []; } } /// 获取需要复习的单词 Future> getReviewWords({ String? vocabularyBookId, int limit = 20, }) async { try { final response = await _apiClient.get( '/vocabulary/study/review', queryParameters: { if (vocabularyBookId != null) 'vocabularyBookId': vocabularyBookId, 'limit': limit, }, ); // 后端返回的结构可能是 {"data": {"words": []}} 或 {"data": []} final responseData = response.data['data']; final List data = responseData is Map ? (responseData['words'] ?? []) : (responseData ?? []); print('=== getReviewWords API响应 ==='); print('数据条数: ${data.length}'); return data.map((json) => Word.fromJson(json)).toList(); } catch (e) { // API调用失败,返回空列表,让UI显示空状态 print('getReviewWords API调用失败: $e'); return []; } } /// 生成单词练习题 Future> generateWordExercise( String wordId, ExerciseType exerciseType, ) async { try { final response = await _apiClient.post( '/vocabulary/study/exercise', data: { 'wordId': wordId, 'exerciseType': exerciseType.name, }, ); return response.data['data']; } catch (e) { throw Exception('生成练习题失败: $e'); } } // ==================== 本地缓存相关 ==================== /// 缓存词汇书到本地 Future cacheVocabularyBook(VocabularyBook book) async { try { await _storageService.setString( 'vocabulary_book_${book.id}', book.toJson().toString(), ); } catch (e) { throw Exception('缓存词汇书失败: $e'); } } /// 从本地获取缓存的词汇书 Future getCachedVocabularyBook(String bookId) async { try { final cached = await _storageService.getString('vocabulary_book_$bookId'); if (cached != null) { // 这里需要实际的JSON解析逻辑 // return VocabularyBook.fromJson(jsonDecode(cached)); } return null; } catch (e) { return null; } } /// 清除本地缓存 Future clearCache() async { try { // 清除所有词汇相关的缓存 await _storageService.remove('vocabulary_books'); await _storageService.remove('study_statistics'); } catch (e) { throw Exception('清除缓存失败: $e'); } } // ==================== 工具方法 ==================== /// 计算单词熟练度 int calculateWordProficiency(UserWordProgress progress) { if (progress.studyCount == 0) return 0; final accuracy = progress.accuracy; final studyCount = progress.studyCount; final reviewInterval = progress.reviewInterval; // 基于准确率、学习次数和复习间隔计算熟练度 int proficiency = (accuracy * 50).round(); proficiency += (studyCount * 5).clamp(0, 30); proficiency += (reviewInterval * 2).clamp(0, 20); return proficiency.clamp(0, 100); } /// 计算下次复习时间 DateTime calculateNextReviewTime(UserWordProgress progress) { final now = DateTime.now(); final accuracy = progress.accuracy; // 基于准确率调整复习间隔 int intervalDays = progress.reviewInterval; if (accuracy >= 0.9) { intervalDays = (intervalDays * 2).clamp(1, 30); } else if (accuracy >= 0.7) { intervalDays = (intervalDays * 1.5).round().clamp(1, 14); } else if (accuracy >= 0.5) { intervalDays = intervalDays.clamp(1, 7); } else { intervalDays = 1; } return now.add(Duration(days: intervalDays)); } /// 随机选择练习类型 ExerciseType getRandomExerciseType() { final types = ExerciseType.values; return types[_random.nextInt(types.length)]; } /// 生成干扰选项 List generateDistractors( String correctAnswer, List wordPool, int count, ) { final distractors = []; final shuffled = List.from(wordPool)..shuffle(_random); for (final word in shuffled) { if (word != correctAnswer && !distractors.contains(word)) { distractors.add(word); if (distractors.length >= count) break; } } return distractors; } // ==================== 本地兜底(无后端时) ==================== /// 当后端不可用或接口缺失时,生成基础示例单词(仅用于开发调试) List _generateSampleWords(int limit) { final now = DateTime.now(); final samples = [ {'w': 'apple', 'cn': '苹果', 'def': 'a round fruit of a tree', 'type': 'noun'}, {'w': 'run', 'cn': '跑步', 'def': 'move fast by using one’s feet', 'type': 'verb'}, {'w': 'happy', 'cn': '开心的', 'def': 'feeling or showing pleasure', 'type': 'adjective'}, {'w': 'quickly', 'cn': '快速地', 'def': 'at a fast speed', 'type': 'adverb'}, {'w': 'book', 'cn': '书', 'def': 'a written or printed work', 'type': 'noun'}, {'w': 'study', 'cn': '学习', 'def': 'apply oneself to learning', 'type': 'verb'}, {'w': 'blue', 'cn': '蓝色的', 'def': 'of the color blue', 'type': 'adjective'}, {'w': 'slowly', 'cn': '缓慢地', 'def': 'at a slow speed', 'type': 'adverb'}, {'w': 'friend', 'cn': '朋友', 'def': 'a person you know well', 'type': 'noun'}, {'w': 'listen', 'cn': '听', 'def': 'give attention to sound', 'type': 'verb'}, {'w': 'green', 'cn': '绿色的', 'def': 'of the color green', 'type': 'adjective'}, {'w': 'often', 'cn': '经常', 'def': 'frequently; many times', 'type': 'adverb'}, {'w': 'school', 'cn': '学校', 'def': 'a place for learning', 'type': 'noun'}, {'w': 'write', 'cn': '写作', 'def': 'mark letters or words on a surface', 'type': 'verb'}, {'w': 'smart', 'cn': '聪明的', 'def': 'intelligent or clever', 'type': 'adjective'}, {'w': 'carefully', 'cn': '小心地', 'def': 'with care or attention', 'type': 'adverb'}, {'w': 'music', 'cn': '音乐', 'def': 'art of arranging sounds', 'type': 'noun'}, {'w': 'learn', 'cn': '学习', 'def': 'gain knowledge or skill', 'type': 'verb'}, {'w': 'bright', 'cn': '明亮的/聪明的', 'def': 'giving much light; intelligent', 'type': 'adjective'}, {'w': 'rarely', 'cn': '很少', 'def': 'not often; seldom', 'type': 'adverb'}, ]; WordType _parseType(String t) { switch (t) { case 'noun': return WordType.noun; case 'verb': return WordType.verb; case 'adjective': return WordType.adjective; case 'adverb': return WordType.adverb; default: return WordType.noun; } } final list = []; for (var i = 0; i < samples.length && list.length < limit; i++) { final s = samples[i]; list.add( Word( id: 'sample_${s['w']}', word: s['w']!, definitions: [ WordDefinition( type: _parseType(s['type']!), definition: s['def']!, translation: s['cn']!, frequency: 3, ), ], examples: [ WordExample( sentence: 'I like ${s['w']}.', translation: '我喜欢${s['cn']}.', ), ], synonyms: const [], antonyms: const [], etymology: null, difficulty: WordDifficulty.beginner, frequency: 5, imageUrl: null, memoryTip: null, createdAt: now, updatedAt: now, ), ); } return list; } /// 根据ID获取单词详情 Future getWordById(String wordId) async { try { final response = await _apiClient.get('/vocabulary/$wordId'); final data = response.data['data']; return Word.fromJson(data); } catch (e) { print('获取单词详情失败: $e'); rethrow; } } }