746 lines
23 KiB
Dart
746 lines
23 KiB
Dart
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<List<VocabularyBook>> getSystemVocabularyBooks({
|
||
VocabularyBookDifficulty? difficulty,
|
||
String? category,
|
||
int page = 1,
|
||
int limit = 20,
|
||
}) async {
|
||
try {
|
||
final response = await _enhancedApiService.get<List<VocabularyBook>>(
|
||
'/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<dynamic> 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<List<Map<String, dynamic>>> getVocabularyBookCategories() async {
|
||
try {
|
||
final response = await _enhancedApiService.get<List<Map<String, dynamic>>>(
|
||
'/vocabulary/books/categories',
|
||
cacheDuration: _longCacheDuration,
|
||
fromJson: (data) {
|
||
final List<dynamic> list = data['data'] ?? [];
|
||
return list.map((item) => item as Map<String, dynamic>).toList();
|
||
},
|
||
);
|
||
|
||
if (response.success && response.data != null) {
|
||
return response.data!;
|
||
} else {
|
||
throw Exception(response.message);
|
||
}
|
||
} catch (e) {
|
||
throw Exception('获取词汇书分类失败: $e');
|
||
}
|
||
}
|
||
|
||
/// 获取用户词汇书列表
|
||
Future<List<VocabularyBook>> getUserVocabularyBooks() async {
|
||
try {
|
||
final response = await _enhancedApiService.get<List<VocabularyBook>>(
|
||
'/vocabulary/books/user',
|
||
cacheDuration: _shortCacheDuration,
|
||
fromJson: (data) {
|
||
final List<dynamic> 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<VocabularyBook> getVocabularyBookDetail(String bookId) async {
|
||
try {
|
||
final response = await _enhancedApiService.get<VocabularyBook>(
|
||
'/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<List<VocabularyBookWord>> getVocabularyBookWords(
|
||
String bookId, {
|
||
int page = 1,
|
||
int limit = 50,
|
||
}) async {
|
||
try {
|
||
final response = await _enhancedApiService.get<List<VocabularyBookWord>>(
|
||
'/vocabulary/books/$bookId/words',
|
||
queryParameters: {
|
||
'page': page,
|
||
'limit': limit,
|
||
},
|
||
cacheDuration: Duration.zero, // 暂时禁用缓存
|
||
fromJson: (data) {
|
||
// 处理分页响应结构: data.items
|
||
final responseData = data['data'];
|
||
final List<dynamic> 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<UserVocabularyBookProgress> getVocabularyBookProgress(
|
||
String bookId, {
|
||
bool forceRefresh = false, // 是否强制刷新(跳过缓存)
|
||
}) async {
|
||
try {
|
||
final response = await _enhancedApiService.get<UserVocabularyBookProgress>(
|
||
'/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<void> addVocabularyBookToUser(String bookId) async {
|
||
try {
|
||
await _apiClient.post('/vocabulary/books/$bookId/add');
|
||
} catch (e) {
|
||
throw Exception('添加词汇书失败: $e');
|
||
}
|
||
}
|
||
|
||
/// 创建自定义词汇书
|
||
Future<VocabularyBook> createCustomVocabularyBook({
|
||
required String name,
|
||
String? description,
|
||
List<String> 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<List<Word>> 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<dynamic> data = response.data['data'] ?? [];
|
||
return data.map((json) => Word.fromJson(json)).toList();
|
||
} catch (e) {
|
||
throw Exception('搜索单词失败: $e');
|
||
}
|
||
}
|
||
|
||
/// 获取单词详情
|
||
Future<Word> 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<UserWordProgress?> 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<UserWordProgress> 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<StudySession> 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<StudySession> endStudySession(
|
||
String sessionId, {
|
||
required int durationSeconds,
|
||
required List<WordExerciseRecord> 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<List<StudySession>> 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<dynamic> data = response.data['data'] ?? [];
|
||
return data.map((json) => StudySession.fromJson(json)).toList();
|
||
} catch (e) {
|
||
throw Exception('获取学习会话历史失败: $e');
|
||
}
|
||
}
|
||
|
||
// ==================== 学习统计相关 ====================
|
||
|
||
/// 获取学习统计
|
||
Future<StudyStatistics> 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<DailyStats> getDailyVocabularyStats({required String userId}) async {
|
||
try {
|
||
final response = await _enhancedApiService.get<DailyStats>(
|
||
'/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<Map<String, dynamic>> getUserVocabularyStats() async {
|
||
try {
|
||
final response = await _enhancedApiService.get<Map<String, dynamic>>(
|
||
'/vocabulary/stats',
|
||
fromJson: (data) => Map<String, dynamic>.from(data['data'] ?? data),
|
||
);
|
||
|
||
if (response.success && response.data != null) {
|
||
return response.data!;
|
||
} else {
|
||
throw Exception(response.message);
|
||
}
|
||
} catch (e) {
|
||
throw Exception('获取用户词汇整体统计失败: $e');
|
||
}
|
||
}
|
||
|
||
/// 获取学习统计历史
|
||
Future<List<StudyStatistics>> 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<dynamic> data = response.data['data'] ?? [];
|
||
return data.map((json) => StudyStatistics.fromJson(json)).toList();
|
||
} catch (e) {
|
||
throw Exception('获取学习统计历史失败: $e');
|
||
}
|
||
}
|
||
|
||
// ==================== 智能学习算法 ====================
|
||
|
||
/// 获取今日需要学习的单词
|
||
Future<List<Word>> 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<dynamic> 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<String, dynamic>)).toList();
|
||
} catch (e, stackTrace) {
|
||
// API调用失败,返回空列表,让UI显示空状态
|
||
print('getTodayStudyWords API调用失败: $e');
|
||
print('堆栈跟踪: $stackTrace');
|
||
return [];
|
||
}
|
||
}
|
||
|
||
/// 获取需要复习的单词
|
||
Future<List<Word>> 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<dynamic> 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<Map<String, dynamic>> 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<void> cacheVocabularyBook(VocabularyBook book) async {
|
||
try {
|
||
await _storageService.setString(
|
||
'vocabulary_book_${book.id}',
|
||
book.toJson().toString(),
|
||
);
|
||
} catch (e) {
|
||
throw Exception('缓存词汇书失败: $e');
|
||
}
|
||
}
|
||
|
||
/// 从本地获取缓存的词汇书
|
||
Future<VocabularyBook?> 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<void> 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<String> generateDistractors(
|
||
String correctAnswer,
|
||
List<String> wordPool,
|
||
int count,
|
||
) {
|
||
final distractors = <String>[];
|
||
final shuffled = List<String>.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<Word> _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 = <Word>[];
|
||
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<Word> 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;
|
||
}
|
||
}
|
||
} |