马良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,401 @@
import 'dart:async';
import 'package:ainoval/models/novel_structure.dart' as novel_models;
import 'package:ainoval/services/api_service/repositories/impl/editor_repository_impl.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../utils/logger.dart';
part 'plan_event.dart';
part 'plan_state.dart';
class PlanBloc extends Bloc<PlanEvent, PlanState> {
PlanBloc({
required EditorRepositoryImpl repository,
required this.novelId,
}) : repository = repository,
super(PlanInitial()) {
on<LoadPlanContent>(_onLoadContent);
on<UpdateActTitle>(_onUpdateActTitle);
on<UpdateChapterTitle>(_onUpdateChapterTitle);
on<UpdateSceneSummary>(_onUpdateSceneSummary);
on<AddNewAct>(_onAddNewAct);
on<AddNewChapter>(_onAddNewChapter);
on<AddNewScene>(_onAddNewScene);
on<MoveScene>(_onMoveScene);
on<DeleteScene>(_onDeleteScene);
}
final EditorRepositoryImpl repository;
final String novelId;
Future<void> _onLoadContent(
LoadPlanContent event, Emitter<PlanState> emit) async {
emit(PlanLoading());
try {
AppLogger.i('PlanBloc/_onLoadContent', '开始加载小说大纲数据');
// 获取小说数据(带场景摘要)
final novel = await repository.getNovelWithSceneSummaries(novelId);
if (novel == null) {
emit(const PlanError(message: '无法加载小说大纲数据'));
return;
}
emit(PlanLoaded(
novel: novel,
isDirty: false,
isSaving: false,
));
} catch (e) {
emit(PlanError(message: e.toString()));
}
}
Future<void> _onUpdateActTitle(
UpdateActTitle event, Emitter<PlanState> emit) async {
final currentState = state;
if (currentState is PlanLoaded) {
try {
// 更新标题逻辑
final acts = currentState.novel.acts.map((act) {
if (act.id == event.actId) {
return act.copyWith(title: event.title);
}
return act;
}).toList();
final updatedNovel = currentState.novel.copyWith(acts: acts);
emit(currentState.copyWith(
novel: updatedNovel,
isDirty: true,
));
// 保存到服务器
await repository.updateActTitle(
novelId,
event.actId,
event.title,
);
emit(currentState.copyWith(isDirty: false));
} catch (e) {
emit(currentState.copyWith(
errorMessage: '更新Act标题失败: ${e.toString()}',
));
}
}
}
Future<void> _onUpdateChapterTitle(
UpdateChapterTitle event, Emitter<PlanState> emit) async {
final currentState = state;
if (currentState is PlanLoaded) {
try {
// 更新标题逻辑
final acts = currentState.novel.acts.map((act) {
if (act.id == event.actId) {
final chapters = act.chapters.map((chapter) {
if (chapter.id == event.chapterId) {
return chapter.copyWith(title: event.title);
}
return chapter;
}).toList();
return act.copyWith(chapters: chapters);
}
return act;
}).toList();
final updatedNovel = currentState.novel.copyWith(acts: acts);
emit(currentState.copyWith(
novel: updatedNovel,
isDirty: true,
));
// 保存到服务器
await repository.updateChapterTitle(
novelId,
event.actId,
event.chapterId,
event.title,
);
emit(currentState.copyWith(isDirty: false));
} catch (e) {
emit(currentState.copyWith(
errorMessage: '更新Chapter标题失败: ${e.toString()}',
));
}
}
}
Future<void> _onUpdateSceneSummary(
UpdateSceneSummary event, Emitter<PlanState> emit) async {
final currentState = state;
if (currentState is PlanLoaded) {
try {
// 更新摘要逻辑
bool updated = false;
final acts = currentState.novel.acts.map((act) {
if (act.id == event.actId) {
final chapters = act.chapters.map((chapter) {
if (chapter.id == event.chapterId) {
final scenes = chapter.scenes.map((scene) {
if (scene.id == event.sceneId) {
updated = true;
final updatedSummary = novel_models.Summary(
id: scene.summary.id,
content: event.summary,
);
return scene.copyWith(summary: updatedSummary);
}
return scene;
}).toList();
return chapter.copyWith(scenes: scenes);
}
return chapter;
}).toList();
return act.copyWith(chapters: chapters);
}
return act;
}).toList();
if (!updated) {
emit(currentState.copyWith(
errorMessage: '未找到对应的场景',
));
return;
}
final updatedNovel = currentState.novel.copyWith(acts: acts);
// 先更新UI以立即反映更改
emit(currentState.copyWith(
novel: updatedNovel,
isDirty: true,
));
// 保存到服务器
await repository.updateSummary(
novelId,
event.actId,
event.chapterId,
event.sceneId,
event.summary,
);
// 只更新isDirty标志保持更新后的novel对象
emit(currentState.copyWith(
novel: updatedNovel,
isDirty: false,
));
} catch (e) {
emit(currentState.copyWith(
errorMessage: '更新场景摘要失败: ${e.toString()}',
));
}
}
}
Future<void> _onAddNewAct(
AddNewAct event, Emitter<PlanState> emit) async {
final currentState = state;
if (currentState is PlanLoaded) {
try {
emit(currentState.copyWith(isSaving: true));
// 调用API创建新Act
final updatedNovel = await repository.addNewAct(
novelId,
event.title,
);
if (updatedNovel == null) {
emit(currentState.copyWith(
isSaving: false,
errorMessage: '添加新Act失败',
));
return;
}
emit(currentState.copyWith(
novel: updatedNovel,
isSaving: false,
));
} catch (e) {
emit(currentState.copyWith(
isSaving: false,
errorMessage: '添加新Act失败: ${e.toString()}',
));
}
}
}
Future<void> _onAddNewChapter(
AddNewChapter event, Emitter<PlanState> emit) async {
final currentState = state;
if (currentState is PlanLoaded) {
try {
emit(currentState.copyWith(isSaving: true));
// 调用API创建新Chapter
final updatedNovel = await repository.addNewChapter(
novelId,
event.actId,
event.title,
);
if (updatedNovel == null) {
emit(currentState.copyWith(
isSaving: false,
errorMessage: '添加新Chapter失败',
));
return;
}
emit(currentState.copyWith(
novel: updatedNovel,
isSaving: false,
));
} catch (e) {
emit(currentState.copyWith(
isSaving: false,
errorMessage: '添加新Chapter失败: ${e.toString()}',
));
}
}
}
Future<void> _onAddNewScene(
AddNewScene event, Emitter<PlanState> emit) async {
final currentState = state;
if (currentState is PlanLoaded) {
try {
emit(currentState.copyWith(isSaving: true));
// 调用API创建新Scene
final updatedNovel = await repository.addNewScene(
novelId,
event.actId,
event.chapterId,
);
if (updatedNovel == null) {
emit(currentState.copyWith(
isSaving: false,
errorMessage: '添加新Scene失败',
));
return;
}
emit(currentState.copyWith(
novel: updatedNovel,
isSaving: false,
));
} catch (e) {
emit(currentState.copyWith(
isSaving: false,
errorMessage: '添加新Scene失败: ${e.toString()}',
));
}
}
}
Future<void> _onMoveScene(
MoveScene event, Emitter<PlanState> emit) async {
final currentState = state;
if (currentState is PlanLoaded) {
try {
emit(currentState.copyWith(isSaving: true));
// 调用API移动Scene
final updatedNovel = await repository.moveScene(
novelId,
event.sourceActId,
event.sourceChapterId,
event.sourceSceneId,
event.targetActId,
event.targetChapterId,
event.targetIndex,
);
if (updatedNovel == null) {
emit(currentState.copyWith(
isSaving: false,
errorMessage: '移动场景失败',
));
return;
}
emit(currentState.copyWith(
novel: updatedNovel,
isSaving: false,
));
} catch (e) {
emit(currentState.copyWith(
isSaving: false,
errorMessage: '移动场景失败: ${e.toString()}',
));
}
}
}
Future<void> _onDeleteScene(
DeleteScene event, Emitter<PlanState> emit) async {
final currentState = state;
if (currentState is PlanLoaded) {
try {
emit(currentState.copyWith(isSaving: true));
// 调用API删除场景
final success = await repository.deleteScene(
novelId,
event.actId,
event.chapterId,
event.sceneId,
);
if (!success) {
emit(currentState.copyWith(
isSaving: false,
errorMessage: '删除场景失败',
));
return;
}
// 从小说结构中删除场景
final updatedActs = currentState.novel.acts.map((act) {
if (act.id == event.actId) {
final updatedChapters = act.chapters.map((chapter) {
if (chapter.id == event.chapterId) {
final updatedScenes = chapter.scenes
.where((scene) => scene.id != event.sceneId)
.toList();
return chapter.copyWith(scenes: updatedScenes);
}
return chapter;
}).toList();
return act.copyWith(chapters: updatedChapters);
}
return act;
}).toList();
final updatedNovel = currentState.novel.copyWith(acts: updatedActs);
emit(currentState.copyWith(
novel: updatedNovel,
isSaving: false,
));
} catch (e) {
emit(currentState.copyWith(
isSaving: false,
errorMessage: '删除场景失败: ${e.toString()}',
));
}
}
}
}

