388 lines
10 KiB
Dart
388 lines
10 KiB
Dart
import 'package:json_annotation/json_annotation.dart';
|
||
import 'word_model.dart';
|
||
import 'vocabulary_book_category.dart';
|
||
|
||
part 'vocabulary_book_model.g.dart';
|
||
|
||
/// 词汇书类型
|
||
enum VocabularyBookType {
|
||
@JsonValue('system')
|
||
system, // 系统词汇书
|
||
@JsonValue('custom')
|
||
custom, // 用户自定义词汇书
|
||
@JsonValue('shared')
|
||
shared, // 共享词汇书
|
||
}
|
||
|
||
/// 词汇书难度
|
||
enum VocabularyBookDifficulty {
|
||
@JsonValue('beginner')
|
||
beginner,
|
||
@JsonValue('elementary')
|
||
elementary,
|
||
@JsonValue('intermediate')
|
||
intermediate,
|
||
@JsonValue('advanced')
|
||
advanced,
|
||
@JsonValue('expert')
|
||
expert,
|
||
}
|
||
|
||
/// 词汇书模型
|
||
@JsonSerializable()
|
||
class VocabularyBook {
|
||
/// 词汇书ID
|
||
final String id;
|
||
|
||
/// 词汇书名称
|
||
final String name;
|
||
|
||
/// 词汇书描述
|
||
final String? description;
|
||
|
||
/// 词汇书类型
|
||
@JsonKey(defaultValue: VocabularyBookType.system)
|
||
final VocabularyBookType type;
|
||
|
||
/// 难度等级
|
||
@JsonKey(name: 'level')
|
||
final VocabularyBookDifficulty difficulty;
|
||
|
||
/// 封面图片URL
|
||
@JsonKey(name: 'cover_image')
|
||
final String? coverImageUrl;
|
||
|
||
/// 单词总数
|
||
@JsonKey(name: 'total_words')
|
||
final int totalWords;
|
||
|
||
/// 创建者ID
|
||
@JsonKey(name: 'creator_id')
|
||
final String? creatorId;
|
||
|
||
/// 创建者名称
|
||
@JsonKey(name: 'creator_name')
|
||
final String? creatorName;
|
||
|
||
/// 是否公开
|
||
@JsonKey(name: 'is_public', defaultValue: false)
|
||
final bool isPublic;
|
||
|
||
/// 标签列表
|
||
@JsonKey(defaultValue: [])
|
||
final List<String> tags;
|
||
|
||
/// 分类
|
||
final String? category;
|
||
|
||
/// 主分类
|
||
@JsonKey(name: 'main_category')
|
||
final VocabularyBookMainCategory? mainCategory;
|
||
|
||
/// 子分类(JSON字符串,根据主分类解析为对应的子分类枚举)
|
||
@JsonKey(name: 'sub_category')
|
||
final String? subCategory;
|
||
|
||
/// 适用等级
|
||
@JsonKey(name: 'target_levels', defaultValue: [])
|
||
final List<String> targetLevels;
|
||
|
||
/// 预计学习天数
|
||
@JsonKey(name: 'estimated_days', defaultValue: 30)
|
||
final int estimatedDays;
|
||
|
||
/// 每日学习单词数
|
||
@JsonKey(name: 'daily_word_count', defaultValue: 20)
|
||
final int dailyWordCount;
|
||
|
||
/// 下载次数
|
||
@JsonKey(name: 'download_count', defaultValue: 0)
|
||
final int downloadCount;
|
||
|
||
/// 评分 (1-5)
|
||
@JsonKey(defaultValue: 0.0)
|
||
final double rating;
|
||
|
||
/// 评价数量
|
||
@JsonKey(name: 'review_count', defaultValue: 0)
|
||
final int reviewCount;
|
||
|
||
/// 创建时间
|
||
@JsonKey(name: 'created_at')
|
||
final DateTime createdAt;
|
||
|
||
/// 更新时间
|
||
@JsonKey(name: 'updated_at')
|
||
final DateTime updatedAt;
|
||
|
||
const VocabularyBook({
|
||
required this.id,
|
||
required this.name,
|
||
this.description,
|
||
required this.type,
|
||
required this.difficulty,
|
||
this.coverImageUrl,
|
||
required this.totalWords,
|
||
this.creatorId,
|
||
this.creatorName,
|
||
this.isPublic = false,
|
||
this.tags = const [],
|
||
this.category,
|
||
this.mainCategory,
|
||
this.subCategory,
|
||
this.targetLevels = const [],
|
||
this.estimatedDays = 30,
|
||
this.dailyWordCount = 20,
|
||
this.downloadCount = 0,
|
||
this.rating = 0.0,
|
||
this.reviewCount = 0,
|
||
required this.createdAt,
|
||
required this.updatedAt,
|
||
});
|
||
|
||
factory VocabularyBook.fromJson(Map<String, dynamic> json) {
|
||
final book = _$VocabularyBookFromJson(json);
|
||
// 如果有category字符串但没有mainCategory,则从category映射
|
||
if (book.category != null && book.mainCategory == null) {
|
||
return book.copyWith(
|
||
mainCategory: VocabularyBookCategoryHelper.getMainCategoryFromName(book.category),
|
||
);
|
||
}
|
||
return book;
|
||
}
|
||
|
||
Map<String, dynamic> toJson() => _$VocabularyBookToJson(this);
|
||
|
||
VocabularyBook copyWith({
|
||
String? id,
|
||
String? name,
|
||
String? description,
|
||
VocabularyBookType? type,
|
||
VocabularyBookDifficulty? difficulty,
|
||
String? coverImageUrl,
|
||
int? totalWords,
|
||
String? creatorId,
|
||
String? creatorName,
|
||
bool? isPublic,
|
||
List<String>? tags,
|
||
String? category,
|
||
VocabularyBookMainCategory? mainCategory,
|
||
String? subCategory,
|
||
List<String>? targetLevels,
|
||
int? estimatedDays,
|
||
int? dailyWordCount,
|
||
int? downloadCount,
|
||
double? rating,
|
||
int? reviewCount,
|
||
DateTime? createdAt,
|
||
DateTime? updatedAt,
|
||
}) {
|
||
return VocabularyBook(
|
||
id: id ?? this.id,
|
||
name: name ?? this.name,
|
||
description: description ?? this.description,
|
||
type: type ?? this.type,
|
||
difficulty: difficulty ?? this.difficulty,
|
||
coverImageUrl: coverImageUrl ?? this.coverImageUrl,
|
||
totalWords: totalWords ?? this.totalWords,
|
||
creatorId: creatorId ?? this.creatorId,
|
||
creatorName: creatorName ?? this.creatorName,
|
||
isPublic: isPublic ?? this.isPublic,
|
||
tags: tags ?? this.tags,
|
||
category: category ?? this.category,
|
||
mainCategory: mainCategory ?? this.mainCategory,
|
||
subCategory: subCategory ?? this.subCategory,
|
||
targetLevels: targetLevels ?? this.targetLevels,
|
||
estimatedDays: estimatedDays ?? this.estimatedDays,
|
||
dailyWordCount: dailyWordCount ?? this.dailyWordCount,
|
||
downloadCount: downloadCount ?? this.downloadCount,
|
||
rating: rating ?? this.rating,
|
||
reviewCount: reviewCount ?? this.reviewCount,
|
||
createdAt: createdAt ?? this.createdAt,
|
||
updatedAt: updatedAt ?? this.updatedAt,
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 用户词汇书学习进度
|
||
@JsonSerializable()
|
||
class UserVocabularyBookProgress {
|
||
/// 进度ID
|
||
@JsonKey(fromJson: _idFromJson)
|
||
final String id;
|
||
|
||
/// 用户ID
|
||
@JsonKey(name: 'user_id', fromJson: _userIdFromJson)
|
||
final String userId;
|
||
|
||
/// 词汇书ID
|
||
@JsonKey(name: 'book_id')
|
||
final String vocabularyBookId;
|
||
|
||
/// 已学习单词数
|
||
@JsonKey(name: 'learned_words')
|
||
final int learnedWords;
|
||
|
||
/// 已掌握单词数
|
||
@JsonKey(name: 'mastered_words')
|
||
final int masteredWords;
|
||
|
||
/// 学习进度百分比 (0-100)
|
||
@JsonKey(name: 'progress_percentage')
|
||
final double progressPercentage;
|
||
|
||
/// 连续学习天数
|
||
@JsonKey(name: 'streak_days')
|
||
final int streakDays;
|
||
|
||
/// 总学习天数
|
||
@JsonKey(name: 'total_study_days')
|
||
final int totalStudyDays;
|
||
|
||
/// 平均每日学习单词数
|
||
@JsonKey(name: 'average_daily_words')
|
||
final double averageDailyWords;
|
||
|
||
/// 预计完成时间
|
||
@JsonKey(name: 'estimated_completion_date')
|
||
final DateTime? estimatedCompletionDate;
|
||
|
||
/// 是否已完成
|
||
@JsonKey(name: 'is_completed')
|
||
final bool isCompleted;
|
||
|
||
/// 完成时间
|
||
@JsonKey(name: 'completed_at')
|
||
final DateTime? completedAt;
|
||
|
||
/// 开始学习时间
|
||
@JsonKey(name: 'started_at')
|
||
final DateTime startedAt;
|
||
|
||
/// 最后学习时间
|
||
@JsonKey(name: 'last_studied_at')
|
||
final DateTime? lastStudiedAt;
|
||
|
||
const UserVocabularyBookProgress({
|
||
required this.id,
|
||
required this.userId,
|
||
required this.vocabularyBookId,
|
||
this.learnedWords = 0,
|
||
this.masteredWords = 0,
|
||
this.progressPercentage = 0.0,
|
||
this.streakDays = 0,
|
||
this.totalStudyDays = 0,
|
||
this.averageDailyWords = 0.0,
|
||
this.estimatedCompletionDate,
|
||
this.isCompleted = false,
|
||
this.completedAt,
|
||
required this.startedAt,
|
||
this.lastStudiedAt,
|
||
});
|
||
|
||
factory UserVocabularyBookProgress.fromJson(Map<String, dynamic> json) => _$UserVocabularyBookProgressFromJson(json);
|
||
Map<String, dynamic> toJson() => _$UserVocabularyBookProgressToJson(this);
|
||
|
||
/// 类型转换方法
|
||
static String _idFromJson(dynamic value) => value?.toString() ?? '0';
|
||
static String _userIdFromJson(dynamic value) => value?.toString() ?? '0';
|
||
|
||
UserVocabularyBookProgress copyWith({
|
||
String? id,
|
||
String? userId,
|
||
String? vocabularyBookId,
|
||
int? learnedWords,
|
||
int? masteredWords,
|
||
double? progressPercentage,
|
||
int? streakDays,
|
||
int? totalStudyDays,
|
||
double? averageDailyWords,
|
||
DateTime? estimatedCompletionDate,
|
||
bool? isCompleted,
|
||
DateTime? completedAt,
|
||
DateTime? startedAt,
|
||
DateTime? lastStudiedAt,
|
||
}) {
|
||
return UserVocabularyBookProgress(
|
||
id: id ?? this.id,
|
||
userId: userId ?? this.userId,
|
||
vocabularyBookId: vocabularyBookId ?? this.vocabularyBookId,
|
||
learnedWords: learnedWords ?? this.learnedWords,
|
||
masteredWords: masteredWords ?? this.masteredWords,
|
||
progressPercentage: progressPercentage ?? this.progressPercentage,
|
||
streakDays: streakDays ?? this.streakDays,
|
||
totalStudyDays: totalStudyDays ?? this.totalStudyDays,
|
||
averageDailyWords: averageDailyWords ?? this.averageDailyWords,
|
||
estimatedCompletionDate: estimatedCompletionDate ?? this.estimatedCompletionDate,
|
||
isCompleted: isCompleted ?? this.isCompleted,
|
||
completedAt: completedAt ?? this.completedAt,
|
||
startedAt: startedAt ?? this.startedAt,
|
||
lastStudiedAt: lastStudiedAt ?? this.lastStudiedAt,
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 词汇书单词关联
|
||
@JsonSerializable()
|
||
class VocabularyBookWord {
|
||
/// 关联ID
|
||
@JsonKey(name: 'id', fromJson: _idFromJson)
|
||
final String id;
|
||
|
||
/// 词汇书ID
|
||
@JsonKey(name: 'book_id')
|
||
final String vocabularyBookId;
|
||
|
||
/// 单词ID
|
||
@JsonKey(name: 'vocabulary_id')
|
||
final String wordId;
|
||
|
||
/// 在词汇书中的顺序
|
||
@JsonKey(name: 'sort_order', defaultValue: 0)
|
||
final int order;
|
||
|
||
/// 单词信息
|
||
final Word? word;
|
||
|
||
/// 添加时间
|
||
@JsonKey(name: 'created_at')
|
||
final DateTime addedAt;
|
||
|
||
const VocabularyBookWord({
|
||
required this.id,
|
||
required this.vocabularyBookId,
|
||
required this.wordId,
|
||
required this.order,
|
||
this.word,
|
||
required this.addedAt,
|
||
});
|
||
|
||
/// 处理id字段的类型转换(后端可能返回int或string)
|
||
static String _idFromJson(dynamic value) {
|
||
if (value is int) {
|
||
return value.toString();
|
||
}
|
||
return value.toString();
|
||
}
|
||
|
||
factory VocabularyBookWord.fromJson(Map<String, dynamic> json) => _$VocabularyBookWordFromJson(json);
|
||
Map<String, dynamic> toJson() => _$VocabularyBookWordToJson(this);
|
||
|
||
VocabularyBookWord copyWith({
|
||
String? id,
|
||
String? vocabularyBookId,
|
||
String? wordId,
|
||
int? order,
|
||
Word? word,
|
||
DateTime? addedAt,
|
||
}) {
|
||
return VocabularyBookWord(
|
||
id: id ?? this.id,
|
||
vocabularyBookId: vocabularyBookId ?? this.vocabularyBookId,
|
||
wordId: wordId ?? this.wordId,
|
||
order: order ?? this.order,
|
||
word: word ?? this.word,
|
||
addedAt: addedAt ?? this.addedAt,
|
||
);
|
||
}
|
||
} |