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.value( value: authServiceInstance), RepositoryProvider.value(value: apiClient), RepositoryProvider.value(value: novelRepository), RepositoryProvider.value(value: chatRepository), RepositoryProvider.value(value: storageRepository), RepositoryProvider.value( value: userAIModelConfigRepository), RepositoryProvider.value( value: publicModelRepository), RepositoryProvider.value( value: creditRepository), RepositoryProvider.value( value: localStorageService), RepositoryProvider( create: (context) => promptRepository, ), RepositoryProvider.value( value: novelFileService, ), RepositoryProvider.value( value: novelSnippetRepository, ), RepositoryProvider.value( value: universalAIRepository, ), RepositoryProvider.value( value: presetAggregationRepository, ), RepositoryProvider.value( value: aiPresetRepository, ), RepositoryProvider.value( value: settingGenerationRepository, ), ], child: MultiBlocProvider( providers: [ BlocProvider( create: (context) => AuthBloc( authService: context.read(), )..add(AuthInitialize()), ), BlocProvider( create: (context) => NovelListBloc( repository: context.read(), ), ), BlocProvider( create: (context) => AiConfigBloc( repository: context.read(), ), ), BlocProvider( create: (context) => PublicModelsBloc( repository: context.read(), ), ), BlocProvider( create: (context) => CreditBloc( repository: context.read(), ), ), BlocProvider( create: (context) => SettingGenerationBloc( repository: context.read(), ), ), /* BlocProvider( create: (context) => ReaderBloc( repository: context.read(), ), ), */ BlocProvider( create: (context) => ChatBloc( repository: context.read(), authService: context.read(), aiConfigBloc: context.read(), publicModelsBloc: context.read(), settingRepository: novelSettingRepository, snippetRepository: novelSnippetRepository, ), ), BlocProvider( create: (context) => EditorVersionBloc( novelRepository: context.read(), ), ), BlocProvider( create: (context) => UniversalAIBloc( repository: context.read(), ), ), BlocProvider( create: (context) => PromptNewBloc( promptRepository: context.read(), ), ), BlocProvider( create: (context) => ThemeBloc()..add(ThemeInitialize()), ), BlocProvider( create: (context) => PresetBloc( aggregationRepository: context.read(), presetRepository: context.read(), ), ), ], child: const MyApp(), ), )); }, (error, stack) { // 兜底:捕获所有未处理异常并记录,避免在 Web 上出现 LegacyJavaScriptObject -> DiagnosticsNode 的崩溃 AppLogger.e('Uncaught', '未捕获异常: $error', error, stack); }); } // 初始化注册配置 Future _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 _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 createState() => _MyAppState(); } class _MyAppState extends State 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( builder: (context, themeState) { return ValueListenableBuilder( 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( 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().add(LoadAiConfigs(userId: userId)); context.read().add(const LoadPublicModels()); // 每次登录都强制重新加载积分,避免复用上个账号缓存 context.read().add(const LoadUserCredits()); // 用户登录成功后,加载一次小说列表数据(仅在未加载时) final novelState = context.read().state; if (novelState is! NovelListLoaded) { context.read().add(LoadNovels()); } // 预设与提示词包 context.read().add(const LoadAllPresetData()); context.read().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().add(const ResetAiConfigs()); } catch (e) { AppLogger.w('MyApp', '重置AiConfigBloc状态失败', e); } try { // 清理小说列表状态 context.read().add(ClearNovels()); AppLogger.i('MyApp', '✅ NovelListBloc状态已清理'); } catch (e) { AppLogger.w('MyApp', '清理NovelListBloc状态失败', e); } // 清空积分显示为游客(0) try { context.read().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'), // 设置默认语言为中文 ); }, ); }, ); } }