Files
ai_english/client/lib/features/vocabulary/models/word_model.dart

519 lines
12 KiB
Dart
Raw Normal View History

2025-11-17 14:09:17 +08:00
import 'package:json_annotation/json_annotation.dart';
part 'word_model.g.dart';
/// 单词难度等级
enum WordDifficulty {
@JsonValue('beginner')
beginner,
@JsonValue('elementary')
elementary,
@JsonValue('intermediate')
intermediate,
@JsonValue('advanced')
advanced,
@JsonValue('expert')
expert,
}
/// 单词类型
enum WordType {
@JsonValue('noun')
noun,
@JsonValue('verb')
verb,
@JsonValue('adjective')
adjective,
@JsonValue('adverb')
adverb,
@JsonValue('preposition')
preposition,
@JsonValue('conjunction')
conjunction,
@JsonValue('interjection')
interjection,
@JsonValue('pronoun')
pronoun,
@JsonValue('article')
article,
@JsonValue('phrase')
phrase,
}
/// 学习状态
enum LearningStatus {
@JsonValue('new')
newWord,
@JsonValue('learning')
learning,
@JsonValue('reviewing')
reviewing,
@JsonValue('mastered')
mastered,
@JsonValue('forgotten')
forgotten,
}
/// 单词模型
@JsonSerializable()
class Word {
/// 单词ID
@JsonKey(name: 'id', fromJson: _idFromJson)
final String id;
/// 处理id字段的类型转换后端返回int64
static String _idFromJson(dynamic value) {
if (value is int) {
return value.toString();
}
return value.toString();
}
/// 单词
final String word;
/// 音标
final String? phonetic;
/// 音频URL
@JsonKey(name: 'audio_url')
final String? audioUrl;
/// 词性和释义列表
@JsonKey(defaultValue: [])
final List<WordDefinition> definitions;
/// 例句列表
@JsonKey(defaultValue: [])
final List<WordExample> examples;
/// 同义词
@JsonKey(defaultValue: [])
final List<String> synonyms;
/// 反义词
@JsonKey(defaultValue: [])
final List<String> antonyms;
/// 词根词缀
final WordEtymology? etymology;
/// 难度等级
@JsonKey(name: 'difficulty', fromJson: _difficultyFromJson)
final WordDifficulty difficulty;
/// 频率等级 (1-5)
@JsonKey(defaultValue: 0)
final int frequency;
/// 图片URL
@JsonKey(name: 'image_url')
final String? imageUrl;
/// 记忆技巧
@JsonKey(name: 'memory_tip')
final String? memoryTip;
/// 创建时间
@JsonKey(name: 'created_at')
final DateTime createdAt;
/// 更新时间
@JsonKey(name: 'updated_at')
final DateTime updatedAt;
const Word({
required this.id,
required this.word,
this.phonetic,
this.audioUrl,
required this.definitions,
this.examples = const [],
this.synonyms = const [],
this.antonyms = const [],
this.etymology,
required this.difficulty,
required this.frequency,
this.imageUrl,
this.memoryTip,
required this.createdAt,
required this.updatedAt,
});
factory Word.fromJson(Map<String, dynamic> json) => _$WordFromJson(json);
Map<String, dynamic> toJson() => _$WordToJson(this);
/// 处理difficulty字段后端可能为null、空字符串或其他值
static WordDifficulty _difficultyFromJson(dynamic value) {
if (value == null || value == '') return WordDifficulty.beginner;
final stringValue = value.toString().toLowerCase();
switch (stringValue) {
case 'beginner':
case '1':
return WordDifficulty.beginner;
case 'elementary':
case '2':
return WordDifficulty.elementary;
case 'intermediate':
case '3':
return WordDifficulty.intermediate;
case 'advanced':
case '4':
return WordDifficulty.advanced;
case 'expert':
case '5':
return WordDifficulty.expert;
default:
return WordDifficulty.beginner; // 默认为初级
}
}
Word copyWith({
String? id,
String? word,
String? phonetic,
String? audioUrl,
List<WordDefinition>? definitions,
List<WordExample>? examples,
List<String>? synonyms,
List<String>? antonyms,
WordEtymology? etymology,
WordDifficulty? difficulty,
int? frequency,
String? imageUrl,
String? memoryTip,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return Word(
id: id ?? this.id,
word: word ?? this.word,
phonetic: phonetic ?? this.phonetic,
audioUrl: audioUrl ?? this.audioUrl,
definitions: definitions ?? this.definitions,
examples: examples ?? this.examples,
synonyms: synonyms ?? this.synonyms,
antonyms: antonyms ?? this.antonyms,
etymology: etymology ?? this.etymology,
difficulty: difficulty ?? this.difficulty,
frequency: frequency ?? this.frequency,
imageUrl: imageUrl ?? this.imageUrl,
memoryTip: memoryTip ?? this.memoryTip,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
}
/// 单词释义
@JsonSerializable()
class WordDefinition {
/// 词性
@JsonKey(name: 'type', fromJson: _typeFromJson)
final WordType type;
/// 释义
final String definition;
/// 中文翻译
@JsonKey(name: 'translation', fromJson: _translationFromJson)
final String translation;
/// 使用频率 (1-5)
@JsonKey(defaultValue: 3)
final int frequency;
const WordDefinition({
required this.type,
required this.definition,
required this.translation,
this.frequency = 3,
});
/// 处理type字段后端可能为null或空字符串
static WordType _typeFromJson(dynamic value) {
if (value == null || value == '') return WordType.noun;
final stringValue = value.toString().toLowerCase();
switch (stringValue) {
case 'noun':
case 'n':
return WordType.noun;
case 'verb':
case 'v':
return WordType.verb;
case 'adjective':
case 'adj':
return WordType.adjective;
case 'adverb':
case 'adv':
return WordType.adverb;
case 'preposition':
case 'prep':
return WordType.preposition;
case 'conjunction':
case 'conj':
return WordType.conjunction;
case 'interjection':
case 'interj':
return WordType.interjection;
case 'pronoun':
case 'pron':
return WordType.pronoun;
case 'article':
case 'art':
return WordType.article;
case 'phrase':
return WordType.phrase;
default:
return WordType.noun; // 默认为名词
}
}
/// 处理translation字段后端可能为null
static String _translationFromJson(dynamic value) {
if (value == null) return '';
return value.toString();
}
factory WordDefinition.fromJson(Map<String, dynamic> json) => _$WordDefinitionFromJson(json);
Map<String, dynamic> toJson() => _$WordDefinitionToJson(this);
WordDefinition copyWith({
WordType? type,
String? definition,
String? translation,
int? frequency,
}) {
return WordDefinition(
type: type ?? this.type,
definition: definition ?? this.definition,
translation: translation ?? this.translation,
frequency: frequency ?? this.frequency,
);
}
}
/// 单词例句
@JsonSerializable()
class WordExample {
/// 例句
@JsonKey(name: 'example', fromJson: _sentenceFromJson)
final String sentence;
/// 中文翻译
@JsonKey(name: 'translation', fromJson: _exampleTranslationFromJson)
final String translation;
/// 音频URL
@JsonKey(name: 'audio_url')
final String? audioUrl;
/// 来源
final String? source;
const WordExample({
required this.sentence,
required this.translation,
this.audioUrl,
this.source,
});
/// 处理example字段映射到sentence
static String _sentenceFromJson(dynamic value) {
if (value == null) return '';
return value.toString();
}
/// 处理translation字段后端可能为null
static String _exampleTranslationFromJson(dynamic value) {
if (value == null) return '';
return value.toString();
}
factory WordExample.fromJson(Map<String, dynamic> json) => _$WordExampleFromJson(json);
Map<String, dynamic> toJson() => _$WordExampleToJson(this);
WordExample copyWith({
String? sentence,
String? translation,
String? audioUrl,
String? source,
}) {
return WordExample(
sentence: sentence ?? this.sentence,
translation: translation ?? this.translation,
audioUrl: audioUrl ?? this.audioUrl,
source: source ?? this.source,
);
}
}
/// 词根词缀
@JsonSerializable()
class WordEtymology {
/// 词根
final List<String> roots;
/// 前缀
final List<String> prefixes;
/// 后缀
final List<String> suffixes;
/// 词源说明
final String? origin;
const WordEtymology({
this.roots = const [],
this.prefixes = const [],
this.suffixes = const [],
this.origin,
});
factory WordEtymology.fromJson(Map<String, dynamic> json) => _$WordEtymologyFromJson(json);
Map<String, dynamic> toJson() => _$WordEtymologyToJson(this);
WordEtymology copyWith({
List<String>? roots,
List<String>? prefixes,
List<String>? suffixes,
String? origin,
}) {
return WordEtymology(
roots: roots ?? this.roots,
prefixes: prefixes ?? this.prefixes,
suffixes: suffixes ?? this.suffixes,
origin: origin ?? this.origin,
);
}
}
/// 用户单词学习记录
@JsonSerializable()
class UserWordProgress {
/// 记录ID
@JsonKey(fromJson: _idFromJson)
final String id;
/// 用户ID
@JsonKey(name: 'user_id', fromJson: _userIdFromJson)
final String userId;
/// 单词ID
@JsonKey(name: 'vocabulary_id', fromJson: _wordIdFromJson)
final String wordId;
/// 学习状态
final LearningStatus status;
/// 学习次数
@JsonKey(name: 'study_count')
final int studyCount;
/// 正确次数
@JsonKey(name: 'correct_count')
final int correctCount;
/// 错误次数
@JsonKey(name: 'wrong_count')
final int wrongCount;
/// 熟练度 (0-100)
final int proficiency;
/// 下次复习时间
@JsonKey(name: 'next_review_at')
final DateTime? nextReviewAt;
/// 复习间隔 (天)
@JsonKey(name: 'review_interval')
final int reviewInterval;
/// 首次学习时间
@JsonKey(name: 'first_studied_at')
final DateTime firstStudiedAt;
/// 最后学习时间
@JsonKey(name: 'last_studied_at')
final DateTime lastStudiedAt;
/// 掌握时间
@JsonKey(name: 'mastered_at')
final DateTime? masteredAt;
const UserWordProgress({
required this.id,
required this.userId,
required this.wordId,
required this.status,
this.studyCount = 0,
this.correctCount = 0,
this.wrongCount = 0,
this.proficiency = 0,
this.nextReviewAt,
this.reviewInterval = 1,
required this.firstStudiedAt,
required this.lastStudiedAt,
this.masteredAt,
});
factory UserWordProgress.fromJson(Map<String, dynamic> json) => _$UserWordProgressFromJson(json);
Map<String, dynamic> toJson() => _$UserWordProgressToJson(this);
UserWordProgress copyWith({
String? id,
String? userId,
String? wordId,
LearningStatus? status,
int? studyCount,
int? correctCount,
int? wrongCount,
int? proficiency,
DateTime? nextReviewAt,
int? reviewInterval,
DateTime? firstStudiedAt,
DateTime? lastStudiedAt,
DateTime? masteredAt,
}) {
return UserWordProgress(
id: id ?? this.id,
userId: userId ?? this.userId,
wordId: wordId ?? this.wordId,
status: status ?? this.status,
studyCount: studyCount ?? this.studyCount,
correctCount: correctCount ?? this.correctCount,
wrongCount: wrongCount ?? this.wrongCount,
proficiency: proficiency ?? this.proficiency,
nextReviewAt: nextReviewAt ?? this.nextReviewAt,
reviewInterval: reviewInterval ?? this.reviewInterval,
firstStudiedAt: firstStudiedAt ?? this.firstStudiedAt,
lastStudiedAt: lastStudiedAt ?? this.lastStudiedAt,
masteredAt: masteredAt ?? this.masteredAt,
);
}
/// 计算学习准确率
double get accuracy {
if (studyCount == 0) return 0.0;
return correctCount / studyCount;
}
/// 是否需要复习
bool get needsReview {
if (nextReviewAt == null) return false;
return DateTime.now().isAfter(nextReviewAt!);
}
/// 是否为新单词
bool get isNew => status == LearningStatus.newWord;
/// 是否已掌握
bool get isMastered => status == LearningStatus.mastered;
/// 类型转换方法
static String _idFromJson(dynamic value) => value.toString();
static String _userIdFromJson(dynamic value) => value.toString();
static String _wordIdFromJson(dynamic value) => value.toString();
}