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

519 lines
12 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';
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();
}