This commit is contained in:
sjk
2025-11-17 13:39:05 +08:00
commit d4cfe2b9de
479 changed files with 109324 additions and 0 deletions

View File

@@ -0,0 +1,260 @@
/// API响应基础模型
class ApiResponse<T> {
final bool success;
final String message;
final T? data;
final String? error;
final int? code;
final Map<String, dynamic>? meta;
const ApiResponse({
required this.success,
required this.message,
this.data,
this.error,
this.code,
this.meta,
});
factory ApiResponse.fromJson(
Map<String, dynamic> json,
T Function(dynamic)? fromJsonT,
) {
return ApiResponse<T>(
success: json['success'] as bool? ?? false,
message: json['message'] as String? ?? '',
data: json['data'] != null && fromJsonT != null
? fromJsonT(json['data'])
: json['data'] as T?,
error: json['error'] as String?,
code: json['code'] as int?,
meta: json['meta'] as Map<String, dynamic>?,
);
}
Map<String, dynamic> toJson() {
return {
'success': success,
'message': message,
'data': data,
'error': error,
'code': code,
'meta': meta,
};
}
/// 成功响应
factory ApiResponse.success({
required String message,
T? data,
Map<String, dynamic>? meta,
}) {
return ApiResponse<T>(
success: true,
message: message,
data: data,
meta: meta,
);
}
/// 失败响应
factory ApiResponse.failure({
required String message,
String? error,
int? code,
Map<String, dynamic>? meta,
}) {
return ApiResponse<T>(
success: false,
message: message,
error: error,
code: code,
meta: meta,
);
}
/// 是否成功
bool get isSuccess => success;
/// 是否失败
bool get isFailure => !success;
@override
String toString() {
return 'ApiResponse(success: $success, message: $message, data: $data, error: $error)';
}
}
/// 分页响应模型
class PaginatedResponse<T> {
final List<T> data;
final PaginationMeta pagination;
const PaginatedResponse({
required this.data,
required this.pagination,
});
factory PaginatedResponse.fromJson(
Map<String, dynamic> json,
T Function(Map<String, dynamic>) fromJsonT,
) {
return PaginatedResponse<T>(
data: (json['data'] as List)
.map((e) => fromJsonT(e as Map<String, dynamic>))
.toList(),
pagination: PaginationMeta.fromJson(json['pagination'] as Map<String, dynamic>),
);
}
Map<String, dynamic> toJson() {
return {
'data': data,
'pagination': pagination.toJson(),
};
}
}
/// 分页元数据模型
class PaginationMeta {
final int currentPage;
final int totalPages;
final int totalItems;
final int itemsPerPage;
final bool hasNextPage;
final bool hasPreviousPage;
const PaginationMeta({
required this.currentPage,
required this.totalPages,
required this.totalItems,
required this.itemsPerPage,
required this.hasNextPage,
required this.hasPreviousPage,
});
factory PaginationMeta.fromJson(Map<String, dynamic> json) {
return PaginationMeta(
currentPage: json['current_page'] as int,
totalPages: json['total_pages'] as int,
totalItems: json['total_items'] as int,
itemsPerPage: json['items_per_page'] as int,
hasNextPage: json['has_next_page'] as bool,
hasPreviousPage: json['has_previous_page'] as bool,
);
}
Map<String, dynamic> toJson() {
return {
'current_page': currentPage,
'total_pages': totalPages,
'total_items': totalItems,
'items_per_page': itemsPerPage,
'has_next_page': hasNextPage,
'has_previous_page': hasPreviousPage,
};
}
}
/// 认证响应模型
class AuthResponse {
final String accessToken;
final String refreshToken;
final String tokenType;
final int expiresIn;
final Map<String, dynamic>? user;
const AuthResponse({
required this.accessToken,
required this.refreshToken,
required this.tokenType,
required this.expiresIn,
this.user,
});
factory AuthResponse.fromJson(Map<String, dynamic> json) {
return AuthResponse(
accessToken: json['access_token'] as String,
refreshToken: json['refresh_token'] as String,
tokenType: json['token_type'] as String? ?? 'Bearer',
expiresIn: json['expires_in'] as int,
user: json['user'] as Map<String, dynamic>?,
);
}
Map<String, dynamic> toJson() {
return {
'access_token': accessToken,
'refresh_token': refreshToken,
'token_type': tokenType,
'expires_in': expiresIn,
'user': user,
};
}
}
/// 错误响应模型
class ErrorResponse {
final String message;
final String? code;
final List<ValidationError>? validationErrors;
final Map<String, dynamic>? details;
const ErrorResponse({
required this.message,
this.code,
this.validationErrors,
this.details,
});
factory ErrorResponse.fromJson(Map<String, dynamic> json) {
return ErrorResponse(
message: json['message'] as String,
code: json['code'] as String?,
validationErrors: json['validation_errors'] != null
? (json['validation_errors'] as List)
.map((e) => ValidationError.fromJson(e as Map<String, dynamic>))
.toList()
: null,
details: json['details'] as Map<String, dynamic>?,
);
}
Map<String, dynamic> toJson() {
return {
'message': message,
'code': code,
'validation_errors': validationErrors?.map((e) => e.toJson()).toList(),
'details': details,
};
}
}
/// 验证错误模型
class ValidationError {
final String field;
final String message;
final String? code;
const ValidationError({
required this.field,
required this.message,
this.code,
});
factory ValidationError.fromJson(Map<String, dynamic> json) {
return ValidationError(
field: json['field'] as String,
message: json['message'] as String,
code: json['code'] as String?,
);
}
Map<String, dynamic> toJson() {
return {
'field': field,
'message': message,
'code': code,
};
}
}

