## 新增功能 - EPUB文件导入功能:支持文件选择、解析和存储 - 文件重复检测:避免重复导入同一文件 - 导入状态反馈:成功/失败消息提示 ## 模型扩展 - Book模型新增多作者支持(authors字段) - 新增章节数统计(chapterCount字段) - 新增语言标识(language字段) - 新增EPUB标识符(identifier字段) - 优化TypeAdapter序列化支持 ## 服务优化 - 新增EpubParserService:EPUB文件解析服务 - 改进DatabaseService:错误处理和数据迁移 - 优化BookRepository:调试日志和错误追踪 ## 依赖更新 - 新增epubx ^4.0.0:EPUB电子书解析库 - 更新pubspec.lock:同步依赖版本 ## UI改进 - AppHeader组件集成完整导入功能 - SafeArea适配:避免系统状态栏重叠 - 优化测试数据结构 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
378 lines
12 KiB
Dart
378 lines
12 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:provider/provider.dart';
|
||
// import 'package:hive_flutter/hive_flutter.dart';
|
||
|
||
// 数据层服务
|
||
import 'services/database_service.dart';
|
||
import 'services/book_repository.dart';
|
||
import 'services/bookshelf_repository.dart';
|
||
import 'services/highlight_repository.dart';
|
||
|
||
// 数据模型(仅导入测试中使用的)
|
||
import 'models/book.dart';
|
||
import 'models/bookshelf.dart';
|
||
import 'models/bookmark.dart';
|
||
import 'models/highlight.dart';
|
||
|
||
// providers
|
||
import 'providers/search_provider.dart';
|
||
|
||
// UI层
|
||
import 'pages/main_navigation.dart';
|
||
import 'theme/app_theme.dart';
|
||
|
||
/// Readful 电子书阅读器应用入口
|
||
///
|
||
/// 这是一个跨平台的Flutter电子书阅读器应用,支持:
|
||
/// - 多格式电子书导入(EPUB、MOBI、TXT、PDF)
|
||
/// - 智能文本高亮和批注系统
|
||
/// - 个性化书签和阅读进度管理
|
||
/// - Material Design 3 界面设计
|
||
/// - 暗夜模式自动跟随系统
|
||
void main() async {
|
||
// 确保Flutter绑定初始化完成,这是使用异步操作的前提
|
||
WidgetsFlutterBinding.ensureInitialized();
|
||
|
||
// 初始化Hive数据库系统
|
||
// 数据库包含:书籍、书架、书签、高亮批注等数据
|
||
try {
|
||
await DatabaseService.instance.init();
|
||
print('✅ 数据库初始化完成,启动应用');
|
||
} catch (e) {
|
||
print('❌ 数据库初始化失败: $e');
|
||
print('🚨 应用将退出,因为数据库初始化是必须的');
|
||
return;
|
||
}
|
||
|
||
// 启动Readful应用
|
||
runApp(const ReadfulApp());
|
||
}
|
||
|
||
/// 测试BookRepository的数据持久化功能
|
||
Future<void> runBookRepositoryTest() async {
|
||
final bookRepository = BookRepository();
|
||
|
||
print('\n🧪 开始测试BookRepository数据持久化功能...\n');
|
||
|
||
try {
|
||
// 1. 获取当前所有书籍
|
||
List<Book> currentBooks = await bookRepository.getAllBooks();
|
||
print('📚 当前书籍数量: ${currentBooks.length}');
|
||
|
||
// 2. 创建测试书籍
|
||
final testBook = Book(
|
||
id: 'test_book_001',
|
||
title: 'Flutter开发入门',
|
||
author: '张三',
|
||
publisher: '技术出版社',
|
||
description: '这是一本关于Flutter开发的入门书籍',
|
||
filePath: '/path/to/flutter_book.epub',
|
||
format: BookFormat.epub,
|
||
fileSize: 1024 * 1024, // 1MB
|
||
addedDate: DateTime.now(),
|
||
status: ReadingStatus.pending,
|
||
totalPages: 300,
|
||
);
|
||
|
||
// 3. 添加测试书籍
|
||
await bookRepository.addBook(testBook);
|
||
|
||
// 4. 再次获取所有书籍
|
||
List<Book> updatedBooks = await bookRepository.getAllBooks();
|
||
print('📚 添加后书籍数量: ${updatedBooks.length}');
|
||
|
||
// 5. 根据ID查找书籍
|
||
Book? foundBook = await bookRepository.getBookById(testBook.id);
|
||
if (foundBook != null) {
|
||
print('🔍 成功找到书籍: ${foundBook.title}');
|
||
print(' - 作者: ${foundBook.author}');
|
||
print(' - 格式: ${foundBook.format.name}');
|
||
print(' - 状态: ${foundBook.status.name}');
|
||
} else {
|
||
print('❌ 未找到指定书籍');
|
||
}
|
||
|
||
// 6. 测试更新书籍
|
||
final updatedBook = testBook.copyWith(status: ReadingStatus.completed);
|
||
await bookRepository.updateBook(updatedBook);
|
||
print('✅ 书籍状态更新为: ${updatedBook.status.name}');
|
||
|
||
// 7. 验证更新后的状态
|
||
Book? updatedFoundBook = await bookRepository.getBookById(testBook.id);
|
||
if (updatedFoundBook != null &&
|
||
updatedFoundBook.status == ReadingStatus.completed) {
|
||
print('✅ 状态更新验证成功!');
|
||
} else {
|
||
print('❌ 状态更新验证失败!');
|
||
}
|
||
|
||
print('\n✅ BookRepository测试完成!所有功能正常工作\n');
|
||
} catch (e) {
|
||
print('❌ BookRepository测试失败: $e\n');
|
||
}
|
||
}
|
||
|
||
///测试书架持久化
|
||
Future<void> runBookshelfRepositoryTest() async {
|
||
final bookshelfRepository = BookshelfRepository();
|
||
|
||
print('\n🧪 开始测试BookshelfRepository数据持久化功能...\n');
|
||
|
||
try {
|
||
// 1. 获取当前所有书架
|
||
List<Bookshelf> currentBookshelves =
|
||
await bookshelfRepository.getAllBookshelves();
|
||
print('📚 当前书架数量: ${currentBookshelves.length}');
|
||
|
||
// 2. 创建测试书架
|
||
final testBookshelf = Bookshelf(
|
||
id: 'test_shelf_001',
|
||
name: '我的测试书架',
|
||
createdTime: DateTime.now(),
|
||
lastModifiedTime: DateTime.now(),
|
||
bookCount: 0,
|
||
type: BookshelfType.custom,
|
||
isDefault: false,
|
||
sortOrder: 1,
|
||
);
|
||
|
||
// 3. 添加测试书架
|
||
await bookshelfRepository.addBookshelf(testBookshelf);
|
||
|
||
// 4. 再次获取所有书架
|
||
List<Bookshelf> updatedBookshelves =
|
||
await bookshelfRepository.getAllBookshelves();
|
||
print('📚 添加后书架数量: ${updatedBookshelves.length}');
|
||
|
||
print('\n✅ BookshelfRepository测试完成!所有功能正常工作\n');
|
||
} catch (e) {
|
||
print('❌ BookshelfRepository测试失败: $e\n');
|
||
}
|
||
}
|
||
|
||
///测试bookmark持久化
|
||
Future<void> runBookmarkRepositoryTest() async {
|
||
final databaseService = DatabaseService.instance;
|
||
|
||
print('\n🧪 开始测试Bookmark数据持久化功能...\n');
|
||
|
||
try {
|
||
// 1. 获取Bookmark Box
|
||
final bookmarksBox = databaseService.getBookmarksBox();
|
||
print('📚 当前书签数量: ${bookmarksBox.length}');
|
||
|
||
// 2. 创建测试书签
|
||
final testBookmark = Bookmark(
|
||
id: 'test_bookmark_001',
|
||
bookId: 'test_book_001',
|
||
chapterId: null,
|
||
title: '第一章开始',
|
||
description: '这是第一章的书签',
|
||
pageIndex: 1,
|
||
position: 0.0,
|
||
previewText: '这是书签的预览文本',
|
||
createdTime: DateTime.now(),
|
||
sortOrder: 1,
|
||
);
|
||
|
||
// 3. 添加测试书签
|
||
await bookmarksBox.put(testBookmark.id, testBookmark);
|
||
|
||
// 4. 再次获取所有书签
|
||
print('📚 添加后书签数量: ${bookmarksBox.length}');
|
||
|
||
// 5. 根据ID查找书签
|
||
Bookmark? foundBookmark = bookmarksBox.get(testBookmark.id);
|
||
if (foundBookmark != null) {
|
||
print('🔍 成功找到书签: ${foundBookmark.title}');
|
||
print(' - 所属书籍ID: ${foundBookmark.bookId}');
|
||
print(' - 页码索引: ${foundBookmark.pageIndex}');
|
||
} else {
|
||
print('❌ 未找到指定书签');
|
||
}
|
||
|
||
print('\n✅ Bookmark测试完成!所有功能正常工作\n');
|
||
} catch (e) {
|
||
print('❌ Bookmark测试失败: $e\n');
|
||
}
|
||
}
|
||
|
||
///測試Highlight持久化
|
||
Future<void> runHighlightRepositoryTest() async {
|
||
final highlightRepository = HighlightRepository();
|
||
|
||
print('\n🧪 开始测试HighlightRepository数据持久化功能...\n');
|
||
|
||
try {
|
||
// 1. 获取当前所有高亮
|
||
List<Highlight> currentHighlights =
|
||
await highlightRepository.getAllHighlights();
|
||
print('📚 当前高亮数量: ${currentHighlights.length}');
|
||
|
||
// 2. 创建测试高亮
|
||
final testHighlight = Highlight(
|
||
id: 'test_highlight_001',
|
||
bookId: 'test_book_001',
|
||
chapterId: null,
|
||
color: HighlightColor.yellow,
|
||
annotationType: AnnotationType.note,
|
||
createdTime: DateTime.now(),
|
||
selectedText: '这是一个测试高亮文本',
|
||
startIndex: 100,
|
||
endIndex: 130,
|
||
);
|
||
|
||
// 3. 添加测试高亮
|
||
await highlightRepository.addHighlight(testHighlight);
|
||
|
||
// 4. 根据ID查找高亮
|
||
Highlight? foundHighlight =
|
||
await highlightRepository.getHighlightById(testHighlight.id);
|
||
if (foundHighlight != null) {
|
||
print('🔍 成功找到高亮: ${foundHighlight.selectedText}');
|
||
print(' - 所属书籍ID: ${foundHighlight.bookId}');
|
||
print(' - 颜色: ${foundHighlight.color.name}');
|
||
} else {
|
||
print('❌ 未找到指定高亮');
|
||
}
|
||
// 5. 再次获取所有高亮
|
||
List<Highlight> updatedHighlights =
|
||
await highlightRepository.getAllHighlights();
|
||
print('📚 添加后高亮数量: ${updatedHighlights.length}');
|
||
|
||
print('\n✅ HighlightRepository测试完成!所有功能正常工作\n');
|
||
} catch (e) {
|
||
print('❌ HighlightRepository测试失败: $e\n');
|
||
}
|
||
}
|
||
|
||
class MyApp extends StatelessWidget {
|
||
const MyApp({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return MaterialApp(
|
||
title: 'Readful',
|
||
theme: ThemeData(primarySwatch: Colors.blue),
|
||
home: Scaffold(
|
||
appBar: AppBar(title: const Text('readful 电子书阅读器')),
|
||
body: const Center(
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Text('欢迎使用 readful', style: TextStyle(fontSize: 20)),
|
||
SizedBox(height: 20),
|
||
Text('✅ 数据库已初始化', style: TextStyle(color: Colors.green)),
|
||
Text('✅ BookRepository测试完成',
|
||
style: TextStyle(color: Colors.green)),
|
||
SizedBox(height: 10),
|
||
Text('请查看控制台输出查看详细测试结果'),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// Readful应用根Widget
|
||
///
|
||
/// 配置Material Design应用,包括:
|
||
/// - 主题系统(亮色/暗色模式自动跟随)
|
||
/// - 应用标题和图标
|
||
/// - 主导航页面
|
||
class ReadfulApp extends StatelessWidget {
|
||
const ReadfulApp({super.key});
|
||
|
||
// 创建测试数据
|
||
List<Book> _createTestData() {
|
||
return [
|
||
Book.create(
|
||
title: 'Flutter开发实战',
|
||
filePath: '/path/to/flutter.epub',
|
||
format: BookFormat.epub,
|
||
fileSize: 2048000,
|
||
author: '张三',
|
||
totalPages: 100,
|
||
).copyWith(
|
||
addedDate: DateTime.now().subtract(const Duration(days: 30)),
|
||
status: ReadingStatus.reading,
|
||
),
|
||
Book.create(
|
||
title: 'Dart编程语言',
|
||
filePath: '/path/to/dart.epub',
|
||
format: BookFormat.epub,
|
||
fileSize: 1536000,
|
||
author: '李四',
|
||
totalPages: 333,
|
||
).copyWith(
|
||
addedDate: DateTime.now().subtract(const Duration(days: 60)),
|
||
status: ReadingStatus.completed,
|
||
),
|
||
Book.create(
|
||
title: '移动应用设计指南',
|
||
filePath: '/path/to/design.pdf',
|
||
format: BookFormat.pdf,
|
||
fileSize: 5120000,
|
||
author: '王五',
|
||
totalPages: 1000,
|
||
tags: ['设计', '移动应用', 'UI'],
|
||
).copyWith(
|
||
addedDate: DateTime.now().subtract(const Duration(days: 10)),
|
||
status: ReadingStatus.pending,
|
||
),
|
||
Book.create(
|
||
title: '人工智能简史',
|
||
filePath: '/path/to/ai_history.epub',
|
||
format: BookFormat.epub,
|
||
fileSize: 3072000,
|
||
// author: null, // 测试作者为null的情况
|
||
totalPages: 192,
|
||
).copyWith(
|
||
addedDate: DateTime.now().subtract(const Duration(days: 5)),
|
||
status: ReadingStatus.reading,
|
||
),
|
||
Book.create(
|
||
title: '算法与数据结构',
|
||
filePath: '/path/to/algorithm.epub',
|
||
format: BookFormat.epub,
|
||
fileSize: 3072000,
|
||
author: '赵六',
|
||
totalPages: 250,
|
||
).copyWith(
|
||
addedDate: DateTime.now().subtract(const Duration(days: 20)),
|
||
status: ReadingStatus.completed,
|
||
),
|
||
];
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
// 在MaterialApp外面包裹ChangeNotifierProvider
|
||
return ChangeNotifierProvider(
|
||
create: (context) {
|
||
final searchProvider = SearchProvider();
|
||
|
||
// 加载测试数据
|
||
final testData = _createTestData();
|
||
searchProvider.loadBooks(testData);
|
||
|
||
|
||
return searchProvider;
|
||
},
|
||
child: MaterialApp(
|
||
title: 'Readful',
|
||
debugShowCheckedModeBanner: false, // 隐藏调试横幅
|
||
|
||
// 主题配置
|
||
theme: AppTheme.lightTheme, // 亮色主题
|
||
darkTheme: AppTheme.darkTheme, // 暗色主题
|
||
themeMode: ThemeMode.system, // 跟随系统主题设置
|
||
|
||
// 主页面:底部Tab导航
|
||
home: const MainNavigation(),
|
||
),
|
||
);
|
||
}
|
||
}
|