Files
wushu/lib/views/profile/level/level-jilu.dart
2026-03-30 02:35:31 +08:00

614 lines
17 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<int> _shuffledIds = [];
final Random _random = Random();
// 离线模式相关
bool _isOfflineMode = false;
List<Map<String, dynamic>> _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<bool> _checkNetworkStatus() async {
try {
final networkStatus = NetworkListenerService().currentStatus;
return networkStatus != NetworkStatus.error;
} catch (e) {
return false;
}
}
/// 检查是否为离线状态(用户设置)
Future<bool> _checkOfflineSetting() async {
try {
final prefs = await SharedPreferences.getInstance();
return !(prefs.getBool('personal_card_online') ?? true);
} catch (e) {
return false;
}
}
/// 检查是否有缓存数据
Future<bool> _hasCachedData() async {
try {
final prefs = await SharedPreferences.getInstance();
final quizData = prefs.getStringList('offline_quiz_data') ?? [];
return quizData.isNotEmpty;
} catch (e) {
return false;
}
}
/// 加载离线缓存数据
Future<void> _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<String, dynamic> _parseStringToMap(String str) {
final result = <String, dynamic>{};
print('开始解析字符串: $str');
try {
// 尝试JSON解析
final jsonMap = jsonDecode(str);
if (jsonMap is Map<String, dynamic>) {
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<PoetryInitResult> 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<int> 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<PoetryQuestionResult> 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<String, dynamic> _formatOfflineQuestion(Map<String, dynamic> 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 = <String, dynamic>{
'id': data['id'] ?? 0,
'question': data['question'] ?? data['question_content'] ?? '未知题目',
'author': data['author'] ?? '未知作者',
'type': data['type'] ?? '',
'grade': data['grade'] ?? '',
'dynasty': data['dynasty'] ?? '',
'options': <Map<String, dynamic>>[],
};
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 = <Map<String, dynamic>>[];
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<PoetryAnswerResult> 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<PoetryHintResult> 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<String, dynamic>? 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<String, dynamic>? 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});
}