186 lines
4.9 KiB
Dart
186 lines
4.9 KiB
Dart
|
|
import 'dart:async';
|
||
|
|
// import 'dart:io';
|
||
|
|
import 'package:logging/logging.dart';
|
||
|
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||
|
|
import 'mining_task_info.dart';
|
||
|
|
import '../utils/path_utils.dart';
|
||
|
|
|
||
|
|
/// 数据库管理服务
|
||
|
|
class DatabaseService {
|
||
|
|
static final DatabaseService _instance = DatabaseService._internal();
|
||
|
|
factory DatabaseService() => _instance;
|
||
|
|
DatabaseService._internal();
|
||
|
|
|
||
|
|
final Logger _logger = Logger('DatabaseService');
|
||
|
|
Database? _database;
|
||
|
|
|
||
|
|
/// 初始化数据库
|
||
|
|
Future<void> initialize() async {
|
||
|
|
try {
|
||
|
|
// Windows/Desktop: 使用 sqflite_common_ffi
|
||
|
|
sqfliteFfiInit();
|
||
|
|
databaseFactory = databaseFactoryFfi;
|
||
|
|
|
||
|
|
// 与 Go 版一致,尽量落到 ./bin/mining_task.db
|
||
|
|
final dbPath = PathUtils.binFile('mining_task.db');
|
||
|
|
|
||
|
|
_database = await databaseFactory.openDatabase(
|
||
|
|
dbPath,
|
||
|
|
options: OpenDatabaseOptions(
|
||
|
|
version: 1,
|
||
|
|
onCreate: (db, version) async {
|
||
|
|
await db.execute('''
|
||
|
|
CREATE TABLE mining_tasks (
|
||
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
|
|
coin TEXT NOT NULL,
|
||
|
|
algo TEXT NOT NULL,
|
||
|
|
pool TEXT NOT NULL,
|
||
|
|
pool_url TEXT NOT NULL,
|
||
|
|
wallet_address TEXT NOT NULL,
|
||
|
|
worker_id TEXT NOT NULL,
|
||
|
|
pool_user TEXT,
|
||
|
|
wallet_mining INTEGER NOT NULL,
|
||
|
|
end_timestamp INTEGER NOT NULL,
|
||
|
|
miner TEXT NOT NULL,
|
||
|
|
status TEXT NOT NULL,
|
||
|
|
created_at INTEGER NOT NULL,
|
||
|
|
updated_at INTEGER NOT NULL
|
||
|
|
)
|
||
|
|
''');
|
||
|
|
},
|
||
|
|
),
|
||
|
|
);
|
||
|
|
|
||
|
|
_logger.info('数据库初始化成功');
|
||
|
|
} catch (e) {
|
||
|
|
_logger.severe('数据库初始化失败: $e');
|
||
|
|
rethrow;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 插入挖矿任务
|
||
|
|
Future<int> insertMiningTask(MiningTaskInfo task) async {
|
||
|
|
if (_database == null) {
|
||
|
|
await initialize();
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||
|
|
return await _database!.insert(
|
||
|
|
'mining_tasks',
|
||
|
|
{
|
||
|
|
'coin': task.coin,
|
||
|
|
'algo': task.algo,
|
||
|
|
'pool': task.pool,
|
||
|
|
'pool_url': task.poolUrl,
|
||
|
|
'wallet_address': task.walletAddress,
|
||
|
|
'worker_id': task.workerId,
|
||
|
|
'pool_user': task.poolUser,
|
||
|
|
'wallet_mining': task.walletMining ? 1 : 0,
|
||
|
|
'end_timestamp': task.endTimestamp,
|
||
|
|
'miner': task.miner,
|
||
|
|
'status': 'running',
|
||
|
|
'created_at': now,
|
||
|
|
'updated_at': now,
|
||
|
|
},
|
||
|
|
);
|
||
|
|
} catch (e) {
|
||
|
|
_logger.severe('插入挖矿任务失败: $e');
|
||
|
|
rethrow;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 完成挖矿任务
|
||
|
|
Future<void> finishMiningTask(int taskId) async {
|
||
|
|
if (_database == null) {
|
||
|
|
await initialize();
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||
|
|
await _database!.update(
|
||
|
|
'mining_tasks',
|
||
|
|
{
|
||
|
|
'status': 'finished',
|
||
|
|
'updated_at': now,
|
||
|
|
},
|
||
|
|
where: 'id = ?',
|
||
|
|
whereArgs: [taskId],
|
||
|
|
);
|
||
|
|
} catch (e) {
|
||
|
|
_logger.severe('完成挖矿任务失败: $e');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 加载未完成的挖矿任务
|
||
|
|
Future<MiningTaskInfo?> loadUnfinishedTask() async {
|
||
|
|
if (_database == null) {
|
||
|
|
await initialize();
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
final results = await _database!.query(
|
||
|
|
'mining_tasks',
|
||
|
|
where: 'status = ?',
|
||
|
|
whereArgs: ['running'],
|
||
|
|
orderBy: 'created_at DESC',
|
||
|
|
limit: 1,
|
||
|
|
);
|
||
|
|
|
||
|
|
if (results.isEmpty) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
final row = results.first;
|
||
|
|
final currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||
|
|
final endTimestamp = row['end_timestamp'] as int;
|
||
|
|
|
||
|
|
if (currentTime >= endTimestamp) {
|
||
|
|
// 任务已过期,标记为完成
|
||
|
|
await finishMiningTask(row['id'] as int);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return MiningTaskInfo(
|
||
|
|
coin: row['coin'] as String,
|
||
|
|
algo: row['algo'] as String,
|
||
|
|
pool: row['pool'] as String,
|
||
|
|
poolUrl: row['pool_url'] as String,
|
||
|
|
walletAddress: row['wallet_address'] as String,
|
||
|
|
workerId: row['worker_id'] as String,
|
||
|
|
poolUser: row['pool_user'] as String?,
|
||
|
|
walletMining: (row['wallet_mining'] as int) == 1,
|
||
|
|
endTimestamp: endTimestamp,
|
||
|
|
miner: row['miner'] as String,
|
||
|
|
);
|
||
|
|
} catch (e) {
|
||
|
|
_logger.severe('加载挖矿任务失败: $e');
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 获取任务历史
|
||
|
|
Future<List<Map<String, dynamic>>> getTaskHistory({int limit = 100}) async {
|
||
|
|
if (_database == null) {
|
||
|
|
await initialize();
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
return await _database!.query(
|
||
|
|
'mining_tasks',
|
||
|
|
orderBy: 'created_at DESC',
|
||
|
|
limit: limit,
|
||
|
|
);
|
||
|
|
} catch (e) {
|
||
|
|
_logger.severe('获取任务历史失败: $e');
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 关闭数据库
|
||
|
|
Future<void> close() async {
|
||
|
|
await _database?.close();
|
||
|
|
_database = null;
|
||
|
|
}
|
||
|
|
}
|