Files
ai_english/client/lib/features/vocabulary/models/vocabulary_book_model.dart
2025-11-17 14:09:17 +08:00

388 lines
10 KiB
Dart
Raw 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.

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,
);
}
}