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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user