/// 测试类型枚举 enum TestType { quick, // 快速测试 standard, // 标准测试 full, // 完整测试 mock, // 模拟考试 vocabulary, // 词汇专项 grammar, // 语法专项 reading, // 阅读专项 listening, // 听力专项 speaking, // 口语专项 writing, // 写作专项 } /// 测试状态枚举 enum TestStatus { notStarted, // 未开始 inProgress, // 进行中 paused, // 暂停 completed, // 已完成 expired, // 已过期 } /// 题目类型枚举 enum QuestionType { multipleChoice, // 单选题 multipleSelect, // 多选题 fillInBlank, // 填空题 reading, // 阅读理解 listening, // 听力理解 speaking, // 口语题 writing, // 写作题 } /// 难度等级枚举 enum DifficultyLevel { beginner, // 初级 elementary, // 基础 intermediate, // 中级 upperIntermediate, // 中高级 advanced, // 高级 expert, // 专家级 } /// 技能类型枚举 enum SkillType { vocabulary, // 词汇 grammar, // 语法 reading, // 阅读 listening, // 听力 speaking, // 口语 writing, // 写作 } /// 测试题目模型 class TestQuestion { final String id; final QuestionType type; final SkillType skillType; final DifficultyLevel difficulty; final String content; final List options; final List correctAnswers; final String? audioUrl; final String? imageUrl; final String? explanation; final int points; final int timeLimit; // 秒 final Map? metadata; const TestQuestion({ required this.id, required this.type, required this.skillType, required this.difficulty, required this.content, this.options = const [], this.correctAnswers = const [], this.audioUrl, this.imageUrl, this.explanation, this.points = 1, this.timeLimit = 60, this.metadata, }); factory TestQuestion.fromJson(Map json) { return TestQuestion( id: json['id'] as String, type: QuestionType.values.firstWhere( (e) => e.name == json['type'], orElse: () => QuestionType.multipleChoice, ), skillType: SkillType.values.firstWhere( (e) => e.name == json['skillType'], orElse: () => SkillType.vocabulary, ), difficulty: DifficultyLevel.values.firstWhere( (e) => e.name == json['difficulty'], orElse: () => DifficultyLevel.intermediate, ), content: json['content'] as String, options: List.from(json['options'] ?? []), correctAnswers: List.from(json['correctAnswers'] ?? []), audioUrl: json['audioUrl'] as String?, imageUrl: json['imageUrl'] as String?, explanation: json['explanation'] as String?, points: json['points'] as int? ?? 1, timeLimit: json['timeLimit'] as int? ?? 60, metadata: json['metadata'] as Map?, ); } Map toJson() { return { 'id': id, 'type': type.name, 'skillType': skillType.name, 'difficulty': difficulty.name, 'content': content, 'options': options, 'correctAnswers': correctAnswers, 'audioUrl': audioUrl, 'imageUrl': imageUrl, 'explanation': explanation, 'points': points, 'timeLimit': timeLimit, 'metadata': metadata, }; } } /// 语言技能枚举 (用于UI显示) enum LanguageSkill { listening, // 听力 reading, // 阅读 speaking, // 口语 writing, // 写作 vocabulary, // 词汇 grammar, // 语法 pronunciation, // 发音 comprehension, // 理解 } /// 用户技能统计模型 class UserSkillStatistics { final SkillType skillType; final double averageScore; final int totalTests; final int correctAnswers; final int totalQuestions; final DifficultyLevel strongestLevel; final DifficultyLevel weakestLevel; final DateTime lastTestDate; const UserSkillStatistics({ required this.skillType, required this.averageScore, required this.totalTests, required this.correctAnswers, required this.totalQuestions, required this.strongestLevel, required this.weakestLevel, required this.lastTestDate, }); factory UserSkillStatistics.fromJson(Map json) { return UserSkillStatistics( skillType: SkillType.values.firstWhere( (e) => e.toString().split('.').last == json['skillType'], ), averageScore: (json['averageScore'] as num).toDouble(), totalTests: json['totalTests'] as int, correctAnswers: json['correctAnswers'] as int, totalQuestions: json['totalQuestions'] as int, strongestLevel: DifficultyLevel.values.firstWhere( (e) => e.toString().split('.').last == json['strongestLevel'], ), weakestLevel: DifficultyLevel.values.firstWhere( (e) => e.toString().split('.').last == json['weakestLevel'], ), lastTestDate: DateTime.parse(json['lastTestDate']), ); } Map toJson() { return { 'skillType': skillType.toString().split('.').last, 'averageScore': averageScore, 'totalTests': totalTests, 'correctAnswers': correctAnswers, 'totalQuestions': totalQuestions, 'strongestLevel': strongestLevel.toString().split('.').last, 'weakestLevel': weakestLevel.toString().split('.').last, 'lastTestDate': lastTestDate.toIso8601String(), }; } double get accuracy => totalQuestions > 0 ? correctAnswers / totalQuestions : 0.0; } /// 用户答案模型 class UserAnswer { final String questionId; final List selectedAnswers; final String? textAnswer; final String? audioUrl; final DateTime answeredAt; final int timeSpent; // 秒 const UserAnswer({ required this.questionId, this.selectedAnswers = const [], this.textAnswer, this.audioUrl, required this.answeredAt, required this.timeSpent, }); factory UserAnswer.fromJson(Map json) { return UserAnswer( questionId: json['questionId'] as String, selectedAnswers: List.from(json['selectedAnswers'] ?? []), textAnswer: json['textAnswer'] as String?, audioUrl: json['audioUrl'] as String?, answeredAt: DateTime.parse(json['answeredAt'] as String), timeSpent: json['timeSpent'] as int, ); } Map toJson() { return { 'questionId': questionId, 'selectedAnswers': selectedAnswers, 'textAnswer': textAnswer, 'audioUrl': audioUrl, 'answeredAt': answeredAt.toIso8601String(), 'timeSpent': timeSpent, }; } } /// 技能得分模型 class SkillScore { final SkillType skillType; final int score; final int maxScore; final double percentage; final DifficultyLevel level; final String feedback; const SkillScore({ required this.skillType, required this.score, required this.maxScore, required this.percentage, required this.level, required this.feedback, }); factory SkillScore.fromJson(Map json) { return SkillScore( skillType: SkillType.values.firstWhere( (e) => e.name == json['skillType'], orElse: () => SkillType.vocabulary, ), score: json['score'] as int, maxScore: json['maxScore'] as int, percentage: (json['percentage'] as num).toDouble(), level: DifficultyLevel.values.firstWhere( (e) => e.name == json['level'], orElse: () => DifficultyLevel.intermediate, ), feedback: json['feedback'] as String, ); } Map toJson() { return { 'skillType': skillType.name, 'score': score, 'maxScore': maxScore, 'percentage': percentage, 'level': level.name, 'feedback': feedback, }; } } /// 测试结果模型 class TestResult { final String id; final String testId; final String userId; final TestType testType; final int totalScore; final int maxScore; final double percentage; final DifficultyLevel overallLevel; final List skillScores; final List answers; final DateTime startTime; final DateTime endTime; final int duration; // 秒 final String feedback; final Map? recommendations; const TestResult({ required this.id, required this.testId, required this.userId, required this.testType, required this.totalScore, required this.maxScore, required this.percentage, required this.overallLevel, required this.skillScores, required this.answers, required this.startTime, required this.endTime, required this.duration, required this.feedback, this.recommendations, }); factory TestResult.fromJson(Map json) { return TestResult( id: json['id'] as String, testId: json['testId'] as String, userId: json['userId'] as String, testType: TestType.values.firstWhere( (e) => e.name == json['testType'], orElse: () => TestType.standard, ), totalScore: json['totalScore'] as int, maxScore: json['maxScore'] as int, percentage: (json['percentage'] as num).toDouble(), overallLevel: DifficultyLevel.values.firstWhere( (e) => e.name == json['overallLevel'], orElse: () => DifficultyLevel.intermediate, ), skillScores: (json['skillScores'] as List) .map((e) => SkillScore.fromJson(e as Map)) .toList(), answers: (json['answers'] as List) .map((e) => UserAnswer.fromJson(e as Map)) .toList(), startTime: DateTime.parse(json['startTime'] as String), endTime: DateTime.parse(json['endTime'] as String), duration: json['duration'] as int, feedback: json['feedback'] as String, recommendations: json['recommendations'] as Map?, ); } Map toJson() { return { 'id': id, 'testId': testId, 'userId': userId, 'testType': testType.name, 'totalScore': totalScore, 'maxScore': maxScore, 'percentage': percentage, 'overallLevel': overallLevel.name, 'skillScores': skillScores.map((e) => e.toJson()).toList(), 'answers': answers.map((e) => e.toJson()).toList(), 'startTime': startTime.toIso8601String(), 'endTime': endTime.toIso8601String(), 'duration': duration, 'feedback': feedback, 'recommendations': recommendations, }; } } /// 测试模板模型 class TestTemplate { final String id; final String name; final String description; final TestType type; final int duration; // 分钟 final int totalQuestions; final Map skillDistribution; final Map difficultyDistribution; final List questionIds; final bool isActive; final DateTime createdAt; final DateTime updatedAt; const TestTemplate({ required this.id, required this.name, required this.description, required this.type, required this.duration, required this.totalQuestions, required this.skillDistribution, required this.difficultyDistribution, required this.questionIds, this.isActive = true, required this.createdAt, required this.updatedAt, }); factory TestTemplate.fromJson(Map json) { return TestTemplate( id: json['id'] as String, name: json['name'] as String, description: json['description'] as String, type: TestType.values.firstWhere( (e) => e.name == json['type'], orElse: () => TestType.standard, ), duration: json['duration'] as int, totalQuestions: json['totalQuestions'] as int, skillDistribution: Map.fromEntries( (json['skillDistribution'] as Map).entries.map( (e) => MapEntry( SkillType.values.firstWhere((skill) => skill.name == e.key), e.value as int, ), ), ), difficultyDistribution: Map.fromEntries( (json['difficultyDistribution'] as Map).entries.map( (e) => MapEntry( DifficultyLevel.values.firstWhere((level) => level.name == e.key), e.value as int, ), ), ), questionIds: List.from(json['questionIds']), isActive: json['isActive'] as bool? ?? true, createdAt: DateTime.parse(json['createdAt'] as String), updatedAt: DateTime.parse(json['updatedAt'] as String), ); } Map toJson() { return { 'id': id, 'name': name, 'description': description, 'type': type.name, 'duration': duration, 'totalQuestions': totalQuestions, 'skillDistribution': skillDistribution.map( (key, value) => MapEntry(key.name, value), ), 'difficultyDistribution': difficultyDistribution.map( (key, value) => MapEntry(key.name, value), ), 'questionIds': questionIds, 'isActive': isActive, 'createdAt': createdAt.toIso8601String(), 'updatedAt': updatedAt.toIso8601String(), }; } } /// 测试会话模型 class TestSession { final String id; final String templateId; final String userId; final TestStatus status; final List questions; final List answers; final int currentQuestionIndex; final DateTime startTime; final DateTime? endTime; final int timeRemaining; // 秒 final Map? metadata; const TestSession({ required this.id, required this.templateId, required this.userId, required this.status, required this.questions, this.answers = const [], this.currentQuestionIndex = 0, required this.startTime, this.endTime, required this.timeRemaining, this.metadata, }); factory TestSession.fromJson(Map json) { return TestSession( id: json['id'] as String, templateId: json['templateId'] as String, userId: json['userId'] as String, status: TestStatus.values.firstWhere( (e) => e.name == json['status'], orElse: () => TestStatus.notStarted, ), questions: (json['questions'] as List) .map((e) => TestQuestion.fromJson(e as Map)) .toList(), answers: (json['answers'] as List?) ?.map((e) => UserAnswer.fromJson(e as Map)) .toList() ?? [], currentQuestionIndex: json['currentQuestionIndex'] as int? ?? 0, startTime: DateTime.parse(json['startTime'] as String), endTime: json['endTime'] != null ? DateTime.parse(json['endTime'] as String) : null, timeRemaining: json['timeRemaining'] as int, metadata: json['metadata'] as Map?, ); } Map toJson() { return { 'id': id, 'templateId': templateId, 'userId': userId, 'status': status.name, 'questions': questions.map((e) => e.toJson()).toList(), 'answers': answers.map((e) => e.toJson()).toList(), 'currentQuestionIndex': currentQuestionIndex, 'startTime': startTime.toIso8601String(), 'endTime': endTime?.toIso8601String(), 'timeRemaining': timeRemaining, 'metadata': metadata, }; } TestSession copyWith({ String? id, String? templateId, String? userId, TestStatus? status, List? questions, List? answers, int? currentQuestionIndex, DateTime? startTime, DateTime? endTime, int? timeRemaining, Map? metadata, }) { return TestSession( id: id ?? this.id, templateId: templateId ?? this.templateId, userId: userId ?? this.userId, status: status ?? this.status, questions: questions ?? this.questions, answers: answers ?? this.answers, currentQuestionIndex: currentQuestionIndex ?? this.currentQuestionIndex, startTime: startTime ?? this.startTime, endTime: endTime ?? this.endTime, timeRemaining: timeRemaining ?? this.timeRemaining, metadata: metadata ?? this.metadata, ); } }