马良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,314 @@
import 'dart:convert';
import 'package:ainoval/models/context_selection_models.dart';
import 'package:ainoval/models/novel_structure.dart';
import 'package:ainoval/models/novel_setting_item.dart';
import 'package:ainoval/models/setting_group.dart';
import 'package:ainoval/models/novel_snippet.dart';
import 'package:ainoval/utils/logger.dart';
/// 上下文选择助手类
///
/// 提供统一的上下文选择管理方法,避免在不同组件中重复实现相同逻辑
class ContextSelectionHelper {
/// 初始化上下文选择数据
///
/// 根据提供的小说、设定、片段数据构建完整的上下文选择结构
static ContextSelectionData initializeContextData({
Novel? novel,
List<NovelSettingItem>? settings,
List<SettingGroup>? settingGroups,
List<NovelSnippet>? snippets,
ContextSelectionData? initialSelections,
}) {
//AppLogger.d('ContextSelectionHelper', '🔧 初始化上下文选择数据');
ContextSelectionData contextData;
if (novel != null) {
// 🚀 使用小说数据构建完整的上下文选择结构
contextData = ContextSelectionDataBuilder.fromNovelWithContext(
novel,
settings: settings ?? [],
settingGroups: settingGroups ?? [],
snippets: snippets ?? [],
);
//AppLogger.d('ContextSelectionHelper', '✅ 从小说构建上下文数据成功: ${contextData.availableItems.length}个可选项');
} else {
// 🚀 创建演示数据作为回退
contextData = _createFallbackContextData();
//AppLogger.d('ContextSelectionHelper', '✅ 创建回退上下文数据: ${contextData.availableItems.length}个可选项');
}
// 🚀 如果有初始选择,应用到构建的数据中
if (initialSelections != null && initialSelections.selectedCount > 0) {
contextData = contextData.applyPresetSelections(initialSelections);
//AppLogger.d('ContextSelectionHelper', '✅ 应用初始选择: ${contextData.selectedCount}个已选项');
}
return contextData;
}
/// 处理上下文选择变化
///
/// 这是核心方法,用于正确处理级联菜单的选择变化
/// [currentData] 当前的上下文选择数据
/// [newData] 从下拉菜单组件返回的新选择数据
/// [isAddOperation] 是否为添加操作true=添加false=删除)
static ContextSelectionData handleSelectionChanged(
ContextSelectionData currentData,
ContextSelectionData newData, {
bool isAddOperation = true,
}) {
//AppLogger.d('ContextSelectionHelper', '🔄 处理上下文选择变化');
//AppLogger.d('ContextSelectionHelper', '当前选择数: ${currentData.selectedCount}');
//AppLogger.d('ContextSelectionHelper', '新数据选择数: ${newData.selectedCount}');
//AppLogger.d('ContextSelectionHelper', '操作类型: ${isAddOperation ? "添加" : "删除"}');
// 🚀 关键修复:直接使用新的选择数据,而不是合并
// 下拉菜单组件已经处理了选择/取消选择的逻辑,我们只需要接受结果
// 确保新数据具有完整的菜单结构
if (newData.availableItems.length < currentData.availableItems.length) {
// 如果新数据的菜单结构不完整,保持当前的菜单结构,只更新选择状态
//AppLogger.d('ContextSelectionHelper', '🔧 修复不完整的菜单结构');
// 重建具有完整结构的数据
final updatedData = currentData.copyWith(
selectedItems: {},
flatItems: currentData.flatItems.map(
(key, value) => MapEntry(key, value.copyWith(selectionState: SelectionState.unselected)),
),
);
// 应用新的选择
ContextSelectionData result = updatedData;
for (final selectedItem in newData.selectedItems.values) {
if (result.flatItems.containsKey(selectedItem.id)) {
result = result.selectItem(selectedItem.id);
}
}
//AppLogger.d('ContextSelectionHelper', '✅ 选择处理完成: ${result.selectedCount}个已选项');
return result;
} else {
// 菜单结构完整,直接使用新数据
//AppLogger.d('ContextSelectionHelper', '✅ 直接使用新选择数据: ${newData.selectedCount}个已选项');
return newData;
}
}
/// 从保存的上下文选择字符串恢复选择状态
///
/// [baseData] 基础的完整菜单结构数据
/// [savedContextSelectionsData] 保存的上下文选择JSON字符串
static ContextSelectionData restoreSelectionsFromSaved(
ContextSelectionData baseData,
String? savedContextSelectionsData,
) {
if (savedContextSelectionsData == null || savedContextSelectionsData.isEmpty) {
//AppLogger.d('ContextSelectionHelper', '📭 没有保存的上下文选择数据');
return baseData;
}
try {
// 🚀 解析保存的选择数据
final savedSelections = _parseSavedContextSelections(
savedContextSelectionsData,
baseData.novelId,
);
if (savedSelections.selectedCount > 0) {
// 应用保存的选择到基础数据
final restoredData = baseData.applyPresetSelections(savedSelections);
//AppLogger.d('ContextSelectionHelper', '✅ 恢复上下文选择: ${restoredData.selectedCount}个已选项');
return restoredData;
}
} catch (e) {
AppLogger.e('ContextSelectionHelper', '恢复上下文选择失败', e);
}
return baseData;
}
/// 解析保存的上下文选择数据
static ContextSelectionData _parseSavedContextSelections(String savedData, String novelId) {
try {
// 🚀 解析JSON数据
final jsonData = jsonDecode(savedData) as Map<String, dynamic>;
// 检查是否有selectedItems字段
if (!jsonData.containsKey('selectedItems')) {
AppLogger.w('ContextSelectionHelper', '保存的数据中没有selectedItems字段');
return ContextSelectionData(novelId: novelId, availableItems: [], flatItems: {});
}
final contextList = jsonData['selectedItems'] as List<dynamic>;
//AppLogger.d('ContextSelectionHelper', '解析保存的上下文选择: ${contextList.length}个项目');
// 将已选择的项目转换为ContextSelectionItem
final selectedItems = <String, ContextSelectionItem>{};
final availableItems = <ContextSelectionItem>[];
final flatItems = <String, ContextSelectionItem>{};
for (var itemData in contextList) {
final item = ContextSelectionItem(
id: itemData['id'] ?? '',
title: itemData['title'] ?? '',
type: ContextSelectionType.values.firstWhere(
(type) => type.displayName == itemData['type'],
orElse: () => ContextSelectionType.fullNovelText,
),
metadata: Map<String, dynamic>.from(itemData['metadata'] ?? {}),
parentId: itemData['parentId'],
selectionState: SelectionState.fullySelected, // 标记为已选择
);
selectedItems[item.id] = item;
availableItems.add(item);
flatItems[item.id] = item;
//AppLogger.d('ContextSelectionHelper', ' ✅ ${item.type.displayName}:${item.id} (${item.title})');
}
return ContextSelectionData(
novelId: novelId,
selectedItems: selectedItems,
availableItems: availableItems,
flatItems: flatItems,
);
} catch (e) {
AppLogger.e('ContextSelectionHelper', '解析保存的上下文选择数据失败', e);
return ContextSelectionData(novelId: novelId, availableItems: [], flatItems: {});
}
}
/// 获取用于保存的上下文选择字符串
///
/// [contextData] 当前的上下文选择数据
static String? getSelectionsForSave(ContextSelectionData? contextData) {
if (contextData == null || contextData.selectedCount == 0) {
return null;
}
try {
return contextData.toSaveString();
} catch (e) {
AppLogger.e('ContextSelectionHelper', '序列化上下文选择失败', e);
return null;
}
}
/// 清除所有选择
///
/// [currentData] 当前的上下文选择数据
static ContextSelectionData clearAllSelections(ContextSelectionData currentData) {
//AppLogger.d('ContextSelectionHelper', '🧹 清除所有上下文选择');
return currentData.copyWith(
selectedItems: {},
flatItems: currentData.flatItems.map(
(key, value) => MapEntry(key, value.copyWith(selectionState: SelectionState.unselected)),
),
);
}
/// 创建回退的上下文选择数据(用于没有小说数据的情况)
static ContextSelectionData _createFallbackContextData() {
final demoItems = [
ContextSelectionItem(
id: 'demo_full_novel',
title: 'Full Novel Text',
type: ContextSelectionType.fullNovelText,
subtitle: '包含所有小说文本,这将产生费用',
metadata: {'wordCount': 0},
),
ContextSelectionItem(
id: 'demo_full_outline',
title: 'Full Outline',
type: ContextSelectionType.fullOutline,
subtitle: '包含所有卷、章节和场景的完整大纲',
metadata: {'actCount': 0, 'chapterCount': 0, 'sceneCount': 0},
),
];
final flatItems = <String, ContextSelectionItem>{};
for (final item in demoItems) {
flatItems[item.id] = item;
}
return ContextSelectionData(
novelId: 'demo_novel',
availableItems: demoItems,
flatItems: flatItems,
);
}
/// 验证上下文选择数据的完整性
///
/// [contextData] 要验证的上下文选择数据
static bool validateContextData(ContextSelectionData? contextData) {
if (contextData == null) {
AppLogger.w('ContextSelectionHelper', '❌ 上下文数据为null');
return false;
}
if (contextData.availableItems.isEmpty) {
AppLogger.w('ContextSelectionHelper', '❌ 上下文数据无可用项目');
return false;
}
if (contextData.flatItems.isEmpty) {
AppLogger.w('ContextSelectionHelper', '❌ 上下文数据扁平化映射为空');
return false;
}
//AppLogger.d('ContextSelectionHelper', '✅ 上下文数据验证通过');
return true;
}
/// 获取上下文选择的统计信息
///
/// [contextData] 上下文选择数据
static Map<String, dynamic> getSelectionStats(ContextSelectionData? contextData) {
if (contextData == null) {
return {'totalItems': 0, 'selectedItems': 0, 'selectionTypes': []};
}
final selectedTypes = contextData.selectedItems.values
.map((item) => item.type.displayName)
.toSet()
.toList();
return {
'totalItems': contextData.availableItems.length,
'selectedItems': contextData.selectedCount,
'selectionTypes': selectedTypes,
'novelId': contextData.novelId,
};
}
}
/// 上下文选择数据扩展方法
extension ContextSelectionDataExt on ContextSelectionData {
/// 转换为保存字符串
String toSaveString() {
if (selectedCount == 0) return '';
final saveData = {
'novelId': novelId,
'selectedItems': selectedItems.values.map((item) => {
'id': item.id,
'title': item.title,
'type': item.type.displayName,
'metadata': item.metadata,
}).toList(),
};
return saveData.toString(); // 简化的序列化,可以根据需要使用 jsonEncode
}
}