229 lines
7.8 KiB
Dart
229 lines
7.8 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:common_utils/common_utils.dart';
|
|
import 'package:external_path/external_path.dart';
|
|
|
|
import 'package:pj_supermarket_cloud/common/index.dart';
|
|
import 'package:sqflite/sqflite.dart';
|
|
|
|
abstract class AppFileTool {
|
|
static const _tag = "app文件工具";
|
|
static const _baseDir = "pj_cashier";
|
|
static const _baseBackupDir = "backup";
|
|
static const _backupDatabaseDir = "database";
|
|
static const _backupLoggerDir = "logger";
|
|
static const _backupFaceDir = "face";
|
|
|
|
/// 检查并创建目录
|
|
static Future<String> _checkAndcreateDir(String dir) async {
|
|
Directory targetDir = Directory(dir);
|
|
if (!await targetDir.exists()) {
|
|
await targetDir.create(recursive: true);
|
|
}
|
|
return dir;
|
|
}
|
|
|
|
/// 获取外部路径
|
|
static Future<String> getExternalPath({
|
|
bool backupExternalPath = true,
|
|
}) async {
|
|
final documentsDir = await ExternalPath.getExternalStoragePublicDirectory(
|
|
ExternalPath.DIRECTORY_DOCUMENTS,
|
|
);
|
|
final backupDir = "$documentsDir/$_baseDir";
|
|
if (backupExternalPath) {
|
|
final sysSetting = await AppSettingTool.getSysSetting();
|
|
sysSetting.externalPath = documentsDir;
|
|
await AppSettingTool.setSysSetting(sysSetting);
|
|
}
|
|
return backupDir;
|
|
}
|
|
|
|
/// 创建数据库文件地址
|
|
static Future<String?> getDatabasePath() async {
|
|
String backupDir = await getExternalPath(backupExternalPath: false);
|
|
String dir = "$backupDir/$_backupDatabaseDir";
|
|
dir = await _checkAndcreateDir(dir);
|
|
return dir;
|
|
}
|
|
|
|
/// 创建数据库文件备份地址
|
|
static Future<String?> getDatabaseBackupPath() async {
|
|
String backupDir = await getExternalPath(backupExternalPath: false);
|
|
String dir = "$backupDir/$_baseBackupDir/$_backupDatabaseDir";
|
|
DateTime now = DateTime.now();
|
|
int year = now.year;
|
|
int month = now.month;
|
|
int day = now.day;
|
|
dir = "$dir/$year-$month";
|
|
dir = await _checkAndcreateDir(dir);
|
|
dir = "$dir/$day.db";
|
|
return dir;
|
|
}
|
|
|
|
/// 创建日志地址
|
|
static Future<String?> getLoggerPath() async {
|
|
final backupDir = await getExternalPath();
|
|
return _checkAndcreateDir("$backupDir/$_backupLoggerDir");
|
|
}
|
|
|
|
/// 创建日志备份地址
|
|
static Future<String?> getLoggerBackupPath() async {
|
|
final backupDir = await getExternalPath(backupExternalPath: false);
|
|
String dir = "$backupDir/$_baseBackupDir/$_backupLoggerDir";
|
|
DateTime now = DateTime.now();
|
|
int year = now.year;
|
|
int month = now.month;
|
|
int day = now.day;
|
|
dir = "$dir/$year-$month";
|
|
dir = await _checkAndcreateDir(dir);
|
|
dir = "$dir/$day.log";
|
|
return dir;
|
|
}
|
|
|
|
/// 创建一次性日志快照地址,避免手动导出覆盖当前仍在写入的实时日志文件。
|
|
static Future<String?> getLoggerSnapshotPath() async {
|
|
final backupDir = await getExternalPath(backupExternalPath: false);
|
|
String dir = "$backupDir/$_baseBackupDir/$_backupLoggerDir/manual";
|
|
dir = await _checkAndcreateDir(dir);
|
|
final now = DateTime.now();
|
|
final stamp = now.toIso8601String().replaceAll(':', '-');
|
|
return "$dir/log-$stamp.log";
|
|
}
|
|
|
|
/// 创建人脸地址
|
|
static Future<String?> getFacePath() async {
|
|
final backupDir = await getExternalPath();
|
|
return _checkAndcreateDir("$backupDir/$_backupFaceDir");
|
|
}
|
|
|
|
/// 创建人脸备份地址
|
|
static Future<String?> getFaceBackupPath() async {
|
|
final backupDir = await getExternalPath();
|
|
return _checkAndcreateDir("$backupDir/$_baseBackupDir/$_backupFaceDir");
|
|
}
|
|
|
|
/// 备份数据库并返回生成文件,页面据此展示真实结果和最近备份时间。
|
|
static Future<String?> backupSqlFilePath() async {
|
|
final sourcePath = await getDatabasePath();
|
|
if (ObjectUtil.isEmpty(sourcePath)) {
|
|
logger.e("|$_tag|备份sql文件失败,无效的sourcePath路径path:|$sourcePath|");
|
|
return null;
|
|
}
|
|
final databaseFilePath = "$sourcePath/${Constants.databaseName}";
|
|
if (!await File(databaseFilePath).exists()) {
|
|
logger.w("|$_tag|备份sql文件失败,无效的sourcePath路径path|$databaseFilePath|");
|
|
return null;
|
|
}
|
|
String? backupPath = await getDatabaseBackupPath();
|
|
if (ObjectUtil.isEmpty(backupPath)) {
|
|
logger.e("|$_tag|备份sql文件失败,无效的backupPath路径path:|$backupPath|");
|
|
return null;
|
|
}
|
|
try {
|
|
await File(databaseFilePath).copy(backupPath!);
|
|
return backupPath;
|
|
} catch (e, stack) {
|
|
logger.e("|$_tag|备份sql文件失败,意外的错误error|$e|stack|$stack|");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// 保留旧接口,应用启动时仍可按原方式进行自动备份。
|
|
static Future<bool> backupSqlFile() async {
|
|
return await backupSqlFilePath() != null;
|
|
}
|
|
|
|
/// 将当前实时日志复制为独立快照,不中断 Logger 正在持有的文件句柄。
|
|
static Future<String?> backupLogFile() async {
|
|
final sourcePath = await getLoggerBackupPath();
|
|
final targetPath = await getLoggerSnapshotPath();
|
|
if (ObjectUtil.isEmpty(sourcePath) || ObjectUtil.isEmpty(targetPath)) {
|
|
return null;
|
|
}
|
|
final source = File(sourcePath!);
|
|
if (!await source.exists()) {
|
|
logger.w("|$_tag|备份日志失败,实时日志不存在path|$sourcePath|");
|
|
return null;
|
|
}
|
|
try {
|
|
await source.copy(targetPath!);
|
|
return targetPath;
|
|
} catch (e, stack) {
|
|
logger.e("|$_tag|备份日志失败error|$e|stack|$stack|");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// 获取目录中最近生成文件的修改时间。
|
|
static Future<DateTime?> getLatestModifiedTime(String? path) async {
|
|
if (ObjectUtil.isEmpty(path)) return null;
|
|
final entity = FileSystemEntity.typeSync(path!);
|
|
if (entity == FileSystemEntityType.file) {
|
|
return File(path).stat().then((value) => value.modified);
|
|
}
|
|
final directory = Directory(path);
|
|
if (!await directory.exists()) return null;
|
|
DateTime? latest;
|
|
await for (final file in directory.list(recursive: true)) {
|
|
if (file is! File) continue;
|
|
final modified = (await file.stat()).modified;
|
|
if (latest == null || modified.isAfter(latest)) latest = modified;
|
|
}
|
|
return latest;
|
|
}
|
|
|
|
// 如果没有数据库将最新的数据库移动为使用的数据库
|
|
static Future<void> useLastBackDatabase() async {
|
|
final databasePath = getDatabasesPath();
|
|
final databaseFilePath = "$databasePath/${Constants.databaseName}";
|
|
if (await File(databaseFilePath).exists()) {
|
|
logger.w("|$_tag|替换数据库错误,数据库文件已存在path|$databaseFilePath|");
|
|
return;
|
|
}
|
|
final path = await getDatabaseBackupPath();
|
|
if (ObjectUtil.isEmpty(path)) {
|
|
logger.w("|$_tag|替换数据库错误,无效的数据库备份path|$path|");
|
|
return;
|
|
}
|
|
final dir = Directory(path!).parent;
|
|
if (!await dir.exists()) {
|
|
logger.w("|$_tag|替换数据库错误,无效的数据库备份目录path|$dir|");
|
|
return;
|
|
}
|
|
final files = dir.listSync();
|
|
files.sort(
|
|
(a, b) => b.statSync().modified.compareTo(a.statSync().modified),
|
|
);
|
|
if (files.isEmpty) {
|
|
logger.w("|$_tag|替换数据库错误,数据库备份目录为空path|$dir|");
|
|
return;
|
|
}
|
|
final lastFile = files.first;
|
|
try {
|
|
if (lastFile is File) {
|
|
final sourcePath = lastFile.path;
|
|
await File(sourcePath).copy(databaseFilePath);
|
|
logger.i(
|
|
"|$_tag|替换数据库成功,sourcePath|$sourcePath|targetPath|$databaseFilePath|",
|
|
);
|
|
}
|
|
} catch (e, stack) {
|
|
logger.e("|$_tag|替换数据库错误,意外的错误error|$e|stack|$stack|");
|
|
}
|
|
}
|
|
|
|
// 获取文件后缀
|
|
static String getFileExtension(String url) {
|
|
int lastDotIndex = url.lastIndexOf('.');
|
|
if (lastDotIndex == -1) {
|
|
return '';
|
|
}
|
|
int lastSlashIndex = url.lastIndexOf('/');
|
|
if (lastDotIndex > lastSlashIndex) {
|
|
return url.substring(lastDotIndex);
|
|
}
|
|
return '';
|
|
}
|
|
}
|