相册功能优化

This commit is contained in:
DelLevin-Home
2026-08-09 15:29:49 +08:00
parent 6d76c33672
commit 68a8d7f6ce
15 changed files with 963 additions and 12 deletions

View File

@@ -0,0 +1,224 @@
import 'package:flutter/foundation.dart';
import '../../models/data_models.dart';
import '../database_helper.dart';
/// 图库数据访问对象 —— 聚合所有实体的图片
class GalleryDao {
final DatabaseHelper _dbHelper = DatabaseHelper.instance;
Future<T> _wrap<T>(String op, Future<T> Function() fn) async {
try {
return await fn();
} catch (e) {
debugPrint('[GalleryDao] $op error: $e');
rethrow;
}
}
DateTime _parseDate(String? str) {
if (str == null || str.isEmpty) return DateTime.now();
return DateTime.tryParse(str)?.toLocal() ?? DateTime.now();
}
/// 获取所有图片(过滤软删除与空路径),按创建时间倒序
Future<List<GalleryItem>> getAllImages() => _wrap('getAllImages', () async {
final db = await _dbHelper.database;
final items = <GalleryItem>[];
// 1. 影视海报movies.poster_path
final movieMaps = await db.query(
'movies',
columns: ['id', 'title', 'poster_path', 'created_at'],
where: 'is_deleted = ? AND poster_path IS NOT NULL AND poster_path != ?',
whereArgs: [0, ''],
);
for (final m in movieMaps) {
items.add(GalleryItem(
path: m['poster_path'] as String,
category: 'movie_poster',
entityType: 'movie',
entityId: m['id'] as String,
entityTitle: (m['title'] as String?) ?? '',
createdAt: _parseDate(m['created_at'] as String?),
));
}
// 2. 影视海报墙movie_posters JOIN movies
final posterMaps = await db.rawQuery(
"SELECT p.poster_path, p.created_at, p.movie_id, m.title AS parent_title "
"FROM movie_posters p INNER JOIN movies m ON p.movie_id = m.id "
"WHERE p.is_deleted = ? AND m.is_deleted = ? "
"AND p.poster_path IS NOT NULL AND p.poster_path != ?",
[0, 0, ''],
);
for (final p in posterMaps) {
items.add(GalleryItem(
path: p['poster_path'] as String,
category: 'movie_posters',
entityType: 'movie',
entityId: p['movie_id'] as String,
entityTitle: (p['parent_title'] as String?) ?? '',
createdAt: _parseDate(p['created_at'] as String?),
));
}
// 3. 书籍封面books.cover_path
final bookMaps = await db.query(
'books',
columns: ['id', 'title', 'cover_path', 'created_at'],
where: 'is_deleted = ? AND cover_path IS NOT NULL AND cover_path != ?',
whereArgs: [0, ''],
);
for (final b in bookMaps) {
items.add(GalleryItem(
path: b['cover_path'] as String,
category: 'book_cover',
entityType: 'book',
entityId: b['id'] as String,
entityTitle: (b['title'] as String?) ?? '',
createdAt: _parseDate(b['created_at'] as String?),
));
}
// 4. 笔记图片notes.images JSON 数组)
final noteMaps = await db.query(
'notes',
columns: ['id', 'title', 'images', 'created_at'],
where: 'is_deleted = ? AND images IS NOT NULL AND images != ?',
whereArgs: [0, ''],
);
for (final n in noteMaps) {
final paths = parseStringListGeneric(n['images']);
final title = (n['title'] as String?) ?? '';
final created = _parseDate(n['created_at'] as String?);
for (final imgPath in paths) {
if (imgPath.isEmpty) continue;
items.add(GalleryItem(
path: imgPath,
category: 'note_image',
entityType: 'note',
entityId: n['id'] as String,
entityTitle: title,
createdAt: created,
));
}
}
// 5. 游戏封面games.cover_path
final gameMaps = await db.query(
'games',
columns: ['id', 'title', 'cover_path', 'created_at'],
where: 'is_deleted = ? AND cover_path IS NOT NULL AND cover_path != ?',
whereArgs: [0, ''],
);
for (final g in gameMaps) {
items.add(GalleryItem(
path: g['cover_path'] as String,
category: 'game_cover',
entityType: 'game',
entityId: g['id'] as String,
entityTitle: (g['title'] as String?) ?? '',
createdAt: _parseDate(g['created_at'] as String?),
));
}
// 6. 游戏截图game_screenshots JOIN games
final shotMaps = await db.rawQuery(
"SELECT s.screenshot_path, s.created_at, s.game_id, g.title AS parent_title "
"FROM game_screenshots s INNER JOIN games g ON s.game_id = g.id "
"WHERE s.is_deleted = ? AND g.is_deleted = ? "
"AND s.screenshot_path IS NOT NULL AND s.screenshot_path != ?",
[0, 0, ''],
);
for (final s in shotMaps) {
items.add(GalleryItem(
path: s['screenshot_path'] as String,
category: 'game_screenshot',
entityType: 'game',
entityId: s['game_id'] as String,
entityTitle: (s['parent_title'] as String?) ?? '',
createdAt: _parseDate(s['created_at'] as String?),
));
}
// 7. 人物照片people.photo_path
final personMaps = await db.query(
'people',
columns: ['id', 'name', 'photo_path', 'created_at'],
where: 'is_deleted = ? AND photo_path IS NOT NULL AND photo_path != ?',
whereArgs: [0, ''],
);
for (final p in personMaps) {
items.add(GalleryItem(
path: p['photo_path'] as String,
category: 'person_photo',
entityType: 'person',
entityId: p['id'] as String,
entityTitle: (p['name'] as String?) ?? '',
createdAt: _parseDate(p['created_at'] as String?),
));
}
// 8. 角色图片movie/book/game_characters JOIN 父表)
final charMovieMaps = await db.rawQuery(
"SELECT c.image_path, c.name, c.movie_id, c.created_at, m.title AS parent_title "
"FROM movie_characters c INNER JOIN movies m ON c.movie_id = m.id "
"WHERE c.is_deleted = ? AND m.is_deleted = ? "
"AND c.image_path IS NOT NULL AND c.image_path != ?",
[0, 0, ''],
);
for (final c in charMovieMaps) {
items.add(GalleryItem(
path: c['image_path'] as String,
category: 'movie_character',
entityType: 'movie',
entityId: c['movie_id'] as String,
entityTitle: (c['name'] as String?) ?? '',
parentTitle: (c['parent_title'] as String?) ?? '',
createdAt: _parseDate(c['created_at'] as String?),
));
}
final charBookMaps = await db.rawQuery(
"SELECT c.image_path, c.name, c.book_id, c.created_at, b.title AS parent_title "
"FROM book_characters c INNER JOIN books b ON c.book_id = b.id "
"WHERE c.is_deleted = ? AND b.is_deleted = ? "
"AND c.image_path IS NOT NULL AND c.image_path != ?",
[0, 0, ''],
);
for (final c in charBookMaps) {
items.add(GalleryItem(
path: c['image_path'] as String,
category: 'book_character',
entityType: 'book',
entityId: c['book_id'] as String,
entityTitle: (c['name'] as String?) ?? '',
parentTitle: (c['parent_title'] as String?) ?? '',
createdAt: _parseDate(c['created_at'] as String?),
));
}
final charGameMaps = await db.rawQuery(
"SELECT c.image_path, c.name, c.game_id, c.created_at, g.title AS parent_title "
"FROM game_characters c INNER JOIN games g ON c.game_id = g.id "
"WHERE c.is_deleted = ? AND g.is_deleted = ? "
"AND c.image_path IS NOT NULL AND c.image_path != ?",
[0, 0, ''],
);
for (final c in charGameMaps) {
items.add(GalleryItem(
path: c['image_path'] as String,
category: 'game_character',
entityType: 'game',
entityId: c['game_id'] as String,
entityTitle: (c['name'] as String?) ?? '',
parentTitle: (c['parent_title'] as String?) ?? '',
createdAt: _parseDate(c['created_at'] as String?),
));
}
// 按创建时间倒序
items.sort((a, b) => b.createdAt.compareTo(a.createdAt));
return items;
});
}

