结构重构

This commit is contained in:
DelLevin-Home
2026-06-25 23:56:12 +08:00
parent 00e948e580
commit ec46d45973
8 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
/// 阅读器文件路径管理
class BookFileHelper {
static final BookFileHelper instance = BookFileHelper._init();
BookFileHelper._init();
String? _rootPath;
Future<String> get _root async {
if (_rootPath != null) return _rootPath!;
final dir = await getApplicationDocumentsDirectory();
_rootPath = p.join(dir.path, 'mooknote', 'book_file');
await Directory(_rootPath!).create(recursive: true);
return _rootPath!;
}
Future<String> get bookFileRoot async => _root;
Future<String> get coverDir async {
final root = await _root;
final dir = p.join(root, 'cover');
await Directory(dir).create(recursive: true);
return dir;
}
Future<String> bookDir(String bookId) async {
final root = await _root;
final dir = p.join(root, bookId);
await Directory(dir).create(recursive: true);
return dir;
}
Future<String> bookFile(String bookId, String fileName) async {
final dir = await bookDir(bookId);
return p.join(dir, fileName);
}
String? relativePath(String absolutePath) {
if (_rootPath == null) return null;
if (absolutePath.startsWith(_rootPath!)) {
return absolutePath.substring(_rootPath!.length + 1);
}
return null;
}
Future<String> absolutePath(String relativePath) async {
final root = await _root;
return p.join(root, relativePath);
}
Future<void> deleteBookFiles(String bookId) async {
final dir = await bookDir(bookId);
if (await Directory(dir).exists()) {
await Directory(dir).delete(recursive: true);
}
}
/// 同步初始化(必须在使用 resolveAbsolutePath 前调用一次 bookFileRoot
Future<void> ensureInitialized() async {
await _root;
}
/// 根据相对路径解析绝对路径(调用前需确保已初始化)
String resolveAbsolutePath(String relativePath) {
if (_rootPath == null) return relativePath;
return p.join(_rootPath!, relativePath);
}
}

View File

@@ -0,0 +1,69 @@
import 'package:flutter/foundation.dart';
import '../models/reader_book.dart';
import 'database_helper.dart';
/// 阅读器书籍 DAO
class ReaderBookDao {
final DatabaseHelper _dbHelper = DatabaseHelper.instance;
Future<T> _wrap<T>(String op, Future<T> Function() fn) async {
try {
return await fn();
} catch (e) {
debugPrint('[ReaderBookDao] $op error: $e');
rethrow;
}
}
Future<List<ReaderBook>> getAllReaderBooks() => _wrap('getAllReaderBooks', () async {
final db = await _dbHelper.database;
final List<Map<String, dynamic>> maps = await db.query(
'reader_books',
where: 'is_deleted = ?',
whereArgs: [0],
orderBy: 'created_at DESC',
);
return List.generate(maps.length, (i) => ReaderBook.fromJson(maps[i]));
});
Future<ReaderBook?> getReaderBookById(String id) => _wrap('getReaderBookById', () async {
final db = await _dbHelper.database;
final List<Map<String, dynamic>> maps = await db.query(
'reader_books',
where: 'id = ? AND is_deleted = ?',
whereArgs: [id, 0],
);
if (maps.isEmpty) return null;
return ReaderBook.fromJson(maps.first);
});
Future<void> insertReaderBook(ReaderBook book) => _wrap('insertReaderBook', () async {
final db = await _dbHelper.database;
await db.insert('reader_books', book.toJson());
});
Future<void> updateReaderBook(ReaderBook book) => _wrap('updateReaderBook', () async {
final db = await _dbHelper.database;
await db.update(
'reader_books',
book.toJson(),
where: 'id = ?',
whereArgs: [book.id],
);
});
Future<void> deleteReaderBook(String id) => _wrap('deleteReaderBook', () async {
final db = await _dbHelper.database;
await db.update(
'reader_books',
{'is_deleted': 1, 'updated_at': DateTime.now().toIso8601String()},
where: 'id = ?',
whereArgs: [id],
);
});
Future<void> permanentDeleteReaderBook(String id) => _wrap('permanentDeleteReaderBook', () async {
final db = await _dbHelper.database;
await db.delete('reader_books', where: 'id = ?', whereArgs: [id]);
});
}

View File

@@ -0,0 +1,69 @@
import 'dart:convert';
import '../service/book_server.dart';
import 'color_converter.dart';
/// 生成 foliate-js 阅读器 URL
String generateReaderUrl({
required String fileUrl,
String cfi = '',
required String backgroundColor,
required String textColor,
bool isDarkMode = false,
}) {
final indexHtmlPath = 'http://127.0.0.1:${Server().port}/foliate-js/index.html';
final jsBg = convertDartColorToJs(backgroundColor);
final jsTc = convertDartColorToJs(textColor);
final style = {
'fontSize': 100, // 100 = base 100%
'fontName': '',
'fontPath': '',
'fontWeight': 400,
'letterSpacing': 0,
'spacing': 1.6,
'paragraphSpacing': 0.6,
'textIndent': 2,
'fontColor': '#$jsTc',
'backgroundColor': '#$jsBg',
'topMargin': 25,
'bottomMargin': 25,
'sideMargin': 15,
'justify': true,
'hyphenate': false,
'pageTurnStyle': 'slide',
'maxColumnCount': 1,
'columnThreshold': 3,
'writingMode': 'horizontal-tb',
'textAlign': 'justify',
'backgroundImage': '',
'bgimgBlur': 0,
'bgimgOpacity': 1.0,
'bgimgFit': 'cover',
'allowScript': false,
'customCSS': '',
'customCSSEnabled': false,
'useBookStyles': true,
'headingFontSize': 130,
'codeHighlightTheme': 'atom-one-light',
};
final readingRules = {
'convertChineseMode': 'none',
'bionicReadingMode': false,
};
final params = {
'importing': false,
'url': fileUrl,
'initialCfi': cfi,
'style': style,
'readingRules': readingRules,
};
final queryParts = params.entries
.map((e) => '${e.key}=${Uri.encodeComponent(jsonEncode(e.value))}')
.join('&');
return '$indexHtmlPath?$queryParts';
}