马良AI写作初始化仓库

This commit is contained in:
邓滨杰
2025-09-10 00:07:52 +08:00
parent 3c06bb1a03
commit 39c0f8840f
1309 changed files with 318528 additions and 0 deletions

View File

@@ -0,0 +1,914 @@
import 'dart:convert';
import 'package:ainoval/models/editor_content.dart';
import 'package:ainoval/models/novel_structure.dart' as novel_models;
import 'package:ainoval/models/novel_summary.dart';
import 'package:ainoval/utils/logger.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart'; // Import uuid package
import 'package:hive/hive.dart';
import '../models/chat_models.dart';
/// 本地存储服务,用于缓存和获取小说数据
class LocalStorageService {
SharedPreferences? _prefs;
final Uuid _uuid = const Uuid(); // For generating unique local IDs
// 添加小说缓存
final Map<String, novel_models.Novel> _novelCache = {};
final Map<String, DateTime> _novelCacheTimestamp = {};
final Duration _cacheTTL = const Duration(minutes: 5); // 缓存有效期
final Map<String, String> _wordCountCache = {}; // 场景字数缓存
// 初始化
Future<void> init() async {
_prefs ??= await SharedPreferences.getInstance();
}
// 确保已初始化
Future<SharedPreferences> _ensureInitialized() async {
if (_prefs == null) {
await init();
}
return _prefs!;
}
// 基础存储方法
Future<String?> getString(String key) async {
final prefs = await _ensureInitialized();
return prefs.getString(key);
}
Future<bool> setString(String key, String value) async {
final prefs = await _ensureInitialized();
return await prefs.setString(key, value);
}
Future<bool> remove(String key) async {
final prefs = await _ensureInitialized();
return await prefs.remove(key);
}
// 存储键
static const String _novelsKey = 'novels';
static const String _currentNovelKey = 'current_novel';
static const String _editorContentPrefix = 'editor_content_';
static const String _editorSettingsKey = 'editor_settings';
// --- New Key for Pending Messages ---
static const String _pendingMessagesKey = 'pending_chat_messages';
// 获取所有小说
Future<List<novel_models.Novel>> getNovels() async {
final prefs = await _ensureInitialized();
final novelsJson = prefs.getStringList(_novelsKey) ?? [];
/* AppLogger.d('LocalStorageService',
'getNovels: Raw JSON list from prefs: $novelsJson');
*/
try {
final novels = novelsJson.map((json) {
// AppLogger.v('LocalStorageService', 'getNovels: Parsing JSON: $json');
final novel = novel_models.Novel.fromJson(jsonDecode(json));
AppLogger.v('LocalStorageService',
'getNovels: Parsed Novel: ID=${novel.id}, Title=${novel.title}, Acts=${novel.acts.length}');
return novel;
}).toList();
AppLogger.i('LocalStorageService',
'getNovels: Successfully parsed ${novels.length} novels.');
return novels;
} catch (e, stackTrace) {
AppLogger.e('LocalStorageService',
'getNovels: Failed to parse novels JSON.', e, stackTrace);
return [];
}
}
// 保存所有小说
Future<void> saveNovels(List<novel_models.Novel> novels) async {
final prefs = await _ensureInitialized();
try {
final novelsJson = novels.map((novel) {
final jsonMap = novel.toJson();
final jsonString = jsonEncode(jsonMap);
AppLogger.v('LocalStorageService',
'saveNovels: Serializing Novel ID=${novel.id}, Title=${novel.title}, Acts=${novel.acts.length}');
return jsonString;
}).toList();
/* AppLogger.d('LocalStorageService',
'saveNovels: Saving JSON list to prefs: $novelsJson'); */
await prefs.setStringList(_novelsKey, novelsJson);
AppLogger.i('LocalStorageService',
'saveNovels: Successfully saved ${novels.length} novels.');
} catch (e, stackTrace) {
AppLogger.e('LocalStorageService', 'saveNovels: Failed to save novels.',
e, stackTrace);
}
}
// 保存小说摘要列表
Future<void> saveNovelSummaries(List<NovelSummary> novels) async {
final prefs = await _ensureInitialized();
final novelsJson =
novels.map((novel) => jsonEncode(novel.toJson())).toList();
await prefs.setStringList('novel_summaries', novelsJson);
}
// 获取单个小说
Future<novel_models.Novel?> getNovel(String id) async {
AppLogger.d('LocalStorageService',
'getNovel: Attempting to get novel with ID: $id');
// 检查缓存是否存在且有效
if (_novelCache.containsKey(id)) {
final cacheTime = _novelCacheTimestamp[id];
if (cacheTime != null && DateTime.now().difference(cacheTime) < _cacheTTL) {
AppLogger.i('LocalStorageService',
'getNovel: Using cached novel: ID=$id, Title=${_novelCache[id]!.title}, Acts=${_novelCache[id]!.acts.length}');
return _novelCache[id];
}
}
// 缓存不存在或已过期,从存储获取
final novels = await getNovels();
try {
final novel = novels.firstWhere(
(novel) => novel.id == id,
);
// 更新缓存
_novelCache[id] = novel;
_novelCacheTimestamp[id] = DateTime.now();
AppLogger.i('LocalStorageService',
'getNovel: Found novel: ID=${novel.id}, Title=${novel.title}, Acts=${novel.acts.length}');
return novel;
} catch (e) {
AppLogger.w(
'LocalStorageService', 'getNovel: Novel with ID $id not found.', e);
return null;
}
}
// 保存单个小说
Future<void> saveNovel(novel_models.Novel novel) async {
//AppLogger.d('LocalStorageService',
// 'saveNovel: Attempting to save novel ID=${novel.id}, Title=${novel.title}, Acts=${novel.acts.length}');
// 检查上次保存时间,如果短时间内多次保存同一个小说,可以合并为一次操作
final cacheTime = _novelCacheTimestamp[novel.id];
final now = DateTime.now();
if (cacheTime != null && now.difference(cacheTime).inMilliseconds < 500) {
// 如果500毫秒内有多次保存只更新缓存延迟实际的存储操作
_novelCache[novel.id] = novel;
_novelCacheTimestamp[novel.id] = now;
//AppLogger.i('LocalStorageService',
// 'saveNovel: Multiple saves detected within 500ms, delaying actual storage operation for novel ID=${novel.id}');
return;
}
// 更新缓存
_novelCache[novel.id] = novel;
_novelCacheTimestamp[novel.id] = now;
try {
final novels = await getNovels();
final index = novels.indexWhere((n) => n.id == novel.id);
if (index >= 0) {
AppLogger.d('LocalStorageService',
'saveNovel: Updating existing novel at index $index.');
novels[index] = novel;
} else {
AppLogger.d('LocalStorageService', 'saveNovel: Adding new novel.');
novels.add(novel);
}
await saveNovels(novels);
AppLogger.i('LocalStorageService',
'saveNovel: Completed saving process for novel ID=${novel.id}.');
} catch (e) {
AppLogger.e('LocalStorageService', 'saveNovel: Failed to save novel', e);
// 从缓存中移除,以便下次重新加载
_novelCache.remove(novel.id);
_novelCacheTimestamp.remove(novel.id);
throw Exception('保存小说失败: $e');
}
}
// 删除小说
Future<void> deleteNovel(String id) async {
final novels = await getNovels();
novels.removeWhere((novel) => novel.id == id);
await saveNovels(novels);
}
// 获取当前正在编辑的小说ID
Future<String?> getCurrentNovelId() async {
final prefs = await _ensureInitialized();
return prefs.getString(_currentNovelKey);
}
// 设置当前正在编辑的小说ID
Future<void> setCurrentNovelId(String id) async {
final prefs = await _ensureInitialized();
final previousId = prefs.getString(_currentNovelKey);
// 如果前一个小说ID存在且与新ID不同清理前一个小说的同步标记
if (previousId != null && previousId.isNotEmpty && previousId != id) {
AppLogger.i('LocalStorageService', '小说ID切换: $previousId -> $id');
// 如果是设置为空ID特殊情况如app关闭不触发清理
if (id.isNotEmpty) {
await clearNovelSyncFlags(previousId);
}
}
await prefs.setString(_currentNovelKey, id);
AppLogger.i('LocalStorageService', '当前小说ID已设置为: $id');
}
// 获取章节内容
Future<EditorContent?> getChapterContent(
String novelId, String chapterId) async {
final prefs = await _ensureInitialized();
final key = _getContentKey(novelId, chapterId);
final jsonString = prefs.getString(key);
if (jsonString == null) {
return null;
}
try {
final json = jsonDecode(jsonString);
return EditorContent.fromJson(json);
} catch (e) {
AppLogger.e('LocalStorageService', '解析章节内容失败', e);
return null;
}
}
// 保存章节内容
Future<void> saveChapterContent(
String novelId, String chapterId, EditorContent content) async {
final prefs = await _ensureInitialized();
final key = _getContentKey(novelId, chapterId);
final jsonString = jsonEncode(content.toJson());
await prefs.setString(key, jsonString);
}
// 获取编辑器内容
Future<EditorContent?> getEditorContent(
String novelId, String chapterId, String sceneId) async {
return getChapterContent(novelId, chapterId);
}
// 保存编辑器内容
Future<void> saveEditorContent(EditorContent content) async {
final parts = content.id.split('-');
if (parts.length == 3) {
final novelId = parts[0];
final chapterId = parts[1];
final sceneId = parts[2];
await saveChapterContent(novelId, chapterId, content);
} else if (parts.length == 2) {
// 兼容旧格式
final novelId = parts[0];
final chapterId = parts[1];
await saveChapterContent(novelId, chapterId, content);
}
}
// 获取编辑器设置
Future<Map<String, dynamic>> getEditorSettings() async {
try {
final prefs = await _ensureInitialized();
final settingsJson = prefs.getString('editor_settings');
if (settingsJson != null) {
return jsonDecode(settingsJson) as Map<String, dynamic>;
}
// 返回默认设置
return {
'fontSize': 16.0,
'lineHeight': 1.5,
'fontFamily': 'Roboto',
'theme': 'light',
'autoSave': true,
};
} catch (e) {
AppLogger.e('LocalStorageService', '获取编辑器设置失败', e);
// 返回默认设置
return {
'fontSize': 16.0,
'lineHeight': 1.5,
'fontFamily': 'Roboto',
'theme': 'light',
'autoSave': true,
};
}
}
// 保存编辑器设置
Future<void> saveEditorSettings(Map<String, dynamic> settings) async {
try {
final prefs = await _ensureInitialized();
await prefs.setString('editor_settings', jsonEncode(settings));
} catch (e) {
AppLogger.e('LocalStorageService', '保存编辑器设置失败', e);
throw Exception('保存编辑器设置失败: $e');
}
}
// 生成内容存储键
String _getContentKey(String novelId, String chapterId) {
return '$_editorContentPrefix${novelId}_$chapterId';
}
/// 获取场景内容
Future<novel_models.Scene?> getSceneContent(
String novelId, String actId, String chapterId, String sceneId) async {
try {
final novel = await getNovel(novelId);
if (novel == null) return null;
final act = novel.acts.firstWhere((a) => a.id == actId);
final chapter = act.chapters.firstWhere((c) => c.id == chapterId);
if (chapter.scenes.isEmpty) return null;
// 查找特定场景
try {
return chapter.scenes.firstWhere((s) => s.id == sceneId);
} catch (e) {
// 如果找不到特定场景,返回第一个场景
return chapter.scenes.first;
}
} catch (e) {
return null;
}
}
/// 保存场景内容
Future<void> saveSceneContent(String novelId, String actId, String chapterId,
String sceneId, novel_models.Scene scene) async {
try {
// 生成场景缓存键
final sceneKey = '${novelId}_${actId}_${chapterId}_$sceneId';
// 如果缓存中有小说,则直接更新缓存中的场景内容
if (_novelCache.containsKey(novelId)) {
final novel = _novelCache[novelId]!;
bool sceneUpdated = false;
// 查找并更新缓存中的场景
final updatedActs = novel.acts.map((act) {
if (act.id == actId) {
final updatedChapters = act.chapters.map((chapter) {
if (chapter.id == chapterId) {
final sceneIndex = chapter.scenes.indexWhere((s) => s.id == sceneId);
if (sceneIndex >= 0) {
// 更新现有场景
final updatedScenes = List<novel_models.Scene>.from(chapter.scenes);
updatedScenes[sceneIndex] = scene;
sceneUpdated = true;
return chapter.copyWith(scenes: updatedScenes);
} else {
// 添加新场景
sceneUpdated = true;
return chapter.copyWith(
scenes: [...chapter.scenes, scene],
);
}
}
return chapter;
}).toList();
if (sceneUpdated) {
return act.copyWith(chapters: updatedChapters);
}
}
return act;
}).toList();
if (sceneUpdated) {
// 更新缓存中的小说
final updatedNovel = novel.copyWith(
acts: updatedActs,
updatedAt: DateTime.now(),
);
_novelCache[novelId] = updatedNovel;
_novelCacheTimestamp[novelId] = DateTime.now();
// 更新字数缓存
_updateWordCountCache(sceneKey, scene.content, scene.wordCount);
AppLogger.i('LocalStorageService',
'saveSceneContent: Updated scene in cached novel: $sceneKey');
}
}
// 正常保存场景到存储
final novel = await getNovel(novelId);
if (novel == null) return;
final acts = novel.acts.map((act) {
if (act.id == actId) {
final chapters = act.chapters.map((chapter) {
if (chapter.id == chapterId) {
// 查找特定场景
final sceneIndex =
chapter.scenes.indexWhere((s) => s.id == sceneId);
List<novel_models.Scene> updatedScenes;
if (sceneIndex >= 0) {
// 更新现有场景
updatedScenes = List.from(chapter.scenes);
updatedScenes[sceneIndex] = scene;
} else {
// 添加新场景
updatedScenes = List.from(chapter.scenes)..add(scene);
}
return chapter.copyWith(scenes: updatedScenes);
}
return chapter;
}).toList();
return act.copyWith(chapters: chapters);
}
return act;
}).toList();
final updatedNovel = novel.copyWith(
acts: acts,
updatedAt: DateTime.now(),
);
await saveNovel(updatedNovel);
// 更新字数缓存
_updateWordCountCache(sceneKey, scene.content, scene.wordCount);
} catch (e) {
AppLogger.e('LocalStorageService', '保存场景内容失败', e);
}
}
/// 保存摘要内容
Future<void> saveSummary(String novelId, String actId, String chapterId,
String sceneId, novel_models.Summary summary) async {
try {
final novel = await getNovel(novelId);
if (novel == null) return;
final acts = novel.acts.map((act) {
if (act.id == actId) {
final chapters = act.chapters.map((chapter) {
if (chapter.id == chapterId) {
// 查找特定场景
final sceneIndex =
chapter.scenes.indexWhere((s) => s.id == sceneId);
List<novel_models.Scene> updatedScenes;
if (sceneIndex >= 0) {
// 更新现有场景
updatedScenes = List.from(chapter.scenes);
updatedScenes[sceneIndex] =
updatedScenes[sceneIndex].copyWith(summary: summary);
} else {
// 如果场景不存在,不做任何操作
updatedScenes = chapter.scenes;
}
return chapter.copyWith(scenes: updatedScenes);
}
return chapter;
}).toList();
return act.copyWith(chapters: chapters);
}
return act;
}).toList();
final updatedNovel = novel.copyWith(
acts: acts,
updatedAt: DateTime.now(),
);
await saveNovel(updatedNovel);
} catch (e) {
AppLogger.e('LocalStorageService', '保存摘要内容失败', e);
}
}
// 标记需要同步的内容(按类型)
Future<void> markForSyncByType(String id, String type) async {
try {
final prefs = await _ensureInitialized();
final syncKey = 'syncList_$type';
final syncList = prefs.getStringList(syncKey) ?? [];
if (!syncList.contains(id)) {
syncList.add(id);
await prefs.setStringList(syncKey, syncList);
AppLogger.i('LocalStorageService', '已标记 $type: $id 需要同步');
}
} catch (e) {
AppLogger.e('LocalStorageService', '标记同步失败', e);
}
}
// 获取需要同步的内容列表(按类型)
Future<List<String>> getSyncList(String type) async {
try {
final prefs = await _ensureInitialized();
final syncKey = 'syncList_$type';
return prefs.getStringList(syncKey) ?? [];
} catch (e) {
AppLogger.e('LocalStorageService', '获取同步列表失败', e);
return [];
}
}
// 清除同步标记按类型和ID
Future<void> clearSyncFlagByType(String type, String id) async {
try {
final prefs = await _ensureInitialized();
final syncKey = 'syncList_$type';
final syncList = prefs.getStringList(syncKey) ?? [];
if (syncList.contains(id)) {
syncList.remove(id);
await prefs.setStringList(syncKey, syncList);
AppLogger.i('LocalStorageService', '已清除 $type: $id 的同步标记');
}
} catch (e) {
AppLogger.e('LocalStorageService', '清除同步标记失败', e);
}
}
// 保存聊天会话列表
Future<void> saveChatSessions(
String novelId, List<ChatSession> sessions) async {
final key = 'chat_sessions_$novelId';
final jsonList =
sessions.map((session) => jsonEncode(session.toJson())).toList();
final prefs = await _ensureInitialized();
await prefs.setStringList(key, jsonList);
}
// 获取聊天会话列表
Future<List<ChatSession>> getChatSessions(String novelId) async {
final key = 'chat_sessions_$novelId';
final prefs = await _ensureInitialized();
final jsonList = prefs.getStringList(key) ?? [];
return jsonList
.map((json) => ChatSession.fromJson(jsonDecode(json)))
.toList();
}
// 添加聊天会话
Future<void> addChatSession(String novelId, ChatSession session,
{bool needsSync = false}) async {
final sessions = await getChatSessions(novelId);
sessions.add(session);
await saveChatSessions(novelId, sessions);
await updateChatSession(session, needsSync: needsSync);
}
// 获取特定会话
Future<ChatSession?> getChatSession(String sessionId) async {
final key = 'chat_session_detail_$sessionId';
final prefs = await _ensureInitialized();
final json = prefs.getString(key);
if (json == null) {
return null;
}
return ChatSession.fromJson(jsonDecode(json));
}
// 更新会话 - 同时处理标记同步
Future<void> updateChatSession(ChatSession session,
{bool needsSync = false}) async {
final key = 'chat_session_detail_${session.id}';
final prefs = await _ensureInitialized();
await prefs.setString(key, jsonEncode(session.toJson()));
if (needsSync) {
await markForSyncByType(session.id, 'chat_session');
}
}
// 删除会话
Future<void> deleteChatSession(String sessionId) async {
final key = 'chat_session_detail_$sessionId';
final prefs = await _ensureInitialized();
await prefs.remove(key);
await clearSyncFlagByType('chat_session', sessionId);
}
// 获取需要同步的所有会话
Future<List<ChatSession>> getSessionsToSync() async {
final syncList = await getSyncList('chat_session');
final sessions = <ChatSession>[];
for (final sessionId in syncList) {
final session = await getChatSession(sessionId);
if (session != null) {
sessions.add(session);
} else {
AppLogger.w('LocalStorageService',
'getSessionsToSync: 未找到标记为同步的会话详情: $sessionId。考虑清除此标记。');
}
}
return sessions;
}
// 清除所有数据
Future<void> clearAll() async {
final prefs = await _ensureInitialized();
await prefs.clear();
}
/// 获取会话的消息列表(用于显示历史记录)
Future<List<ChatMessage>?> getMessagesForSession(String sessionId) async {
final prefs = await _ensureInitialized();
final key = 'chat_messages_$sessionId'; // Key for storing full history
final jsonList = prefs.getStringList(key);
if (jsonList == null) return null;
try {
// Sort messages by timestamp after parsing
final messages = jsonList
.map((json) => ChatMessage.fromJson(jsonDecode(json)))
.toList();
messages.sort((a, b) => a.timestamp.compareTo(b.timestamp));
return messages;
} catch (e, stackTrace) {
AppLogger.e(
'LocalStorageService', '解析会话 $sessionId 的消息失败', e, stackTrace);
return null;
}
}
/// 保存会话的消息列表(用于缓存历史记录)
Future<void> saveMessagesForSession(
String sessionId, List<ChatMessage> messages) async {
final prefs = await _ensureInitialized();
final key = 'chat_messages_$sessionId'; // Key for storing full history
// Sort messages by timestamp before saving
messages.sort((a, b) => a.timestamp.compareTo(b.timestamp));
final jsonList = messages.map((msg) => jsonEncode(msg.toJson())).toList();
await prefs.setStringList(key, jsonList);
}
/// 添加单条消息到会话历史(例如,收到新消息或发送成功后)
Future<void> addMessageToSessionHistory(
String sessionId, ChatMessage message) async {
final messages = await getMessagesForSession(sessionId) ?? [];
// Avoid duplicates if message already exists
if (!messages.any((m) => m.id == message.id)) {
messages.add(message);
await saveMessagesForSession(sessionId, messages);
}
}
/// 添加待发送消息到队列
Future<String> addPendingMessage({
required String userId,
required String sessionId,
required String content,
required Map<String, dynamic>? metadata,
}) async {
final prefs = await _ensureInitialized();
final pendingList = prefs.getStringList(_pendingMessagesKey) ?? [];
final localId = _uuid.v4(); // Generate unique local ID
final pendingMessageData = {
'localId': localId, // Unique ID for removal later
'userId': userId,
'sessionId': sessionId,
'content': content,
'metadata': metadata,
'timestamp':
DateTime.now().toIso8601String(), // Store time added to queue
};
pendingList.add(jsonEncode(pendingMessageData));
await prefs.setStringList(_pendingMessagesKey, pendingList);
AppLogger.i('LocalStorageService',
'添加待发送消息到队列: sessionId=$sessionId, localId=$localId');
return localId; // Return localId in case UI needs it
}
/// 获取所有待发送消息
Future<List<Map<String, dynamic>>> getPendingMessages() async {
final prefs = await _ensureInitialized();
final jsonList = prefs.getStringList(_pendingMessagesKey) ?? [];
try {
return jsonList
.map((json) => jsonDecode(json) as Map<String, dynamic>)
.toList();
} catch (e, stackTrace) {
AppLogger.e('LocalStorageService', '解析待发送消息队列失败', e, stackTrace);
// Optionally clear the corrupted queue
// await prefs.remove(_pendingMessagesKey);
return [];
}
}
/// 从队列中移除已发送的消息 (通过 localId)
Future<void> removePendingMessage(String localId) async {
final prefs = await _ensureInitialized();
final pendingList = prefs.getStringList(_pendingMessagesKey) ?? [];
final updatedList = <String>[];
bool removed = false;
for (final jsonString in pendingList) {
try {
final data = jsonDecode(jsonString) as Map<String, dynamic>;
if (data['localId'] != localId) {
updatedList.add(jsonString);
} else {
removed = true;
}
} catch (e) {
// Skip corrupted entry
AppLogger.w('LocalStorageService', '移除待发送消息时跳过损坏条目: $e');
}
}
if (removed) {
await prefs.setStringList(_pendingMessagesKey, updatedList);
AppLogger.i('LocalStorageService', '从队列移除待发送消息: localId=$localId');
} else {
AppLogger.w('LocalStorageService', '尝试移除待发送消息,但未找到: localId=$localId');
}
}
// 清理所有同步标记
Future<void> clearAllSyncFlags() async {
final prefs = await _ensureInitialized();
final syncTypes = ['novel', 'scene', 'editor', 'chat_session'];
for (final type in syncTypes) {
final syncKey = 'syncList_$type';
await prefs.remove(syncKey);
}
AppLogger.i('LocalStorageService', '已清理所有同步标记');
}
// 清理指定小说的同步标记
Future<void> clearNovelSyncFlags(String novelId) async {
if (novelId.isEmpty) return;
final prefs = await _ensureInitialized();
// 清理小说本身的同步标记
const novelSyncKey = 'syncList_novel';
final novelSyncList = prefs.getStringList(novelSyncKey) ?? [];
if (novelSyncList.contains(novelId)) {
novelSyncList.remove(novelId);
await prefs.setStringList(novelSyncKey, novelSyncList);
AppLogger.i('LocalStorageService', '已清理小说同步标记: $novelId');
}
// 清理场景同步标记
const sceneSyncKey = 'syncList_scene';
final sceneSyncList = prefs.getStringList(sceneSyncKey) ?? [];
final updatedSceneSyncList = sceneSyncList.where((sceneKey) {
final parts = sceneKey.split('_');
return parts.isEmpty || parts[0] != novelId;
}).toList();
if (updatedSceneSyncList.length != sceneSyncList.length) {
await prefs.setStringList(sceneSyncKey, updatedSceneSyncList);
AppLogger.i('LocalStorageService',
'已清理场景同步标记: ${sceneSyncList.length - updatedSceneSyncList.length} 个场景小说ID: $novelId');
}
// 清理编辑器内容同步标记
const editorSyncKey = 'syncList_editor';
final editorSyncList = prefs.getStringList(editorSyncKey) ?? [];
final updatedEditorSyncList = editorSyncList.where((contentKey) {
final parts = contentKey.split('_');
return parts.isEmpty || parts[0] != novelId;
}).toList();
if (updatedEditorSyncList.length != editorSyncList.length) {
await prefs.setStringList(editorSyncKey, updatedEditorSyncList);
AppLogger.i('LocalStorageService',
'已清理编辑器内容同步标记: ${editorSyncList.length - updatedEditorSyncList.length} 个内容小说ID: $novelId');
}
// 清理聊天会话同步标记
// 注意这需要先获取所有会话然后检查它们的metadata中的novelId
final sessions = await getSessionsToSync();
final sessionsToRemove = sessions.where((session) =>
session.metadata != null && session.metadata!['novelId'] == novelId).toList();
for (final session in sessionsToRemove) {
await clearSyncFlagByType('chat_session', session.id);
AppLogger.i('LocalStorageService', '已清理聊天会话同步标记: ${session.id}小说ID: $novelId');
}
}
/// 获取指定章节的所有场景键
Future<List<String>> getSceneKeysForChapter(
String novelId,
String actId,
String chapterId,
) async {
try {
final box = await Hive.openBox('scenes');
final prefix = '${novelId}_${actId}_${chapterId}_';
// 过滤出所有属于该章节的场景键
final List<String> sceneKeys = [];
for (final key in box.keys) {
if (key is String && key.startsWith(prefix)) {
// 从键中提取场景ID
final sceneId = key.substring(prefix.length);
sceneKeys.add(sceneId);
}
}
return sceneKeys;
} catch (e) {
AppLogger.e('LocalStorageService', '获取章节场景键失败', e);
return [];
}
}
/// 删除场景内容
Future<void> deleteSceneContent(
String novelId,
String actId,
String chapterId,
String sceneId,
) async {
final sceneKey = '${novelId}_${actId}_${chapterId}_$sceneId';
try {
// 使用SharedPreferences删除场景内容
final prefs = await _ensureInitialized();
await prefs.remove('scene_$sceneKey');
// 从场景索引中移除
final indexKey = 'scenes_index_${novelId}_${actId}_$chapterId';
final sceneIds = prefs.getStringList(indexKey) ?? [];
if (sceneIds.contains(sceneId)) {
sceneIds.remove(sceneId);
await prefs.setStringList(indexKey, sceneIds);
}
AppLogger.i('LocalStorageService', '本地场景内容已删除: $sceneKey');
} catch (e) {
AppLogger.e('LocalStorageService', '删除场景内容失败: $sceneKey', e);
throw Exception('删除场景内容失败: $e');
}
}
// 优化的字数统计缓存
void _updateWordCountCache(String sceneKey, String content, int wordCount) {
final contentHash = content.hashCode.toString();
final cacheKey = '${sceneKey}_$contentHash';
_wordCountCache[cacheKey] = wordCount.toString();
}
// 从缓存获取字数统计
int? getWordCountFromCache(String sceneKey, String content) {
final contentHash = content.hashCode.toString();
final cacheKey = '${sceneKey}_$contentHash';
final cachedCount = _wordCountCache[cacheKey];
if (cachedCount != null) {
return int.tryParse(cachedCount);
}
return null;
}
// 清除指定小说的缓存
Future<void> clearNovelCache(String novelId) async {
AppLogger.i('LocalStorageService', '清除小说缓存: $novelId');
_novelCache.remove(novelId);
_novelCacheTimestamp.remove(novelId);
}
// 清除所有小说缓存
Future<void> clearAllNovelCache() async {
AppLogger.i('LocalStorageService', '清除所有小说缓存');
_novelCache.clear();
_novelCacheTimestamp.clear();
}
}