View File

@@ -27,6 +27,18 @@ class GameDao {
return List.generate(maps.length, (i) => Game.fromJson(maps[i]));
});
// 根据ID获取游戏记录
Future<Game?> getGameById(String id) => _wrap('getGameById', () async {
final db = await _dbHelper.database;
final List<Map<String, dynamic>> maps = await db.query(
'games',
where: 'id = ? AND is_deleted = ?',
whereArgs: [id, 0],
);
if (maps.isEmpty) return null;
return Game.fromJson(maps.first);
});
// 分页查询游戏记录
Future<List<Game>> getGamesPaged({String? status, int limit = 20, int offset = 0, int sortMode = 0}) => _wrap('getGamesPaged', () async {
final db = await _dbHelper.database;

View File

@@ -27,6 +27,18 @@ class MovieDao {
return List.generate(maps.length, (i) => Movie.fromJson(maps[i]));
});
// 根据ID获取影视记录
Future<Movie?> getMovieById(String id) => _wrap('getMovieById', () async {
final db = await _dbHelper.database;
final List<Map<String, dynamic>> maps = await db.query(
'movies',
where: 'id = ? AND is_deleted = ?',
whereArgs: [id, 0],
);
if (maps.isEmpty) return null;
return Movie.fromJson(maps.first);
});
// 分页查询影视记录
Future<List<Movie>> getMoviesPaged({String? status, String? category, int limit = 20, int offset = 0, int sortMode = 0}) => _wrap('getMoviesPaged', () async {
final db = await _dbHelper.database;