diff --git a/Wiki/Architecture.md b/Wiki/Architecture.md deleted file mode 100644 index 63820a3..0000000 --- a/Wiki/Architecture.md +++ /dev/null @@ -1,94 +0,0 @@ -# 整体架构 - -## 分层架构 - -``` -┌─────────────────────────────────────────────┐ -│ Pages (UI) │ lib/pages/ -│ home_page, movie_tab_page, book_tab_page, │ -│ note_tab_page, detail/form/share pages... │ -├─────────────────────────────────────────────┤ -│ Widgets (共享组件) │ lib/widgets/ -│ list_items, star_rating, drawer, bottom_nav │ -├─────────────────────────────────────────────┤ -│ Providers (状态管理) │ lib/providers/ -│ AppProvider │ -├─────────────────────────────────────────────┤ -│ Utils (业务逻辑) │ lib/utils/ -│ DAO层 │ 同步服务 │ 图片管理 │ 主题 │ 偏好设置 │ -├─────────────────────────────────────────────┤ -│ Models (数据模型) │ lib/models/ -│ Movie │ Book │ Note │ Reviews │ Posters... │ -├─────────────────────────────────────────────┤ -│ Database (SQLite) │ lib/utils/database_helper.dart -│ sqflite │ -└─────────────────────────────────────────────┘ -``` - -## 目录职责 - -### lib/models/ -数据模型定义,所有模型集中在 `data_models.dart` 中:`Movie`、`Book`、`Note`、`MovieReview`、`BookReview`、`MoviePoster`、`BookExcerpt`。每个模型提供 `fromJson` / `toJson` / `copyWith` 方法。 - -### lib/providers/ -全局状态管理,仅 `AppProvider`(ChangeNotifier)一个文件。管理所有数据列表、UI 状态、CRUD 操作。 - -### lib/utils/ -业务逻辑层: -- `database_helper.dart` — SQLite 数据库单例(版本 13) -- `movie/`、`book/`、`note/` — 各实体 DAO -- `tag/tag_dao.dart` — 标签管理 -- `sync/` — 同步服务(ServerSyncService、ServerDataService、WebDAV、AutoBackup) -- `theme/app_theme.dart` — 主题定义 -- `user_prefs.dart` — SharedPreferences 包装 -- `image_path_helper.dart` — 图片路径管理 -- `app_router.dart` — 路由生成器 - -### lib/pages/ -UI 页面,按功能域组织: -- `movies/` — 影视相关(列表、详情、表单、影评、海报墙、分享、豆瓣 WebView) -- `book/` — 书籍相关(列表、详情、表单、书评、摘抄、分享) -- `note/` — 笔记相关(列表、详情、表单、分享) -- `sync/` — 同步页面(备份、云同步、服务端同步、WebDAV) -- `markdown_reader/` — Markdown 阅读器 - -### lib/widgets/ -共享可复用 Widget:列表项、星级评分、抽屉菜单、底部导航、骨架屏等。 - -## 启动流程 - -`main.dart` 中的初始化顺序: - -``` -1. WidgetsFlutterBinding.ensureInitialized() -2. UserPrefs.init() ← SharedPreferences 初始化 -3. AppProvider() ← 创建全局状态 -4. runApp(MyApp) ← 启动 UI -5. (异步) _validateSyncOnStartup() ← 校验同步激活码 -6. (异步) appProvider.initDatabase() ← 加载数据(本地或远程) -7. (异步) appProvider.initMainTabIndex() ← 恢复用户默认标签 -8. (异步) _initAutoBackup() ← 启动自动备份(如已启用) -9. (异步) _initUsageStats() ← 启动用户统计上报 -``` - -步骤 5-9 均为 `unawaited`,不阻塞 UI 渲染。 - -## 本地/远程双模式 - -`AppProvider._useRemote` 决定数据来源: - -```dart -bool get _useRemote { - final prefs = UserPrefs(); - return prefs.syncEnabled && - prefs.syncServerUrl.isNotEmpty && - prefs.syncActivationCode.isNotEmpty && - ServerDataService.instance.isAvailable; -} -``` - -所有 DAO 方法(增删改查)在 `AppProvider` 中都有双路径: -- `_useRemote == true` → 调用 `ServerDataService`(HTTP API) -- `_useRemote == false` → 调用本地 DAO(SQLite) - -[返回首页](Home.md) diff --git a/Wiki/Data-Access-Layer.md b/Wiki/Data-Access-Layer.md deleted file mode 100644 index 3ddd620..0000000 --- a/Wiki/Data-Access-Layer.md +++ /dev/null @@ -1,81 +0,0 @@ -# 数据访问层 - -## DAO 模式 - -每个实体有独立的 DAO 类,封装 SQLite CRUD 操作: - -| DAO | 文件 | 实体 | -|-----|------|------| -| `MovieDao` | `lib/utils/movie/movie_dao.dart` | Movie | -| `BookDao` | `lib/utils/book/book_dao.dart` | Book | -| `NoteDao` | `lib/utils/note/note_dao.dart` | Note | -| `MovieReviewDao` | `lib/utils/movie/movie_review_dao.dart` | MovieReview | -| `MoviePosterDao` | `lib/utils/movie/movie_poster_dao.dart` | MoviePoster | -| `BookReviewDao` | `lib/utils/book/book_review_dao.dart` | BookReview | -| `BookExcerptDao` | `lib/utils/book/book_excerpt_dao.dart` | BookExcerpt | -| `TagDao` | `lib/utils/tag/tag_dao.dart` | Tag | - -## DatabaseHelper - -`lib/utils/database_helper.dart` — SQLite 数据库单例管理器。 - -```dart -class DatabaseHelper { - static final DatabaseHelper instance = DatabaseHelper._init(); - static Database? _database; - - Future get database; // 获取数据库实例(懒初始化) - Future reopenDatabase(); // 关闭并重新打开(WebDAV 同步后调用) - Future close(); // 关闭数据库 -} -``` - -- 数据库文件名:`mooknote.db` -- 当前版本:13 -- 初始化时执行 `_createDB`,版本不匹配时执行 `_onUpgrade` 迁移链 - -## DAO 通用方法 - -以 `MovieDao` 为例: - -```dart -getAllMovies() // 获取所有未删除记录 -getMoviesPaged({status, limit, offset}) // 分页查询 -getMoviesByStatus(status) // 按状态筛选 -getMoviesByDirector(director) // 按导演筛选 -getMoviesByActor(actor) // 按演员筛选 -insertMovie(movie) // 插入 -updateMovie(movie) // 更新 -deleteMovie(id) // 软删除 -getDeletedMovies() // 获取已删除记录 -restoreMovie(id) // 恢复 -permanentDeleteMovie(id) // 彻底删除 -``` - -## 软删除机制 - -所有实体使用 `is_deleted` 字段实现软删除: - -``` -删除操作 → UPDATE SET is_deleted = 1 WHERE id = ? -查询操作 → WHERE is_deleted = 0 -恢复操作 → UPDATE SET is_deleted = 0 WHERE id = ? -彻底删除 → DELETE FROM table WHERE id = ? -``` - -回收站页面(`lib/pages/recycle_bin_page.dart`)展示所有 `is_deleted = 1` 的记录,支持恢复和彻底删除。 - -## TagDao - -标签 DAO 管理 `tags` 表,支持: - -- `getTagsByType(type)` — 获取某类型所有标签 -- `addTag(name, type)` — 添加标签(UNIQUE 约束防重复) -- `renameTag(tagId, newName)` — 重命名标签并级联更新关联条目 -- `deleteTag(tagId, {replacementName})` — 删除标签,可选替换关联条目中的标签名 -- `deleteTagOnly(tagId)` — 仅删除标签记录,不修改关联条目 -- `getTagById(tagId)` — 获取单个标签 - -标签级联逻辑:重命名或删除标签时,会遍历关联实体表(movies.genres / books.genres / notes.tags),将旧标签名替换为新名称或移除。 - -[返回首页](Home.md) diff --git a/Wiki/Data-Models.md b/Wiki/Data-Models.md deleted file mode 100644 index f8baba3..0000000 --- a/Wiki/Data-Models.md +++ /dev/null @@ -1,203 +0,0 @@ -# 数据模型 - -所有数据模型定义在 `lib/models/data_models.dart` 中。 - -## 模型总览 - -| 模型 | 说明 | 关键字段 | -|------|------|---------| -| `Movie` | 影视条目 | title, posterPath, directors, actors, genres, rating, status | -| `Book` | 书籍条目 | title, coverPath, authors, publisher, genres, rating, status, isbn | -| `Note` | 笔记 | title, content, contentType, tags, images | -| `MovieReview` | 影评 | movieId, content, reviewer, source, reviewType (1=短评, 2=长评) | -| `BookReview` | 书评 | bookId, content, reviewer, source, reviewType | -| `MoviePoster` | 影视海报 | movieId, posterPath | -| `BookExcerpt` | 书籍摘抄 | bookId, chapter, content, comment | - -## 公共字段 - -所有实体模型都包含: -- `id` — UUID 字符串(创建时生成) -- `isDeleted` — 软删除标记(0/1) -- `createdAt` / `updatedAt` — ISO 8601 时间戳(UTC) - -## copyWith 模式 - -模型使用 `_CopyWithNullSentinel` 区分"未传参"和"传了 null": - -```dart -class _CopyWithNullSentinel { - const _CopyWithNullSentinel(); -} -const _copyWithNull = _CopyWithNullSentinel(); - -// 用法示例 -Movie copyWith({ - Object? posterPath = _copyWithNull, // 未传参时保留原值 - Object? summary = _copyWithNull, // 传 null 时清除值 -}) { ... } -``` - -这样 `movie.copyWith(title: '新标题')` 不会意外清空 `posterPath`。 - -## JSON 编码列表字段 - -`directors`、`actors`、`genres`、`tags` 等列表字段在 SQLite 中存储为 JSON 字符串: - -```dart -// 序列化 -'genres': jsonEncode(['科幻', '动作']) - -// 反序列化 -static List parseStringList(dynamic data) { - if (data == null) return []; - if (data is List) return data.map((e) => e.toString()).toList(); - if (data is String) { - final decoded = jsonDecode(data); - if (decoded is List) return decoded.map((e) => e.toString()).toList(); - } - return []; -} -``` - -## 状态值 - -### 影视状态 (Movie.status) -| 值 | 含义 | -|----|------| -| `want_to_watch` | 想看 | -| `watching` | 在看 | -| `watched` | 已看 | - -### 书籍状态 (Book.status) -| 值 | 含义 | -|----|------| -| `want_to_read` | 想读 | -| `reading` | 在读 | -| `read` | 已读 | - -## SQLite 表结构 - -数据库文件:`mooknote.db`,当前版本:13 - -### movies 表 - -| 列名 | 类型 | 说明 | -|------|------|------| -| id | TEXT PK | UUID | -| title | TEXT NOT NULL | 标题 | -| poster_path | TEXT | 海报本地路径 | -| release_date | TEXT | 上映日期 | -| directors | TEXT | JSON 数组 | -| writers | TEXT | JSON 数组 | -| actors | TEXT | JSON 数组 | -| genres | TEXT | JSON 数组 | -| alternate_titles | TEXT | JSON 数组 | -| summary | TEXT | 剧情简介 | -| rating | REAL | 评分 1-10 | -| status | TEXT NOT NULL | 状态 | -| watch_date | TEXT | 观看日期 | -| created_at | TEXT NOT NULL | 创建时间 | -| updated_at | TEXT NOT NULL | 更新时间 | -| is_deleted | INTEGER DEFAULT 0 | 软删除 | - -### books 表 - -| 列名 | 类型 | 说明 | -|------|------|------| -| id | TEXT PK | UUID | -| title | TEXT NOT NULL | 标题 | -| cover_path | TEXT | 封面本地路径 | -| authors | TEXT | JSON 数组 | -| alternate_titles | TEXT | JSON 数组 | -| publisher | TEXT | 出版社 | -| genres | TEXT | JSON 数组 | -| summary | TEXT | 简介 | -| rating | REAL | 评分 1-10 | -| status | TEXT NOT NULL | 状态 | -| isbn | TEXT | ISBN | -| publish_date | TEXT | 出版日期 | -| created_at | TEXT NOT NULL | 创建时间 | -| updated_at | TEXT NOT NULL | 更新时间 | -| is_deleted | INTEGER DEFAULT 0 | 软删除 | - -### notes 表 - -| 列名 | 类型 | 说明 | -|------|------|------| -| id | TEXT PK | UUID | -| title | TEXT DEFAULT '' | 标题 | -| content | TEXT NOT NULL | 内容 | -| content_type | TEXT DEFAULT 'markdown' | 内容类型 | -| tags | TEXT | JSON 数组 | -| images | TEXT | JSON 数组(图片路径列表)| -| created_at | TEXT NOT NULL | 创建时间 | -| updated_at | TEXT NOT NULL | 更新时间 | -| is_deleted | INTEGER DEFAULT 0 | 软删除 | - -### movie_reviews 表 - -| 列名 | 类型 | 说明 | -|------|------|------| -| id | TEXT PK | UUID | -| movie_id | TEXT NOT NULL FK | 关联影视 ID | -| content | TEXT NOT NULL | 评论内容 | -| reviewer | TEXT | 评论者 | -| source | TEXT | 来源 | -| review_type | INTEGER DEFAULT 1 | 1=短评, 2=长评 | -| is_deleted | INTEGER DEFAULT 0 | 软删除 | -| created_at / updated_at | TEXT | 时间戳 | - -### book_reviews 表(结构同 movie_reviews,外键为 book_id) - -### movie_posters 表 - -| 列名 | 类型 | 说明 | -|------|------|------| -| id | TEXT PK | UUID | -| movie_id | TEXT NOT NULL FK | 关联影视 ID | -| poster_path | TEXT NOT NULL | 海报本地路径 | -| is_deleted | INTEGER DEFAULT 0 | 软删除 | -| created_at | TEXT | 创建时间 | - -### book_excerpts 表 - -| 列名 | 类型 | 说明 | -|------|------|------| -| id | TEXT PK | UUID | -| book_id | TEXT NOT NULL FK | 关联书籍 ID | -| chapter | TEXT | 章节 | -| content | TEXT NOT NULL | 摘抄内容 | -| comment | TEXT | 感悟评论 | -| is_deleted | INTEGER DEFAULT 0 | 软删除 | -| created_at / updated_at | TEXT | 时间戳 | - -### tags 表 - -| 列名 | 类型 | 说明 | -|------|------|------| -| id | TEXT PK | UUID | -| name | TEXT NOT NULL | 标签名 | -| type | TEXT NOT NULL | movie_genre / book_genre / note_tag | -| created_at | TEXT NOT NULL | 创建时间 | -| UNIQUE | (name, type) | 同类型标签名唯一 | - -## 数据库迁移链 - -| 版本 | 变更 | -|------|------| -| v1 | 初始版本 | -| v2 | 升级 movies 表结构(添加 directors, writers, actors, genres 等字段)| -| v3 | 升级 books 表结构(拆分 author → authors, 添加 alternateTitles, publisher, genres)| -| v4 | 重建 notes 表(添加 content_type 字段)| -| v5 | 创建 movie_reviews 和 movie_posters 表 | -| v6 | 为 notes 表添加 is_deleted 字段 | -| v7 | 创建 book_reviews 和 book_excerpts 表 | -| v8 | 确保 book_reviews/book_excerpts 存在(兼容修复)| -| v9 | 为 notes 表添加 images 字段 | -| v10 | 为 movies 表添加 watch_date 字段 | -| v11 | 为 books 表添加 isbn 和 publish_date 字段 | -| v12 | 确保 notes 表有 title 列 | -| v13 | 创建 tags 表并回填已有数据 | - -[返回首页](Home.md) diff --git a/Wiki/Getting-Started.md b/Wiki/Getting-Started.md deleted file mode 100644 index 6f6123e..0000000 --- a/Wiki/Getting-Started.md +++ /dev/null @@ -1,102 +0,0 @@ -# 快速开始 - -## 环境要求 - -- **Flutter SDK** 3.5.0+ -- **Android Studio**(含 Android SDK Platform API 33+) -- **Android SDK Build-Tools** -- Android 真机(USB 调试)或模拟器 - -## 安装步骤 - -### 1. 克隆项目 - -```bash -cd mooknote -``` - -### 2. 安装依赖 - -```bash -flutter pub get -``` - -如果网络问题导致失败,使用国内镜像: - -```powershell -$env:PUB_HOSTED_URL="https://pub.flutter-io.cn" -$env:FLUTTER_STORAGE_BASE_URL="https://storage.flutter-io.cn" -flutter pub get -``` - -或设置代理: - -```powershell -$env:HTTP_PROXY="http://127.0.0.1:10808" -$env:HTTPS_PROXY="http://127.0.0.1:10808" -flutter pub get -``` - -### 3. 连接设备 - -- 连接 Android 真机(需开启 USB 调试) -- 或启动 Android 模拟器 - -### 4. 运行应用 - -```bash -flutter run -``` - -## 常用命令 - -```bash -# 安装依赖 -flutter pub get - -# 运行应用 -flutter run - -# 构建 release APK -flutter build apk --release - -# 构建 App Bundle(推荐用于 Google Play) -flutter build appbundle --release - -# 清理构建缓存 -flutter clean - -# 静态分析 -flutter analyze -``` - -## 服务端部署(可选) - -服务端用于用户统计和数据同步,位于 `server/` 目录。 - -```bash -cd server -pip install -r requirements.txt -python app.py -``` - -默认监听 `0.0.0.0:27047`,可通过环境变量 `PORT` 修改端口。 - -生产环境推荐 gunicorn: - -```bash -pip install gunicorn -gunicorn -w 2 -b 0.0.0.0:27047 app:app -``` - -## 用户统计服务器配置 - -在 `lib/utils/usage_stats_service.dart` 中配置统计服务器地址: - -```dart -static String serverUrl = 'http://192.168.31.48:5000'; -``` - -置空则禁用用户统计功能。 - -[返回首页](Home.md) diff --git a/Wiki/Home.md b/Wiki/Home.md deleted file mode 100644 index 7f6b2b7..0000000 --- a/Wiki/Home.md +++ /dev/null @@ -1,38 +0,0 @@ -# MookNote Wiki - -极简风格的观影阅读笔记应用,基于 Flutter 框架开发。 - -## 功能概览 - -- **影视管理** — 增删改查、影评、海报墙、豆瓣链接爬取 -- **书籍管理** — 增删改查、书评、摘抄 -- **笔记管理** — Markdown 笔记、标签、图片 -- **数据同步** — 服务端实时同步、WebDAV 同步、自动本地备份 -- **回收站** — 软删除 + 恢复/彻底删除 -- **标签管理** — 影视类型、书籍类型、笔记标签统一管理 -- **统计分析** — 观影/阅读数据统计 - -## 技术栈 - -| 层级 | 技术 | -|------|------| -| 框架 | Flutter 3.5+, Dart | -| 状态管理 | Provider (ChangeNotifier) | -| 本地存储 | SQLite (sqflite) | -| 远程同步 | HTTP API (http 包) | -| 后端 | Python Flask | -| 主题 | Material 3, 极简黑白灰 | - -## Wiki 目录 - -- [快速开始](Getting-Started.md) — 环境配置、安装与运行 -- [整体架构](Architecture.md) — 分层架构、目录结构、启动流程 -- [数据模型](Data-Models.md) — 模型定义、数据库表结构、迁移链 -- [状态管理](State-Management.md) — AppProvider、Tab 状态、分页加载 -- [数据访问层](Data-Access-Layer.md) — DAO 模式、软删除、回收站 -- [图片存储](Image-Storage.md) — 图片路径管理、存储结构 -- [同步系统](Sync-System.md) — 服务端同步、WebDAV、自动备份 -- [服务端 API](Server-API.md) — Flask 后端、API 端点、激活码机制 -- [页面与路由](Pages-and-Navigation.md) — 页面结构、路由表、过渡动画 -- [主题与 UI](Theme-and-UI.md) — 主题配置、共享组件 -- [用户偏好](User-Preferences.md) — SharedPreferences 配置项 diff --git a/Wiki/Image-Storage.md b/Wiki/Image-Storage.md deleted file mode 100644 index 120301d..0000000 --- a/Wiki/Image-Storage.md +++ /dev/null @@ -1,84 +0,0 @@ -# 图片存储 - -## ImagePathHelper - -`lib/utils/image_path_helper.dart` — 单例图片路径管理器。 - -```dart -class ImagePathHelper { - static final ImagePathHelper instance = ImagePathHelper._init(); -} -``` - -## 存储目录结构 - -图片存储在应用文档目录下: - -``` -{appDocumentsDir}/images/ -├── movies/ -│ └── {movieId}/ -│ ├── poster.jpg ← 影视海报 -│ └── posterimgs/ ← 海报墙图片 -│ ├── img1.jpg -│ └── img2.jpg -├── books/ -│ └── {bookId}/ -│ └── cover.jpg ← 书籍封面 -└── notes/ - └── {noteId}/ - ├── img1.jpg ← 笔记图片 - └── img2.jpg -``` - -## 路径方法 - -### 影视 - -```dart -getMovieImagesDir(movieId) → images/movies/{movieId}/ -getMoviePosterPath(movieId, file) → images/movies/{movieId}/{file} -getMoviePosterImgsDir(movieId) → images/movies/{movieId}/posterimgs/ -getMoviePosterImgPath(movieId, file)→ images/movies/{movieId}/posterimgs/{file} -``` - -### 书籍 - -```dart -getBookImagesDir(bookId) → images/books/{bookId}/ -getBookCoverPath(bookId, file) → images/books/{bookId}/{file} -``` - -### 笔记 - -```dart -getNoteImagesDir(noteId) → images/notes/{noteId}/ -getNoteImagePath(noteId, file) → images/notes/{noteId}/{file} -``` - -## 文件操作 - -```dart -ensureDirExists(dirPath) // 确保目录存在 -moveFile(src, targetDir, fileName) // 移动文件 -copyFile(src, targetDir, fileName) // 复制文件 -deleteFile(filePath) // 删除单个文件 -deleteMovieImages(movieId) // 删除影视所有图片目录 -deleteBookImages(bookId) // 删除书籍所有图片目录 -deleteNoteImages(noteId) // 删除笔记所有图片目录 -``` - -## 远程图片同步 - -当 `_useRemote == true` 时,CRUD 操作会自动调用 `ServerDataService.uploadLocalImages(paths)` 上传本地图片到服务端: - -```dart -// AppProvider 中 -await _uploadImagesIfRemote([movie.posterPath]); -``` - -## 图片显示 - -`FadeInLocalImage` Widget(`lib/widgets/fade_in_local_image.dart`)处理本地图片的渐入显示。 - -[返回首页](Home.md) diff --git a/Wiki/Pages-and-Navigation.md b/Wiki/Pages-and-Navigation.md deleted file mode 100644 index 0f2f3d9..0000000 --- a/Wiki/Pages-and-Navigation.md +++ /dev/null @@ -1,100 +0,0 @@ -# 页面与路由 - -## 页面结构 - -### 主页框架 - -``` -HomePage -├── CustomDrawer (侧边菜单) -├── PageView -│ ├── MainContentPage (主内容) -│ │ ├── MovieTabPage (观影列表, mainTabIndex=0) -│ │ ├── BookTabPage (阅读列表, mainTabIndex=1) -│ │ └── NoteTabPage (笔记列表, mainTabIndex=2) -│ └── ProfilePage (我的, bottomNavIndex=2) -└── BottomNavBar (底部导航) -``` - -底部导航切换 `bottomNavIndex`: -- 0 → 主内容页(MainContentPage) -- 1 → 新增页(根据当前 mainTabIndex 跳转对应表单) -- 2 → 个人页(ProfilePage) - -### 影视模块页面 - -| 页面 | 文件 | 说明 | -|------|------|------| -| MovieTabPage | `pages/movies/movie_tab_page.dart` | 影视列表(按状态筛选)| -| MovieFormPage | `pages/movies/movie_form_page.dart` | 新增/编辑影视 | -| MovieDetailPage | `pages/movies/movie_detail_page.dart` | 影视详情 | -| MovieReviewsPage | `pages/movies/movie_reviews_page.dart` | 影评列表 | -| MovieReviewFormPage | `pages/movies/movie_review_form_page.dart` | 新增/编辑影评 | -| MovieReviewDetailPage | `pages/movies/movie_review_detail_page.dart` | 影评详情 | -| MoviePostersPage | `pages/movies/movie_posters_page.dart` | 海报墙 | -| PosterGalleryPage | `pages/movies/poster_gallery_page.dart` | 海报画廊浏览 | -| MovieSharePage | `pages/movies/movie_share_page.dart` | 影视分享 | -| DoubanWebViewPage | `pages/movies/douban_webview_page.dart` | 豆瓣 WebView | - -### 书籍模块页面 - -| 页面 | 文件 | 说明 | -|------|------|------| -| BookTabPage | `pages/book/book_tab_page.dart` | 书籍列表 | -| BookFormPage | `pages/book/book_form_page.dart` | 新增/编辑书籍 | -| BookDetailPage | `pages/book/book_detail_page.dart` | 书籍详情 | -| BookReviewsPage | `pages/book/book_reviews_page.dart` | 书评列表 | -| BookReviewFormPage | `pages/book/book_review_form_page.dart` | 新增/编辑书评 | -| BookReviewDetailPage | `pages/book/book_review_detail_page.dart` | 书评详情 | -| BookExcerptsPage | `pages/book/book_excerpts_page.dart` | 摘抄列表 | -| BookExcerptFormPage | `pages/book/book_excerpt_form_page.dart` | 新增/编辑摘抄 | -| BookSharePage | `pages/book/book_share_page.dart` | 书籍分享 | - -### 笔记模块页面 - -| 页面 | 文件 | 说明 | -|------|------|------| -| NoteTabPage | `pages/note/note_tab_page.dart` | 笔记列表 | -| NoteFormPage | `pages/note/note_form_page.dart` | 新增/编辑笔记 | -| NoteDetailPage | `pages/note/note_detail_page.dart` | 笔记详情 | -| NoteSharePage | `pages/note/note_share_page.dart` | 笔记分享 | - -### 其他页面 - -| 页面 | 文件 | 说明 | -|------|------|------| -| HomePage | `pages/home_page.dart` | 主页框架 | -| MainContentPage | `pages/main_content_page.dart` | 主内容区域 | -| ProfilePage | `pages/profile_page.dart` | 个人页 | -| SearchPage | `pages/search_page.dart` | 全局搜索 | -| StatisticsPage | `pages/statistics_page.dart` | 统计分析 | -| StrollPage | `pages/stroll_page.dart` | 浏览/发现 | -| RecycleBinPage | `pages/recycle_bin_page.dart` | 回收站 | -| TagManagementPage | `pages/tag_management_page.dart` | 标签管理 | -| AppIconPickerPage | `pages/app_icon_picker_page.dart` | 应用图标选择 | -| BackupPage | `pages/sync/backup_page.dart` | 本地备份 | -| CloudSyncPage | `pages/sync/cloud_sync_page.dart` | 云同步 | -| ServerSyncPage | `pages/sync/server_sync_page.dart` | 服务端同步配置 | -| WebDAVSyncPage | `pages/sync/webdav_sync_page.dart` | WebDAV 同步 | -| MdReaderTabPage | `pages/markdown_reader/md_reader_tab_page.dart` | Markdown 阅读器目录 | -| MdViewerPage | `pages/markdown_reader/md_viewer_page.dart` | Markdown 查看器 | - -## 路由 - -`AppRouter`(`lib/utils/app_router.dart`)使用 `onGenerateRoute` 生成路由: - -| 路由名 | 参数 | 目标页面 | -|--------|------|---------| -| `/movie-form` | `Movie?` 或 `{initialStatus}` | MovieFormPage | -| `/book-form` | `Book?` 或 `{initialStatus}` | BookFormPage | -| `/note-form` | `Note?` | NoteFormPage | -| `/movie-detail` | `Movie` | MovieDetailPage | -| `/book-detail` | `Book` | BookDetailPage | -| `/note-detail` | `Note` | NoteDetailPage | -| `/douban-webview` | `String` (URL) | DoubanWebViewPage | - -## 过渡动画 - -所有路由使用 `SlideUpPageRoute`(`lib/utils/slide_up_page_route.dart`),实现从底部滑入的过渡效果。 - -[返回首页](Home.md) diff --git a/Wiki/Server-API.md b/Wiki/Server-API.md deleted file mode 100644 index cb0c517..0000000 --- a/Wiki/Server-API.md +++ /dev/null @@ -1,117 +0,0 @@ -# 服务端 API - -## 概述 - -服务端为 Python Flask 应用,位于 `server/` 目录。提供用户统计、数据同步和管理后台功能。 - -## 模块结构 - -| 文件 | 职责 | -|------|------| -| `app.py` | 入口,注册所有路由模块 | -| `config.py` | 配置(JWT 密钥、数据库路径、备份目录)| -| `database.py` | 数据库初始化与管理(SQLite)| -| `auth.py` | 认证路由(激活码验证)| -| `admin_api.py` | 管理后台 API | -| `sync_api.py` | 同步 API(文件上传/下载、心跳)| -| `data_api.py` | 数据 CRUD API(影视/书籍/笔记/标签/图片)| -| `web_ui.py` | Web 管理界面 | -| `static/` | 静态资源 | -| `requirements.txt` | Python 依赖 | - -## API 端点 - -### 认证 - -| 方法 | 路径 | 说明 | -|------|------|------| -| POST | `/api/activate` | 校验激活码 | - -请求体:`{"code": "激活码", "device_id": "设备标识"}` - -响应:`{"valid": true, "expires_at": "...", "is_permanent": false}` - -### 数据 CRUD - -所有请求需在 body 中附带 `code` 字段。 - -| 方法 | 路径 | 说明 | -|------|------|------| -| POST | `/api/data/movies` | 获取影视列表(支持 status/limit/offset)| -| POST | `/api/data/movie/save` | 保存影视(新建/更新)| -| POST | `/api/data/movie/delete` | 删除影视 | -| POST | `/api/data/books` | 获取书籍列表 | -| POST | `/api/data/book/save` | 保存书籍 | -| POST | `/api/data/book/delete` | 删除书籍 | -| POST | `/api/data/notes` | 获取笔记列表 | -| POST | `/api/data/note/save` | 保存笔记 | -| POST | `/api/data/note/delete` | 删除笔记 | -| POST | `/api/data/movie_reviews` | 获取影评 | -| POST | `/api/data/movie_review/save` | 保存影评 | -| POST | `/api/data/movie_review/delete` | 删除影评 | -| POST | `/api/data/movie_posters` | 获取海报 | -| POST | `/api/data/movie_poster/save` | 保存海报 | -| POST | `/api/data/movie_poster/delete` | 删除海报 | -| POST | `/api/data/book_reviews` | 获取书评 | -| POST | `/api/data/book_review/save` | 保存书评 | -| POST | `/api/data/book_review/delete` | 删除书评 | -| POST | `/api/data/book_excerpts` | 获取摘抄 | -| POST | `/api/data/book_excerpt/save` | 保存摘抄 | -| POST | `/api/data/book_excerpt/delete` | 删除摘抄 | -| POST | `/api/data/tags` | 获取标签 | -| POST | `/api/data/tag/save` | 保存标签 | -| POST | `/api/data/tag/delete` | 删除标签 | - -### 同步 - -| 方法 | 路径 | 说明 | -|------|------|------| -| POST | `/api/heartbeat` | 设备心跳上报 | -| POST | `/api/sync/upload` | 上传数据库/图片文件 | -| POST | `/api/sync/download` | 下载数据库/图片文件 | - -### 图片 - -| 方法 | 路径 | 说明 | -|------|------|------| -| POST | `/api/data/image/upload` | 上传图片 | -| GET | `/api/data/image/{code}/{path}` | 获取图片 | - -## 激活码机制 - -每个激活码对应一个独立的 SQLite 数据库: - -``` -server/backups/{CODE}/ -├── database/ -│ └── mooknote.db ← 该激活码的数据 -├── images/ ← 图片文件 -└── avatars/ ← 头像文件 -``` - -激活码校验通过 `admin_api.py` 中的 `_verify_code()` 函数完成,支持有效期和永久有效两种模式。 - -## 服务端数据库 - -服务端自身使用 `stats.db` 存储设备信息和心跳日志: - -| 表 | 说明 | -|----|------| -| `devices` | 设备注册(device_hash, first_seen, last_seen)| -| `heartbeat_logs` | 心跳日志(device_hash, ip, device_type, device_name)| -| `changelog` | 更新日志 | - -每个激活码的数据存储在独立的 `backups/{code}/database/mooknote.db` 中,表结构与客户端一致。 - -## 启动 - -```bash -cd server -python app.py # 开发环境(waitress) -# 或 -gunicorn -w 2 -b 0.0.0.0:27047 app:app # 生产环境 -``` - -默认端口:27047(可通过环境变量 `PORT` 修改)。 - -[返回首页](Home.md) diff --git a/Wiki/State-Management.md b/Wiki/State-Management.md deleted file mode 100644 index a86e94d..0000000 --- a/Wiki/State-Management.md +++ /dev/null @@ -1,111 +0,0 @@ -# 状态管理 - -## AppProvider - -`AppProvider`(`lib/providers/app_provider.dart`)是应用唯一的 ChangeNotifier,通过 `MultiProvider` 在根节点注入,所有页面通过 `context.watch()` 或 `context.read()` 访问。 - -## 管理的状态 - -### 数据列表 - -| 字段 | 类型 | 说明 | -|------|------|------| -| `_movies` | `List` | 影视数据 | -| `_books` | `List` | 书籍数据 | -| `_notes` | `List` | 笔记数据 | - -### UI 状态 - -| 字段 | 类型 | 说明 | -|------|------|------| -| `_mainTabIndex` | `int` | 主标签页(0=观影, 1=阅读, 2=笔记)| -| `_bottomNavIndex` | `int` | 底部导航(0=主页, 1=新增, 2=我的)| -| `_bottomNavVisible` | `bool` | 底部导航栏可见性 | -| `_themeMode` | `ThemeMode` | 主题模式 | -| `_movieStatusIndex` | `int` | 影视状态筛选(0=已看, 1=想看, 2=在看)| -| `_bookStatusIndex` | `int` | 书籍状态筛选(0=读完, 1=在读, 2=准备读)| -| `_drawerOpen` | `bool` | 侧边菜单状态 | - -### DAO 实例 - -```dart -final MovieDao _movieDao = MovieDao(); -final BookDao _bookDao = BookDao(); -final NoteDao _noteDao = NoteDao(); -final MovieReviewDao _reviewDao = MovieReviewDao(); -final MoviePosterDao _posterDao = MoviePosterDao(); -final BookReviewDao _bookReviewDao = BookReviewDao(); -final BookExcerptDao _bookExcerptDao = BookExcerptDao(); -final TagDao _tagDao = TagDao(); -``` - -## 数据加载 - -### 全量加载 - -```dart -initDatabase() → loadMovies() + loadBooks() + loadNotes() -``` - -根据 `_useRemote` 决定从本地 SQLite 或远程 API 加载。 - -### 分页加载 - -分页大小:`_pageSize = 20` - -```dart -loadMoviesPaged({String? status, required int offset}) -loadBooksPaged({String? status, required int offset}) -loadNotesPaged({required int offset}) -``` - -供列表页触底加载使用。 - -## CRUD 操作 - -所有 CRUD 方法都走双路径(本地/远程),并自动重新加载数据: - -```dart -addMovie(Movie movie) → 本地: _movieDao.insertMovie / 远程: ServerDataService.saveMovie → loadMovies() -updateMovie(Movie movie) → 本地: _movieDao.updateMovie / 远程: ServerDataService.saveMovie → loadMovies() -removeMovie(String id) → 本地: _movieDao.deleteMovie / 远程: ServerDataService.deleteMovie → loadMovies() -``` - -Book、Note 同理。远程模式下还会自动上传关联图片。 - -## 子实体操作 - -影评、海报、书评、摘抄的 CRUD 方法不直接修改内存列表,而是按需查询: - -```dart -getMovieReviews(movieId) / addMovieReview(review) / updateMovieReview(review) / removeMovieReview(id) -getMoviePosters(movieId) / addMoviePoster(poster) / removeMoviePoster(id) -getBookReviews(bookId) / addBookReview(review) / updateBookReview(review) / removeBookReview(id) -getBookExcerpts(bookId) / addBookExcerpt(excerpt) / updateBookExcerpt(excerpt) / removeBookExcerpt(id) -``` - -## 回收站 - -```dart -getDeletedMovies() / restoreMovie(id) / permanentDeleteMovie(id) -getDeletedBooks() / restoreBook(id) / permanentDeleteBook(id) -getDeletedNotes() / restoreNote(id) / permanentDeleteNote(id) -clearRecycleBin() // 清空所有已删除条目 -``` - -永久删除时会同时清理关联图片目录。 - -## 标签管理 - -```dart -getTags(type) // 获取某类型标签 -addTag(name, type) // 添加标签 -renameTag(tagId, newName, type) // 重命名(级联更新关联条目) -deleteTag(tagId, type, {replacementName}) // 删除(可替换关联条目的标签名) -deleteTagOnly(tagId, type) // 仅删除标签本身,不影响条目 -syncTagsFromData() // 从现有数据回填标签表 -``` - -标签类型:`movie_genre`、`book_genre`、`note_tag`。 - -[返回首页](Home.md) diff --git a/Wiki/Sync-System.md b/Wiki/Sync-System.md deleted file mode 100644 index 7cd41c8..0000000 --- a/Wiki/Sync-System.md +++ /dev/null @@ -1,93 +0,0 @@ -# 同步系统 - -MookNote 支持三种数据同步方式。 - -## 1. 服务端实时同步 - -通过激活码连接远程服务器,数据实时双向同步。 - -### 核心服务 - -#### ServerDataService (`lib/utils/sync/server_data_service.dart`) - -远程 API 数据操作层,所有方法通过 HTTP POST 调用服务端接口: - -```dart -// 影视 -getMovies({status, limit, offset}) → POST /api/data/movies -saveMovie(movie) → POST /api/data/movie/save -deleteMovie(id) → POST /api/data/movie/delete - -// 书籍、笔记、影评、海报、书评、摘抄、标签同理 -``` - -每个请求都附带 `code`(激活码)进行身份验证。 - -#### ServerSyncService (`lib/utils/sync/server_sync_service.dart`) - -同步协调服务: - -- `checkActivation()` — 校验激活码有效性 -- `syncWithServer()` — 智能合并本地与服务端数据 -- `downloadToLocal()` — 从服务端下载数据到本地(关闭同步时调用) - -### 启动时校验流程 - -``` -1. 检查 syncEnabled、syncServerUrl、syncActivationCode 是否已配置 -2. 调用 /api/activate 校验激活码 -3. 有效 → 更新有效期信息,继续同步模式 -4. 无效/过期 → 调用 downloadToLocal() 下载数据 → 关闭同步开关 -``` - -### 双模式切换 - -`AppProvider._useRemote` 控制: - -```dart -bool get _useRemote { - return prefs.syncEnabled && - prefs.syncServerUrl.isNotEmpty && - prefs.syncActivationCode.isNotEmpty && - ServerDataService.instance.isAvailable; -} -``` - -## 2. WebDAV 同步 - -通过 WebDAV 协议同步数据库文件和图片。 - -相关页面:`lib/pages/sync/webdav_sync_page.dart` -服务类:`lib/utils/sync/webdav_service.dart` - -## 3. 自动本地备份 - -### AutoBackupService (`lib/utils/sync/auto_backup_service.dart`) - -定时自动备份到设备下载目录: - -- **间隔**:5 分钟 -- **保留数量**:最近 5 个备份文件 -- **备份目录**:`Download/mooknote/`(Android)或 `Documents/mooknote/`(iOS) -- **文件格式**:`auto_backup_{yyyyMMdd_HHmmss}.zip` - -### BackupService (`lib/utils/sync/backup_service.dart`) - -手动备份与恢复服务,导出数据为 ZIP 文件(包含数据库和图片)。 - -相关页面:`lib/pages/sync/backup_page.dart` - -## 同步相关配置 - -在 `UserPrefs` 中: - -| 键 | 说明 | -|----|------| -| `syncServerUrl` | 服务器地址 | -| `syncActivationCode` | 激活码 | -| `syncExpiresAt` | 激活码有效期 | -| `syncIsPermanent` | 是否永久有效 | -| `syncEnabled` | 同步开关 | -| `syncLastEntryId` | 上次同步的 entry ID | - -[返回首页](Home.md) diff --git a/Wiki/Theme-and-UI.md b/Wiki/Theme-and-UI.md deleted file mode 100644 index 95f2247..0000000 --- a/Wiki/Theme-and-UI.md +++ /dev/null @@ -1,75 +0,0 @@ -# 主题与 UI - -## AppTheme - -`lib/utils/theme/app_theme.dart` — 极简主义黑白灰主题配置。 - -### 颜色体系 - -#### 中性色板 - -| 颜色 | 值 | 用途 | -|------|------|------| -| `_black` | `#1A1A1A` | 主要文字、主色 | -| `_darkGray` | `#333333` | 次要文字 | -| `_gray` | `#666666` | 辅助文字 | -| `_lightGray` | `#999999` | 占位文字 | -| `_lighterGray` | `#E5E5E5` | 分割线、边框 | -| `_offWhite` | `#F5F5F5` | 背景色 | -| `_white` | `#FFFFFF` | 主背景 | - -#### 强调色 - -| 颜色 | 值 | 用途 | -|------|------|------| -| `accent` | `#0066FF` | 关键操作高亮 | -| `error` | `#DC2626` | 错误状态 | - -#### 状态颜色 - -| 状态 | 颜色 | 说明 | -|------|------|------| -| `watched` / `readColor` | `#1A1A1A` | 已看/已读(纯黑)| -| `watching` / `readingColor` | `#666666` | 在看/在读(中灰)| -| `wantToWatch` / `wantToReadColor` | `#999999` | 想看/想读(浅灰)| - -### Material 3 配置 - -```dart -ThemeData( - useMaterial3: true, - brightness: Brightness.light/dark, - scaffoldBackgroundColor: _white/_black, - colorScheme: ColorScheme.light/dark(...), - fontFamily: 'Inter', -) -``` - -提供 `lightTheme` 和 `darkTheme` 两套主题。 - -### 字体 - -- 字体族:`Inter` -- 字重:`w400`(Regular)、`w500`(Medium)、`w600`(Semibold) - -## 共享 Widget - -| Widget | 文件 | 说明 | -|--------|------|------| -| `MovieListItem` | `widgets/movie_list_item.dart` | 影视列表项 | -| `BookListItem` | `widgets/book_list_item.dart` | 书籍列表项 | -| `NoteListItem` | `widgets/note_list_item.dart` | 笔记列表项 | -| `MovieStatusBar` | `widgets/movie_status_bar.dart` | 影视状态筛选栏 | -| `BookStatusBar` | `widgets/book_status_bar.dart` | 书籍状态筛选栏 | -| `AnimatedStarRating` | `widgets/animated_star_rating.dart` | 动画星级评分 | -| `BottomNavBar` | `widgets/bottom_nav_bar.dart` | 底部导航栏 | -| `CustomDrawer` | `widgets/custom_drawer.dart` | 侧边抽屉菜单 | -| `FadeInLocalImage` | `widgets/fade_in_local_image.dart` | 本地图片渐入显示 | -| `AppRefreshIndicator` | `widgets/app_refresh_indicator.dart` | 自定义下拉刷新 | -| `ShimmerSkeleton` | `widgets/shimmer_skeleton.dart` | 骨架屏加载效果 | - -## 系统 UI - -`MyApp` 在 `main.dart` 中通过 `SystemChrome.setSystemUIOverlayStyle` 控制状态栏和导航栏样式,随主题模式自动切换亮/暗色。 - -[返回首页](Home.md) diff --git a/Wiki/User-Preferences.md b/Wiki/User-Preferences.md deleted file mode 100644 index 08efdac..0000000 --- a/Wiki/User-Preferences.md +++ /dev/null @@ -1,94 +0,0 @@ -# 用户偏好 - -## UserPrefs - -`lib/utils/user_prefs.dart` — SharedPreferences 单例包装器。 - -```dart -class UserPrefs { - static final UserPrefs _instance = UserPrefs._internal(); - factory UserPrefs() => _instance; -} -``` - -使用前必须调用 `UserPrefs.init()`(在 `main.dart` 中完成)。 - -## 配置项列表 - -### 用户信息 - -| 键 | 类型 | 默认值 | 说明 | -|----|------|--------|------| -| `nickname` | String | `'Mook'` | 昵称 | -| `motto` | String | `'好运不会眷顾一无所有之人。'` | 座右铭 | -| `avatarPath` | String? | `null` | 头像本地路径 | - -### 应用设置 - -| 键 | 类型 | 默认值 | 说明 | -|----|------|--------|------| -| `themeMode` | int | `0` | 0=跟随系统, 1=浅色, 2=深色 | -| `showExactReleaseDate` | bool | `true` | 上映日期显示到日/月 | -| `isFirstLaunch` | bool | `true` | 是否首次启动 | - -### 主界面显示 - -| 键 | 类型 | 默认值 | 说明 | -|----|------|--------|------| -| `hideBottomNavOnScroll` | bool | `true` | 滚动时隐藏底部导航 | -| `showMovieTab` | bool | `true` | 是否显示观影标签 | -| `showBookTab` | bool | `true` | 是否显示阅读标签 | -| `showNoteTab` | bool | `true` | 是否显示笔记标签 | -| `defaultMainTabIndex` | int | `0` | 默认启动标签(0=影视, 1=阅读, 2=笔记)| - -### 布局样式 - -| 键 | 类型 | 默认值 | 说明 | -|----|------|--------|------| -| `noteLayoutStyle` | int | `0` | 笔记布局(0=列表, 1=瀑布流, 2=时间线)| -| `movieLayoutStyle` | int | `0` | 影视布局(0=海报网格, 1=列表)| -| `bookLayoutStyle` | int | `0` | 阅读布局(0=封面网格, 1=列表)| - -### Markdown 阅读器 - -| 键 | 类型 | 默认值 | 说明 | -|----|------|--------|------| -| `lastMdFolder` | String? | `null` | 最近选择的目录 | -| `showEmptyDirs` | bool | `true` | 是否显示空目录 | -| `showImageOnlyDirs` | bool | `true` | 是否显示纯图片目录 | - -### 应用图标 - -| 键 | 类型 | 默认值 | 说明 | -|----|------|--------|------| -| `appIconName` | String | `'app_icon'` | 当前应用图标名称 | - -### 用户统计 - -| 键 | 类型 | 默认值 | 说明 | -|----|------|--------|------| -| `deviceId` | String | `''` | 匿名设备标识(首次启动自动生成)| - -### 服务端同步 - -| 键 | 类型 | 默认值 | 说明 | -|----|------|--------|------| -| `syncServerUrl` | String | `''` | 服务器地址 | -| `syncActivationCode` | String | `''` | 激活码 | -| `syncExpiresAt` | String | `''` | 激活码有效期 | -| `syncIsPermanent` | bool | `false` | 是否永久有效 | -| `syncEnabled` | bool | `true` | 实时同步开关 | -| `syncLastEntryId` | int | `0` | 上次同步的 entry ID | - -## 主题模式迁移 - -`UserPrefs.init()` 中包含旧版 `isDarkMode`(布尔值)到新版 `themeMode`(三态值)的自动迁移: - -```dart -if (_prefs!.containsKey('isDarkMode') && !_prefs!.containsKey('themeMode')) { - final oldValue = _prefs!.getBool('isDarkMode') ?? false; - await _prefs!.setInt('themeMode', oldValue ? 2 : 0); -} -``` - -[返回首页](Home.md)