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 _initDatabase() async { final databasePath = await getDatabasesPath(); final path = join(databasePath, _dbName); _database = await openDatabase(path, version: 1, onCreate: _onCreate); } /// 数据库创建时的回调函数,用于执行建表语句。 Future _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 insertProblem(Problem problem) async { problem.id = const Uuid().v4(); return await _database.insert(_tableName, problem.toMap()); } /// 根据 ID 删除数据库中的问题。 /// 返回删除的行数。 Future deleteProblem(String id) async { return await _database.delete(_tableName, where: 'id = ?', whereArgs: [id]); } /// 更新数据库中已存在的问题。 /// 返回更新的行数。 Future updateProblem(Problem problem) async { return await _database.update( _tableName, problem.toMap(), where: 'id = ?', whereArgs: [problem.id], ); } /// 根据 ID 获取单个问题。 /// 如果找到问题,返回 Problem 对象;否则,返回 null。 Future getProblemById(String id) async { final List> 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> getProblems({ DateTime? startDate, DateTime? endDate, String uploadStatus = '全部', String bindStatus = '全部', }) async { final List whereClauses = []; final List 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> 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(); } }