马良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,996 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:ainoval/blocs/preset/preset_event.dart';
import 'package:ainoval/blocs/preset/preset_state.dart';
import 'package:ainoval/services/api_service/repositories/preset_aggregation_repository.dart';
import 'package:ainoval/services/api_service/repositories/ai_preset_repository.dart';
import 'package:ainoval/models/preset_models.dart';
import 'package:ainoval/utils/logger.dart';
/// 预设管理BLoC
/// 负责处理预设相关的业务逻辑和状态管理
class PresetBloc extends Bloc<PresetEvent, PresetState> {
static const String _tag = 'PresetBloc';
final PresetAggregationRepository _aggregationRepository;
final AIPresetRepository _presetRepository;
PresetBloc({
required PresetAggregationRepository aggregationRepository,
required AIPresetRepository presetRepository,
}) : _aggregationRepository = aggregationRepository,
_presetRepository = presetRepository,
super(const PresetState.initial()) {
on<LoadUserPresetOverview>(_onLoadUserPresetOverview);
on<LoadPresetPackage>(_onLoadPresetPackage);
on<LoadBatchPresetPackages>(_onLoadBatchPresetPackages);
on<LoadGroupedPresets>(_onLoadGroupedPresets);
on<LoadAllPresetData>(_onLoadAllPresetData);
on<AddPresetToCache>(_onAddPresetToCache);
on<SelectPreset>(_onSelectPreset);
on<CreatePreset>(_onCreatePreset);
on<OverwritePreset>(_onOverwritePreset);
on<UpdatePreset>(_onUpdatePreset);
on<DeletePreset>(_onDeletePreset);
on<DuplicatePreset>(_onDuplicatePreset);
on<TogglePresetFavorite>(_onTogglePresetFavorite);
on<TogglePresetQuickAccess>(_onTogglePresetQuickAccess);
on<SearchPresets>(_onSearchPresets);
on<ClearPresetSearch>(_onClearPresetSearch);
on<RefreshPresetData>(_onRefreshPresetData);
on<WarmupPresetCache>(_onWarmupPresetCache);
}
/// 加载用户预设概览
Future<void> _onLoadUserPresetOverview(
LoadUserPresetOverview event,
Emitter<PresetState> emit,
) async {
try {
emit(state.copyWith(isLoading: true, errorMessage: null));
final overview = await _aggregationRepository.getUserPresetOverview();
emit(state.copyWith(
isLoading: false,
userOverview: overview,
));
AppLogger.i(_tag, '用户预设概览加载成功');
} catch (e) {
AppLogger.e(_tag, '加载用户预设概览失败', e);
emit(state.copyWith(
isLoading: false,
errorMessage: '加载用户预设概览失败: ${e.toString()}',
));
}
}
/// 加载预设包
Future<void> _onLoadPresetPackage(
LoadPresetPackage event,
Emitter<PresetState> emit,
) async {
try {
emit(state.copyWith(isLoading: true, errorMessage: null));
final package = await _aggregationRepository.getCompletePresetPackage(
event.featureType,
novelId: event.novelId,
);
emit(state.copyWith(
isLoading: false,
currentPackage: package,
));
AppLogger.i(_tag, '预设包加载成功: ${event.featureType}');
} catch (e) {
AppLogger.e(_tag, '加载预设包失败: ${event.featureType}', e);
emit(state.copyWith(
isLoading: false,
errorMessage: '加载预设包失败: ${e.toString()}',
));
}
}
/// 加载批量预设包
Future<void> _onLoadBatchPresetPackages(
LoadBatchPresetPackages event,
Emitter<PresetState> emit,
) async {
try {
emit(state.copyWith(isLoading: true, errorMessage: null));
final packages = await _aggregationRepository.getBatchPresetPackages(
featureTypes: event.featureTypes,
novelId: event.novelId,
);
emit(state.copyWith(
isLoading: false,
batchPackages: packages,
));
AppLogger.i(_tag, '批量预设包加载成功: ${packages.length}');
} catch (e) {
AppLogger.e(_tag, '加载批量预设包失败', e);
emit(state.copyWith(
isLoading: false,
errorMessage: '加载批量预设包失败: ${e.toString()}',
));
}
}
/// 加载分组预设
Future<void> _onLoadGroupedPresets(
LoadGroupedPresets event,
Emitter<PresetState> emit,
) async {
try {
emit(state.copyWith(isLoading: true, errorMessage: null));
final groupedPresets = await _presetRepository.getUserPresetsByFeatureType(
userId: event.userId,
);
// 加载系统预设并合并
final systemPresets = await _presetRepository.getSystemPresets();
// 合并系统预设到分组中
final mergedGroupedPresets = Map<String, List<AIPromptPreset>>.from(groupedPresets);
for (final preset in systemPresets) {
final featureType = preset.aiFeatureType;
if (!mergedGroupedPresets.containsKey(featureType)) {
mergedGroupedPresets[featureType] = [];
}
mergedGroupedPresets[featureType]!.insert(0, preset);
}
emit(state.copyWith(
isLoading: false,
groupedPresets: mergedGroupedPresets,
));
AppLogger.i(_tag, '分组预设加载成功: ${mergedGroupedPresets.length} 个分组');
} catch (e) {
AppLogger.e(_tag, '加载分组预设失败', e);
emit(state.copyWith(
isLoading: false,
errorMessage: '加载分组预设失败: ${e.toString()}',
));
}
}
/// 选择预设
Future<void> _onSelectPreset(
SelectPreset event,
Emitter<PresetState> emit,
) async {
try {
// 🚀 修复:优先从已加载的聚合数据中查找预设,避免重复请求后端
AIPromptPreset? preset;
if (state.allPresetData != null) {
// 从聚合数据的所有预设中查找
preset = state.allPresetData!.allPresets
.where((p) => p.presetId == event.presetId)
.firstOrNull;
if (preset != null) {
AppLogger.i(_tag, '✅ 从聚合数据中找到预设: ${event.presetId}');
}
}
// 如果聚合数据中没有找到,尝试从分组预设中查找
if (preset == null && state.groupedPresets.isNotEmpty) {
for (final presets in state.groupedPresets.values) {
preset = presets
.where((p) => p.presetId == event.presetId)
.firstOrNull;
if (preset != null) {
AppLogger.i(_tag, '✅ 从分组预设中找到预设: ${event.presetId}');
break;
}
}
}
// 最后的回退:如果缓存中都没有,才去后端获取
if (preset == null) {
AppLogger.w(_tag, '⚠️ 缓存中未找到预设,从后端获取: ${event.presetId}');
preset = await _presetRepository.getPresetById(event.presetId);
}
emit(state.copyWith(
selectedPreset: preset,
errorMessage: null,
));
AppLogger.i(_tag, '📘 预设选择成功: ${event.presetId}');
} catch (e) {
AppLogger.e(_tag, '选择预设失败: ${event.presetId}', e);
emit(state.copyWith(
errorMessage: '选择预设失败: ${e.toString()}',
));
}
}
/// 创建预设
Future<void> _onCreatePreset(
CreatePreset event,
Emitter<PresetState> emit,
) async {
try {
emit(state.copyWith(isLoading: true, errorMessage: null));
final newPreset = await _presetRepository.createPreset(event.request);
// 🚀 优化直接更新本地状态不重新请求API
final updatedGroupedPresets = Map<String, List<AIPromptPreset>>.from(state.groupedPresets);
final newFeatureType = newPreset.aiFeatureType;
// 🚀 修复:处理功能类型格式不一致问题
// 先查找是否存在相同功能类型的其他格式键
String? existingKey = _findExistingFeatureTypeKey(updatedGroupedPresets, newFeatureType);
final targetKey = existingKey ?? newFeatureType;
if (updatedGroupedPresets.containsKey(targetKey)) {
// 将新预设添加到对应功能类型的列表开头
updatedGroupedPresets[targetKey] = [newPreset, ...updatedGroupedPresets[targetKey]!];
} else {
// 如果该功能类型还没有预设,创建新列表
updatedGroupedPresets[targetKey] = [newPreset];
}
AppLogger.i(_tag, '📋 预设添加到分组: $targetKey (原始类型: $newFeatureType)');
// 🚀 新增:同时更新聚合数据缓存
final newAllPresetData = state.allPresetData != null
? _addPresetToAggregatedData(state.allPresetData!, newPreset)
: null;
emit(state.copyWith(
isLoading: false,
selectedPreset: newPreset,
groupedPresets: updatedGroupedPresets,
allPresetData: newAllPresetData,
));
AppLogger.i(_tag, '📘 预设创建成功: ${newPreset.presetId}');
} catch (e) {
AppLogger.e(_tag, '❌ 创建预设失败', e);
emit(state.copyWith(
isLoading: false,
errorMessage: '创建预设失败: ${e.toString()}',
));
}
}
/// 覆盖更新预设(完整对象)
Future<void> _onOverwritePreset(
OverwritePreset event,
Emitter<PresetState> emit,
) async {
try {
emit(state.copyWith(isLoading: true, errorMessage: null));
final updatedPreset = await _presetRepository.overwritePreset(event.preset);
// 🚀 直接更新本地缓存
final updatedGroupedPresets = Map<String, List<AIPromptPreset>>.from(state.groupedPresets);
final newFeatureType = updatedPreset.aiFeatureType;
String? existingKey = _findExistingFeatureTypeKey(updatedGroupedPresets, newFeatureType);
final targetKey = existingKey ?? newFeatureType;
if (updatedGroupedPresets.containsKey(targetKey)) {
final presetList = updatedGroupedPresets[targetKey]!;
final index = presetList.indexWhere((p) => p.presetId == updatedPreset.presetId);
if (index != -1) {
presetList[index] = updatedPreset;
}
}
// 🚀 同时更新聚合数据缓存
final newAllPresetData = _replacePresetInAggregatedData(state.allPresetData, updatedPreset);
emit(state.copyWith(
isLoading: false,
selectedPreset: updatedPreset,
groupedPresets: updatedGroupedPresets,
allPresetData: newAllPresetData,
));
AppLogger.i(_tag, '📘 预设覆盖更新成功: ${updatedPreset.presetId}');
} catch (e) {
AppLogger.e(_tag, '❌ 覆盖更新预设失败: ${event.preset.presetId}', e);
emit(state.copyWith(
isLoading: false,
errorMessage: '覆盖更新预设失败: ${e.toString()}',
));
}
}
/// 更新预设
Future<void> _onUpdatePreset(
UpdatePreset event,
Emitter<PresetState> emit,
) async {
try {
emit(state.copyWith(isLoading: true, errorMessage: null));
AIPromptPreset updatedPreset;
if (event.infoRequest != null) {
updatedPreset = await _presetRepository.updatePresetInfo(
event.presetId,
event.infoRequest!,
);
} else if (event.promptsRequest != null) {
updatedPreset = await _presetRepository.updatePresetPrompts(
event.presetId,
event.promptsRequest!,
);
} else {
throw Exception('更新请求参数错误');
}
// 🚀 优化直接更新本地状态不重新请求API
final updatedGroupedPresets = Map<String, List<AIPromptPreset>>.from(state.groupedPresets);
final newFeatureType = updatedPreset.aiFeatureType;
// 🚀 修复:处理功能类型格式不一致问题
String? existingKey = _findExistingFeatureTypeKey(updatedGroupedPresets, newFeatureType);
final targetKey = existingKey ?? newFeatureType;
if (updatedGroupedPresets.containsKey(targetKey)) {
// 找到并替换对应的预设
final presetList = updatedGroupedPresets[targetKey]!;
final index = presetList.indexWhere((p) => p.presetId == event.presetId);
if (index != -1) {
presetList[index] = updatedPreset;
AppLogger.i(_tag, '📋 预设更新在分组: $targetKey');
}
} else {
AppLogger.w(_tag, '⚠️ 未找到预设分组进行更新: $targetKey');
}
// 🚀 新增:同时更新聚合数据缓存
final newAllPresetData = _replacePresetInAggregatedData(state.allPresetData, updatedPreset);
emit(state.copyWith(
isLoading: false,
selectedPreset: updatedPreset,
groupedPresets: updatedGroupedPresets,
allPresetData: newAllPresetData,
));
AppLogger.i(_tag, '📘 预设更新成功: ${event.presetId}');
} catch (e) {
AppLogger.e(_tag, '❌ 更新预设失败: ${event.presetId}', e);
emit(state.copyWith(
isLoading: false,
errorMessage: '更新预设失败: ${e.toString()}',
));
}
}
/// 删除预设
Future<void> _onDeletePreset(
DeletePreset event,
Emitter<PresetState> emit,
) async {
try {
emit(state.copyWith(isLoading: true, errorMessage: null));
await _presetRepository.deletePreset(event.presetId);
// 🚀 优化直接更新本地状态不重新请求API
final updatedGroupedPresets = Map<String, List<AIPromptPreset>>.from(state.groupedPresets);
// 从所有功能类型的列表中移除该预设
for (final entry in updatedGroupedPresets.entries.toList()) {
final presetList = entry.value;
presetList.removeWhere((p) => p.presetId == event.presetId);
// 如果该功能类型的预设列表为空,移除该分组
if (presetList.isEmpty) {
updatedGroupedPresets.remove(entry.key);
}
}
// 如果删除的是当前选中预设,清除选择
final selectedPreset = state.selectedPreset?.presetId == event.presetId ? null : state.selectedPreset;
// 🚀 新增:同时更新聚合数据缓存
final newAllPresetData = _removePresetFromAggregatedData(state.allPresetData, event.presetId);
emit(state.copyWith(
isLoading: false,
selectedPreset: selectedPreset,
groupedPresets: updatedGroupedPresets,
allPresetData: newAllPresetData,
));
AppLogger.i(_tag, '📘 预设删除成功: ${event.presetId}');
} catch (e) {
AppLogger.e(_tag, '❌ 删除预设失败: ${event.presetId}', e);
emit(state.copyWith(
isLoading: false,
errorMessage: '删除预设失败: ${e.toString()}',
));
}
}
/// 🚀 复制预设
Future<void> _onDuplicatePreset(
DuplicatePreset event,
Emitter<PresetState> emit,
) async {
try {
emit(state.copyWith(isLoading: true, errorMessage: null));
final duplicatedPreset = await _presetRepository.duplicatePreset(event.presetId, event.request);
// 🚀 直接更新本地缓存,类似创建预设的逻辑
final updatedGroupedPresets = Map<String, List<AIPromptPreset>>.from(state.groupedPresets);
final featureType = duplicatedPreset.aiFeatureType;
if (updatedGroupedPresets.containsKey(featureType)) {
// 将复制的预设添加到对应功能类型的列表开头
updatedGroupedPresets[featureType] = [duplicatedPreset, ...updatedGroupedPresets[featureType]!];
} else {
// 如果该功能类型还没有预设,创建新列表
updatedGroupedPresets[featureType] = [duplicatedPreset];
}
// 🚀 同时更新聚合数据缓存
final newAllPresetData = state.allPresetData != null
? _addPresetToAggregatedData(state.allPresetData!, duplicatedPreset)
: null;
emit(state.copyWith(
isLoading: false,
selectedPreset: duplicatedPreset,
groupedPresets: updatedGroupedPresets,
allPresetData: newAllPresetData,
));
AppLogger.i(_tag, '📘 预设复制成功: ${duplicatedPreset.presetId}');
} catch (e) {
AppLogger.e(_tag, '❌ 复制预设失败: ${event.presetId}', e);
emit(state.copyWith(
isLoading: false,
errorMessage: '复制预设失败: ${e.toString()}',
));
}
}
/// 切换预设收藏状态
Future<void> _onTogglePresetFavorite(
TogglePresetFavorite event,
Emitter<PresetState> emit,
) async {
try {
final updatedPreset = await _presetRepository.toggleFavorite(event.presetId);
// 🚀 优化直接更新本地状态不重新请求API
final updatedGroupedPresets = Map<String, List<AIPromptPreset>>.from(state.groupedPresets);
final newFeatureType = updatedPreset.aiFeatureType;
// 🚀 修复:处理功能类型格式不一致问题
String? existingKey = _findExistingFeatureTypeKey(updatedGroupedPresets, newFeatureType);
final targetKey = existingKey ?? newFeatureType;
if (updatedGroupedPresets.containsKey(targetKey)) {
// 找到并替换对应的预设
final presetList = updatedGroupedPresets[targetKey]!;
final index = presetList.indexWhere((p) => p.presetId == event.presetId);
if (index != -1) {
presetList[index] = updatedPreset;
AppLogger.i(_tag, '📋 预设收藏状态更新在分组: $targetKey');
}
} else {
AppLogger.w(_tag, '⚠️ 未找到预设分组进行收藏状态更新: $targetKey');
}
// 更新选中的预设
final selectedPreset = state.selectedPreset?.presetId == event.presetId
? updatedPreset
: state.selectedPreset;
// 🚀 新增:同时更新聚合数据缓存
final newAllPresetData = _replacePresetInAggregatedData(state.allPresetData, updatedPreset);
emit(state.copyWith(
selectedPreset: selectedPreset,
groupedPresets: updatedGroupedPresets,
allPresetData: newAllPresetData,
));
AppLogger.i(_tag, '📘 预设收藏状态切换成功: ${event.presetId}');
} catch (e) {
AppLogger.e(_tag, '❌ 切换预设收藏状态失败: ${event.presetId}', e);
emit(state.copyWith(
errorMessage: '切换收藏状态失败: ${e.toString()}',
));
}
}
/// 切换预设快捷访问状态
Future<void> _onTogglePresetQuickAccess(
TogglePresetQuickAccess event,
Emitter<PresetState> emit,
) async {
try {
final updatedPreset = await _presetRepository.toggleQuickAccess(event.presetId);
// 🚀 优化直接更新本地状态不重新请求API
final updatedGroupedPresets = Map<String, List<AIPromptPreset>>.from(state.groupedPresets);
final newFeatureType = updatedPreset.aiFeatureType;
// 🚀 修复:处理功能类型格式不一致问题
String? existingKey = _findExistingFeatureTypeKey(updatedGroupedPresets, newFeatureType);
final targetKey = existingKey ?? newFeatureType;
if (updatedGroupedPresets.containsKey(targetKey)) {
// 找到并替换对应的预设
final presetList = updatedGroupedPresets[targetKey]!;
final index = presetList.indexWhere((p) => p.presetId == event.presetId);
if (index != -1) {
presetList[index] = updatedPreset;
AppLogger.i(_tag, '📋 预设快捷访问状态更新在分组: $targetKey');
}
} else {
AppLogger.w(_tag, '⚠️ 未找到预设分组进行快捷访问状态更新: $targetKey');
}
// 更新选中的预设
final selectedPreset = state.selectedPreset?.presetId == event.presetId
? updatedPreset
: state.selectedPreset;
// 🚀 新增:同时更新聚合数据缓存
final newAllPresetData = _replacePresetInAggregatedData(state.allPresetData, updatedPreset);
emit(state.copyWith(
selectedPreset: selectedPreset,
groupedPresets: updatedGroupedPresets,
allPresetData: newAllPresetData,
));
AppLogger.i(_tag, '📘 预设快捷访问状态切换成功: ${event.presetId}');
} catch (e) {
AppLogger.e(_tag, '❌ 切换预设快捷访问状态失败: ${event.presetId}', e);
emit(state.copyWith(
errorMessage: '切换快捷访问状态失败: ${e.toString()}',
));
}
}
/// 搜索预设
Future<void> _onSearchPresets(
SearchPresets event,
Emitter<PresetState> emit,
) async {
try {
final searchParams = PresetSearchParams(
keyword: event.query,
featureType: event.featureType,
tags: event.tags,
sortBy: event.sortBy ?? 'recent',
);
final searchResults = await _presetRepository.searchPresets(searchParams);
emit(state.copyWith(
searchResults: searchResults,
searchQuery: event.query,
errorMessage: null,
));
AppLogger.i(_tag, '预设搜索完成: ${searchResults.length} 个结果');
} catch (e) {
AppLogger.e(_tag, '搜索预设失败', e);
emit(state.copyWith(
errorMessage: '搜索预设失败: ${e.toString()}',
));
}
}
/// 清除搜索
Future<void> _onClearPresetSearch(
ClearPresetSearch event,
Emitter<PresetState> emit,
) async {
emit(state.copyWith(
searchResults: [],
searchQuery: '',
));
AppLogger.i(_tag, '预设搜索已清除');
}
/// 刷新预设数据
Future<void> _onRefreshPresetData(
RefreshPresetData event,
Emitter<PresetState> emit,
) async {
// 重新加载所有数据
add(const LoadUserPresetOverview());
add(const LoadGroupedPresets());
AppLogger.i(_tag, '预设数据刷新中...');
}
/// 🚀 查找现有分组中相同功能类型的键(已统一格式,现在只做直接匹配)
String? _findExistingFeatureTypeKey(Map<String, List<AIPromptPreset>> groupedPresets, String newFeatureType) {
// 如果直接存在返回null使用新的键
if (groupedPresets.containsKey(newFeatureType)) {
return null;
}
// 🚀 已统一为新格式,不再需要映射,直接使用新的功能类型键
AppLogger.i(_tag, '📋 使用新的功能类型键: $newFeatureType');
return null;
}
/// 🚀 新增预设到本地缓存
Future<void> _onAddPresetToCache(
AddPresetToCache event,
Emitter<PresetState> emit,
) async {
try {
final newPreset = event.preset;
AppLogger.i(_tag, '🚀 添加新预设到本地缓存: ${newPreset.presetName}');
// 🚀 更新聚合数据缓存
if (state.allPresetData != null) {
final updatedData = _addPresetToAggregatedData(state.allPresetData!, newPreset);
// 同时更新分组预设以保持兼容性
final updatedGroupedPresets = Map<String, List<AIPromptPreset>>.from(state.groupedPresets);
final featureType = newPreset.aiFeatureType;
if (updatedGroupedPresets.containsKey(featureType)) {
// 将新预设添加到列表开头
updatedGroupedPresets[featureType] = [newPreset, ...updatedGroupedPresets[featureType]!];
} else {
// 创建新的功能类型分组
updatedGroupedPresets[featureType] = [newPreset];
}
emit(state.copyWith(
allPresetData: updatedData,
groupedPresets: updatedGroupedPresets,
errorMessage: null,
));
AppLogger.i(_tag, '✅ 预设已添加到本地缓存: ${featureType}');
} else {
// 如果没有聚合数据,只更新分组预设
final updatedGroupedPresets = Map<String, List<AIPromptPreset>>.from(state.groupedPresets);
final featureType = newPreset.aiFeatureType;
if (updatedGroupedPresets.containsKey(featureType)) {
updatedGroupedPresets[featureType] = [newPreset, ...updatedGroupedPresets[featureType]!];
} else {
updatedGroupedPresets[featureType] = [newPreset];
}
emit(state.copyWith(
groupedPresets: updatedGroupedPresets,
errorMessage: null,
));
AppLogger.w(_tag, '⚠️ 仅更新分组预设,聚合数据不存在');
}
} catch (e) {
AppLogger.e(_tag, '❌ 添加预设到本地缓存失败', e);
emit(state.copyWith(
errorMessage: '添加预设到缓存失败: ${e.toString()}',
));
}
}
/// 🚀 加载所有预设聚合数据
Future<void> _onLoadAllPresetData(
LoadAllPresetData event,
Emitter<PresetState> emit,
) async {
try {
emit(state.copyWith(isLoading: true, errorMessage: null));
AppLogger.i(_tag, '🚀 开始加载所有预设聚合数据: novelId=${event.novelId}');
final allPresetData = await _aggregationRepository.getAllUserPresetData(
novelId: event.novelId,
);
emit(state.copyWith(
isLoading: false,
allPresetData: allPresetData,
// 同时更新其他相关字段以保持兼容性
userOverview: allPresetData.overview,
groupedPresets: allPresetData.mergedGroupedPresets,
batchPackages: allPresetData.packagesByFeatureType,
favoritePresets: allPresetData.favoritePresets,
quickAccessPresets: allPresetData.quickAccessPresets,
recentlyUsedPresets: allPresetData.recentlyUsedPresets,
errorMessage: null,
));
AppLogger.i(_tag, '✅ 所有预设聚合数据加载完成');
AppLogger.i(_tag, '📊 数据统计: 系统预设${allPresetData.systemPresets.length}个, 用户预设分组${allPresetData.userPresetsByFeatureType.length}');
AppLogger.i(_tag, '📈 合并分组: ${allPresetData.mergedGroupedPresets.length}个功能类型');
allPresetData.mergedGroupedPresets.forEach((featureType, presets) {
AppLogger.i(_tag, ' - $featureType: ${presets.length}个预设');
});
} catch (e) {
AppLogger.e(_tag, '❌ 加载所有预设聚合数据失败', e);
emit(state.copyWith(
isLoading: false,
errorMessage: '加载预设数据失败: ${e.toString()}',
));
}
}
/// 预热预设缓存
Future<void> _onWarmupPresetCache(
WarmupPresetCache event,
Emitter<PresetState> emit,
) async {
try {
AppLogger.i(_tag, '开始预热预设缓存...');
final warmupResult = await _aggregationRepository.warmupCache();
emit(state.copyWith(
warmupResult: warmupResult,
errorMessage: null,
));
AppLogger.i(_tag, '预设缓存预热完成: ${warmupResult.success ? "成功" : "失败"}');
if (warmupResult.success) {
AppLogger.i(_tag, '预热了 ${warmupResult.warmedFeatureTypes} 个功能类型,${warmupResult.warmedPresets} 个预设,耗时 ${warmupResult.durationMs}ms');
}
} catch (e) {
AppLogger.e(_tag, '预设缓存预热失败', e);
emit(state.copyWith(
errorMessage: '预设缓存预热失败: ${e.toString()}',
));
}
}
/// 🚀 向聚合缓存中添加新预设
AllUserPresetData _addPresetToAggregatedData(AllUserPresetData data, AIPromptPreset newPreset) {
final featureType = newPreset.aiFeatureType;
// 更新用户预设分组
final userByFeature = Map<String, List<AIPromptPreset>>.from(data.userPresetsByFeatureType);
if (userByFeature.containsKey(featureType)) {
// 添加到现有分组的开头
userByFeature[featureType] = [newPreset, ...userByFeature[featureType]!];
} else {
// 创建新的功能类型分组
userByFeature[featureType] = [newPreset];
}
// 更新包分组(如果存在)
final packages = Map<String, PresetPackage>.from(data.packagesByFeatureType);
if (packages.containsKey(featureType)) {
final oldPackage = packages[featureType]!;
packages[featureType] = PresetPackage(
featureType: featureType,
systemPresets: oldPackage.systemPresets,
userPresets: [newPreset, ...oldPackage.userPresets],
favoritePresets: oldPackage.favoritePresets,
quickAccessPresets: oldPackage.quickAccessPresets,
recentlyUsedPresets: oldPackage.recentlyUsedPresets,
totalCount: oldPackage.totalCount + 1,
cachedAt: DateTime.now(),
);
}
// 如果新预设是收藏、快捷访问等特殊状态,也需要更新对应列表
final favoritePresets = newPreset.isFavorite
? [newPreset, ...data.favoritePresets]
: data.favoritePresets;
final quickAccessPresets = newPreset.showInQuickAccess
? [newPreset, ...data.quickAccessPresets]
: data.quickAccessPresets;
// 添加到最近使用列表的开头
final recentlyUsedPresets = [newPreset, ...data.recentlyUsedPresets];
// 更新概览统计
final currentStats = data.overview.presetsByFeatureType[featureType];
final updatedStats = currentStats != null
? PresetTypeStats(
systemCount: currentStats.systemCount,
userCount: currentStats.userCount + 1,
favoriteCount: newPreset.isFavorite ? currentStats.favoriteCount + 1 : currentStats.favoriteCount,
recentUsageCount: currentStats.recentUsageCount + 1,
)
: PresetTypeStats(
systemCount: 0,
userCount: 1,
favoriteCount: newPreset.isFavorite ? 1 : 0,
recentUsageCount: 1,
);
final overview = UserPresetOverview(
totalPresets: data.overview.totalPresets + 1,
systemPresets: data.overview.systemPresets,
userPresets: data.overview.userPresets + 1,
favoritePresets: favoritePresets.length,
presetsByFeatureType: {
...data.overview.presetsByFeatureType,
featureType: updatedStats,
},
recentFeatureTypes: _updateRecentFeatureTypes(data.overview.recentFeatureTypes, featureType),
popularTags: data.overview.popularTags,
generatedAt: DateTime.now(),
);
return AllUserPresetData(
userId: data.userId,
overview: overview,
packagesByFeatureType: packages,
systemPresets: data.systemPresets,
userPresetsByFeatureType: userByFeature,
favoritePresets: favoritePresets,
quickAccessPresets: quickAccessPresets,
recentlyUsedPresets: recentlyUsedPresets,
timestamp: DateTime.now(),
cacheDuration: data.cacheDuration,
);
}
/// 🚀 更新最近使用的功能类型列表
List<String> _updateRecentFeatureTypes(List<String> current, String newFeatureType) {
final updated = [newFeatureType];
for (final type in current) {
if (type != newFeatureType && updated.length < 5) {
updated.add(type);
}
}
return updated;
}
/// 🚀 从聚合缓存中删除指定预设
AllUserPresetData? _removePresetFromAggregatedData(AllUserPresetData? data, String presetId) {
if (data == null) return null;
bool found = false;
// 从系统预设列表中移除
final system = data.systemPresets.where((p) => p.presetId != presetId).toList();
if (system.length != data.systemPresets.length) found = true;
// 从用户预设分组中移除
final userByFeature = <String, List<AIPromptPreset>>{};
data.userPresetsByFeatureType.forEach((k, list) {
final filtered = list.where((p) => p.presetId != presetId).toList();
if (filtered.isNotEmpty) {
userByFeature[k] = filtered;
}
if (filtered.length != list.length) found = true;
});
// 从收藏/快捷/最近列表中移除
final fav = data.favoritePresets.where((p) => p.presetId != presetId).toList();
final quick = data.quickAccessPresets.where((p) => p.presetId != presetId).toList();
final recent = data.recentlyUsedPresets.where((p) => p.presetId != presetId).toList();
if (!found) return data; // 未找到则直接返回原数据
// 更新包分组
final packages = Map<String, PresetPackage>.from(data.packagesByFeatureType);
packages.forEach((featureType, package) {
final filteredUser = package.userPresets.where((p) => p.presetId != presetId).toList();
final filteredSystem = package.systemPresets.where((p) => p.presetId != presetId).toList();
if (filteredUser.length != package.userPresets.length ||
filteredSystem.length != package.systemPresets.length) {
packages[featureType] = PresetPackage(
featureType: featureType,
systemPresets: filteredSystem,
userPresets: filteredUser,
favoritePresets: package.favoritePresets.where((p) => p.presetId != presetId).toList(),
quickAccessPresets: package.quickAccessPresets.where((p) => p.presetId != presetId).toList(),
recentlyUsedPresets: package.recentlyUsedPresets.where((p) => p.presetId != presetId).toList(),
totalCount: filteredUser.length + filteredSystem.length,
cachedAt: DateTime.now(),
);
}
});
// 更新概览统计
final overview = UserPresetOverview(
totalPresets: data.overview.totalPresets - 1,
systemPresets: system.length,
userPresets: userByFeature.values.fold(0, (sum, list) => sum + list.length),
favoritePresets: fav.length,
presetsByFeatureType: data.overview.presetsByFeatureType, // 保持不变,可选优化
recentFeatureTypes: data.overview.recentFeatureTypes,
popularTags: data.overview.popularTags,
generatedAt: DateTime.now(),
);
return AllUserPresetData(
userId: data.userId,
overview: overview,
packagesByFeatureType: packages,
systemPresets: system,
userPresetsByFeatureType: userByFeature,
favoritePresets: fav,
quickAccessPresets: quick,
recentlyUsedPresets: recent,
timestamp: DateTime.now(),
cacheDuration: data.cacheDuration,
);
}
/// 🚀 在聚合缓存中替换指定预设
AllUserPresetData? _replacePresetInAggregatedData(AllUserPresetData? data, AIPromptPreset updated) {
if (data == null) return null;
bool replaced = false;
// 更新系统预设列表
List<AIPromptPreset> system = data.systemPresets
.map((p) => p.presetId == updated.presetId ? updated : p)
.toList();
if (!replaced) replaced = system.any((p) => p.presetId == updated.presetId);
// 更新用户预设分组
final userByFeature = <String, List<AIPromptPreset>>{};
data.userPresetsByFeatureType.forEach((k, list) {
userByFeature[k] = list.map((p) => p.presetId == updated.presetId ? updated : p).toList();
if (!replaced) {
replaced = list.any((p) => p.presetId == updated.presetId);
}
});
// 更新收藏/快捷/最近
List<AIPromptPreset> _mapList(List<AIPromptPreset> src) =>
src.map((p) => p.presetId == updated.presetId ? updated : p).toList();
final fav = _mapList(data.favoritePresets);
final quick = _mapList(data.quickAccessPresets);
final recent = _mapList(data.recentlyUsedPresets);
// 如果所有列表都未包含,则根据预设类型追加到正确列表
if (!replaced) {
if (updated.isSystem) {
system.add(updated);
} else {
userByFeature.putIfAbsent(updated.aiFeatureType, () => []);
userByFeature[updated.aiFeatureType]!.add(updated);
}
// 快捷访问
if (updated.showInQuickAccess && !quick.any((p) => p.presetId == updated.presetId)) {
quick.insert(0, updated);
}
// 收藏
if (updated.isFavorite && !fav.any((p) => p.presetId == updated.presetId)) {
fav.insert(0, updated);
}
// 最近使用无需处理
}
return AllUserPresetData(
userId: data.userId,
overview: data.overview,
packagesByFeatureType: data.packagesByFeatureType,
systemPresets: system,
userPresetsByFeatureType: userByFeature,
favoritePresets: fav,
quickAccessPresets: quick,
recentlyUsedPresets: recent,
timestamp: DateTime.now(), // 🔧 修复:更新为当前时间戳
cacheDuration: data.cacheDuration,
);
}
}

