import 'dart:convert'; import 'dart:math'; import 'package:shared_preferences/shared_preferences.dart'; import '../../../utils/http/http_client.dart'; import '../../../services/network_listener_service.dart'; /// 时间: 2026-03-28 /// 功能: 诗词答题逻辑管理器 /// 介绍: 处理诗词答题的网络请求和业务逻辑 /// 最新变化: 添加离线模式支持,离线时从本地缓存加载题目 class PoetryLevelManager with NetworkListenerMixin { int _currentIndex = 0; int _total = 0; List _shuffledIds = []; final Random _random = Random(); // 离线模式相关 bool _isOfflineMode = false; List> _offlineQuestions = []; bool _isOnline = true; /// 获取当前题目 ID int get currentId => _shuffledIds.isNotEmpty ? _shuffledIds[_currentIndex] : 0; /// 获取当前题目索引 int get currentIndex => _currentIndex; /// 获取题目总数 int get total => _total; /// 是否已初始化 bool get isInitialized => _shuffledIds.isNotEmpty || _offlineQuestions.isNotEmpty; /// 是否为离线模式 bool get isOfflineMode => _isOfflineMode; /// 检查网络状态 Future _checkNetworkStatus() async { try { final networkStatus = NetworkListenerService().currentStatus; return networkStatus != NetworkStatus.error; } catch (e) { return false; } } /// 检查是否为离线状态(用户设置) Future _checkOfflineSetting() async { try { final prefs = await SharedPreferences.getInstance(); return !(prefs.getBool('personal_card_online') ?? true); } catch (e) { return false; } } /// 检查是否有缓存数据 Future _hasCachedData() async { try { final prefs = await SharedPreferences.getInstance(); final quizData = prefs.getStringList('offline_quiz_data') ?? []; return quizData.isNotEmpty; } catch (e) { return false; } } /// 加载离线缓存数据 Future _loadOfflineCache() async { try { final prefs = await SharedPreferences.getInstance(); final quizData = prefs.getStringList('offline_quiz_data') ?? []; _offlineQuestions = []; for (final item in quizData) { try { final map = _parseStringToMap(item); if (map.isNotEmpty) { _offlineQuestions.add(map); } } catch (e) { print('解析离线数据失败: $e'); } } if (_offlineQuestions.isNotEmpty) { _total = _offlineQuestions.length; _shuffledIds = List.generate(_total, (index) => index); _shuffleList(_shuffledIds); _currentIndex = 0; } } catch (e) { print('加载离线缓存失败: $e'); } } /// 解析字符串为Map Map _parseStringToMap(String str) { final result = {}; print('开始解析字符串: $str'); try { // 尝试JSON解析 final jsonMap = jsonDecode(str); if (jsonMap is Map) { print('JSON解析成功: $jsonMap'); return jsonMap; } } catch (e) { print('JSON解析失败: $e'); // 不是JSON格式,尝试其他解析方式 } // 尝试解析 Map 字符串格式 if (str.startsWith('{') && str.endsWith('}')) { final content = str.substring(1, str.length - 1); final pairs = content.split(', '); for (final pair in pairs) { final colonIndex = pair.indexOf(': '); if (colonIndex > 0) { final key = pair.substring(0, colonIndex).replaceAll('"', ''); var value = pair.substring(colonIndex + 2).replaceAll('"', ''); // 尝试转换数字 if (int.tryParse(value) != null) { result[key] = int.parse(value); } else if (double.tryParse(value) != null) { result[key] = double.parse(value); } else if (value == 'true') { result[key] = true; } else if (value == 'false') { result[key] = false; } else { result[key] = value; } } } } print('解析结果: $result'); return result; } /// 初始化题目列表(打乱顺序) Future initializeQuestions() async { try { // 检查网络状态 _isOnline = await _checkNetworkStatus(); final isOfflineSetting = await _checkOfflineSetting(); // 判断是否使用离线模式 if (!_isOnline || isOfflineSetting) { _isOfflineMode = true; // 检查是否有缓存数据 final hasCache = await _hasCachedData(); if (!hasCache) { return PoetryInitResult( success: false, message: '离线模式下无缓存数据,请先下载或检查网络连接', needDownload: true, ); } // 加载离线缓存 await _loadOfflineCache(); if (_offlineQuestions.isEmpty) { return PoetryInitResult( success: false, message: '离线缓存数据为空,请先下载数据', needDownload: true, ); } return PoetryInitResult( success: true, message: '已加载离线缓存 ${_offlineQuestions.length} 条题目', isOffline: true, ); } // 在线模式 _isOfflineMode = false; // 先调用 fetch 获取新题 final fetchResponse = await HttpClient.get( 'poe/api.php', queryParameters: {'action': 'fetch'}, ); if (fetchResponse.isSuccess) { final fetchData = fetchResponse.jsonData; if (fetchData['code'] == 0) { _total = fetchData['data']['total'] ?? 0; } } // 再调用 refresh 刷新缓存 final refreshResponse = await HttpClient.get( 'poe/api.php', queryParameters: {'action': 'refresh'}, ); if (refreshResponse.isSuccess) { final refreshData = refreshResponse.jsonData; if (refreshData['code'] == 0) { _total = refreshData['data']['total'] ?? _total; } } // 生成并打乱题目 ID 列表 _generateShuffledIds(); _currentIndex = 0; return PoetryInitResult( success: true, message: '已加载在线题库 $_total 条题目', isOffline: false, ); } catch (e) { print('初始化题目失败: $e'); // 尝试加载离线缓存 final hasCache = await _hasCachedData(); if (hasCache) { _isOfflineMode = true; await _loadOfflineCache(); if (_offlineQuestions.isNotEmpty) { return PoetryInitResult( success: true, message: '网络异常,已加载离线缓存 ${_offlineQuestions.length} 条题目', isOffline: true, ); } } return PoetryInitResult( success: false, message: '初始化题目失败: $e', needDownload: !hasCache, ); } } /// 生成并打乱题目 ID 列表 void _generateShuffledIds() { _shuffledIds = List.generate(_total, (index) => index); _shuffleList(_shuffledIds); } /// 打乱列表顺序(Fisher-Yates 算法) void _shuffleList(List list) { for (int i = list.length - 1; i > 0; i--) { int j = _random.nextInt(i + 1); int temp = list[i]; list[i] = list[j]; list[j] = temp; } } /// 下一题 void nextQuestion() { if (_shuffledIds.isEmpty && _offlineQuestions.isEmpty) return; _currentIndex++; if (_currentIndex >= _total) { _currentIndex = 0; // 循环时重新打乱顺序 if (_shuffledIds.isNotEmpty) { _shuffleList(_shuffledIds); } } } /// 上一题 void previousQuestion() { if (_shuffledIds.isEmpty && _offlineQuestions.isEmpty) return; _currentIndex--; if (_currentIndex < 0) { _currentIndex = _total - 1; } } /// 加载题目 Future loadQuestion() async { if (isNetworkLoading('load_question')) { return PoetryQuestionResult(success: false, message: '正在加载中...'); } if (_shuffledIds.isEmpty && _offlineQuestions.isEmpty) { return PoetryQuestionResult(success: false, message: '题目列表未初始化'); } // 离线模式:从缓存加载 if (_isOfflineMode && _offlineQuestions.isNotEmpty) { final questionIndex = _shuffledIds[_currentIndex]; if (questionIndex >= 0 && questionIndex < _offlineQuestions.length) { final questionData = _offlineQuestions[questionIndex]; // 构建标准格式的题目数据 final formattedData = _formatOfflineQuestion(questionData); return PoetryQuestionResult( success: true, data: formattedData, questionId: questionIndex, questionIndex: _currentIndex, isOffline: true, ); } return PoetryQuestionResult(success: false, message: '题目索引越界'); } startNetworkLoading('load_question'); try { final id = currentId; final response = await HttpClient.get( 'poe/api.php', queryParameters: {'action': 'question', 'id': id.toString()}, ); if (response.isSuccess) { final data = response.jsonData; if (data['code'] == 0) { final questionData = data['data']; return PoetryQuestionResult( success: true, data: questionData, questionId: id, questionIndex: _currentIndex, isOffline: false, ); } else { return PoetryQuestionResult( success: false, message: data['msg'] ?? '获取题目失败', ); } } else { return PoetryQuestionResult( success: false, message: '网络错误: ${response.statusCode}', ); } } catch (e) { return PoetryQuestionResult(success: false, message: '加载失败: $e'); } finally { endNetworkLoading('load_question'); } } /// 格式化离线题目数据 Map _formatOfflineQuestion(Map data) { print('格式化离线题目,原始数据: $data'); // 检查是否已经是标准格式 if (data.containsKey('question') && data.containsKey('options')) { final options = data['options']; print('发现options字段,类型: ${options.runtimeType}, 值: $options'); // 确保options是List类型 if (options is List) { print('options已经是List类型,直接返回'); return data; } } // 尝试从缓存数据中提取字段 final result = { 'id': data['id'] ?? 0, 'question': data['question'] ?? data['question_content'] ?? '未知题目', 'author': data['author'] ?? '未知作者', 'type': data['type'] ?? '', 'grade': data['grade'] ?? '', 'dynasty': data['dynasty'] ?? '', 'options': >[], }; print('构建基础数据: $result'); // 尝试解析options字段 dynamic optionsData = data['options']; if (optionsData != null) { print('开始解析options,类型: ${optionsData.runtimeType}'); if (optionsData is List) { // 已经是List,直接使用 result['options'] = optionsData; print('options是List,直接使用'); } else if (optionsData is String) { // 是String,尝试解析为List try { print('options是String,尝试解析: $optionsData'); final parsedOptions = jsonDecode(optionsData); print('解析结果类型: ${parsedOptions.runtimeType}'); if (parsedOptions is List) { result['options'] = parsedOptions; print('options解析成功为List'); } } catch (e) { print('解析options字符串失败: $e'); } } } print('当前options: ${result['options']}'); // 如果没有有效的options,尝试从其他字段构建 if ((result['options'] as List).isEmpty) { print('options为空,尝试从其他字段构建'); final options = >[]; for (int i = 1; i <= 4; i++) { final optionKey = 'option_$i'; if (data.containsKey(optionKey)) { options.add({'index': i, 'content': data[optionKey]}); print('从$optionKey构建选项'); } } result['options'] = options; } print('最终格式化结果: $result'); return result; } /// 提交答案 Future submitAnswer(int questionId, int answer) async { // 离线模式:本地验证答案 if (_isOfflineMode && _offlineQuestions.isNotEmpty) { if (questionId >= 0 && questionId < _offlineQuestions.length) { final questionData = _offlineQuestions[questionId]; final correctAnswer = questionData['correct_answer'] ?? questionData['answer'] ?? questionData['correct']; // 比较答案 bool isCorrect = false; if (correctAnswer != null) { if (correctAnswer is int) { isCorrect = correctAnswer == answer; } else if (correctAnswer is String) { isCorrect = correctAnswer == answer.toString(); } } return PoetryAnswerResult( success: true, message: isCorrect ? '回答正确!' : '回答错误', isCorrect: isCorrect, nextQuestion: null, ); } return PoetryAnswerResult(success: false, message: '题目不存在'); } startNetworkLoading('submit_answer'); try { final response = await HttpClient.get( 'poe/api.php', queryParameters: { 'action': 'answer', 'id': questionId.toString(), 'answer': answer.toString(), }, ); if (response.isSuccess) { final data = response.jsonData; if (data['code'] == 0) { return PoetryAnswerResult( success: true, message: data['msg'], isCorrect: data['data']['correct'] == true, nextQuestion: data['data']['has_next'] == true ? {'id': data['data']['next_id']} : null, ); } else { return PoetryAnswerResult( success: false, message: data['msg'] ?? '提交失败', ); } } else { return PoetryAnswerResult( success: false, message: '网络错误: ${response.statusCode}', ); } } catch (e) { return PoetryAnswerResult(success: false, message: '提交失败: $e'); } finally { endNetworkLoading('submit_answer'); } } /// 获取提示 Future getHint(int questionId) async { // 离线模式:从缓存数据获取提示 if (_isOfflineMode && _offlineQuestions.isNotEmpty) { if (questionId >= 0 && questionId < _offlineQuestions.length) { final questionData = _offlineQuestions[questionId]; final hint = questionData['hint'] ?? '作者: ${questionData['author'] ?? '未知'},朝代: ${questionData['dynasty'] ?? '未知'}'; return PoetryHintResult(success: true, message: hint); } return PoetryHintResult(success: false, message: '题目不存在'); } startNetworkLoading('get_hint'); try { final response = await HttpClient.get( 'poe/api.php', queryParameters: {'action': 'hint', 'id': questionId.toString()}, ); if (response.isSuccess) { final data = response.jsonData; if (data['code'] == 0) { return PoetryHintResult(success: true, message: data['data']['hint']); } else { return PoetryHintResult( success: false, message: data['msg'] ?? '获取提示失败', ); } } else { return PoetryHintResult( success: false, message: '网络错误: ${response.statusCode}', ); } } catch (e) { return PoetryHintResult(success: false, message: '获取提示失败: $e'); } finally { endNetworkLoading('get_hint'); } } } /// 初始化结果 class PoetryInitResult { final bool success; final String? message; final bool isOffline; final bool needDownload; PoetryInitResult({ required this.success, this.message, this.isOffline = false, this.needDownload = false, }); } /// 题目加载结果 class PoetryQuestionResult { final bool success; final String? message; final Map? data; final int questionId; final int questionIndex; final bool isOffline; PoetryQuestionResult({ required this.success, this.message, this.data, this.questionId = 0, this.questionIndex = 0, this.isOffline = false, }); } /// 答案提交结果 class PoetryAnswerResult { final bool success; final String? message; final bool isCorrect; final Map? nextQuestion; PoetryAnswerResult({ required this.success, this.message, this.isCorrect = false, this.nextQuestion, }); } /// 提示获取结果 class PoetryHintResult { final bool success; final String? message; PoetryHintResult({required this.success, this.message}); }