readful/lib/models/book.dart
ddshi 25b7c5ae35 feat: 实现核心数据模型设计和Flutter学习文档
主要功能:
-  完成Book模型(电子书基本信息管理)
-  完成Highlight模型(文本高亮功能)
-  建立Flutter数据模型设计标准

技术特点:
- 采用不可变对象设计模式
- 实现完整的序列化支持(toMap/fromMap)
- 提供便利的工厂构造函数
- 正确处理枚举类型序列化
- 完善的空值安全处理

学习文档:
- 📚 项目结构与环境配置指南
- 🎯 数据模型设计思路分析
- 💡 Flutter数据模型最佳实践

下一步:
- 实现Annotation批注模型
- 设计本地存储方案
- 开始UI组件开发

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 20:16:51 +08:00

237 lines
6.5 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 电子书数据模型
// 这是Flutter中数据模型的标准写法后续其他模型都可以参考这个结构
/// 书籍文件格式枚举
/// 使用enum提供类型安全的枚举值
enum BookFormat {
epub, // EPUB格式电子书
mobi, // Kindle MOBI格式
txt, // 纯文本格式
pdf // PDF格式
}
/// 阅读状态枚举
enum ReadingStatus {
reading, // 阅读中
completed, // 已完成
pending // 待阅读
}
/// 书籍数据模型
///
/// 设计原则:
/// 1. 所有字段都是final不可变性
/// 2. 提供copyWith方法创建新对象
/// 3. 提供序列化和反序列化方法
/// 4. 使用空值安全语法
class Book {
/// 唯一标识符 - 使用UUID确保全局唯一
final String id;
/// 书名 - 必需字段
final String title;
/// 作者 - 可为null因为有些书籍可能没有作者信息
final String? author;
/// 出版社 - 可选字段
final String? publisher;
/// 书籍简介 - 可选字段
final String? description;
/// 封面图片路径 - 可选字段
final String? coverImagePath;
/// 文件路径 - 必需字段,指向实际的电子书文件
final String filePath;
/// 文件格式 - 必需字段
final BookFormat format;
/// 文件大小(字节)- 必需字段
final int fileSize;
/// 添加到书架的时间 - 必需字段
final DateTime addedDate;
/// 阅读状态 - 必需字段
final ReadingStatus status;
/// 评分1-5星- 可选字段
final double? rating;
/// 标签列表 - 使用空列表作为默认值避免null指针
final List<String> tags;
/// 总页数 - 必需字段
final int totalPages;
/// 构造函数
///
/// 使用required关键字标记必需字段这是Flutter的空值安全特性
const Book({
required this.id, // 必需
required this.title, // 必需
this.author, // 可选
this.publisher, // 可选
this.description, // 可选
this.coverImagePath, // 可选
required this.filePath, // 必需
required this.format, // 必需
required this.fileSize, // 必需
required this.addedDate, // 必需
required this.status, // 必需
this.rating, // 可选
this.tags = const [], // 默认空列表避免null
required this.totalPages, // 必需
});
/// copyWith方法 - 创建对象的副本
///
/// 这是Flutter中不可变对象的标准模式
/// 当需要修改对象时,不是直接修改原对象,而是创建一个新对象
///
/// 使用示例:
/// ```dart
/// final newBook = book.copyWith(
/// title: "新书名",
/// status: ReadingStatus.completed,
/// );
/// ```
Book copyWith({
String? id,
String? title,
String? author,
String? publisher,
String? description,
String? coverImagePath,
String? filePath,
BookFormat? format,
int? fileSize,
DateTime? addedDate,
ReadingStatus? status,
double? rating,
List<String>? tags,
int? totalPages,
}) {
return Book(
id: id ?? this.id, // 如果id不为null就使用新值否则保持原值
title: title ?? this.title,
author: author ?? this.author,
publisher: publisher ?? this.publisher,
description: description ?? this.description,
coverImagePath: coverImagePath ?? this.coverImagePath,
filePath: filePath ?? this.filePath,
format: format ?? this.format,
fileSize: fileSize ?? this.fileSize,
addedDate: addedDate ?? this.addedDate,
status: status ?? this.status,
rating: rating ?? this.rating,
tags: tags ?? this.tags,
totalPages: totalPages ?? this.totalPages,
);
}
/// toMap方法 - 将对象转换为Map
///
/// 这个方法用于:
/// 1. 本地存储如Hive数据库
/// 2. 网络传输转换为JSON
/// 3. 状态保存
Map<String, dynamic> toMap() {
return {
'id': id,
'title': title,
'author': author,
'publisher': publisher,
'description': description,
'coverImagePath': coverImagePath,
'filePath': filePath,
'format': format.name, // 枚举值转换为字符串
'fileSize': fileSize,
'addedDate': addedDate.toIso8601String(), // DateTime转换为字符串
'status': status.name,
'rating': rating,
'tags': tags, // List直接存储
'totalPages': totalPages,
};
}
/// fromMap构造函数 - 从Map创建Book对象
///
/// 这是toMap的逆操作用于
/// 1. 从本地存储中读取数据
/// 2. 从网络请求中解析数据
/// 3. 从保存的状态中恢复对象
factory Book.fromMap(Map<String, dynamic> map) {
return Book(
id: map['id'],
title: map['title'],
author: map['author'],
publisher: map['publisher'],
description: map['description'],
coverImagePath: map['coverImagePath'],
filePath: map['filePath'],
format: BookFormat.values.firstWhere(
// 字符串转换回枚举
(e) => e.name == map['format'],
),
fileSize: map['fileSize'],
addedDate: DateTime.parse(map['addedDate']),
status: ReadingStatus.values.firstWhere(
(e) => e.name == map['status'],
),
rating: map['rating'],
tags: List<String>.from(map['tags'] ?? []), // 确保返回List<String>
totalPages: map['totalPages'],
);
}
/// 创建一个新Book实例的工厂方法
///
/// 这是一个便利方法用于创建基本的Book对象
/// 通常在用户导入新书籍时使用
factory Book.create({
required String title,
required String filePath,
required BookFormat format,
required int fileSize,
String? author,
int totalPages = 0,
}) {
return Book(
id: _generateId(), // 生成唯一ID
title: title,
author: author,
filePath: filePath,
format: format,
fileSize: fileSize,
totalPages: totalPages,
addedDate: DateTime.now(), // 当前时间作为添加时间
status: ReadingStatus.pending, // 默认状态为待阅读
);
}
/// 生成唯一ID的私有方法
static String _generateId() {
return '${DateTime.now().millisecondsSinceEpoch}_${(DateTime.now().microsecond).toString()}';
}
/// 重写toString方法便于调试
@override
String toString() {
return 'Book(id: $id, title: $title, author: $author, format: $format)';
}
/// 重写equals和hashCode用于对象比较
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is Book && other.id == id;
}
@override
int get hashCode => id.hashCode;
}