init
This commit is contained in:
@@ -0,0 +1,416 @@
|
||||
import '../../../core/models/api_response.dart';
|
||||
import '../../../core/services/enhanced_api_service.dart';
|
||||
import '../models/test_models.dart';
|
||||
|
||||
/// 综合测试API服务类
|
||||
/// 使用后端API替代静态数据
|
||||
class TestApiService {
|
||||
static final TestApiService _instance = TestApiService._internal();
|
||||
factory TestApiService() => _instance;
|
||||
TestApiService._internal();
|
||||
|
||||
final EnhancedApiService _enhancedApiService = EnhancedApiService();
|
||||
|
||||
// 缓存时长配置
|
||||
static const Duration _shortCacheDuration = Duration(minutes: 5);
|
||||
static const Duration _longCacheDuration = Duration(hours: 1);
|
||||
|
||||
/// 获取所有测试模板
|
||||
Future<ApiResponse<List<TestTemplate>>> getTestTemplates() async {
|
||||
try {
|
||||
final response = await _enhancedApiService.get<List<TestTemplate>>(
|
||||
'/tests/templates',
|
||||
cacheDuration: _longCacheDuration,
|
||||
fromJson: (data) {
|
||||
final templates = data['templates'] as List?;
|
||||
if (templates == null) return [];
|
||||
return templates.map((json) => TestTemplate.fromJson(json)).toList();
|
||||
},
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '获取测试模板成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取测试模板失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 根据类型获取测试模板
|
||||
Future<ApiResponse<List<TestTemplate>>> getTestTemplatesByType(
|
||||
TestType type,
|
||||
) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.get<List<TestTemplate>>(
|
||||
'/tests/templates',
|
||||
queryParameters: {
|
||||
'type': type.toString().split('.').last,
|
||||
},
|
||||
cacheDuration: _longCacheDuration,
|
||||
fromJson: (data) {
|
||||
final templates = data['templates'] as List?;
|
||||
if (templates == null) return [];
|
||||
return templates.map((json) => TestTemplate.fromJson(json)).toList();
|
||||
},
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '获取测试模板成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取测试模板失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 根据ID获取测试模板
|
||||
Future<ApiResponse<TestTemplate>> getTestTemplateById(String id) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.get<TestTemplate>(
|
||||
'/tests/templates/$id',
|
||||
cacheDuration: _longCacheDuration,
|
||||
fromJson: (data) => TestTemplate.fromJson(data['data'] ?? data),
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '获取测试模板成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取测试模板失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建测试会话
|
||||
Future<ApiResponse<TestSession>> createTestSession({
|
||||
required String templateId,
|
||||
required String userId,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.post<TestSession>(
|
||||
'/tests/sessions',
|
||||
data: {
|
||||
'template_id': templateId,
|
||||
'user_id': userId,
|
||||
},
|
||||
fromJson: (data) => TestSession.fromJson(data['data'] ?? data),
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '创建测试会话成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '创建测试会话失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 开始测试
|
||||
Future<ApiResponse<TestSession>> startTest(String sessionId) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.put<TestSession>(
|
||||
'/tests/sessions/$sessionId/start',
|
||||
fromJson: (data) => TestSession.fromJson(data['data'] ?? data),
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '开始测试成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '开始测试失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 提交答案
|
||||
Future<ApiResponse<TestSession>> submitAnswer({
|
||||
required String sessionId,
|
||||
required UserAnswer answer,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.post<TestSession>(
|
||||
'/tests/sessions/$sessionId/answers',
|
||||
data: {
|
||||
'question_id': answer.questionId,
|
||||
'answer': answer.answer,
|
||||
},
|
||||
fromJson: (data) => TestSession.fromJson(data['data'] ?? data),
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '提交答案成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '提交答案失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 暂停测试
|
||||
Future<ApiResponse<TestSession>> pauseTest(String sessionId) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.put<TestSession>(
|
||||
'/tests/sessions/$sessionId/pause',
|
||||
fromJson: (data) => TestSession.fromJson(data['data'] ?? data),
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '测试已暂停');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '暂停测试失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 恢复测试
|
||||
Future<ApiResponse<TestSession>> resumeTest(String sessionId) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.put<TestSession>(
|
||||
'/tests/sessions/$sessionId/resume',
|
||||
fromJson: (data) => TestSession.fromJson(data['data'] ?? data),
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '测试已恢复');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '恢复测试失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 完成测试
|
||||
Future<ApiResponse<TestResult>> completeTest(String sessionId) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.put<TestResult>(
|
||||
'/tests/sessions/$sessionId/complete',
|
||||
fromJson: (data) => TestResult.fromJson(data['data'] ?? data),
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '完成测试成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '完成测试失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取测试结果
|
||||
Future<ApiResponse<TestResult>> getTestResult(String sessionId) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.get<TestResult>(
|
||||
'/tests/sessions/$sessionId/result',
|
||||
cacheDuration: _shortCacheDuration,
|
||||
fromJson: (data) => TestResult.fromJson(data['data'] ?? data),
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '获取测试结果成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取测试结果失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取用户测试历史
|
||||
Future<ApiResponse<List<TestSession>>> getUserTestHistory({
|
||||
int page = 1,
|
||||
int limit = 20,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.get<List<TestSession>>(
|
||||
'/tests/sessions',
|
||||
queryParameters: {
|
||||
'page': page,
|
||||
'limit': limit,
|
||||
},
|
||||
cacheDuration: _shortCacheDuration,
|
||||
fromJson: (data) {
|
||||
final sessions = data['sessions'] as List?;
|
||||
if (sessions == null) return [];
|
||||
return sessions.map((json) => TestSession.fromJson(json)).toList();
|
||||
},
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '获取测试历史成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取测试历史失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取用户测试统计
|
||||
Future<ApiResponse<Map<String, dynamic>>> getUserTestStats() async {
|
||||
try {
|
||||
final response = await _enhancedApiService.get<Map<String, dynamic>>(
|
||||
'/tests/stats',
|
||||
cacheDuration: _shortCacheDuration,
|
||||
fromJson: (data) => data,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '获取测试统计成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取测试统计失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取最近的测试结果
|
||||
Future<ApiResponse<List<TestResult>>> getRecentTestResults(
|
||||
String? userId, {
|
||||
int limit = 5,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.get<List<TestResult>>(
|
||||
'/tests/sessions/recent',
|
||||
queryParameters: {
|
||||
if (userId != null) 'user_id': userId,
|
||||
'limit': limit,
|
||||
},
|
||||
cacheDuration: _shortCacheDuration,
|
||||
fromJson: (data) {
|
||||
final sessions = data['sessions'] as List?;
|
||||
if (sessions == null) return [];
|
||||
return sessions.map((json) => TestResult.fromJson(json)).toList();
|
||||
},
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '获取最近测试结果成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取最近测试结果失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取测试结果详情
|
||||
Future<ApiResponse<TestResult>> getTestResultById(String resultId) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.get<TestResult>(
|
||||
'/tests/results/$resultId',
|
||||
cacheDuration: _shortCacheDuration,
|
||||
fromJson: (data) => TestResult.fromJson(data['data'] ?? data),
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '获取测试结果成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取测试结果失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取用户技能统计
|
||||
Future<ApiResponse<Map<SkillType, double>>> getUserSkillStatistics(
|
||||
String userId,
|
||||
) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.get<Map<SkillType, double>>(
|
||||
'/tests/users/$userId/skills',
|
||||
cacheDuration: _shortCacheDuration,
|
||||
fromJson: (data) {
|
||||
final skillsData = data['skills'] as Map<String, dynamic>?;
|
||||
if (skillsData == null) return {};
|
||||
|
||||
final result = <SkillType, double>{};
|
||||
skillsData.forEach((key, value) {
|
||||
final skillType = _parseSkillType(key);
|
||||
if (skillType != null) {
|
||||
result[skillType] = (value as num).toDouble();
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '获取技能统计成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取技能统计失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取题目统计
|
||||
Future<ApiResponse<Map<String, dynamic>>> getQuestionStatistics() async {
|
||||
try {
|
||||
final response = await _enhancedApiService.get<Map<String, dynamic>>(
|
||||
'/tests/questions/statistics',
|
||||
cacheDuration: _longCacheDuration,
|
||||
fromJson: (data) => data,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(data: response.data!, message: '获取题目统计成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取题目统计失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 删除测试结果
|
||||
Future<ApiResponse<void>> deleteTestResult(String resultId) async {
|
||||
try {
|
||||
final response = await _enhancedApiService.delete<void>(
|
||||
'/tests/results/$resultId',
|
||||
fromJson: (data) => null,
|
||||
);
|
||||
|
||||
if (response.success) {
|
||||
return ApiResponse.success(message: '删除测试结果成功');
|
||||
} else {
|
||||
return ApiResponse.error(message: response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '删除测试结果失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析技能类型
|
||||
SkillType? _parseSkillType(String key) {
|
||||
switch (key.toLowerCase()) {
|
||||
case 'vocabulary':
|
||||
return SkillType.vocabulary;
|
||||
case 'grammar':
|
||||
return SkillType.grammar;
|
||||
case 'reading':
|
||||
return SkillType.reading;
|
||||
case 'listening':
|
||||
return SkillType.listening;
|
||||
case 'speaking':
|
||||
return SkillType.speaking;
|
||||
case 'writing':
|
||||
return SkillType.writing;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
import '../models/test_models.dart';
|
||||
import '../data/test_static_data.dart';
|
||||
import '../../../core/models/api_response.dart';
|
||||
|
||||
/// 综合测试服务类
|
||||
class TestService {
|
||||
// 单例模式
|
||||
static final TestService _instance = TestService._internal();
|
||||
factory TestService() => _instance;
|
||||
TestService._internal();
|
||||
|
||||
/// 获取所有测试模板
|
||||
Future<ApiResponse<List<TestTemplate>>> getTestTemplates() async {
|
||||
try {
|
||||
// 模拟网络延迟
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
final templates = TestStaticData.getAllTemplates();
|
||||
return ApiResponse.success(
|
||||
data: templates,
|
||||
message: '获取测试模板成功',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '获取测试模板失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 根据类型获取测试模板
|
||||
Future<ApiResponse<List<TestTemplate>>> getTestTemplatesByType(
|
||||
TestType type,
|
||||
) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
|
||||
final templates = TestStaticData.getTemplatesByType(type);
|
||||
return ApiResponse.success(
|
||||
data: templates,
|
||||
message: '获取${_getTestTypeName(type)}模板成功',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '获取测试模板失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 根据ID获取测试模板
|
||||
Future<ApiResponse<TestTemplate>> getTestTemplateById(String id) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
final template = TestStaticData.getTemplateById(id);
|
||||
if (template == null) {
|
||||
return ApiResponse.error(message: '测试模板不存在');
|
||||
}
|
||||
|
||||
return ApiResponse.success(
|
||||
data: template,
|
||||
message: '获取测试模板成功',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '获取测试模板失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建测试会话
|
||||
Future<ApiResponse<TestSession>> createTestSession({
|
||||
required String templateId,
|
||||
required String userId,
|
||||
}) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 800));
|
||||
|
||||
final session = TestStaticData.createTestSession(
|
||||
templateId: templateId,
|
||||
userId: userId,
|
||||
);
|
||||
|
||||
return ApiResponse.success(
|
||||
data: session,
|
||||
message: '创建测试会话成功',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '创建测试会话失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 开始测试
|
||||
Future<ApiResponse<TestSession>> startTest(String sessionId) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
|
||||
// 模拟开始测试,实际应用中会更新数据库中的会话状态
|
||||
// 这里返回一个模拟的已开始的会话
|
||||
return ApiResponse.success(
|
||||
data: TestSession(
|
||||
id: sessionId,
|
||||
templateId: 'template_quick',
|
||||
userId: 'user_001',
|
||||
status: TestStatus.inProgress,
|
||||
questions: TestStaticData.getQuestionsByIds(['vocab_001', 'grammar_001']),
|
||||
answers: [],
|
||||
currentQuestionIndex: 0,
|
||||
startTime: DateTime.now(),
|
||||
timeRemaining: 900, // 15分钟
|
||||
),
|
||||
message: '测试已开始',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '开始测试失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 提交答案
|
||||
Future<ApiResponse<TestSession>> submitAnswer({
|
||||
required String sessionId,
|
||||
required UserAnswer answer,
|
||||
}) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 400));
|
||||
|
||||
// 模拟提交答案并返回更新后的会话
|
||||
return ApiResponse.success(
|
||||
data: TestSession(
|
||||
id: sessionId,
|
||||
templateId: 'template_quick',
|
||||
userId: 'user_001',
|
||||
status: TestStatus.inProgress,
|
||||
questions: TestStaticData.getQuestionsByIds(['vocab_001', 'grammar_001']),
|
||||
answers: [answer],
|
||||
currentQuestionIndex: 1,
|
||||
startTime: DateTime.now().subtract(const Duration(minutes: 5)),
|
||||
timeRemaining: 600, // 剩余10分钟
|
||||
),
|
||||
message: '答案提交成功',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '提交答案失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 暂停测试
|
||||
Future<ApiResponse<TestSession>> pauseTest(String sessionId) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
return ApiResponse.success(
|
||||
data: TestSession(
|
||||
id: sessionId,
|
||||
templateId: 'template_quick',
|
||||
userId: 'user_001',
|
||||
status: TestStatus.paused,
|
||||
questions: TestStaticData.getQuestionsByIds(['vocab_001', 'grammar_001']),
|
||||
answers: [],
|
||||
currentQuestionIndex: 0,
|
||||
startTime: DateTime.now().subtract(const Duration(minutes: 5)),
|
||||
timeRemaining: 600,
|
||||
),
|
||||
message: '测试已暂停',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '暂停测试失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 恢复测试
|
||||
Future<ApiResponse<TestSession>> resumeTest(String sessionId) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
return ApiResponse.success(
|
||||
data: TestSession(
|
||||
id: sessionId,
|
||||
templateId: 'template_quick',
|
||||
userId: 'user_001',
|
||||
status: TestStatus.inProgress,
|
||||
questions: TestStaticData.getQuestionsByIds(['vocab_001', 'grammar_001']),
|
||||
answers: [],
|
||||
currentQuestionIndex: 0,
|
||||
startTime: DateTime.now().subtract(const Duration(minutes: 5)),
|
||||
timeRemaining: 600,
|
||||
),
|
||||
message: '测试已恢复',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '恢复测试失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 完成测试
|
||||
Future<ApiResponse<TestResult>> completeTest({
|
||||
required String sessionId,
|
||||
required List<UserAnswer> answers,
|
||||
}) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 1000));
|
||||
|
||||
// 模拟计算测试结果
|
||||
final result = _calculateTestResult(sessionId, answers);
|
||||
|
||||
// 保存结果到静态数据
|
||||
TestStaticData.addTestResult(result);
|
||||
|
||||
return ApiResponse.success(
|
||||
data: result,
|
||||
message: '测试完成,结果已保存',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '完成测试失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取用户测试历史
|
||||
Future<ApiResponse<List<TestResult>>> getUserTestHistory(
|
||||
String userId, {
|
||||
int page = 1,
|
||||
int limit = 10,
|
||||
}) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 600));
|
||||
|
||||
final allResults = TestStaticData.getUserResults(userId);
|
||||
final startIndex = (page - 1) * limit;
|
||||
final endIndex = startIndex + limit;
|
||||
|
||||
final results = allResults.length > startIndex
|
||||
? allResults.sublist(
|
||||
startIndex,
|
||||
endIndex > allResults.length ? allResults.length : endIndex,
|
||||
)
|
||||
: <TestResult>[];
|
||||
|
||||
return ApiResponse.success(
|
||||
data: results,
|
||||
message: '获取测试历史成功',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '获取测试历史失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取最近的测试结果
|
||||
Future<ApiResponse<List<TestResult>>> getRecentTestResults(
|
||||
String userId, {
|
||||
int limit = 5,
|
||||
}) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 400));
|
||||
|
||||
final results = TestStaticData.getRecentResults(userId, limit: limit);
|
||||
return ApiResponse.success(
|
||||
data: results,
|
||||
message: '获取最近测试结果成功',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '获取最近测试结果失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取测试结果详情
|
||||
Future<ApiResponse<TestResult>> getTestResultById(String resultId) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
|
||||
final result = TestStaticData.getResultById(resultId);
|
||||
if (result == null) {
|
||||
return ApiResponse.error(message: '测试结果不存在');
|
||||
}
|
||||
|
||||
return ApiResponse.success(
|
||||
data: result,
|
||||
message: '获取测试结果成功',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '获取测试结果失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取用户技能统计
|
||||
Future<ApiResponse<Map<SkillType, double>>> getUserSkillStatistics(
|
||||
String userId,
|
||||
) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
final statistics = TestStaticData.getSkillStatistics(userId);
|
||||
return ApiResponse.success(
|
||||
data: statistics,
|
||||
message: '获取技能统计成功',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '获取技能统计失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取题目统计
|
||||
Future<ApiResponse<Map<String, dynamic>>> getQuestionStatistics() async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
|
||||
final difficultyStats = TestStaticData.getDifficultyStatistics();
|
||||
final skillStats = TestStaticData.getSkillDistributionStatistics();
|
||||
|
||||
return ApiResponse.success(
|
||||
data: {
|
||||
'difficultyDistribution': difficultyStats,
|
||||
'skillDistribution': skillStats,
|
||||
'totalQuestions': TestStaticData.getAllQuestions().length,
|
||||
},
|
||||
message: '获取题目统计成功',
|
||||
);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '获取题目统计失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 删除测试结果
|
||||
Future<ApiResponse<bool>> deleteTestResult(String resultId) async {
|
||||
try {
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
|
||||
final success = TestStaticData.deleteTestResult(resultId);
|
||||
if (success) {
|
||||
return ApiResponse.success(
|
||||
data: true,
|
||||
message: '删除测试结果成功',
|
||||
);
|
||||
} else {
|
||||
return ApiResponse.error(message: '测试结果不存在');
|
||||
}
|
||||
} catch (e) {
|
||||
return ApiResponse.error(
|
||||
message: '删除测试结果失败: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取测试类型名称
|
||||
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 '写作专项';
|
||||
}
|
||||
}
|
||||
|
||||
/// 计算测试结果(模拟)
|
||||
TestResult _calculateTestResult(String sessionId, List<UserAnswer> answers) {
|
||||
// 这里是一个简化的计算逻辑,实际应用中会更复杂
|
||||
final totalQuestions = answers.length;
|
||||
final correctAnswers = (totalQuestions * 0.8).round(); // 模拟80%正确率
|
||||
final totalScore = correctAnswers;
|
||||
final maxScore = totalQuestions;
|
||||
final percentage = (totalScore / maxScore) * 100;
|
||||
|
||||
return TestResult(
|
||||
id: 'result_${DateTime.now().millisecondsSinceEpoch}',
|
||||
testId: 'template_quick',
|
||||
userId: 'user_001',
|
||||
testType: TestType.quick,
|
||||
totalScore: totalScore,
|
||||
maxScore: maxScore,
|
||||
percentage: percentage,
|
||||
overallLevel: _calculateOverallLevel(percentage),
|
||||
skillScores: _calculateSkillScores(answers),
|
||||
answers: answers,
|
||||
startTime: DateTime.now().subtract(const Duration(minutes: 15)),
|
||||
endTime: DateTime.now(),
|
||||
duration: 900,
|
||||
feedback: _generateFeedback(percentage),
|
||||
recommendations: {
|
||||
'nextLevel': 'intermediate',
|
||||
'focusAreas': ['grammar', 'vocabulary'],
|
||||
'suggestedStudyTime': 45,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 计算整体水平
|
||||
DifficultyLevel _calculateOverallLevel(double percentage) {
|
||||
if (percentage >= 90) return DifficultyLevel.advanced;
|
||||
if (percentage >= 80) return DifficultyLevel.upperIntermediate;
|
||||
if (percentage >= 70) return DifficultyLevel.intermediate;
|
||||
if (percentage >= 60) return DifficultyLevel.elementary;
|
||||
return DifficultyLevel.beginner;
|
||||
}
|
||||
|
||||
/// 计算技能得分
|
||||
List<SkillScore> _calculateSkillScores(List<UserAnswer> answers) {
|
||||
// 简化的技能得分计算
|
||||
return [
|
||||
SkillScore(
|
||||
skillType: SkillType.vocabulary,
|
||||
score: 4,
|
||||
maxScore: 5,
|
||||
percentage: 80.0,
|
||||
level: DifficultyLevel.elementary,
|
||||
feedback: '词汇掌握良好',
|
||||
),
|
||||
SkillScore(
|
||||
skillType: SkillType.grammar,
|
||||
score: 3,
|
||||
maxScore: 4,
|
||||
percentage: 75.0,
|
||||
level: DifficultyLevel.elementary,
|
||||
feedback: '语法基础扎实',
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/// 生成反馈
|
||||
String _generateFeedback(double percentage) {
|
||||
if (percentage >= 90) {
|
||||
return '优秀!您的英语水平很高,继续保持。';
|
||||
} else if (percentage >= 80) {
|
||||
return '良好!您的英语基础扎实,可以尝试更高难度的内容。';
|
||||
} else if (percentage >= 70) {
|
||||
return '不错!您的英语水平中等,建议加强薄弱环节的练习。';
|
||||
} else if (percentage >= 60) {
|
||||
return '及格!您的英语基础还需要加强,建议多练习基础知识。';
|
||||
} else {
|
||||
return '需要努力!建议从基础开始,系统性地学习英语。';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
import '../models/test_models.dart';
|
||||
import 'test_service.dart';
|
||||
import '../../../core/models/api_response.dart';
|
||||
import '../../../core/network/api_client.dart';
|
||||
import '../../../core/network/api_endpoints.dart';
|
||||
|
||||
/// 测试服务实现类(HTTP,使用后端真实API)
|
||||
class TestServiceImpl implements TestService {
|
||||
final ApiClient _apiClient = ApiClient.instance;
|
||||
|
||||
@override
|
||||
Future<ApiResponse<List<TestTemplate>>> getTestTemplates() async {
|
||||
try {
|
||||
final response = await _apiClient.get('${ApiEndpoints.baseUrl}/tests/templates');
|
||||
if (response.statusCode == 200) {
|
||||
final root = response.data['data'];
|
||||
List<dynamic> list = const [];
|
||||
if (root is List) {
|
||||
list = root;
|
||||
} else if (root is Map<String, dynamic>) {
|
||||
final v = root['templates'];
|
||||
if (v is List) list = v;
|
||||
}
|
||||
final templates = list.map((e) => TestTemplate.fromJson(e)).toList();
|
||||
return ApiResponse.success(message: '获取测试模板成功', data: templates);
|
||||
}
|
||||
return ApiResponse.error(message: '加载测试模板失败', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取测试模板失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<List<TestTemplate>>> getTestTemplatesByType(
|
||||
TestType type, {
|
||||
bool? isActive,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _apiClient.get(
|
||||
'${ApiEndpoints.baseUrl}/tests/templates',
|
||||
queryParameters: {
|
||||
'type': type.name,
|
||||
if (isActive != null) 'active': isActive,
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
final root = response.data['data'];
|
||||
List<dynamic> list = const [];
|
||||
if (root is List) {
|
||||
list = root;
|
||||
} else if (root is Map<String, dynamic>) {
|
||||
final v = root['templates'];
|
||||
if (v is List) list = v;
|
||||
}
|
||||
final templates = list.map((e) => TestTemplate.fromJson(e)).toList();
|
||||
return ApiResponse.success(message: '获取测试模板成功', data: templates);
|
||||
}
|
||||
return ApiResponse.error(message: '加载测试模板失败', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取测试模板失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<TestTemplate>> getTestTemplateById(String id) async {
|
||||
try {
|
||||
final response = await _apiClient.get('${ApiEndpoints.baseUrl}/tests/templates/$id');
|
||||
if (response.statusCode == 200) {
|
||||
return ApiResponse.success(
|
||||
message: '获取测试模板成功',
|
||||
data: TestTemplate.fromJson(response.data['data']),
|
||||
);
|
||||
}
|
||||
return ApiResponse.error(message: '模板不存在', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取测试模板失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<TestSession>> createTestSession({
|
||||
required String templateId,
|
||||
required String userId,
|
||||
Map<String, dynamic>? metadata,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _apiClient.post(
|
||||
'${ApiEndpoints.baseUrl}/tests/sessions',
|
||||
data: {
|
||||
'template_id': templateId,
|
||||
'user_id': userId,
|
||||
'metadata': metadata ?? {},
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return ApiResponse.success(
|
||||
message: '创建测试会话成功',
|
||||
data: TestSession.fromJson(response.data['data']),
|
||||
);
|
||||
}
|
||||
return ApiResponse.error(message: '创建测试会话失败', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '创建测试会话失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<TestSession>> startTest(String sessionId) async {
|
||||
try {
|
||||
final response = await _apiClient.put('${ApiEndpoints.baseUrl}/tests/sessions/$sessionId/start');
|
||||
if (response.statusCode == 200) {
|
||||
return ApiResponse.success(
|
||||
message: '测试已开始',
|
||||
data: TestSession.fromJson(response.data['data']),
|
||||
);
|
||||
}
|
||||
return ApiResponse.error(message: '开始测试失败', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '开始测试失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<TestSession>> submitAnswer({
|
||||
required String sessionId,
|
||||
required UserAnswer answer,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _apiClient.post(
|
||||
'${ApiEndpoints.baseUrl}/tests/sessions/$sessionId/answers',
|
||||
data: answer.toJson(),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return ApiResponse.success(
|
||||
message: '答案提交成功',
|
||||
data: TestSession.fromJson(response.data['data']),
|
||||
);
|
||||
}
|
||||
return ApiResponse.error(message: '提交答案失败', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '提交答案失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<TestSession>> pauseTest(String sessionId) async {
|
||||
try {
|
||||
final response = await _apiClient.put('${ApiEndpoints.baseUrl}/tests/sessions/$sessionId/pause');
|
||||
if (response.statusCode == 200) {
|
||||
return ApiResponse.success(
|
||||
message: '测试已暂停',
|
||||
data: TestSession.fromJson(response.data['data']),
|
||||
);
|
||||
}
|
||||
return ApiResponse.error(message: '暂停测试失败', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '暂停测试失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<TestSession>> resumeTest(String sessionId) async {
|
||||
try {
|
||||
final response = await _apiClient.put('${ApiEndpoints.baseUrl}/tests/sessions/$sessionId/resume');
|
||||
if (response.statusCode == 200) {
|
||||
return ApiResponse.success(
|
||||
message: '测试已恢复',
|
||||
data: TestSession.fromJson(response.data['data']),
|
||||
);
|
||||
}
|
||||
return ApiResponse.error(message: '恢复测试失败', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '恢复测试失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<TestResult>> completeTest({
|
||||
required String sessionId,
|
||||
required List<UserAnswer> answers,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _apiClient.put(
|
||||
'${ApiEndpoints.baseUrl}/tests/sessions/$sessionId/complete',
|
||||
data: {
|
||||
'answers': answers.map((a) => a.toJson()).toList(),
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return ApiResponse.success(
|
||||
message: '提交测试成功',
|
||||
data: TestResult.fromJson(response.data['data']),
|
||||
);
|
||||
}
|
||||
return ApiResponse.error(message: '完成测试失败', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '完成测试失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<List<TestResult>>> getUserTestHistory(
|
||||
String userId, {
|
||||
int page = 1,
|
||||
int limit = 10,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _apiClient.get(
|
||||
'${ApiEndpoints.baseUrl}/tests/sessions',
|
||||
queryParameters: {
|
||||
'page': page,
|
||||
'limit': limit,
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
final root = response.data['data'];
|
||||
List<dynamic> list = const [];
|
||||
if (root is List) {
|
||||
list = root;
|
||||
} else if (root is Map<String, dynamic>) {
|
||||
final v = root['sessions'];
|
||||
if (v is List) list = v;
|
||||
}
|
||||
final results = list.map((e) => TestResult.fromJson(e)).toList();
|
||||
return ApiResponse.success(message: '获取测试历史成功', data: results);
|
||||
}
|
||||
return ApiResponse.error(message: '获取测试历史失败', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取测试历史失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<List<TestResult>>> getRecentTestResults(
|
||||
String userId, {
|
||||
int limit = 10,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _apiClient.get(
|
||||
'${ApiEndpoints.baseUrl}/tests/sessions',
|
||||
queryParameters: {
|
||||
'limit': limit,
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
final root = response.data['data'];
|
||||
List<dynamic> list = const [];
|
||||
if (root is List) {
|
||||
list = root;
|
||||
} else if (root is Map<String, dynamic>) {
|
||||
final v = root['sessions'];
|
||||
if (v is List) list = v;
|
||||
}
|
||||
final results = list.map((e) => TestResult.fromJson(e)).toList();
|
||||
return ApiResponse.success(message: '获取最近测试结果成功', data: results);
|
||||
}
|
||||
return ApiResponse.error(message: '获取最近测试结果失败', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取最近测试结果失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<TestResult>> getTestResultById(String resultId) async {
|
||||
try {
|
||||
final response = await _apiClient.get('${ApiEndpoints.baseUrl}/tests/results/$resultId');
|
||||
if (response.statusCode == 200) {
|
||||
return ApiResponse.success(
|
||||
message: '获取测试结果成功',
|
||||
data: TestResult.fromJson(response.data['data']),
|
||||
);
|
||||
}
|
||||
return ApiResponse.error(message: '测试结果不存在', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取测试结果失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<Map<SkillType, double>>> getUserSkillStatistics(
|
||||
String userId, {
|
||||
int? daysPeriod,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _apiClient.get('${ApiEndpoints.baseUrl}/tests/stats');
|
||||
if (response.statusCode == 200) {
|
||||
final Map<String, dynamic> data = response.data['data'] ?? {};
|
||||
final Map<SkillType, double> stats = {};
|
||||
data.forEach((k, v) {
|
||||
final skill = SkillType.values.firstWhere((s) => s.name == k, orElse: () => SkillType.reading);
|
||||
stats[skill] = (v as num).toDouble();
|
||||
});
|
||||
return ApiResponse.success(message: '获取技能统计成功', data: stats);
|
||||
}
|
||||
return ApiResponse.error(message: '获取技能统计失败', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取技能统计失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<Map<String, dynamic>>> getQuestionStatistics() async {
|
||||
try {
|
||||
final response = await _apiClient.get('${ApiEndpoints.baseUrl}/tests/stats');
|
||||
if (response.statusCode == 200) {
|
||||
return ApiResponse.success(
|
||||
message: '获取题目统计成功',
|
||||
data: response.data['data'] ?? {},
|
||||
);
|
||||
}
|
||||
return ApiResponse.error(message: '获取题目统计失败', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '获取题目统计失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<bool>> deleteTestResult(String resultId) async {
|
||||
try {
|
||||
final response = await _apiClient.delete('${ApiEndpoints.baseUrl}/tests/results/$resultId');
|
||||
if (response.statusCode == 200) {
|
||||
return ApiResponse.success(message: '删除测试结果成功', data: true);
|
||||
}
|
||||
return ApiResponse.error(message: '删除测试结果失败', code: response.statusCode);
|
||||
} catch (e) {
|
||||
return ApiResponse.error(message: '删除测试结果失败: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user