View File

@@ -0,0 +1,90 @@
import 'package:json_annotation/json_annotation.dart';
part 'notification_model.g.dart';
/// 通知模型
@JsonSerializable()
class NotificationModel {
final int id;
@JsonKey(name: 'user_id')
final int userId;
final String type;
final String title;
final String content;
final String? link;
@JsonKey(name: 'is_read')
final bool isRead;
final int priority;
@JsonKey(name: 'created_at')
final DateTime createdAt;
@JsonKey(name: 'read_at')
final DateTime? readAt;
const NotificationModel({
required this.id,
required this.userId,
required this.type,
required this.title,
required this.content,
this.link,
required this.isRead,
required this.priority,
required this.createdAt,
this.readAt,
});
factory NotificationModel.fromJson(Map<String, dynamic> json) =>
_$NotificationModelFromJson(json);
Map<String, dynamic> toJson() => _$NotificationModelToJson(this);
/// 通知类型
String get typeLabel {
switch (type) {
case 'system':
return '系统通知';
case 'learning':
return '学习提醒';
case 'achievement':
return '成就通知';
default:
return '通知';
}
}
/// 优先级
String get priorityLabel {
switch (priority) {
case 0:
return '普通';
case 1:
return '重要';
case 2:
return '紧急';
default:
return '普通';
}
}
/// 格式化时间
String get timeAgo {
final now = DateTime.now();
final difference = now.difference(createdAt);
if (difference.inDays > 0) {
return '${difference.inDays}天前';
} else if (difference.inHours > 0) {
return '${difference.inHours}小时前';
} else if (difference.inMinutes > 0) {
return '${difference.inMinutes}分钟前';
} else {
return '刚刚';
}
}
}

View File

@@ -0,0 +1,37 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'notification_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
NotificationModel _$NotificationModelFromJson(Map<String, dynamic> json) =>
NotificationModel(
id: (json['id'] as num).toInt(),
userId: (json['user_id'] as num).toInt(),
type: json['type'] as String,
title: json['title'] as String,
content: json['content'] as String,
link: json['link'] as String?,
isRead: json['is_read'] as bool,
priority: (json['priority'] as num).toInt(),
createdAt: DateTime.parse(json['created_at'] as String),
readAt: json['read_at'] == null
? null
: DateTime.parse(json['read_at'] as String),
);
Map<String, dynamic> _$NotificationModelToJson(NotificationModel instance) =>
<String, dynamic>{
'id': instance.id,
'user_id': instance.userId,
'type': instance.type,
'title': instance.title,
'content': instance.content,
'link': instance.link,
'is_read': instance.isRead,
'priority': instance.priority,
'created_at': instance.createdAt.toIso8601String(),
'read_at': instance.readAt?.toIso8601String(),
};