View File

@@ -0,0 +1,138 @@
part of 'plan_bloc.dart';
abstract class PlanEvent extends Equatable {
const PlanEvent();
@override
List<Object?> get props => [];
}
class LoadPlanContent extends PlanEvent {
const LoadPlanContent();
}
class UpdateActTitle extends PlanEvent {
const UpdateActTitle({
required this.actId,
required this.title,
});
final String actId;
final String title;
@override
List<Object?> get props => [actId, title];
}
class UpdateChapterTitle extends PlanEvent {
const UpdateChapterTitle({
required this.actId,
required this.chapterId,
required this.title,
});
final String actId;
final String chapterId;
final String title;
@override
List<Object?> get props => [actId, chapterId, title];
}
class UpdateSceneSummary extends PlanEvent {
const UpdateSceneSummary({
required this.novelId,
required this.actId,
required this.chapterId,
required this.sceneId,
required this.summary,
});
final String novelId;
final String actId;
final String chapterId;
final String sceneId;
final String summary;
@override
List<Object?> get props => [novelId, actId, chapterId, sceneId, summary];
}
class AddNewAct extends PlanEvent {
const AddNewAct({this.title = '新Act'});
final String title;
@override
List<Object?> get props => [title];
}
class AddNewChapter extends PlanEvent {
const AddNewChapter({
required this.novelId,
required this.actId,
this.title = '新章节',
});
final String novelId;
final String actId;
final String title;
@override
List<Object?> get props => [novelId, actId, title];
}
class AddNewScene extends PlanEvent {
const AddNewScene({
required this.novelId,
required this.actId,
required this.chapterId,
});
final String novelId;
final String actId;
final String chapterId;
@override
List<Object?> get props => [novelId, actId, chapterId];
}
class MoveScene extends PlanEvent {
const MoveScene({
required this.novelId,
required this.sourceActId,
required this.sourceChapterId,
required this.sourceSceneId,
required this.targetActId,
required this.targetChapterId,
required this.targetIndex,
});
final String novelId;
final String sourceActId;
final String sourceChapterId;
final String sourceSceneId;
final String targetActId;
final String targetChapterId;
final int targetIndex;
@override
List<Object?> get props => [
novelId,
sourceActId,
sourceChapterId,
sourceSceneId,
targetActId,
targetChapterId,
targetIndex,
];
}
class DeleteScene extends PlanEvent {
const DeleteScene({
required this.novelId,
required this.actId,
required this.chapterId,
required this.sceneId,
});
final String novelId;
final String actId;
final String chapterId;
final String sceneId;
@override
List<Object?> get props => [novelId, actId, chapterId, sceneId];
}