View File

@@ -0,0 +1,272 @@
import 'package:equatable/equatable.dart';
import 'package:ainoval/models/preset_models.dart';
/// 预设管理事件基类
abstract class PresetEvent extends Equatable {
const PresetEvent();
@override
List<Object?> get props => [];
}
/// 加载用户预设概览
class LoadUserPresetOverview extends PresetEvent {
const LoadUserPresetOverview();
}
/// 加载预设包
class LoadPresetPackage extends PresetEvent {
final String featureType;
final String? novelId;
const LoadPresetPackage({
required this.featureType,
this.novelId,
});
@override
List<Object?> get props => [featureType, novelId];
}
/// 加载批量预设包
class LoadBatchPresetPackages extends PresetEvent {
final List<String>? featureTypes;
final String? novelId;
const LoadBatchPresetPackages({
this.featureTypes,
this.novelId,
});
@override
List<Object?> get props => [featureTypes, novelId];
}
/// 加载分组预设
class LoadGroupedPresets extends PresetEvent {
final String? userId;
const LoadGroupedPresets({this.userId});
@override
List<Object?> get props => [userId];
}
/// 选择预设
class SelectPreset extends PresetEvent {
final String presetId;
const SelectPreset({required this.presetId});
@override
List<Object?> get props => [presetId];
}
/// 创建预设
class CreatePreset extends PresetEvent {
final CreatePresetRequest request;
const CreatePreset({required this.request});
@override
List<Object?> get props => [request];
}
/// 覆盖更新预设(完整对象)
class OverwritePreset extends PresetEvent {
final AIPromptPreset preset;
const OverwritePreset({required this.preset});
@override
List<Object?> get props => [preset];
}
/// 更新预设
class UpdatePreset extends PresetEvent {
final String presetId;
final UpdatePresetInfoRequest? infoRequest;
final UpdatePresetPromptsRequest? promptsRequest;
const UpdatePreset({
required this.presetId,
this.infoRequest,
this.promptsRequest,
});
@override
List<Object?> get props => [presetId, infoRequest, promptsRequest];
}
/// 删除预设
class DeletePreset extends PresetEvent {
final String presetId;
const DeletePreset({required this.presetId});
@override
List<Object?> get props => [presetId];
}
/// 复制预设
class DuplicatePreset extends PresetEvent {
final String presetId;
final DuplicatePresetRequest request;
const DuplicatePreset({
required this.presetId,
required this.request,
});
@override
List<Object?> get props => [presetId, request];
}
/// 切换预设收藏状态
class TogglePresetFavorite extends PresetEvent {
final String presetId;
const TogglePresetFavorite({required this.presetId});
@override
List<Object?> get props => [presetId];
}
/// 切换预设快捷访问状态
class TogglePresetQuickAccess extends PresetEvent {
final String presetId;
const TogglePresetQuickAccess({required this.presetId});
@override
List<Object?> get props => [presetId];
}
/// 记录预设使用
class RecordPresetUsage extends PresetEvent {
final String presetId;
const RecordPresetUsage({required this.presetId});
@override
List<Object?> get props => [presetId];
}
/// 搜索预设
class SearchPresets extends PresetEvent {
final String query;
final String? featureType;
final List<String>? tags;
final String? sortBy;
const SearchPresets({
required this.query,
this.featureType,
this.tags,
this.sortBy,
});
@override
List<Object?> get props => [query, featureType, tags, sortBy];
}
/// 清除预设搜索
class ClearPresetSearch extends PresetEvent {
const ClearPresetSearch();
}
/// 获取预设统计信息
class LoadPresetStatistics extends PresetEvent {
const LoadPresetStatistics();
}
/// 获取收藏预设
class LoadFavoritePresets extends PresetEvent {
final String? novelId;
final String? featureType;
const LoadFavoritePresets({
this.novelId,
this.featureType,
});
@override
List<Object?> get props => [novelId, featureType];
}
/// 获取最近使用预设
class LoadRecentlyUsedPresets extends PresetEvent {
final int limit;
final String? novelId;
final String? featureType;
const LoadRecentlyUsedPresets({
this.limit = 10,
this.novelId,
this.featureType,
});
@override
List<Object?> get props => [limit, novelId, featureType];
}
/// 获取快捷访问预设
class LoadQuickAccessPresets extends PresetEvent {
final String? featureType;
final String? novelId;
const LoadQuickAccessPresets({
this.featureType,
this.novelId,
});
@override
List<Object?> get props => [featureType, novelId];
}
/// 刷新预设数据
class RefreshPresetData extends PresetEvent {
const RefreshPresetData();
}
/// 预热缓存
class WarmupPresetCache extends PresetEvent {
const WarmupPresetCache();
}
/// 获取缓存统计
class LoadCacheStats extends PresetEvent {
const LoadCacheStats();
}
/// 清除缓存
class ClearPresetCache extends PresetEvent {
const ClearPresetCache();
}
/// 健康检查
class PresetHealthCheck extends PresetEvent {
const PresetHealthCheck();
}
/// 🚀 加载所有预设聚合数据
/// 一次性加载用户的所有预设相关数据避免多次API调用
class LoadAllPresetData extends PresetEvent {
final String? novelId;
const LoadAllPresetData({this.novelId});
@override
List<Object?> get props => [novelId];
}
/// 🚀 新增预设到本地缓存
/// 创建预设成功后直接添加到本地缓存,避免重新加载
class AddPresetToCache extends PresetEvent {
final AIPromptPreset preset;
const AddPresetToCache({required this.preset});
@override
List<Object?> get props => [preset];
}