View File

@@ -0,0 +1,220 @@
import 'package:json_annotation/json_annotation.dart';
part 'user_model.g.dart';
/// 用户模型
@JsonSerializable()
class UserModel {
@JsonKey(name: 'user_id')
final int userId;
final String username;
final String email;
final String? nickname;
final String? avatar;
final String? phone;
final DateTime? birthday;
final String? gender;
final String? bio;
@JsonKey(name: 'learning_level')
final String learningLevel;
@JsonKey(name: 'target_language')
final String targetLanguage;
@JsonKey(name: 'native_language')
final String nativeLanguage;
@JsonKey(name: 'daily_goal')
final int dailyGoal;
@JsonKey(name: 'study_streak')
final int studyStreak;
@JsonKey(name: 'total_study_days')
final int totalStudyDays;
@JsonKey(name: 'vocabulary_count')
final int vocabularyCount;
@JsonKey(name: 'experience_points')
final int experiencePoints;
@JsonKey(name: 'current_level')
final int currentLevel;
@JsonKey(name: 'created_at')
final DateTime createdAt;
@JsonKey(name: 'updated_at')
final DateTime updatedAt;
@JsonKey(name: 'last_login_at')
final DateTime? lastLoginAt;
@JsonKey(name: 'is_premium')
final bool isPremium;
@JsonKey(name: 'premium_expires_at')
final DateTime? premiumExpiresAt;
const UserModel({
required this.userId,
required this.username,
required this.email,
this.nickname,
this.avatar,
this.phone,
this.birthday,
this.gender,
this.bio,
required this.learningLevel,
required this.targetLanguage,
required this.nativeLanguage,
required this.dailyGoal,
required this.studyStreak,
required this.totalStudyDays,
required this.vocabularyCount,
required this.experiencePoints,
required this.currentLevel,
required this.createdAt,
required this.updatedAt,
this.lastLoginAt,
required this.isPremium,
this.premiumExpiresAt,
});
factory UserModel.fromJson(Map<String, dynamic> json) => _$UserModelFromJson(json);
Map<String, dynamic> toJson() => _$UserModelToJson(this);
UserModel copyWith({
int? userId,
String? username,
String? email,
String? nickname,
String? avatar,
String? phone,
DateTime? birthday,
String? gender,
String? bio,
String? learningLevel,
String? targetLanguage,
String? nativeLanguage,
int? dailyGoal,
int? studyStreak,
int? totalStudyDays,
int? vocabularyCount,
int? experiencePoints,
int? currentLevel,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? lastLoginAt,
bool? isPremium,
DateTime? premiumExpiresAt,
}) {
return UserModel(
userId: userId ?? this.userId,
username: username ?? this.username,
email: email ?? this.email,
nickname: nickname ?? this.nickname,
avatar: avatar ?? this.avatar,
phone: phone ?? this.phone,
birthday: birthday ?? this.birthday,
gender: gender ?? this.gender,
bio: bio ?? this.bio,
learningLevel: learningLevel ?? this.learningLevel,
targetLanguage: targetLanguage ?? this.targetLanguage,
nativeLanguage: nativeLanguage ?? this.nativeLanguage,
dailyGoal: dailyGoal ?? this.dailyGoal,
studyStreak: studyStreak ?? this.studyStreak,
totalStudyDays: totalStudyDays ?? this.totalStudyDays,
vocabularyCount: vocabularyCount ?? this.vocabularyCount,
experiencePoints: experiencePoints ?? this.experiencePoints,
currentLevel: currentLevel ?? this.currentLevel,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
lastLoginAt: lastLoginAt ?? this.lastLoginAt,
isPremium: isPremium ?? this.isPremium,
premiumExpiresAt: premiumExpiresAt ?? this.premiumExpiresAt,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is UserModel &&
other.userId == userId &&
other.username == username &&
other.email == email;
}
@override
int get hashCode {
return userId.hashCode ^
username.hashCode ^
email.hashCode;
}
@override
String toString() {
return 'UserModel(userId: $userId, username: $username, email: $email)';
}
}
/// 用户统计信息模型
@JsonSerializable()
class UserStatsModel {
@JsonKey(name: 'total_words_learned')
final int totalWordsLearned;
@JsonKey(name: 'words_learned_today')
final int wordsLearnedToday;
@JsonKey(name: 'study_time_today')
final int studyTimeToday; // 分钟
@JsonKey(name: 'total_study_time')
final int totalStudyTime; // 分钟
@JsonKey(name: 'listening_score')
final double listeningScore;
@JsonKey(name: 'reading_score')
final double readingScore;
@JsonKey(name: 'writing_score')
final double writingScore;
@JsonKey(name: 'speaking_score')
final double speakingScore;
@JsonKey(name: 'overall_score')
final double overallScore;
@JsonKey(name: 'weekly_progress')
final List<int> weeklyProgress;
@JsonKey(name: 'monthly_progress')
final List<int> monthlyProgress;
const UserStatsModel({
required this.totalWordsLearned,
required this.wordsLearnedToday,
required this.studyTimeToday,
required this.totalStudyTime,
required this.listeningScore,
required this.readingScore,
required this.writingScore,
required this.speakingScore,
required this.overallScore,
required this.weeklyProgress,
required this.monthlyProgress,
});
factory UserStatsModel.fromJson(Map<String, dynamic> json) => _$UserStatsModelFromJson(json);
Map<String, dynamic> toJson() => _$UserStatsModelToJson(this);
}

View File

@@ -0,0 +1,99 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'user_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
UserModel _$UserModelFromJson(Map<String, dynamic> json) => UserModel(
userId: (json['user_id'] as num).toInt(),
username: json['username'] as String,
email: json['email'] as String,
nickname: json['nickname'] as String?,
avatar: json['avatar'] as String?,
phone: json['phone'] as String?,
birthday: json['birthday'] == null
? null
: DateTime.parse(json['birthday'] as String),
gender: json['gender'] as String?,
bio: json['bio'] as String?,
learningLevel: json['learning_level'] as String,
targetLanguage: json['target_language'] as String,
nativeLanguage: json['native_language'] as String,
dailyGoal: (json['daily_goal'] as num).toInt(),
studyStreak: (json['study_streak'] as num).toInt(),
totalStudyDays: (json['total_study_days'] as num).toInt(),
vocabularyCount: (json['vocabulary_count'] as num).toInt(),
experiencePoints: (json['experience_points'] as num).toInt(),
currentLevel: (json['current_level'] as num).toInt(),
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
lastLoginAt: json['last_login_at'] == null
? null
: DateTime.parse(json['last_login_at'] as String),
isPremium: json['is_premium'] as bool,
premiumExpiresAt: json['premium_expires_at'] == null
? null
: DateTime.parse(json['premium_expires_at'] as String),
);
Map<String, dynamic> _$UserModelToJson(UserModel instance) => <String, dynamic>{
'user_id': instance.userId,
'username': instance.username,
'email': instance.email,
'nickname': instance.nickname,
'avatar': instance.avatar,
'phone': instance.phone,
'birthday': instance.birthday?.toIso8601String(),
'gender': instance.gender,
'bio': instance.bio,
'learning_level': instance.learningLevel,
'target_language': instance.targetLanguage,
'native_language': instance.nativeLanguage,
'daily_goal': instance.dailyGoal,
'study_streak': instance.studyStreak,
'total_study_days': instance.totalStudyDays,
'vocabulary_count': instance.vocabularyCount,
'experience_points': instance.experiencePoints,
'current_level': instance.currentLevel,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'last_login_at': instance.lastLoginAt?.toIso8601String(),
'is_premium': instance.isPremium,
'premium_expires_at': instance.premiumExpiresAt?.toIso8601String(),
};
UserStatsModel _$UserStatsModelFromJson(Map<String, dynamic> json) =>
UserStatsModel(
totalWordsLearned: (json['total_words_learned'] as num).toInt(),
wordsLearnedToday: (json['words_learned_today'] as num).toInt(),
studyTimeToday: (json['study_time_today'] as num).toInt(),
totalStudyTime: (json['total_study_time'] as num).toInt(),
listeningScore: (json['listening_score'] as num).toDouble(),
readingScore: (json['reading_score'] as num).toDouble(),
writingScore: (json['writing_score'] as num).toDouble(),
speakingScore: (json['speaking_score'] as num).toDouble(),
overallScore: (json['overall_score'] as num).toDouble(),
weeklyProgress: (json['weekly_progress'] as List<dynamic>)
.map((e) => (e as num).toInt())
.toList(),
monthlyProgress: (json['monthly_progress'] as List<dynamic>)
.map((e) => (e as num).toInt())
.toList(),
);
Map<String, dynamic> _$UserStatsModelToJson(UserStatsModel instance) =>
<String, dynamic>{
'total_words_learned': instance.totalWordsLearned,
'words_learned_today': instance.wordsLearnedToday,
'study_time_today': instance.studyTimeToday,
'total_study_time': instance.totalStudyTime,
'listening_score': instance.listeningScore,
'reading_score': instance.readingScore,
'writing_score': instance.writingScore,
'speaking_score': instance.speakingScore,
'overall_score': instance.overallScore,
'weekly_progress': instance.weeklyProgress,
'monthly_progress': instance.monthlyProgress,
};

View File

@@ -0,0 +1,292 @@
/// 词汇模型
class VocabularyModel {
final int wordId;
final String word;
final String pronunciation;
final String phonetic;
final List<WordMeaning> meanings;
final String? etymology;
final List<String> examples;
final String? imageUrl;
final String? audioUrl;
final int difficulty;
final List<String> tags;
final DateTime createdAt;
final DateTime updatedAt;
const VocabularyModel({
required this.wordId,
required this.word,
required this.pronunciation,
required this.phonetic,
required this.meanings,
this.etymology,
required this.examples,
this.imageUrl,
this.audioUrl,
required this.difficulty,
required this.tags,
required this.createdAt,
required this.updatedAt,
});
factory VocabularyModel.fromJson(Map<String, dynamic> json) {
return VocabularyModel(
wordId: json['word_id'] as int,
word: json['word'] as String,
pronunciation: json['pronunciation'] as String,
phonetic: json['phonetic'] as String,
meanings: (json['meanings'] as List)
.map((e) => WordMeaning.fromJson(e as Map<String, dynamic>))
.toList(),
etymology: json['etymology'] as String?,
examples: (json['examples'] as List).cast<String>(),
imageUrl: json['image_url'] as String?,
audioUrl: json['audio_url'] as String?,
difficulty: json['difficulty'] as int,
tags: (json['tags'] as List).cast<String>(),
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
);
}
Map<String, dynamic> toJson() {
return {
'word_id': wordId,
'word': word,
'pronunciation': pronunciation,
'phonetic': phonetic,
'meanings': meanings.map((e) => e.toJson()).toList(),
'etymology': etymology,
'examples': examples,
'image_url': imageUrl,
'audio_url': audioUrl,
'difficulty': difficulty,
'tags': tags,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
};
}
VocabularyModel copyWith({
int? wordId,
String? word,
String? pronunciation,
String? phonetic,
List<WordMeaning>? meanings,
String? etymology,
List<String>? examples,
String? imageUrl,
String? audioUrl,
int? difficulty,
List<String>? tags,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return VocabularyModel(
wordId: wordId ?? this.wordId,
word: word ?? this.word,
pronunciation: pronunciation ?? this.pronunciation,
phonetic: phonetic ?? this.phonetic,
meanings: meanings ?? this.meanings,
etymology: etymology ?? this.etymology,
examples: examples ?? this.examples,
imageUrl: imageUrl ?? this.imageUrl,
audioUrl: audioUrl ?? this.audioUrl,
difficulty: difficulty ?? this.difficulty,
tags: tags ?? this.tags,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
}
/// 词汇含义模型
class WordMeaning {
final String partOfSpeech;
final String definition;
final String? chineseDefinition;
final List<String> synonyms;
final List<String> antonyms;
final List<String> examples;
const WordMeaning({
required this.partOfSpeech,
required this.definition,
this.chineseDefinition,
required this.synonyms,
required this.antonyms,
required this.examples,
});
factory WordMeaning.fromJson(Map<String, dynamic> json) {
return WordMeaning(
partOfSpeech: json['part_of_speech'] as String,
definition: json['definition'] as String,
chineseDefinition: json['chinese_definition'] as String?,
synonyms: (json['synonyms'] as List).cast<String>(),
antonyms: (json['antonyms'] as List).cast<String>(),
examples: (json['examples'] as List).cast<String>(),
);
}
Map<String, dynamic> toJson() {
return {
'part_of_speech': partOfSpeech,
'definition': definition,
'chinese_definition': chineseDefinition,
'synonyms': synonyms,
'antonyms': antonyms,
'examples': examples,
};
}
}
/// 用户词汇学习记录模型
class UserVocabularyModel {
final int userWordId;
final int userId;
final int wordId;
final VocabularyModel? vocabulary;
final LearningStatus status;
final int reviewCount;
final int correctCount;
final int incorrectCount;
final double masteryLevel;
final DateTime? lastReviewAt;
final DateTime? nextReviewAt;
final DateTime createdAt;
final DateTime updatedAt;
const UserVocabularyModel({
required this.userWordId,
required this.userId,
required this.wordId,
this.vocabulary,
required this.status,
required this.reviewCount,
required this.correctCount,
required this.incorrectCount,
required this.masteryLevel,
this.lastReviewAt,
this.nextReviewAt,
required this.createdAt,
required this.updatedAt,
});
factory UserVocabularyModel.fromJson(Map<String, dynamic> json) {
return UserVocabularyModel(
userWordId: json['user_word_id'] as int,
userId: json['user_id'] as int,
wordId: json['word_id'] as int,
vocabulary: json['vocabulary'] != null
? VocabularyModel.fromJson(json['vocabulary'] as Map<String, dynamic>)
: null,
status: LearningStatus.values.firstWhere(
(e) => e.name == json['status'],
orElse: () => LearningStatus.new_word,
),
reviewCount: json['review_count'] as int,
correctCount: json['correct_count'] as int,
incorrectCount: json['incorrect_count'] as int,
masteryLevel: (json['mastery_level'] as num).toDouble(),
lastReviewAt: json['last_review_at'] != null
? DateTime.parse(json['last_review_at'] as String)
: null,
nextReviewAt: json['next_review_at'] != null
? DateTime.parse(json['next_review_at'] as String)
: null,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
);
}
Map<String, dynamic> toJson() {
return {
'user_word_id': userWordId,
'user_id': userId,
'word_id': wordId,
'vocabulary': vocabulary?.toJson(),
'status': status.name,
'review_count': reviewCount,
'correct_count': correctCount,
'incorrect_count': incorrectCount,
'mastery_level': masteryLevel,
'last_review_at': lastReviewAt?.toIso8601String(),
'next_review_at': nextReviewAt?.toIso8601String(),
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
};
}
}
/// 学习状态枚举
enum LearningStatus {
new_word('new_word', '新单词'),
learning('learning', '学习中'),
reviewing('reviewing', '复习中'),
mastered('mastered', '已掌握'),
forgotten('forgotten', '已遗忘');
const LearningStatus(this.value, this.label);
final String value;
final String label;
}
/// 词库模型
class VocabularyBookModel {
final int bookId;
final String name;
final String description;
final String category;
final String level;
final int totalWords;
final String? coverImage;
final bool isPremium;
final DateTime createdAt;
final DateTime updatedAt;
const VocabularyBookModel({
required this.bookId,
required this.name,
required this.description,
required this.category,
required this.level,
required this.totalWords,
this.coverImage,
required this.isPremium,
required this.createdAt,
required this.updatedAt,
});
factory VocabularyBookModel.fromJson(Map<String, dynamic> json) {
return VocabularyBookModel(
bookId: json['book_id'] as int,
name: json['name'] as String,
description: json['description'] as String,
category: json['category'] as String,
level: json['level'] as String,
totalWords: json['total_words'] as int,
coverImage: json['cover_image'] as String?,
isPremium: json['is_premium'] as bool,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
);
}
Map<String, dynamic> toJson() {
return {
'book_id': bookId,
'name': name,
'description': description,
'category': category,
'level': level,
'total_words': totalWords,
'cover_image': coverImage,
'is_premium': isPremium,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
};
}
}