init
This commit is contained in:
@@ -0,0 +1,591 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import '../models/test_models.dart';
|
||||
import '../services/test_api_service.dart';
|
||||
import '../../../core/models/api_response.dart';
|
||||
|
||||
/// 综合测试状态管理类
|
||||
class TestProvider with ChangeNotifier {
|
||||
final TestApiService _testService;
|
||||
|
||||
TestProvider({TestApiService? testService})
|
||||
: _testService = testService ?? TestApiService();
|
||||
|
||||
// 加载状<E8BDBD>? bool _isLoading = false;
|
||||
bool get isLoading => _isLoading;
|
||||
|
||||
// 错误信息
|
||||
String? _errorMessage;
|
||||
String? get errorMessage => _errorMessage;
|
||||
|
||||
// 测试模板
|
||||
List<TestTemplate> _templates = [];
|
||||
List<TestTemplate> get templates => _templates;
|
||||
|
||||
// 当前测试会话
|
||||
TestSession? _currentSession;
|
||||
TestSession? get currentSession => _currentSession;
|
||||
|
||||
// 当前题目
|
||||
TestQuestion? get currentQuestion {
|
||||
if (_currentSession == null ||
|
||||
_currentSession!.currentQuestionIndex >= _currentSession!.questions.length) {
|
||||
return null;
|
||||
}
|
||||
return _currentSession!.questions[_currentSession!.currentQuestionIndex];
|
||||
}
|
||||
|
||||
// 测试结果
|
||||
List<TestResult> _testResults = [];
|
||||
List<TestResult> get testResults => _testResults;
|
||||
|
||||
TestResult? _currentResult;
|
||||
TestResult? get currentResult => _currentResult;
|
||||
|
||||
// 用户技能统<E883BD>? Map<SkillType, double> _skillStatistics = {};
|
||||
Map<SkillType, double> get skillStatistics => _skillStatistics;
|
||||
|
||||
// 题目统计
|
||||
Map<String, dynamic> _questionStatistics = {};
|
||||
Map<String, dynamic> get questionStatistics => _questionStatistics;
|
||||
|
||||
// 计时器相<E599A8>? int _timeRemaining = 0;
|
||||
int get timeRemaining => _timeRemaining;
|
||||
|
||||
bool _isTimerRunning = false;
|
||||
bool get isTimerRunning => _isTimerRunning;
|
||||
|
||||
/// 设置加载状<E8BDBD>? void _setLoading(bool loading) {
|
||||
_isLoading = loading;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 设置错误信息
|
||||
void _setError(String? error) {
|
||||
_errorMessage = error;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 清除错误信息
|
||||
void clearError() {
|
||||
_setError(null);
|
||||
}
|
||||
|
||||
/// 加载所有测试模<E8AF95>? Future<void> loadTestTemplates() async {
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.getTestTemplates();
|
||||
if (response.success && response.data != null) {
|
||||
_templates = response.data!;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('加载测试模板失败: ${e.toString()}');
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 根据类型加载测试模板
|
||||
Future<void> loadTestTemplatesByType(TestType type) async {
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.getTestTemplatesByType(type);
|
||||
if (response.success && response.data != null) {
|
||||
_templates = response.data!;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('加载测试模板失败: ${e.toString()}');
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建测试会话
|
||||
Future<bool> createTestSession({
|
||||
required String templateId,
|
||||
required String userId,
|
||||
}) async {
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.createTestSession(
|
||||
templateId: templateId,
|
||||
userId: userId,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_currentSession = response.data!;
|
||||
_timeRemaining = _currentSession!.timeRemaining;
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('创建测试会话失败: ${e.toString()}');
|
||||
return false;
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 开始测<E5A78B>? Future<bool> startTest() async {
|
||||
if (_currentSession == null) {
|
||||
_setError('没有活动的测试会<EFBFBD>?);
|
||||
return false;
|
||||
}
|
||||
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.startTest(_currentSession!.id);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_currentSession = response.data!;
|
||||
_timeRemaining = _currentSession!.timeRemaining;
|
||||
_startTimer();
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('开始测试失<EFBFBD>? ${e.toString()}');
|
||||
return false;
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 提交答案
|
||||
Future<bool> submitAnswer(UserAnswer answer) async {
|
||||
if (_currentSession == null) {
|
||||
_setError('没有活动的测试会<EFBFBD>?);
|
||||
return false;
|
||||
}
|
||||
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.submitAnswer(
|
||||
sessionId: _currentSession!.id,
|
||||
answer: answer,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_currentSession = response.data!;
|
||||
_timeRemaining = _currentSession!.timeRemaining;
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('提交答案失败: ${e.toString()}');
|
||||
return false;
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 下一<E4B88B>? void nextQuestion() {
|
||||
if (_currentSession != null &&
|
||||
_currentSession!.currentQuestionIndex < _currentSession!.questions.length - 1) {
|
||||
_currentSession = _currentSession!.copyWith(
|
||||
currentQuestionIndex: _currentSession!.currentQuestionIndex + 1,
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/// 上一<E4B88A>? void previousQuestion() {
|
||||
if (_currentSession != null && _currentSession!.currentQuestionIndex > 0) {
|
||||
_currentSession = _currentSession!.copyWith(
|
||||
currentQuestionIndex: _currentSession!.currentQuestionIndex - 1,
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/// 跳转到指定题<E5AE9A>? void goToQuestion(int index) {
|
||||
if (_currentSession != null &&
|
||||
index >= 0 &&
|
||||
index < _currentSession!.questions.length) {
|
||||
_currentSession = _currentSession!.copyWith(
|
||||
currentQuestionIndex: index,
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/// 暂停测试
|
||||
Future<bool> pauseTest() async {
|
||||
if (_currentSession == null) {
|
||||
_setError('没有活动的测试会<EFBFBD>?);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
final response = await _testService.pauseTest(_currentSession!.id);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_currentSession = response.data!;
|
||||
_pauseTimer();
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('暂停测试失败: ${e.toString()}');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 恢复测试
|
||||
Future<bool> resumeTest() async {
|
||||
if (_currentSession == null) {
|
||||
_setError('没有活动的测试会<EFBFBD>?);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
final response = await _testService.resumeTest(_currentSession!.id);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_currentSession = response.data!;
|
||||
_startTimer();
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('恢复测试失败: ${e.toString()}');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 完成测试
|
||||
Future<bool> completeTest() async {
|
||||
if (_currentSession == null) {
|
||||
_setError('没有活动的测试会话');
|
||||
return false;
|
||||
}
|
||||
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.completeTest(_currentSession!.id);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_currentResult = response.data!;
|
||||
_stopTimer();
|
||||
_currentSession = _currentSession!.copyWith(
|
||||
status: TestStatus.completed,
|
||||
endTime: DateTime.now(),
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('完成测试失败: ${e.toString()}');
|
||||
return false;
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载用户测试历史
|
||||
Future<void> loadUserTestHistory(String userId, {int page = 1, int limit = 10}) async {
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.getUserTestHistory(
|
||||
page: page,
|
||||
limit: limit,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
if (page == 1) {
|
||||
_testResults = response.data!;
|
||||
} else {
|
||||
_testResults.addAll(response.data!);
|
||||
}
|
||||
} else {
|
||||
_setError(response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('加载测试历史失败: ${e.toString()}');
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载最近的测试结果
|
||||
Future<void> loadRecentTestResults(String userId, {int limit = 5}) async {
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.getRecentTestResults(
|
||||
userId,
|
||||
limit: limit,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_testResults = response.data!;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('加载最近测试结果失<EFBFBD>? ${e.toString()}');
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载测试结果详情
|
||||
Future<void> loadTestResultById(String resultId) async {
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.getTestResultById(resultId);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_currentResult = response.data!;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('加载测试结果失败: ${e.toString()}');
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载用户技能统<E883BD>? Future<void> loadUserSkillStatistics(String userId) async {
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.getUserSkillStatistics(userId);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_skillStatistics = response.data!;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('加载技能统计失<EFBFBD>? ${e.toString()}');
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载题目统计
|
||||
Future<void> loadQuestionStatistics() async {
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.getQuestionStatistics();
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_questionStatistics = response.data!;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('加载题目统计失败: ${e.toString()}');
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 删除测试结果
|
||||
Future<bool> deleteTestResult(String resultId) async {
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.deleteTestResult(resultId);
|
||||
|
||||
if (response.success) {
|
||||
_testResults.removeWhere((result) => result.id == resultId);
|
||||
notifyListeners();
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('删除测试结果失败: ${e.toString()}');
|
||||
return false;
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 重置当前会话
|
||||
void resetCurrentSession() {
|
||||
_currentSession = null;
|
||||
_currentResult = null;
|
||||
_stopTimer();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 开始计时器
|
||||
void _startTimer() {
|
||||
_isTimerRunning = true;
|
||||
_updateTimer();
|
||||
}
|
||||
|
||||
/// 暂停计时<E8AEA1>? void _pauseTimer() {
|
||||
_isTimerRunning = false;
|
||||
}
|
||||
|
||||
/// 停止计时<E8AEA1>? void _stopTimer() {
|
||||
_isTimerRunning = false;
|
||||
_timeRemaining = 0;
|
||||
}
|
||||
|
||||
/// 更新计时<E8AEA1>? void _updateTimer() {
|
||||
if (_isTimerRunning && _timeRemaining > 0) {
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
if (_isTimerRunning) {
|
||||
_timeRemaining--;
|
||||
notifyListeners();
|
||||
|
||||
if (_timeRemaining <= 0) {
|
||||
// 时间到,自动完成测试
|
||||
completeTest();
|
||||
} else {
|
||||
_updateTimer();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 格式化剩余时<E4BD99>? String getFormattedTimeRemaining() {
|
||||
final hours = _timeRemaining ~/ 3600;
|
||||
final minutes = (_timeRemaining % 3600) ~/ 60;
|
||||
final seconds = _timeRemaining % 60;
|
||||
|
||||
if (hours > 0) {
|
||||
return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
||||
} else {
|
||||
return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取测试进度百分<E799BE>? double getTestProgress() {
|
||||
if (_currentSession == null || _currentSession!.questions.isEmpty) {
|
||||
return 0.0;
|
||||
}
|
||||
return (_currentSession!.currentQuestionIndex + 1) / _currentSession!.questions.length;
|
||||
}
|
||||
|
||||
/// 获取已回答题目数<E79BAE>? int getAnsweredQuestionsCount() {
|
||||
return _currentSession?.answers.length ?? 0;
|
||||
}
|
||||
|
||||
/// 获取未回答题目数<E79BAE>? int getUnansweredQuestionsCount() {
|
||||
if (_currentSession == null) return 0;
|
||||
return _currentSession!.questions.length - _currentSession!.answers.length;
|
||||
}
|
||||
|
||||
/// 检查是否所有题目都已回<E5B7B2>? bool areAllQuestionsAnswered() {
|
||||
if (_currentSession == null) return false;
|
||||
return _currentSession!.answers.length == _currentSession!.questions.length;
|
||||
}
|
||||
|
||||
/// 获取技能类型的中文名称
|
||||
String getSkillTypeName(SkillType skillType) {
|
||||
switch (skillType) {
|
||||
case SkillType.vocabulary:
|
||||
return '词汇';
|
||||
case SkillType.grammar:
|
||||
return '语法';
|
||||
case SkillType.reading:
|
||||
return '阅读';
|
||||
case SkillType.listening:
|
||||
return '听力';
|
||||
case SkillType.speaking:
|
||||
return '口语';
|
||||
case SkillType.writing:
|
||||
return '写作';
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取难度等级的中文名<E69687>? String getDifficultyLevelName(DifficultyLevel level) {
|
||||
switch (level) {
|
||||
case DifficultyLevel.beginner:
|
||||
return '初级';
|
||||
case DifficultyLevel.elementary:
|
||||
return '基础';
|
||||
case DifficultyLevel.intermediate:
|
||||
return '中级';
|
||||
case DifficultyLevel.upperIntermediate:
|
||||
return '中高<EFBFBD>?;
|
||||
case DifficultyLevel.advanced:
|
||||
return '高级';
|
||||
case DifficultyLevel.expert:
|
||||
return '专家<EFBFBD>?;
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取测试类型的中文名<E69687>? String getTestTypeName(TestType type) {
|
||||
switch (type) {
|
||||
case TestType.quick:
|
||||
return '快速测<EFBFBD>?;
|
||||
case TestType.standard:
|
||||
return '标准测试';
|
||||
case TestType.full:
|
||||
return '完整测试';
|
||||
case TestType.mock:
|
||||
return '模拟考试';
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载最近的测试结果
|
||||
Future<void> loadRecentResults({String? userId}) async {
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.getRecentTestResults(userId);
|
||||
if (response.success && response.data != null) {
|
||||
_testResults = response.data!;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('加载测试结果失败: ${e.toString()}');
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_stopTimer();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../models/test_models.dart';
|
||||
import '../services/test_service.dart';
|
||||
import '../services/test_service_impl.dart';
|
||||
import '../../../core/models/api_response.dart';
|
||||
|
||||
/// 测试服务提供者
|
||||
final testServiceProvider = Provider<TestService>((ref) {
|
||||
return TestServiceImpl();
|
||||
});
|
||||
|
||||
/// 测试状态类
|
||||
class TestState {
|
||||
final bool isLoading;
|
||||
final String? errorMessage;
|
||||
final List<TestTemplate> templates;
|
||||
final TestSession? currentSession;
|
||||
final List<TestResult> testResults;
|
||||
final TestResult? currentResult;
|
||||
final Map<SkillType, double> skillStatistics;
|
||||
final Map<String, dynamic> questionStatistics;
|
||||
final int timeRemaining;
|
||||
final bool isTimerRunning;
|
||||
|
||||
const TestState({
|
||||
this.isLoading = false,
|
||||
this.errorMessage,
|
||||
this.templates = const [],
|
||||
this.currentSession,
|
||||
this.testResults = const [],
|
||||
this.currentResult,
|
||||
this.skillStatistics = const {},
|
||||
this.questionStatistics = const {},
|
||||
this.timeRemaining = 0,
|
||||
this.isTimerRunning = false,
|
||||
});
|
||||
|
||||
TestState copyWith({
|
||||
bool? isLoading,
|
||||
String? errorMessage,
|
||||
List<TestTemplate>? templates,
|
||||
TestSession? currentSession,
|
||||
List<TestResult>? testResults,
|
||||
TestResult? currentResult,
|
||||
Map<SkillType, double>? skillStatistics,
|
||||
Map<String, dynamic>? questionStatistics,
|
||||
int? timeRemaining,
|
||||
bool? isTimerRunning,
|
||||
}) {
|
||||
return TestState(
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
templates: templates ?? this.templates,
|
||||
currentSession: currentSession ?? this.currentSession,
|
||||
testResults: testResults ?? this.testResults,
|
||||
currentResult: currentResult ?? this.currentResult,
|
||||
skillStatistics: skillStatistics ?? this.skillStatistics,
|
||||
questionStatistics: questionStatistics ?? this.questionStatistics,
|
||||
timeRemaining: timeRemaining ?? this.timeRemaining,
|
||||
isTimerRunning: isTimerRunning ?? this.isTimerRunning,
|
||||
);
|
||||
}
|
||||
|
||||
/// 获取当前题目
|
||||
TestQuestion? get currentQuestion {
|
||||
if (currentSession == null ||
|
||||
currentSession!.currentQuestionIndex >= currentSession!.questions.length) {
|
||||
return null;
|
||||
}
|
||||
return currentSession!.questions[currentSession!.currentQuestionIndex];
|
||||
}
|
||||
}
|
||||
|
||||
/// 测试状态管理器
|
||||
class TestNotifier extends StateNotifier<TestState> {
|
||||
final TestService _testService;
|
||||
|
||||
TestNotifier(this._testService) : super(const TestState());
|
||||
|
||||
/// 设置加载状态
|
||||
void _setLoading(bool loading) {
|
||||
state = state.copyWith(isLoading: loading);
|
||||
}
|
||||
|
||||
/// 设置错误信息
|
||||
void _setError(String? error) {
|
||||
state = state.copyWith(errorMessage: error);
|
||||
}
|
||||
|
||||
/// 清除错误信息
|
||||
void clearError() {
|
||||
_setError(null);
|
||||
}
|
||||
|
||||
/// 加载所有测试模板
|
||||
Future<void> loadTestTemplates() async {
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.getTestTemplates();
|
||||
if (response.success && response.data != null) {
|
||||
state = state.copyWith(templates: response.data!, isLoading: false);
|
||||
} else {
|
||||
_setError(response.message);
|
||||
_setLoading(false);
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('加载测试模板失败: ${e.toString()}');
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载指定类型的测试模板
|
||||
Future<void> loadTestTemplatesByType(TestType type) async {
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.getTestTemplatesByType(type);
|
||||
if (response.success && response.data != null) {
|
||||
state = state.copyWith(templates: response.data!, isLoading: false);
|
||||
} else {
|
||||
_setError(response.message);
|
||||
_setLoading(false);
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('加载测试模板失败: ${e.toString()}');
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载最近的测试结果
|
||||
Future<void> loadRecentResults({String? userId}) async {
|
||||
if (userId == null) {
|
||||
_setError('用户ID不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.getRecentTestResults(userId);
|
||||
if (response.success && response.data != null) {
|
||||
state = state.copyWith(testResults: response.data!, isLoading: false);
|
||||
} else {
|
||||
_setError(response.message);
|
||||
_setLoading(false);
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('加载测试结果失败: ${e.toString()}');
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建测试会话
|
||||
Future<bool> createTestSession({
|
||||
required String templateId,
|
||||
required String userId,
|
||||
}) async {
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.createTestSession(
|
||||
templateId: templateId,
|
||||
userId: userId,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
state = state.copyWith(
|
||||
currentSession: response.data!,
|
||||
timeRemaining: response.data!.timeRemaining,
|
||||
isLoading: false,
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
_setLoading(false);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('创建测试会话失败: ${e.toString()}');
|
||||
_setLoading(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 开始测试
|
||||
Future<bool> startTest() async {
|
||||
if (state.currentSession == null) {
|
||||
_setError('没有活动的测试会话');
|
||||
return false;
|
||||
}
|
||||
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.startTest(state.currentSession!.id);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
state = state.copyWith(
|
||||
currentSession: response.data!,
|
||||
isTimerRunning: true,
|
||||
isLoading: false,
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
_setLoading(false);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('开始测试失败: ${e.toString()}');
|
||||
_setLoading(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置当前测试会话
|
||||
void setCurrentSession(TestSession session) {
|
||||
state = state.copyWith(
|
||||
currentSession: session,
|
||||
timeRemaining: session.timeRemaining,
|
||||
isTimerRunning: true,
|
||||
);
|
||||
}
|
||||
|
||||
/// 设置当前测试结果
|
||||
void setCurrentResult(TestResult result) {
|
||||
state = state.copyWith(
|
||||
currentResult: result,
|
||||
isLoading: false,
|
||||
);
|
||||
}
|
||||
|
||||
/// 记录答案(本地记录,不提交到服务器)
|
||||
void recordAnswer(UserAnswer answer) {
|
||||
if (state.currentSession == null) {
|
||||
_setError('没有活动的测试会话');
|
||||
return;
|
||||
}
|
||||
|
||||
final currentSession = state.currentSession!;
|
||||
final updatedAnswers = List<UserAnswer>.from(currentSession.answers);
|
||||
|
||||
// 移除该题目的旧答案
|
||||
updatedAnswers.removeWhere((a) => a.questionId == answer.questionId);
|
||||
// 添加新答案
|
||||
updatedAnswers.add(answer);
|
||||
|
||||
final updatedSession = currentSession.copyWith(answers: updatedAnswers);
|
||||
state = state.copyWith(currentSession: updatedSession);
|
||||
}
|
||||
|
||||
/// 提交答案
|
||||
Future<bool> submitAnswer(UserAnswer answer) async {
|
||||
if (state.currentSession == null) {
|
||||
_setError('没有活动的测试会话');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
final response = await _testService.submitAnswer(
|
||||
sessionId: state.currentSession!.id,
|
||||
answer: answer,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
state = state.copyWith(currentSession: response.data!);
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('提交答案失败: ${e.toString()}');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 完成测试
|
||||
Future<TestResult?> completeTest() async {
|
||||
if (state.currentSession == null) {
|
||||
_setError('没有活动的测试会话');
|
||||
return null;
|
||||
}
|
||||
|
||||
_setLoading(true);
|
||||
_setError(null);
|
||||
|
||||
try {
|
||||
final response = await _testService.completeTest(
|
||||
sessionId: state.currentSession!.id,
|
||||
answers: state.currentSession!.answers,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
state = state.copyWith(
|
||||
currentResult: response.data!,
|
||||
currentSession: null,
|
||||
isTimerRunning: false,
|
||||
isLoading: false,
|
||||
);
|
||||
return response.data!;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
_setLoading(false);
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('完成测试失败: ${e.toString()}');
|
||||
_setLoading(false);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// 下一题
|
||||
void nextQuestion() {
|
||||
if (state.currentSession == null) {
|
||||
_setError('没有活动的测试会话');
|
||||
return;
|
||||
}
|
||||
|
||||
final currentSession = state.currentSession!;
|
||||
if (currentSession.currentQuestionIndex < currentSession.questions.length - 1) {
|
||||
final updatedSession = TestSession(
|
||||
id: currentSession.id,
|
||||
userId: currentSession.userId,
|
||||
templateId: currentSession.templateId,
|
||||
questions: currentSession.questions,
|
||||
answers: currentSession.answers,
|
||||
startTime: currentSession.startTime,
|
||||
endTime: currentSession.endTime,
|
||||
status: currentSession.status,
|
||||
timeRemaining: currentSession.timeRemaining,
|
||||
currentQuestionIndex: currentSession.currentQuestionIndex + 1,
|
||||
);
|
||||
|
||||
state = state.copyWith(currentSession: updatedSession);
|
||||
}
|
||||
}
|
||||
|
||||
/// 上一题
|
||||
void previousQuestion() {
|
||||
if (state.currentSession == null) {
|
||||
_setError('没有活动的测试会话');
|
||||
return;
|
||||
}
|
||||
|
||||
final currentSession = state.currentSession!;
|
||||
if (currentSession.currentQuestionIndex > 0) {
|
||||
final updatedSession = TestSession(
|
||||
id: currentSession.id,
|
||||
userId: currentSession.userId,
|
||||
templateId: currentSession.templateId,
|
||||
questions: currentSession.questions,
|
||||
answers: currentSession.answers,
|
||||
startTime: currentSession.startTime,
|
||||
endTime: currentSession.endTime,
|
||||
status: currentSession.status,
|
||||
timeRemaining: currentSession.timeRemaining,
|
||||
currentQuestionIndex: currentSession.currentQuestionIndex - 1,
|
||||
);
|
||||
|
||||
state = state.copyWith(currentSession: updatedSession);
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取测试类型的中文名称
|
||||
String getTestTypeName(TestType type) {
|
||||
switch (type) {
|
||||
case TestType.quick:
|
||||
return '快速测试';
|
||||
case TestType.standard:
|
||||
return '标准测试';
|
||||
case TestType.full:
|
||||
return '完整测试';
|
||||
case TestType.mock:
|
||||
return '模拟考试';
|
||||
case TestType.vocabulary:
|
||||
return '词汇专项';
|
||||
case TestType.grammar:
|
||||
return '语法专项';
|
||||
case TestType.reading:
|
||||
return '阅读专项';
|
||||
case TestType.listening:
|
||||
return '听力专项';
|
||||
case TestType.speaking:
|
||||
return '口语专项';
|
||||
case TestType.writing:
|
||||
return '写作专项';
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取技能类型的中文名称
|
||||
String getSkillTypeName(SkillType skill) {
|
||||
switch (skill) {
|
||||
case SkillType.vocabulary:
|
||||
return '词汇';
|
||||
case SkillType.grammar:
|
||||
return '语法';
|
||||
case SkillType.reading:
|
||||
return '阅读';
|
||||
case SkillType.listening:
|
||||
return '听力';
|
||||
case SkillType.speaking:
|
||||
return '口语';
|
||||
case SkillType.writing:
|
||||
return '写作';
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取难度等级的中文名称
|
||||
String getDifficultyLevelName(DifficultyLevel level) {
|
||||
switch (level) {
|
||||
case DifficultyLevel.beginner:
|
||||
return '初级';
|
||||
case DifficultyLevel.elementary:
|
||||
return '基础';
|
||||
case DifficultyLevel.intermediate:
|
||||
return '中级';
|
||||
case DifficultyLevel.upperIntermediate:
|
||||
return '中高级';
|
||||
case DifficultyLevel.advanced:
|
||||
return '高级';
|
||||
case DifficultyLevel.expert:
|
||||
return '专家级';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 测试状态提供者
|
||||
final testProvider = StateNotifierProvider<TestNotifier, TestState>((ref) {
|
||||
final testService = ref.watch(testServiceProvider);
|
||||
return TestNotifier(testService);
|
||||
});
|
||||
|
||||
/// 当前测试会话提供者
|
||||
final currentTestSessionProvider = Provider<TestSession?>((ref) {
|
||||
return ref.watch(testProvider).currentSession;
|
||||
});
|
||||
|
||||
/// 当前题目提供者
|
||||
final currentQuestionProvider = Provider<TestQuestion?>((ref) {
|
||||
return ref.watch(testProvider).currentQuestion;
|
||||
});
|
||||
|
||||
/// 测试模板提供者
|
||||
final testTemplatesProvider = Provider<List<TestTemplate>>((ref) {
|
||||
return ref.watch(testProvider).templates;
|
||||
});
|
||||
|
||||
/// 测试结果提供者
|
||||
final testResultsProvider = Provider<List<TestResult>>((ref) {
|
||||
return ref.watch(testProvider).testResults;
|
||||
});
|
||||
Reference in New Issue
Block a user