View File

@@ -0,0 +1,61 @@
part of 'plan_bloc.dart';
abstract class PlanState extends Equatable {
const PlanState();
@override
List<Object?> get props => [];
}
class PlanInitial extends PlanState {}
class PlanLoading extends PlanState {}
class PlanLoaded extends PlanState {
const PlanLoaded({
required this.novel,
this.isDirty = false,
this.isSaving = false,
this.lastSaveTime,
this.errorMessage,
});
final novel_models.Novel novel;
final bool isDirty;
final bool isSaving;
final DateTime? lastSaveTime;
final String? errorMessage;
@override
List<Object?> get props => [
novel,
isDirty,
isSaving,
lastSaveTime,
errorMessage,
];
PlanLoaded copyWith({
novel_models.Novel? novel,
bool? isDirty,
bool? isSaving,
DateTime? lastSaveTime,
String? errorMessage,
}) {
return PlanLoaded(
novel: novel ?? this.novel,
isDirty: isDirty ?? this.isDirty,
isSaving: isSaving ?? this.isSaving,
lastSaveTime: lastSaveTime ?? this.lastSaveTime,
errorMessage: errorMessage ?? this.errorMessage,
);
}
}
class PlanError extends PlanState {
const PlanError({required this.message});
final String message;
@override
List<Object?> get props => [message];
}