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.
148 lines
4.7 KiB
148 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(); |
|
} |
|
}
|
|
|