|
|
@ -1,27 +1,35 @@ |
|
|
|
|
|
|
|
import 'package:get/get.dart'; |
|
|
|
import 'package:sqflite/sqflite.dart'; |
|
|
|
import 'package:sqflite/sqflite.dart'; |
|
|
|
import 'package:path/path.dart'; |
|
|
|
import 'package:path/path.dart'; |
|
|
|
import 'package:uuid/uuid.dart'; |
|
|
|
import 'package:uuid/uuid.dart'; |
|
|
|
import '../models/problem_model.dart'; |
|
|
|
import '../models/problem_model.dart'; |
|
|
|
|
|
|
|
|
|
|
|
class LocalDatabase { |
|
|
|
/// LocalDatabase 是一个 GetxService,它负责管理本地 SQLite 数据库。 |
|
|
|
static final LocalDatabase _instance = LocalDatabase._internal(); |
|
|
|
/// GetxService 确保这个类在整个应用生命周期中都是一个单例,并且不会被自动销毁。 |
|
|
|
factory LocalDatabase() => _instance; |
|
|
|
class LocalDatabase extends GetxService { |
|
|
|
static Database? _database; |
|
|
|
static const String _dbName = 'problems.db'; |
|
|
|
static const String _tableName = 'problems'; |
|
|
|
static const String _tableName = 'problems'; |
|
|
|
|
|
|
|
|
|
|
|
LocalDatabase._internal(); |
|
|
|
/// 私有的数据库实例,只能在 LocalDatabase 类内部访问。 |
|
|
|
|
|
|
|
late Database _database; |
|
|
|
|
|
|
|
|
|
|
|
Future<Database> get database async { |
|
|
|
/// onInit 是 GetxService 的生命周期方法,在服务首次被创建时调用。 |
|
|
|
if (_database != null) return _database!; |
|
|
|
/// 它是执行异步初始化的最佳位置,确保在使用服务前,数据库已经准备就绪。 |
|
|
|
_database = await _initDatabase(); |
|
|
|
@override |
|
|
|
return _database!; |
|
|
|
void onInit() { |
|
|
|
|
|
|
|
super.onInit(); |
|
|
|
|
|
|
|
_initDatabase(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Future<Database> _initDatabase() async { |
|
|
|
/// 异步初始化数据库。它会打开数据库连接,如果数据库不存在则创建。 |
|
|
|
String path = join(await getDatabasesPath(), 'problems.db'); |
|
|
|
Future<void> _initDatabase() async { |
|
|
|
return await openDatabase(path, version: 1, onCreate: _onCreate); |
|
|
|
final databasePath = await getDatabasesPath(); |
|
|
|
|
|
|
|
final path = join(databasePath, _dbName); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_database = await openDatabase(path, version: 1, onCreate: _onCreate); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// 数据库创建时的回调函数,用于执行建表语句。 |
|
|
|
Future<void> _onCreate(Database db, int version) async { |
|
|
|
Future<void> _onCreate(Database db, int version) async { |
|
|
|
await db.execute(''' |
|
|
|
await db.execute(''' |
|
|
|
CREATE TABLE $_tableName( |
|
|
|
CREATE TABLE $_tableName( |
|
|
@ -37,15 +45,17 @@ class LocalDatabase { |
|
|
|
'''); |
|
|
|
'''); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// 插入一个新问题到数据库。在插入前,会为其生成一个唯一的 UUID。 |
|
|
|
|
|
|
|
/// 返回插入的行数。 |
|
|
|
Future<int> insertProblem(Problem problem) async { |
|
|
|
Future<int> insertProblem(Problem problem) async { |
|
|
|
final db = await database; |
|
|
|
|
|
|
|
problem.id = const Uuid().v4(); |
|
|
|
problem.id = const Uuid().v4(); |
|
|
|
return await db.insert(_tableName, problem.toMap()); |
|
|
|
return await _database.insert(_tableName, problem.toMap()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// 更新数据库中已存在的问题。 |
|
|
|
|
|
|
|
/// 返回更新的行数。 |
|
|
|
Future<int> updateProblem(Problem problem) async { |
|
|
|
Future<int> updateProblem(Problem problem) async { |
|
|
|
final db = await database; |
|
|
|
return await _database.update( |
|
|
|
return await db.update( |
|
|
|
|
|
|
|
_tableName, |
|
|
|
_tableName, |
|
|
|
problem.toMap(), |
|
|
|
problem.toMap(), |
|
|
|
where: 'id = ?', |
|
|
|
where: 'id = ?', |
|
|
@ -53,48 +63,41 @@ class LocalDatabase { |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// 根据 ID 删除数据库中的问题。 |
|
|
|
|
|
|
|
/// 返回删除的行数。 |
|
|
|
Future<int> deleteProblem(String id) async { |
|
|
|
Future<int> deleteProblem(String id) async { |
|
|
|
final db = await database; |
|
|
|
return await _database.delete(_tableName, where: 'id = ?', whereArgs: [id]); |
|
|
|
return await db.delete(_tableName, where: 'id = ?', whereArgs: [id]); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 通用查询方法 |
|
|
|
/// 通用查询方法,根据可选的筛选条件获取问题列表。 |
|
|
|
/// 根据可选的参数筛选问题。 |
|
|
|
/// - `startDate`/`endDate`:筛选创建时间范围。 |
|
|
|
/// 时间范围、 |
|
|
|
/// - `uploadStatus`:筛选上传状态('已上传', '未上传', '全部')。 |
|
|
|
/// 上传状态 |
|
|
|
/// - `bindStatus`:筛选绑定状态('已绑定', '未绑定', '全部')。 |
|
|
|
/// 绑定状态 |
|
|
|
|
|
|
|
Future<List<Problem>> getProblems({ |
|
|
|
Future<List<Problem>> getProblems({ |
|
|
|
DateTime? startDate, |
|
|
|
DateTime? startDate, |
|
|
|
DateTime? endDate, |
|
|
|
DateTime? endDate, |
|
|
|
String? uploadStatus, // '已上传', '未上传', '全部' |
|
|
|
String uploadStatus = '全部', |
|
|
|
String? bindStatus, // '已绑定', '未绑定', '全部' |
|
|
|
String bindStatus = '全部', |
|
|
|
}) async { |
|
|
|
}) async { |
|
|
|
final db = await database; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 使用可变列表来构建筛选条件 |
|
|
|
|
|
|
|
final List<String> whereClauses = []; |
|
|
|
final List<String> whereClauses = []; |
|
|
|
final List<dynamic> whereArgs = []; |
|
|
|
final List<dynamic> whereArgs = []; |
|
|
|
|
|
|
|
|
|
|
|
// 根据 startDate 添加筛选条件 |
|
|
|
|
|
|
|
if (startDate != null) { |
|
|
|
if (startDate != null) { |
|
|
|
whereClauses.add('createdAt >= ?'); |
|
|
|
whereClauses.add('createdAt >= ?'); |
|
|
|
whereArgs.add(startDate.millisecondsSinceEpoch); |
|
|
|
whereArgs.add(startDate.millisecondsSinceEpoch); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 根据 endDate 添加筛选条件 |
|
|
|
|
|
|
|
if (endDate != null) { |
|
|
|
if (endDate != null) { |
|
|
|
whereClauses.add('createdAt <= ?'); |
|
|
|
whereClauses.add('createdAt <= ?'); |
|
|
|
whereArgs.add(endDate.millisecondsSinceEpoch); |
|
|
|
whereArgs.add(endDate.millisecondsSinceEpoch); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 根据 uploadStatus 添加筛选条件 |
|
|
|
if (uploadStatus != '全部') { |
|
|
|
if (uploadStatus != null && uploadStatus != '全部') { |
|
|
|
|
|
|
|
whereClauses.add('isUploaded = ?'); |
|
|
|
whereClauses.add('isUploaded = ?'); |
|
|
|
whereArgs.add(uploadStatus == '已上传' ? 1 : 0); |
|
|
|
whereArgs.add(uploadStatus == '已上传' ? 1 : 0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 根据 bindStatus 添加筛选条件 |
|
|
|
if (bindStatus != '全部') { |
|
|
|
if (bindStatus != null && bindStatus != '全部') { |
|
|
|
|
|
|
|
if (bindStatus == '已绑定') { |
|
|
|
if (bindStatus == '已绑定') { |
|
|
|
whereClauses.add('censorTaskId IS NOT NULL'); |
|
|
|
whereClauses.add('censorTaskId IS NOT NULL'); |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -102,10 +105,9 @@ class LocalDatabase { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 将所有条件用 ' AND ' 连接起来 |
|
|
|
|
|
|
|
final String whereString = whereClauses.join(' AND '); |
|
|
|
final String whereString = whereClauses.join(' AND '); |
|
|
|
|
|
|
|
|
|
|
|
final List<Map<String, dynamic>> maps = await db.query( |
|
|
|
final List<Map<String, dynamic>> maps = await _database.query( |
|
|
|
_tableName, |
|
|
|
_tableName, |
|
|
|
where: whereString.isEmpty ? null : whereString, |
|
|
|
where: whereString.isEmpty ? null : whereString, |
|
|
|
whereArgs: whereArgs.isEmpty ? null : whereArgs, |
|
|
|
whereArgs: whereArgs.isEmpty ? null : whereArgs, |
|
|
@ -116,4 +118,12 @@ class LocalDatabase { |
|
|
|
return Problem.fromMap(maps[i]); |
|
|
|
return Problem.fromMap(maps[i]); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// onClose 是 GetxService 的生命周期方法,在服务被销毁前调用。 |
|
|
|
|
|
|
|
/// 虽然 GetxService 默认是永久的,但养成关闭数据库连接的习惯是很好的实践。 |
|
|
|
|
|
|
|
@override |
|
|
|
|
|
|
|
void onClose() { |
|
|
|
|
|
|
|
_database.close(); |
|
|
|
|
|
|
|
super.onClose(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|