View File

@@ -0,0 +1,240 @@
import 'package:equatable/equatable.dart';
import 'package:ainoval/models/preset_models.dart';
/// 预设管理状态
class PresetState extends Equatable {
/// 是否正在加载
final bool isLoading;
/// 错误信息
final String? errorMessage;
/// 用户预设概览
final UserPresetOverview? userOverview;
/// 当前预设包
final PresetPackage? currentPackage;
/// 批量预设包
final Map<String, PresetPackage> batchPackages;
/// 按功能类型分组的预设
final Map<String, List<AIPromptPreset>> groupedPresets;
/// 当前选中的预设
final AIPromptPreset? selectedPreset;
/// 搜索结果
final List<AIPromptPreset> searchResults;
/// 搜索查询
final String searchQuery;
/// 预设统计信息
final PresetStatistics? statistics;
/// 收藏预设列表
final List<AIPromptPreset> favoritePresets;
/// 最近使用预设列表
final List<AIPromptPreset> recentlyUsedPresets;
/// 快捷访问预设列表
final List<AIPromptPreset> quickAccessPresets;
/// 缓存预热结果
final CacheWarmupResult? warmupResult;
/// 缓存统计信息
final AggregationCacheStats? cacheStats;
/// 健康检查结果
final Map<String, dynamic>? healthStatus;
/// 🚀 所有预设聚合数据
final AllUserPresetData? allPresetData;
const PresetState({
this.isLoading = false,
this.errorMessage,
this.userOverview,
this.currentPackage,
this.batchPackages = const {},
this.groupedPresets = const {},
this.selectedPreset,
this.searchResults = const [],
this.searchQuery = '',
this.statistics,
this.favoritePresets = const [],
this.recentlyUsedPresets = const [],
this.quickAccessPresets = const [],
this.warmupResult,
this.cacheStats,
this.healthStatus,
this.allPresetData,
});
/// 初始状态
const PresetState.initial() : this();
/// 加载状态
PresetState.loading() : this(isLoading: true);
/// 错误状态
PresetState.error(String message) : this(errorMessage: message);
/// 复制状态并更新指定字段
PresetState copyWith({
bool? isLoading,
String? errorMessage,
UserPresetOverview? userOverview,
PresetPackage? currentPackage,
Map<String, PresetPackage>? batchPackages,
Map<String, List<AIPromptPreset>>? groupedPresets,
AIPromptPreset? selectedPreset,
List<AIPromptPreset>? searchResults,
String? searchQuery,
PresetStatistics? statistics,
List<AIPromptPreset>? favoritePresets,
List<AIPromptPreset>? recentlyUsedPresets,
List<AIPromptPreset>? quickAccessPresets,
CacheWarmupResult? warmupResult,
AggregationCacheStats? cacheStats,
Map<String, dynamic>? healthStatus,
AllUserPresetData? allPresetData,
}) {
return PresetState(
isLoading: isLoading ?? this.isLoading,
errorMessage: errorMessage,
userOverview: userOverview ?? this.userOverview,
currentPackage: currentPackage ?? this.currentPackage,
batchPackages: batchPackages ?? this.batchPackages,
groupedPresets: groupedPresets ?? this.groupedPresets,
selectedPreset: selectedPreset,
searchResults: searchResults ?? this.searchResults,
searchQuery: searchQuery ?? this.searchQuery,
statistics: statistics ?? this.statistics,
favoritePresets: favoritePresets ?? this.favoritePresets,
recentlyUsedPresets: recentlyUsedPresets ?? this.recentlyUsedPresets,
quickAccessPresets: quickAccessPresets ?? this.quickAccessPresets,
warmupResult: warmupResult ?? this.warmupResult,
cacheStats: cacheStats ?? this.cacheStats,
healthStatus: healthStatus ?? this.healthStatus,
allPresetData: allPresetData ?? this.allPresetData,
);
}
/// 是否有数据
bool get hasData {
return userOverview != null ||
currentPackage != null ||
batchPackages.isNotEmpty ||
groupedPresets.isNotEmpty ||
searchResults.isNotEmpty;
}
/// 是否有错误
bool get hasError => errorMessage != null;
/// 是否有选中的预设
bool get hasSelectedPreset => selectedPreset != null;
/// 是否正在搜索
bool get isSearching => searchQuery.isNotEmpty;
/// 获取所有预设的总数
int get totalPresetCount {
return groupedPresets.values.fold(0, (sum, presets) => sum + presets.length);
}
/// 获取用户预设数量
int get userPresetCount {
return groupedPresets.values
.expand((presets) => presets)
.where((preset) => !preset.isSystem)
.length;
}
/// 获取系统预设数量
int get systemPresetCount {
return groupedPresets.values
.expand((presets) => presets)
.where((preset) => preset.isSystem)
.length;
}
/// 获取收藏预设数量
int get favoritePresetCount {
return groupedPresets.values
.expand((presets) => presets)
.where((preset) => preset.isFavorite)
.length;
}
/// 获取快捷访问预设数量
int get quickAccessPresetCount {
return groupedPresets.values
.expand((presets) => presets)
.where((preset) => preset.showInQuickAccess)
.length;
}
/// 获取指定功能类型的预设列表
List<AIPromptPreset> getPresetsByFeatureType(String featureType) {
return groupedPresets[featureType] ?? [];
}
/// 获取所有预设的平铺列表
List<AIPromptPreset> get allPresets {
return groupedPresets.values.expand((presets) => presets).toList();
}
/// 🚀 获取合并后的分组预设(系统预设+用户预设,按功能分组)
/// 优先使用allPresetData中的合并数据如果没有则使用旧的groupedPresets
Map<String, List<AIPromptPreset>> get mergedGroupedPresets {
if (allPresetData != null) {
return allPresetData!.mergedGroupedPresets;
}
return groupedPresets;
}
/// 是否已加载聚合数据
bool get hasAllPresetData => allPresetData != null;
@override
List<Object?> get props => [
isLoading,
errorMessage,
userOverview,
currentPackage,
batchPackages,
groupedPresets,
selectedPreset,
searchResults,
searchQuery,
statistics,
favoritePresets,
recentlyUsedPresets,
quickAccessPresets,
warmupResult,
cacheStats,
healthStatus,
allPresetData,
];
@override
String toString() {
return '''PresetState(
isLoading: $isLoading,
hasError: $hasError,
hasData: $hasData,
totalPresets: $totalPresetCount,
userPresets: $userPresetCount,
systemPresets: $systemPresetCount,
favoritePresets: $favoritePresetCount,
quickAccessPresets: $quickAccessPresetCount,
selectedPreset: ${selectedPreset?.presetName ?? 'null'},
searchQuery: '$searchQuery',
)''';
}
}