马良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

569
AINoval/lib/main.dart Normal file
View File

@@ -0,0 +1,569 @@
import 'dart:io';
import 'dart:async';
// <<< 导入 AiConfigBloc >>>
import 'package:ainoval/blocs/ai_config/ai_config_bloc.dart';
// 导入聊天相关的类
import 'package:ainoval/blocs/auth/auth_bloc.dart';
import 'package:ainoval/blocs/chat/chat_bloc.dart';
import 'package:ainoval/blocs/credit/credit_bloc.dart';
import 'package:ainoval/blocs/editor_version_bloc.dart';
import 'package:ainoval/blocs/novel_list/novel_list_bloc.dart';
import 'package:ainoval/blocs/public_models/public_models_bloc.dart';
import 'package:ainoval/blocs/setting_generation/setting_generation_bloc.dart';
import 'package:ainoval/config/app_config.dart'; // 引入 AppConfig
import 'package:ainoval/l10n/l10n.dart';
import 'package:ainoval/models/app_registration_config.dart';
// import 'package:ainoval/screens/novel_list/novel_list_screen.dart'; // 已删除,使用新页面
import 'package:ainoval/screens/novel_list/novel_list_real_data_screen.dart' deferred as novel_list;
import 'package:ainoval/services/api_service/base/api_client.dart';
import 'package:ainoval/services/api_service/base/sse_client.dart';
// <<< 移除未使用的 Codex Impl 引用 >>>
// import 'package:ainoval/services/api_service/repositories/impl/codex_repository_impl.dart';
import 'package:ainoval/services/api_service/repositories/chat_repository.dart'; // <<< 导入接口
// ApiService import might not be needed directly in main unless provided
// import 'package:ainoval/services/api_service.dart';
import 'package:ainoval/services/api_service/repositories/impl/chat_repository_impl.dart';
import 'package:ainoval/services/api_service/repositories/impl/credit_repository_impl.dart';
import 'package:ainoval/services/api_service/repositories/impl/novel_repository_impl.dart';
import 'package:ainoval/services/api_service/repositories/impl/novel_setting_repository_impl.dart';
import 'package:ainoval/services/api_service/repositories/impl/public_model_repository_impl.dart';
import 'package:ainoval/services/api_service/repositories/impl/storage_repository_impl.dart';
import 'package:ainoval/services/api_service/repositories/impl/user_ai_model_config_repository_impl.dart';
import 'package:ainoval/services/api_service/repositories/impl/setting_generation_repository_impl.dart';
import 'package:ainoval/services/api_service/repositories/impl/universal_ai_repository_impl.dart';
import 'package:ainoval/services/api_service/repositories/impl/preset_aggregation_repository_impl.dart';
import 'package:ainoval/services/api_service/repositories/impl/ai_preset_repository_impl.dart';
import 'package:ainoval/services/api_service/repositories/impl/novel_snippet_repository_impl.dart';
import 'package:ainoval/services/api_service/repositories/novel_repository.dart'; // <<< 导入接口
import 'package:ainoval/services/image_cache_service.dart';
// import 'package:ainoval/services/api_service/repositories/novel_setting_repository.dart';
import 'package:ainoval/services/api_service/repositories/credit_repository.dart';
import 'package:ainoval/services/api_service/repositories/public_model_repository.dart';
import 'package:ainoval/services/api_service/repositories/storage_repository.dart';
// <<< 导入 AI Config 仓库 >>>
import 'package:ainoval/services/api_service/repositories/user_ai_model_config_repository.dart';
import 'package:ainoval/services/api_service/repositories/setting_generation_repository.dart';
import 'package:ainoval/services/api_service/repositories/universal_ai_repository.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/services/api_service/repositories/novel_snippet_repository.dart';
import 'package:ainoval/services/auth_service.dart' as auth_service;
import 'package:ainoval/services/local_storage_service.dart';
import 'package:ainoval/services/novel_file_service.dart'; // 导入小说文件服务
// import 'package:ainoval/services/websocket_service.dart';
import 'package:ainoval/utils/web_theme.dart';
import 'package:ainoval/utils/logger.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:ainoval/l10n/app_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'package:ainoval/services/api_service/repositories/prompt_repository.dart';
import 'package:ainoval/services/api_service/repositories/impl/prompt_repository_impl.dart';
// 重复导入清理(下方已存在这些导入)
import 'package:ainoval/blocs/universal_ai/universal_ai_bloc.dart';
import 'package:ainoval/utils/navigation_logger.dart';
import 'package:ainoval/blocs/prompt_new/prompt_new_bloc.dart';
import 'package:ainoval/blocs/prompt_new/prompt_new_event.dart';
import 'package:ainoval/blocs/theme/theme_bloc.dart';
import 'package:ainoval/blocs/theme/theme_event.dart';
import 'package:ainoval/blocs/theme/theme_state.dart';
// 导入预设管理BLoC
import 'package:ainoval/blocs/preset/preset_bloc.dart';
import 'package:ainoval/blocs/preset/preset_event.dart';
// 导入预设聚合仓储
import 'package:ainoval/screens/unified_management/unified_management_screen.dart' deferred as unified_mgmt;
void main() {
runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
// Web 平台下:覆盖 Flutter 全局错误处理,避免 Inspector 在处理 JS 对象时报错
FlutterError.onError = (FlutterErrorDetails details) {
if (kIsWeb) {
// 直接输出字符串化的异常信息,避免 DiagnosticsNode 转换
debugPrint('FlutterError: ${details.exceptionAsString()}');
if (details.stack != null) {
debugPrint(details.stack.toString());
}
} else {
FlutterError.presentError(details);
}
};
// 初始化日志系统
AppLogger.init();
// 初始化Hive本地存储
await Hive.initFlutter();
// 初始化注册配置
await _initializeRegistrationConfig();
// 创建必要的资源文件夹 - 仅在非Web平台执行
if (!kIsWeb) {
await _createResourceDirectories();
}
// 初始化LocalStorageService
final localStorageService = LocalStorageService();
await localStorageService.init();
// 创建AuthService
final authServiceInstance = auth_service.AuthService();
await authServiceInstance.init();
// 创建 ApiClient 实例并传入 AuthService
final apiClient = ApiClient(authService: authServiceInstance);
// 创建 SseClient 实例 (单例模式)
final sseClient = SseClient();
/*
// 创建ApiService (如果 ApiService 需要 ApiClient, 则传入)
// 假设 ApiService 构造函数接受 apiClient (如果不需要则忽略)
final apiService = ApiService(/* apiClient: apiClient */);
// 创建WebSocketService
final webSocketService = WebSocketService(); */
// 创建NovelRepository (它不再需要MockDataService)
final novelRepository = NovelRepositoryImpl(/* apiClient: apiClient */);
// 创建ChatRepository并传入 ApiClient
final chatRepository = ChatRepositoryImpl(
apiClient: apiClient, // 使用直接创建的 apiClient
);
// 创建StorageRepository实例
final storageRepository = StorageRepositoryImpl(apiClient);
// 创建UserAIModelConfigRepository
final userAIModelConfigRepository =
UserAIModelConfigRepositoryImpl(apiClient: apiClient);
// 创建PublicModelRepository
final publicModelRepository = PublicModelRepositoryImpl(apiClient: apiClient);
// 创建CreditRepository
final creditRepository = CreditRepositoryImpl(apiClient: apiClient);
// 创建NovelSettingRepository
final novelSettingRepository = NovelSettingRepositoryImpl(apiClient: apiClient);
// 创建PromptRepository
final promptRepository = PromptRepositoryImpl(apiClient);
// 创建NovelFileService
final novelFileService = NovelFileService(
novelRepository: novelRepository,
// editorRepository 暂时为空,可以后续添加
);
// 创建NovelSnippetRepository
final novelSnippetRepository = NovelSnippetRepositoryImpl(apiClient);
// 创建UniversalAIRepository
final universalAIRepository = UniversalAIRepositoryImpl(apiClient: apiClient);
// 创建PresetAggregationRepository
final presetAggregationRepository = PresetAggregationRepositoryImpl(apiClient);
// 创建AIPresetRepository
final aiPresetRepository = AIPresetRepositoryImpl(apiClient: apiClient);
// 创建SettingGenerationRepository
final settingGenerationRepository = SettingGenerationRepositoryImpl(
apiClient: apiClient,
sseClient: sseClient,
);
// 初始化图片缓存服务(如需预热可在此调用)
// ImageCacheService().prewarm();
AppLogger.i('Main', '应用程序初始化完成,准备启动界面');
runApp(MultiRepositoryProvider(
providers: [
RepositoryProvider<auth_service.AuthService>.value(
value: authServiceInstance),
RepositoryProvider<ApiClient>.value(value: apiClient),
RepositoryProvider<NovelRepository>.value(value: novelRepository),
RepositoryProvider<ChatRepository>.value(value: chatRepository),
RepositoryProvider<StorageRepository>.value(value: storageRepository),
RepositoryProvider<UserAIModelConfigRepository>.value(
value: userAIModelConfigRepository),
RepositoryProvider<PublicModelRepository>.value(
value: publicModelRepository),
RepositoryProvider<CreditRepository>.value(
value: creditRepository),
RepositoryProvider<LocalStorageService>.value(
value: localStorageService),
RepositoryProvider<PromptRepository>(
create: (context) => promptRepository,
),
RepositoryProvider<NovelFileService>.value(
value: novelFileService,
),
RepositoryProvider<NovelSnippetRepository>.value(
value: novelSnippetRepository,
),
RepositoryProvider<UniversalAIRepository>.value(
value: universalAIRepository,
),
RepositoryProvider<PresetAggregationRepository>.value(
value: presetAggregationRepository,
),
RepositoryProvider<AIPresetRepository>.value(
value: aiPresetRepository,
),
RepositoryProvider<SettingGenerationRepository>.value(
value: settingGenerationRepository,
),
],
child: MultiBlocProvider(
providers: [
BlocProvider<AuthBloc>(
create: (context) => AuthBloc(
authService: context.read<auth_service.AuthService>(),
)..add(AuthInitialize()),
),
BlocProvider<NovelListBloc>(
create: (context) => NovelListBloc(
repository: context.read<NovelRepository>(),
),
),
BlocProvider<AiConfigBloc>(
create: (context) => AiConfigBloc(
repository: context.read<UserAIModelConfigRepository>(),
),
),
BlocProvider<PublicModelsBloc>(
create: (context) => PublicModelsBloc(
repository: context.read<PublicModelRepository>(),
),
),
BlocProvider<CreditBloc>(
create: (context) => CreditBloc(
repository: context.read<CreditRepository>(),
),
),
BlocProvider<SettingGenerationBloc>(
create: (context) => SettingGenerationBloc(
repository: context.read<SettingGenerationRepository>(),
),
),
/*
BlocProvider<ReaderBloc>(
create: (context) => ReaderBloc(
repository: context.read<NovelRepository>(),
),
),
*/
BlocProvider<ChatBloc>(
create: (context) => ChatBloc(
repository: context.read<ChatRepository>(),
authService: context.read<auth_service.AuthService>(),
aiConfigBloc: context.read<AiConfigBloc>(),
publicModelsBloc: context.read<PublicModelsBloc>(),
settingRepository: novelSettingRepository,
snippetRepository: novelSnippetRepository,
),
),
BlocProvider<EditorVersionBloc>(
create: (context) => EditorVersionBloc(
novelRepository: context.read<NovelRepository>(),
),
),
BlocProvider<UniversalAIBloc>(
create: (context) => UniversalAIBloc(
repository: context.read<UniversalAIRepository>(),
),
),
BlocProvider<PromptNewBloc>(
create: (context) => PromptNewBloc(
promptRepository: context.read<PromptRepository>(),
),
),
BlocProvider<ThemeBloc>(
create: (context) => ThemeBloc()..add(ThemeInitialize()),
),
BlocProvider<PresetBloc>(
create: (context) => PresetBloc(
aggregationRepository: context.read<PresetAggregationRepository>(),
presetRepository: context.read<AIPresetRepository>(),
),
),
],
child: const MyApp(),
),
));
}, (error, stack) {
// 兜底:捕获所有未处理异常并记录,避免在 Web 上出现 LegacyJavaScriptObject -> DiagnosticsNode 的崩溃
AppLogger.e('Uncaught', '未捕获异常: $error', error, stack);
});
}
// 初始化注册配置
Future<void> _initializeRegistrationConfig() async {
try {
// 确保注册配置已初始化,设置默认值
// 默认开启邮箱注册和手机注册,需要验证码验证
final phoneEnabled = await AppRegistrationConfig.isPhoneRegistrationEnabled();
final emailEnabled = await AppRegistrationConfig.isEmailRegistrationEnabled();
final verificationRequired = await AppRegistrationConfig.isVerificationRequired();
AppLogger.i('Registration',
'📝 注册配置已加载 - 邮箱注册: $emailEnabled, 手机注册: $phoneEnabled, 验证码验证: $verificationRequired');
// 如果没有任何注册方式可用,启用默认的邮箱注册
if (!phoneEnabled && !emailEnabled) {
await AppRegistrationConfig.setEmailRegistrationEnabled(true);
AppLogger.i('Registration', '🔧 已自动启用邮箱注册功能');
}
} catch (e) {
AppLogger.e('Registration', '初始化注册配置失败', e);
}
}
// 创建资源文件夹
Future<void> _createResourceDirectories() async {
try {
final appDir = await getApplicationDocumentsDirectory();
final assetsDir = Directory('${appDir.path}/assets');
final imagesDir = Directory('${assetsDir.path}/images');
final iconsDir = Directory('${assetsDir.path}/icons');
// 创建资源目录
if (!await assetsDir.exists()) {
await assetsDir.create(recursive: true);
}
// 创建图像目录
if (!await imagesDir.exists()) {
await imagesDir.create(recursive: true);
}
// 创建图标目录
if (!await iconsDir.exists()) {
await iconsDir.create(recursive: true);
}
AppLogger.i('ResourceDir', '资源文件夹创建成功');
} catch (e) {
AppLogger.e('ResourceDir', '创建资源文件夹失败', e);
}
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
bool _postLoginBootstrapped = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
ImageCacheService().clearCache();
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.paused:
case AppLifecycleState.detached:
// 应用进入后台或被关闭时清理图片缓存
ImageCacheService().clearCache();
break;
case AppLifecycleState.resumed:
// 应用恢复时可以预加载一些图片
break;
default:
break;
}
}
@override
Widget build(BuildContext context) {
return BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, themeState) {
return ValueListenableBuilder<String>(
valueListenable: WebTheme.variantListenable,
builder: (context, variant, _) {
// 根据当前变体重建主题
return MaterialApp(
navigatorObservers: [NavigationLogger()],
title: 'AINoval',
theme: WebTheme.buildLightTheme(),
darkTheme: WebTheme.buildDarkTheme(),
themeMode: themeState.themeMode,
initialRoute: '/',
routes: {
'/': (context) => BlocConsumer<AuthBloc, AuthState>(
listenWhen: (prev, curr) =>
curr is AuthAuthenticated || curr is AuthUnauthenticated,
listener: (context, state) {
AppLogger.i('MyApp', '🔔 AuthBloc状态变化: ${state.runtimeType}');
if (state is AuthAuthenticated) {
if (_postLoginBootstrapped) {
AppLogger.i('MyApp', '🔁 已完成登录后的初始化,跳过重复触发');
}
final userId = AppConfig.userId;
if (userId != null) {
AppLogger.i('MyApp',
'User authenticated, loading AiConfigs, PublicModels, Credits, Novels, Presets and PromptPackages for user $userId');
// 并行加载用户AI配置、公共模型和用户积分
if (!_postLoginBootstrapped) {
context.read<AiConfigBloc>().add(LoadAiConfigs(userId: userId));
context.read<PublicModelsBloc>().add(const LoadPublicModels());
// 每次登录都强制重新加载积分,避免复用上个账号缓存
context.read<CreditBloc>().add(const LoadUserCredits());
// 用户登录成功后,加载一次小说列表数据(仅在未加载时)
final novelState = context.read<NovelListBloc>().state;
if (novelState is! NovelListLoaded) {
context.read<NovelListBloc>().add(LoadNovels());
}
// 预设与提示词包
context.read<PresetBloc>().add(const LoadAllPresetData());
context.read<PromptNewBloc>().add(const LoadAllPromptPackages());
_postLoginBootstrapped = true;
}
} else {
AppLogger.e('MyApp',
'User authenticated but userId is null in AppConfig!');
}
} else if (state is AuthUnauthenticated) {
AppLogger.i('MyApp', '✅ 用户已退出登录清理所有BLoC状态');
_postLoginBootstrapped = false;
// 清理所有BLoC状态停止进行中的请求
try {
// 重置 AI 配置,避免跨用户复用本地缓存/内存状态
context.read<AiConfigBloc>().add(const ResetAiConfigs());
} catch (e) {
AppLogger.w('MyApp', '重置AiConfigBloc状态失败', e);
}
try {
// 清理小说列表状态
context.read<NovelListBloc>().add(ClearNovels());
AppLogger.i('MyApp', '✅ NovelListBloc状态已清理');
} catch (e) {
AppLogger.w('MyApp', '清理NovelListBloc状态失败', e);
}
// 清空积分显示为游客0
try {
context.read<CreditBloc>().add(const ClearCredits());
AppLogger.i('MyApp', '✅ CreditBloc状态已清空');
} catch (e) {
AppLogger.w('MyApp', '清空CreditBloc状态失败', e);
}
// 清除用户显示名称为游客
AppConfig.setUsername(null);
AppConfig.setUserId(null);
AppConfig.setAuthToken(null);
// 可以根据需要添加其他BLoC的清理逻辑
// 但暂时先清理最关键的小说列表避免404请求
} else if (state is AuthLoading) {
AppLogger.i('MyApp', '⏳ 认证状态加载中...');
} else if (state is AuthError) {
AppLogger.w('MyApp', '❌ 认证错误: ${state.message}');
}
},
buildWhen: (prev, curr) =>
curr is AuthAuthenticated || curr is AuthUnauthenticated,
builder: (context, state) {
AppLogger.i('MyApp', '🏗️ 构建UI当前状态: ${state.runtimeType}');
if (state is AuthAuthenticated) {
AppLogger.i(
'MyApp', '📚 显示小说列表界面');
// 🚀 登录成功后异步加载并应用用户的主题变体,确保全局组件使用保存的主题色
final userId = AppConfig.userId;
if (userId != null) {
() async {
try {
final settings = await NovelRepositoryImpl.getInstance().getUserEditorSettings(userId);
WebTheme.applyVariant(settings.themeVariant);
AppLogger.i('MyApp', '🎨 已应用用户主题变体: ${settings.themeVariant}');
} catch (e) {
AppLogger.w('MyApp', '无法应用用户主题变体: $e');
}
}();
}
// 异步加载小说列表页面,实现代码分割
return FutureBuilder(
future: novel_list.loadLibrary(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return novel_list.NovelListRealDataScreen();
}
return const Center(
child: CircularProgressIndicator(),
);
},
);
}
// 未登录:默认展示小说列表的“游客模式”界面,受控于页面内的鉴权弹窗
return FutureBuilder(
future: novel_list.loadLibrary(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return novel_list.NovelListRealDataScreen();
}
return const Center(
child: CircularProgressIndicator(),
);
},
);
},
),
'/unified-management': (context) => FutureBuilder(
future: unified_mgmt.loadLibrary(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return unified_mgmt.UnifiedManagementScreen();
}
return const Center(child: CircularProgressIndicator());
},
),
},
debugShowCheckedModeBanner: false,
// 添加完整的本地化支持
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: L10n.all,
locale: const Locale('zh', 'CN'), // 设置默认语言为中文
);
},
);
},
);
}
}