init
This commit is contained in:
121
client/lib/core/models/api_response.dart
Normal file
121
client/lib/core/models/api_response.dart
Normal file
@@ -0,0 +1,121 @@
|
||||
/// API响应基础模型
|
||||
class ApiResponse<T> {
|
||||
final bool success;
|
||||
final String message;
|
||||
final T? data;
|
||||
final int? code;
|
||||
final Map<String, dynamic>? errors;
|
||||
|
||||
const ApiResponse({
|
||||
required this.success,
|
||||
required this.message,
|
||||
this.data,
|
||||
this.code,
|
||||
this.errors,
|
||||
});
|
||||
|
||||
factory ApiResponse.success({
|
||||
required String message,
|
||||
T? data,
|
||||
int? code,
|
||||
}) {
|
||||
return ApiResponse<T>(
|
||||
success: true,
|
||||
message: message,
|
||||
data: data,
|
||||
code: code ?? 200,
|
||||
);
|
||||
}
|
||||
|
||||
factory ApiResponse.error({
|
||||
required String message,
|
||||
int? code,
|
||||
Map<String, dynamic>? errors,
|
||||
}) {
|
||||
return ApiResponse<T>(
|
||||
success: false,
|
||||
message: message,
|
||||
code: code ?? 400,
|
||||
errors: errors,
|
||||
);
|
||||
}
|
||||
|
||||
factory ApiResponse.fromJson(
|
||||
Map<String, dynamic> json,
|
||||
T Function(dynamic)? fromJsonT,
|
||||
) {
|
||||
return ApiResponse<T>(
|
||||
success: json['success'] ?? false,
|
||||
message: json['message'] ?? '',
|
||||
data: json['data'] != null && fromJsonT != null
|
||||
? fromJsonT(json['data'])
|
||||
: json['data'],
|
||||
code: json['code'],
|
||||
errors: json['errors'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'success': success,
|
||||
'message': message,
|
||||
'data': data,
|
||||
'code': code,
|
||||
'errors': errors,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ApiResponse{success: $success, message: $message, data: $data, code: $code}';
|
||||
}
|
||||
}
|
||||
|
||||
/// 分页响应模型
|
||||
class PaginatedResponse<T> {
|
||||
final List<T> data;
|
||||
final int total;
|
||||
final int page;
|
||||
final int pageSize;
|
||||
final int totalPages;
|
||||
final bool hasNext;
|
||||
final bool hasPrevious;
|
||||
|
||||
const PaginatedResponse({
|
||||
required this.data,
|
||||
required this.total,
|
||||
required this.page,
|
||||
required this.pageSize,
|
||||
required this.totalPages,
|
||||
required this.hasNext,
|
||||
required this.hasPrevious,
|
||||
});
|
||||
|
||||
factory PaginatedResponse.fromJson(
|
||||
Map<String, dynamic> json,
|
||||
T Function(Map<String, dynamic>) fromJsonT,
|
||||
) {
|
||||
final List<dynamic> dataList = json['data'] ?? [];
|
||||
return PaginatedResponse<T>(
|
||||
data: dataList.map((item) => fromJsonT(item)).toList(),
|
||||
total: json['total'] ?? 0,
|
||||
page: json['page'] ?? 1,
|
||||
pageSize: json['page_size'] ?? 10,
|
||||
totalPages: json['total_pages'] ?? 0,
|
||||
hasNext: json['has_next'] ?? false,
|
||||
hasPrevious: json['has_previous'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'data': data,
|
||||
'total': total,
|
||||
'page': page,
|
||||
'page_size': pageSize,
|
||||
'total_pages': totalPages,
|
||||
'has_next': hasNext,
|
||||
'has_previous': hasPrevious,
|
||||
};
|
||||
}
|
||||
}
|
||||
280
client/lib/core/models/user_model.dart
Normal file
280
client/lib/core/models/user_model.dart
Normal file
@@ -0,0 +1,280 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'user_model.g.dart';
|
||||
|
||||
/// 用户模型
|
||||
@JsonSerializable()
|
||||
class User {
|
||||
final String id;
|
||||
final String username;
|
||||
final String email;
|
||||
final String? phone;
|
||||
final String? avatar;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final UserProfile? profile;
|
||||
final UserSettings? settings;
|
||||
|
||||
const User({
|
||||
required this.id,
|
||||
required this.username,
|
||||
required this.email,
|
||||
this.phone,
|
||||
this.avatar,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
this.profile,
|
||||
this.settings,
|
||||
});
|
||||
|
||||
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$UserToJson(this);
|
||||
|
||||
User copyWith({
|
||||
String? id,
|
||||
String? username,
|
||||
String? email,
|
||||
String? phone,
|
||||
String? avatar,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
UserProfile? profile,
|
||||
UserSettings? settings,
|
||||
}) {
|
||||
return User(
|
||||
id: id ?? this.id,
|
||||
username: username ?? this.username,
|
||||
email: email ?? this.email,
|
||||
phone: phone ?? this.phone,
|
||||
avatar: avatar ?? this.avatar,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
profile: profile ?? this.profile,
|
||||
settings: settings ?? this.settings,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 用户资料
|
||||
@JsonSerializable()
|
||||
class UserProfile {
|
||||
final String? firstName;
|
||||
final String? lastName;
|
||||
final String? phone;
|
||||
final String? bio;
|
||||
final String? avatar;
|
||||
final String? realName;
|
||||
final String? gender;
|
||||
final DateTime? birthday;
|
||||
final String? location;
|
||||
final String? occupation;
|
||||
final String? education;
|
||||
final List<String>? interests;
|
||||
final LearningGoal? learningGoal;
|
||||
final EnglishLevel? currentLevel;
|
||||
final EnglishLevel? targetLevel;
|
||||
final EnglishLevel? englishLevel;
|
||||
final UserSettings? settings;
|
||||
|
||||
const UserProfile({
|
||||
this.firstName,
|
||||
this.lastName,
|
||||
this.phone,
|
||||
this.bio,
|
||||
this.avatar,
|
||||
this.realName,
|
||||
this.gender,
|
||||
this.birthday,
|
||||
this.location,
|
||||
this.occupation,
|
||||
this.education,
|
||||
this.interests,
|
||||
this.learningGoal,
|
||||
this.currentLevel,
|
||||
this.targetLevel,
|
||||
this.englishLevel,
|
||||
this.settings,
|
||||
});
|
||||
|
||||
factory UserProfile.fromJson(Map<String, dynamic> json) => _$UserProfileFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$UserProfileToJson(this);
|
||||
|
||||
UserProfile copyWith({
|
||||
String? firstName,
|
||||
String? lastName,
|
||||
String? phone,
|
||||
String? bio,
|
||||
String? avatar,
|
||||
String? realName,
|
||||
String? gender,
|
||||
DateTime? birthday,
|
||||
String? location,
|
||||
String? occupation,
|
||||
String? education,
|
||||
List<String>? interests,
|
||||
LearningGoal? learningGoal,
|
||||
EnglishLevel? currentLevel,
|
||||
EnglishLevel? targetLevel,
|
||||
EnglishLevel? englishLevel,
|
||||
UserSettings? settings,
|
||||
}) {
|
||||
return UserProfile(
|
||||
firstName: firstName ?? this.firstName,
|
||||
lastName: lastName ?? this.lastName,
|
||||
phone: phone ?? this.phone,
|
||||
bio: bio ?? this.bio,
|
||||
avatar: avatar ?? this.avatar,
|
||||
realName: realName ?? this.realName,
|
||||
gender: gender ?? this.gender,
|
||||
birthday: birthday ?? this.birthday,
|
||||
location: location ?? this.location,
|
||||
occupation: occupation ?? this.occupation,
|
||||
education: education ?? this.education,
|
||||
interests: interests ?? this.interests,
|
||||
learningGoal: learningGoal ?? this.learningGoal,
|
||||
currentLevel: currentLevel ?? this.currentLevel,
|
||||
targetLevel: targetLevel ?? this.targetLevel,
|
||||
englishLevel: englishLevel ?? this.englishLevel,
|
||||
settings: settings ?? this.settings,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 用户设置
|
||||
@JsonSerializable()
|
||||
class UserSettings {
|
||||
final bool notificationsEnabled;
|
||||
final bool soundEnabled;
|
||||
final bool vibrationEnabled;
|
||||
final String language;
|
||||
final String theme;
|
||||
final int dailyGoal;
|
||||
final int dailyWordGoal;
|
||||
final int dailyStudyMinutes;
|
||||
final List<String> reminderTimes;
|
||||
final bool autoPlayAudio;
|
||||
final double audioSpeed;
|
||||
final bool showTranslation;
|
||||
final bool showPronunciation;
|
||||
|
||||
const UserSettings({
|
||||
this.notificationsEnabled = true,
|
||||
this.soundEnabled = true,
|
||||
this.vibrationEnabled = true,
|
||||
this.language = 'zh-CN',
|
||||
this.theme = 'system',
|
||||
this.dailyGoal = 30,
|
||||
this.dailyWordGoal = 20,
|
||||
this.dailyStudyMinutes = 30,
|
||||
this.reminderTimes = const ['09:00', '20:00'],
|
||||
this.autoPlayAudio = true,
|
||||
this.audioSpeed = 1.0,
|
||||
this.showTranslation = true,
|
||||
this.showPronunciation = true,
|
||||
});
|
||||
|
||||
factory UserSettings.fromJson(Map<String, dynamic> json) => _$UserSettingsFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$UserSettingsToJson(this);
|
||||
|
||||
UserSettings copyWith({
|
||||
bool? notificationsEnabled,
|
||||
bool? soundEnabled,
|
||||
bool? vibrationEnabled,
|
||||
String? language,
|
||||
String? theme,
|
||||
int? dailyGoal,
|
||||
int? dailyWordGoal,
|
||||
int? dailyStudyMinutes,
|
||||
List<String>? reminderTimes,
|
||||
bool? autoPlayAudio,
|
||||
double? audioSpeed,
|
||||
bool? showTranslation,
|
||||
bool? showPronunciation,
|
||||
}) {
|
||||
return UserSettings(
|
||||
notificationsEnabled: notificationsEnabled ?? this.notificationsEnabled,
|
||||
soundEnabled: soundEnabled ?? this.soundEnabled,
|
||||
vibrationEnabled: vibrationEnabled ?? this.vibrationEnabled,
|
||||
language: language ?? this.language,
|
||||
theme: theme ?? this.theme,
|
||||
dailyGoal: dailyGoal ?? this.dailyGoal,
|
||||
dailyWordGoal: dailyWordGoal ?? this.dailyWordGoal,
|
||||
dailyStudyMinutes: dailyStudyMinutes ?? this.dailyStudyMinutes,
|
||||
reminderTimes: reminderTimes ?? this.reminderTimes,
|
||||
autoPlayAudio: autoPlayAudio ?? this.autoPlayAudio,
|
||||
audioSpeed: audioSpeed ?? this.audioSpeed,
|
||||
showTranslation: showTranslation ?? this.showTranslation,
|
||||
showPronunciation: showPronunciation ?? this.showPronunciation,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 学习目标
|
||||
enum LearningGoal {
|
||||
@JsonValue('daily_communication')
|
||||
dailyCommunication,
|
||||
@JsonValue('business_english')
|
||||
businessEnglish,
|
||||
@JsonValue('academic_study')
|
||||
academicStudy,
|
||||
@JsonValue('exam_preparation')
|
||||
examPreparation,
|
||||
@JsonValue('travel')
|
||||
travel,
|
||||
@JsonValue('hobby')
|
||||
hobby,
|
||||
}
|
||||
|
||||
/// 英语水平
|
||||
enum EnglishLevel {
|
||||
@JsonValue('beginner')
|
||||
beginner,
|
||||
@JsonValue('elementary')
|
||||
elementary,
|
||||
@JsonValue('intermediate')
|
||||
intermediate,
|
||||
@JsonValue('upper_intermediate')
|
||||
upperIntermediate,
|
||||
@JsonValue('advanced')
|
||||
advanced,
|
||||
@JsonValue('proficient')
|
||||
proficient,
|
||||
@JsonValue('expert')
|
||||
expert,
|
||||
}
|
||||
|
||||
/// 认证响应
|
||||
@JsonSerializable()
|
||||
class AuthResponse {
|
||||
final User user;
|
||||
final String token;
|
||||
final String? refreshToken;
|
||||
final DateTime expiresAt;
|
||||
|
||||
const AuthResponse({
|
||||
required this.user,
|
||||
required this.token,
|
||||
this.refreshToken,
|
||||
required this.expiresAt,
|
||||
});
|
||||
|
||||
factory AuthResponse.fromJson(Map<String, dynamic> json) => _$AuthResponseFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$AuthResponseToJson(this);
|
||||
}
|
||||
|
||||
/// Token刷新响应
|
||||
@JsonSerializable()
|
||||
class TokenRefreshResponse {
|
||||
final String token;
|
||||
final String? refreshToken;
|
||||
final DateTime expiresAt;
|
||||
|
||||
const TokenRefreshResponse({
|
||||
required this.token,
|
||||
this.refreshToken,
|
||||
required this.expiresAt,
|
||||
});
|
||||
|
||||
factory TokenRefreshResponse.fromJson(Map<String, dynamic> json) => _$TokenRefreshResponseFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$TokenRefreshResponseToJson(this);
|
||||
}
|
||||
172
client/lib/core/models/user_model.g.dart
Normal file
172
client/lib/core/models/user_model.g.dart
Normal file
@@ -0,0 +1,172 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'user_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
User _$UserFromJson(Map<String, dynamic> json) => User(
|
||||
id: json['id'] as String,
|
||||
username: json['username'] as String,
|
||||
email: json['email'] as String,
|
||||
phone: json['phone'] as String?,
|
||||
avatar: json['avatar'] as String?,
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
||||
profile: json['profile'] == null
|
||||
? null
|
||||
: UserProfile.fromJson(json['profile'] as Map<String, dynamic>),
|
||||
settings: json['settings'] == null
|
||||
? null
|
||||
: UserSettings.fromJson(json['settings'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'username': instance.username,
|
||||
'email': instance.email,
|
||||
'phone': instance.phone,
|
||||
'avatar': instance.avatar,
|
||||
'createdAt': instance.createdAt.toIso8601String(),
|
||||
'updatedAt': instance.updatedAt.toIso8601String(),
|
||||
'profile': instance.profile,
|
||||
'settings': instance.settings,
|
||||
};
|
||||
|
||||
UserProfile _$UserProfileFromJson(Map<String, dynamic> json) => UserProfile(
|
||||
firstName: json['firstName'] as String?,
|
||||
lastName: json['lastName'] as String?,
|
||||
phone: json['phone'] as String?,
|
||||
bio: json['bio'] as String?,
|
||||
avatar: json['avatar'] as String?,
|
||||
realName: json['realName'] as String?,
|
||||
gender: json['gender'] as String?,
|
||||
birthday: json['birthday'] == null
|
||||
? null
|
||||
: DateTime.parse(json['birthday'] as String),
|
||||
location: json['location'] as String?,
|
||||
occupation: json['occupation'] as String?,
|
||||
education: json['education'] as String?,
|
||||
interests: (json['interests'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList(),
|
||||
learningGoal:
|
||||
$enumDecodeNullable(_$LearningGoalEnumMap, json['learningGoal']),
|
||||
currentLevel:
|
||||
$enumDecodeNullable(_$EnglishLevelEnumMap, json['currentLevel']),
|
||||
targetLevel:
|
||||
$enumDecodeNullable(_$EnglishLevelEnumMap, json['targetLevel']),
|
||||
englishLevel:
|
||||
$enumDecodeNullable(_$EnglishLevelEnumMap, json['englishLevel']),
|
||||
settings: json['settings'] == null
|
||||
? null
|
||||
: UserSettings.fromJson(json['settings'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UserProfileToJson(UserProfile instance) =>
|
||||
<String, dynamic>{
|
||||
'firstName': instance.firstName,
|
||||
'lastName': instance.lastName,
|
||||
'phone': instance.phone,
|
||||
'bio': instance.bio,
|
||||
'avatar': instance.avatar,
|
||||
'realName': instance.realName,
|
||||
'gender': instance.gender,
|
||||
'birthday': instance.birthday?.toIso8601String(),
|
||||
'location': instance.location,
|
||||
'occupation': instance.occupation,
|
||||
'education': instance.education,
|
||||
'interests': instance.interests,
|
||||
'learningGoal': _$LearningGoalEnumMap[instance.learningGoal],
|
||||
'currentLevel': _$EnglishLevelEnumMap[instance.currentLevel],
|
||||
'targetLevel': _$EnglishLevelEnumMap[instance.targetLevel],
|
||||
'englishLevel': _$EnglishLevelEnumMap[instance.englishLevel],
|
||||
'settings': instance.settings,
|
||||
};
|
||||
|
||||
const _$LearningGoalEnumMap = {
|
||||
LearningGoal.dailyCommunication: 'daily_communication',
|
||||
LearningGoal.businessEnglish: 'business_english',
|
||||
LearningGoal.academicStudy: 'academic_study',
|
||||
LearningGoal.examPreparation: 'exam_preparation',
|
||||
LearningGoal.travel: 'travel',
|
||||
LearningGoal.hobby: 'hobby',
|
||||
};
|
||||
|
||||
const _$EnglishLevelEnumMap = {
|
||||
EnglishLevel.beginner: 'beginner',
|
||||
EnglishLevel.elementary: 'elementary',
|
||||
EnglishLevel.intermediate: 'intermediate',
|
||||
EnglishLevel.upperIntermediate: 'upper_intermediate',
|
||||
EnglishLevel.advanced: 'advanced',
|
||||
EnglishLevel.proficient: 'proficient',
|
||||
EnglishLevel.expert: 'expert',
|
||||
};
|
||||
|
||||
UserSettings _$UserSettingsFromJson(Map<String, dynamic> json) => UserSettings(
|
||||
notificationsEnabled: json['notificationsEnabled'] as bool? ?? true,
|
||||
soundEnabled: json['soundEnabled'] as bool? ?? true,
|
||||
vibrationEnabled: json['vibrationEnabled'] as bool? ?? true,
|
||||
language: json['language'] as String? ?? 'zh-CN',
|
||||
theme: json['theme'] as String? ?? 'system',
|
||||
dailyGoal: (json['dailyGoal'] as num?)?.toInt() ?? 30,
|
||||
dailyWordGoal: (json['dailyWordGoal'] as num?)?.toInt() ?? 20,
|
||||
dailyStudyMinutes: (json['dailyStudyMinutes'] as num?)?.toInt() ?? 30,
|
||||
reminderTimes: (json['reminderTimes'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['09:00', '20:00'],
|
||||
autoPlayAudio: json['autoPlayAudio'] as bool? ?? true,
|
||||
audioSpeed: (json['audioSpeed'] as num?)?.toDouble() ?? 1.0,
|
||||
showTranslation: json['showTranslation'] as bool? ?? true,
|
||||
showPronunciation: json['showPronunciation'] as bool? ?? true,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UserSettingsToJson(UserSettings instance) =>
|
||||
<String, dynamic>{
|
||||
'notificationsEnabled': instance.notificationsEnabled,
|
||||
'soundEnabled': instance.soundEnabled,
|
||||
'vibrationEnabled': instance.vibrationEnabled,
|
||||
'language': instance.language,
|
||||
'theme': instance.theme,
|
||||
'dailyGoal': instance.dailyGoal,
|
||||
'dailyWordGoal': instance.dailyWordGoal,
|
||||
'dailyStudyMinutes': instance.dailyStudyMinutes,
|
||||
'reminderTimes': instance.reminderTimes,
|
||||
'autoPlayAudio': instance.autoPlayAudio,
|
||||
'audioSpeed': instance.audioSpeed,
|
||||
'showTranslation': instance.showTranslation,
|
||||
'showPronunciation': instance.showPronunciation,
|
||||
};
|
||||
|
||||
AuthResponse _$AuthResponseFromJson(Map<String, dynamic> json) => AuthResponse(
|
||||
user: User.fromJson(json['user'] as Map<String, dynamic>),
|
||||
token: json['token'] as String,
|
||||
refreshToken: json['refreshToken'] as String?,
|
||||
expiresAt: DateTime.parse(json['expiresAt'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$AuthResponseToJson(AuthResponse instance) =>
|
||||
<String, dynamic>{
|
||||
'user': instance.user,
|
||||
'token': instance.token,
|
||||
'refreshToken': instance.refreshToken,
|
||||
'expiresAt': instance.expiresAt.toIso8601String(),
|
||||
};
|
||||
|
||||
TokenRefreshResponse _$TokenRefreshResponseFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
TokenRefreshResponse(
|
||||
token: json['token'] as String,
|
||||
refreshToken: json['refreshToken'] as String?,
|
||||
expiresAt: DateTime.parse(json['expiresAt'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$TokenRefreshResponseToJson(
|
||||
TokenRefreshResponse instance) =>
|
||||
<String, dynamic>{
|
||||
'token': instance.token,
|
||||
'refreshToken': instance.refreshToken,
|
||||
'expiresAt': instance.expiresAt.toIso8601String(),
|
||||
};
|
||||
Reference in New Issue
Block a user