You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
4.7 KiB

import 'package:get/get.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:uuid/uuid.dart';
import '../models/problem_model.dart';
/// LocalDatabase 是一个 GetxService,它负责管理本地 SQLite 数据库。
/// GetxService 确保这个类在整个应用生命周期中都是一个单例,并且不会被自动销毁。
class LocalDatabase extends GetxService {
static const String _dbName = 'problems.db';
static const String _tableName = 'problems';
/// 私有的数据库实例,只能在 LocalDatabase 类内部访问。
late Database _database;
/// onInit 是 GetxService 的生命周期方法,在服务首次被创建时调用。
/// 它是执行异步初始化的最佳位置,确保在使用服务前,数据库已经准备就绪。
@override
void onInit() {
super.onInit();
_initDatabase();
}
/// 异步初始化数据库。它会打开数据库连接,如果数据库不存在则创建。
Future<void> _initDatabase() async {
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 {
await db.execute('''
CREATE TABLE $_tableName(
id TEXT PRIMARY KEY,
description TEXT NOT NULL,
location TEXT NOT NULL,
imageUrls TEXT NOT NULL,
createdAt INTEGER NOT NULL,
isUploaded INTEGER NOT NULL,
censorTaskId TEXT,
bindData TEXT
)
''');
}
/// 插入一个新问题到数据库。在插入前,会为其生成一个唯一的 UUID。
/// 返回插入的行数。
Future<int> insertProblem(Problem problem) async {
problem.id = const Uuid().v4();
return await _database.insert(_tableName, problem.toMap());
}
/// 根据 ID 删除数据库中的问题。
/// 返回删除的行数。
Future<int> deleteProblem(String id) async {
return await _database.delete(_tableName, where: 'id = ?', whereArgs: [id]);
}
/// 更新数据库中已存在的问题。
/// 返回更新的行数。
Future<int> updateProblem(Problem problem) async {
return await _database.update(
_tableName,
problem.toMap(),
where: 'id = ?',
whereArgs: [problem.id],
);
}
/// 根据 ID 获取单个问题。
/// 如果找到问题,返回 Problem 对象;否则,返回 null。
Future<Problem?> getProblemById(String id) async {
final List<Map<String, dynamic>> maps = await _database.query(
_tableName,
where: 'id = ?',
whereArgs: [id],
limit: 1, // 限制结果为1,因为ID是唯一的
);
if (maps.isNotEmpty) {
// 找到问题,将其转换为 Problem 对象
return Problem.fromMap(maps.first);
} else {
// 未找到问题,返回 null
return null;
}
}
/// 通用查询方法,根据可选的筛选条件获取问题列表。
/// - `startDate`/`endDate`:筛选创建时间范围。
/// - `uploadStatus`:筛选上传状态('已上传', '未上传', '全部')。
/// - `bindStatus`:筛选绑定状态('已绑定', '未绑定', '全部')。
Future<List<Problem>> getProblems({
DateTime? startDate,
DateTime? endDate,
String uploadStatus = '全部',
String bindStatus = '全部',
}) async {
final List<String> whereClauses = [];
final List<dynamic> whereArgs = [];
if (startDate != null) {
whereClauses.add('createdAt >= ?');
whereArgs.add(startDate.millisecondsSinceEpoch);
}
if (endDate != null) {
whereClauses.add('createdAt <= ?');
whereArgs.add(endDate.millisecondsSinceEpoch);
}
if (uploadStatus != '全部') {
whereClauses.add('isUploaded = ?');
whereArgs.add(uploadStatus == '已上传' ? 1 : 0);
}
if (bindStatus != '全部') {
if (bindStatus == '已绑定') {
whereClauses.add('censorTaskId IS NOT NULL');
} else {
whereClauses.add('censorTaskId IS NULL');
}
}
final String whereString = whereClauses.join(' AND ');
final List<Map<String, dynamic>> maps = await _database.query(
_tableName,
where: whereString.isEmpty ? null : whereString,
whereArgs: whereArgs.isEmpty ? null : whereArgs,
orderBy: 'createdAt DESC',
);
return List.generate(maps.length, (i) {
return Problem.fromMap(maps[i]);
});
}
/// onClose 是 GetxService 的生命周期方法,在服务被销毁前调用。
/// 虽然 GetxService 默认是永久的,但养成关闭数据库连接的习惯是很好的实践。
@override
void onClose() {
_database.close();
super.onClose();
}
}