|
|
|
// sqlite_provider.dart
|
|
|
|
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
import 'package:problem_check_system/data/models/problem_sync_status.dart';
|
|
|
|
import 'package:problem_check_system/data/models/problem_model.dart';
|
|
|
|
import 'package:sqflite/sqflite.dart';
|
|
|
|
import 'package:path/path.dart';
|
|
|
|
|
|
|
|
/// `SQLiteProvider` 是一个 GetxService,负责管理本地 SQLite 数据库。
|
|
|
|
/// 作为一个单例服务,它在整个应用生命周期中只会被创建一次。
|
|
|
|
class SQLiteProvider extends GetxService {
|
|
|
|
static const String _dbName = 'problems.db';
|
|
|
|
static const String _tableName = 'problems';
|
|
|
|
static const int _dbVersion = 1;
|
|
|
|
|
|
|
|
late Database _database;
|
|
|
|
|
|
|
|
@override
|
|
|
|
void onInit() {
|
|
|
|
super.onInit();
|
|
|
|
_initDatabase();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 异步初始化数据库连接
|
|
|
|
Future<void> _initDatabase() async {
|
|
|
|
try {
|
|
|
|
final databasePath = await getDatabasesPath();
|
|
|
|
final path = join(databasePath, _dbName);
|
|
|
|
|
|
|
|
_database = await openDatabase(
|
|
|
|
path,
|
|
|
|
version: _dbVersion,
|
|
|
|
onCreate: _onCreate,
|
|
|
|
onUpgrade: _onUpgrade,
|
|
|
|
);
|
|
|
|
|
|
|
|
Get.log('数据库初始化成功');
|
|
|
|
} catch (e) {
|
|
|
|
Get.log('数据库初始化失败:$e', isError: true);
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 数据库创建时的回调函数
|
|
|
|
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,
|
|
|
|
creationTime INTEGER NOT NULL,
|
|
|
|
lastModifiedTime INTEGER NOT NULL,
|
|
|
|
syncStatus INTEGER NOT NULL,
|
|
|
|
censorTaskId TEXT,
|
|
|
|
bindData TEXT,
|
|
|
|
isChecked INTEGER NOT NULL
|
|
|
|
)
|
|
|
|
''');
|
|
|
|
|
|
|
|
Get.log('数据库表创建成功');
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 数据库版本升级处理
|
|
|
|
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
|
|
|
|
Get.log('正在将数据库从版本 $oldVersion 升级到 $newVersion...');
|
|
|
|
|
|
|
|
// 版本升级迁移逻辑
|
|
|
|
for (int version = oldVersion + 1; version <= newVersion; version++) {
|
|
|
|
await _runMigration(db, version);
|
|
|
|
}
|
|
|
|
|
|
|
|
Get.log('数据库升级完成');
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 执行特定版本的数据库迁移
|
|
|
|
Future<void> _runMigration(Database db, int version) async {
|
|
|
|
switch (version) {
|
|
|
|
case 2:
|
|
|
|
// 版本2迁移逻辑
|
|
|
|
// await db.execute('ALTER TABLE $_tableName ADD COLUMN newColumn TEXT;');
|
|
|
|
break;
|
|
|
|
// 添加更多版本迁移逻辑
|
|
|
|
default:
|
|
|
|
Get.log('没有找到版本 $version 的迁移脚本');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 插入问题记录,并设置同步状态为未同步
|
|
|
|
Future<int> insertProblem(Problem problem) async {
|
|
|
|
try {
|
|
|
|
final result = await _database.insert(
|
|
|
|
_tableName,
|
|
|
|
problem.toMap(),
|
|
|
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
|
|
|
);
|
|
|
|
|
|
|
|
Get.log('问题记录插入成功,ID: ${problem.id}');
|
|
|
|
return result;
|
|
|
|
} catch (e) {
|
|
|
|
Get.log('插入问题失败(ID: ${problem.id}):$e', isError: true);
|
|
|
|
throw Exception('');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 从数据库物理删除问题记录
|
|
|
|
Future<int> deleteProblem(String problemId) async {
|
|
|
|
try {
|
|
|
|
final result = await _database.delete(
|
|
|
|
_tableName,
|
|
|
|
where: 'id = ?',
|
|
|
|
whereArgs: [problemId],
|
|
|
|
);
|
|
|
|
|
|
|
|
if (result > 0) {
|
|
|
|
Get.log('问题删除成功,ID: $problemId');
|
|
|
|
} else {
|
|
|
|
Get.log('未找到要删除的问题,ID: $problemId');
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
} catch (e) {
|
|
|
|
Get.log('删除问题失败(ID: $problemId):$e', isError: true);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 更新问题记录,并设置同步状态为未同步
|
|
|
|
Future<int> updateProblem(Problem problem) async {
|
|
|
|
try {
|
|
|
|
final result = await _database.update(
|
|
|
|
_tableName,
|
|
|
|
problem.toMap(),
|
|
|
|
where: 'id = ?',
|
|
|
|
whereArgs: [problem.id],
|
|
|
|
);
|
|
|
|
|
|
|
|
if (result > 0) {
|
|
|
|
Get.log('问题更新成功,ID: ${problem.id}');
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
} catch (e) {
|
|
|
|
Get.log('更新问题失败(ID: ${problem.id}):$e', isError: true);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// /// 获取需要同步的问题记录(所有同步状态为未同步的记录)
|
|
|
|
// Future<List<Problem>> getProblemsForSync() async {
|
|
|
|
// try {
|
|
|
|
// final results = await _database.query(
|
|
|
|
// _tableName,
|
|
|
|
// where: 'syncStatus = ?',
|
|
|
|
// whereArgs: [SyncStatus.notSynced.index],
|
|
|
|
// orderBy: 'creationTime ASC',
|
|
|
|
// );
|
|
|
|
|
|
|
|
// Get.log('找到 ${results.length} 条需要同步的记录');
|
|
|
|
// return results.map((json) => Problem.fromMap(json)).toList();
|
|
|
|
// } catch (e) {
|
|
|
|
// Get.log('获取待同步问题失败:$e', isError: true);
|
|
|
|
// return [];
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
/// 标记问题为已同步(在同步成功后调用)
|
|
|
|
Future<int> markAsSynced(String id) async {
|
|
|
|
try {
|
|
|
|
final result = await _database.update(
|
|
|
|
_tableName,
|
|
|
|
{'syncStatus': ProblemSyncStatus.synced.index},
|
|
|
|
where: 'id = ?',
|
|
|
|
whereArgs: [id],
|
|
|
|
);
|
|
|
|
|
|
|
|
if (result > 0) {
|
|
|
|
Get.log('问题标记为已同步,ID: $id');
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
} catch (e) {
|
|
|
|
Get.log('标记同步状态失败(ID: $id):$e', isError: true);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 根据ID获取问题记录
|
|
|
|
Future<Problem?> getProblemById(String id) async {
|
|
|
|
try {
|
|
|
|
final results = await _database.query(
|
|
|
|
_tableName,
|
|
|
|
where: 'id = ?',
|
|
|
|
whereArgs: [id],
|
|
|
|
limit: 1,
|
|
|
|
);
|
|
|
|
|
|
|
|
return results.isNotEmpty ? Problem.fromMap(results.first) : null;
|
|
|
|
} catch (e) {
|
|
|
|
Get.log('获取问题失败(ID: $id):$e', isError: true);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 获取问题列表(支持多种筛选条件)
|
|
|
|
Future<List<Problem>> getProblems({
|
|
|
|
DateTime? startDate,
|
|
|
|
DateTime? endDate,
|
|
|
|
String? syncStatus,
|
|
|
|
String? bindStatus,
|
|
|
|
}) async {
|
|
|
|
try {
|
|
|
|
final whereClauses = <String>[];
|
|
|
|
final whereArgs = <dynamic>[];
|
|
|
|
|
|
|
|
// 时间范围筛选
|
|
|
|
if (startDate != null) {
|
|
|
|
whereClauses.add('creationTime >= ?');
|
|
|
|
whereArgs.add(startDate.millisecondsSinceEpoch);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (endDate != null) {
|
|
|
|
whereClauses.add('creationTime <= ?');
|
|
|
|
whereArgs.add(endDate.millisecondsSinceEpoch);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 同步状态筛选
|
|
|
|
if (syncStatus != null && syncStatus != '全部') {
|
|
|
|
if (syncStatus == '未上传') {
|
|
|
|
whereClauses.add('syncStatus IN (?, ?, ?)');
|
|
|
|
whereArgs.addAll([
|
|
|
|
ProblemSyncStatus.pendingCreate.index,
|
|
|
|
ProblemSyncStatus.pendingUpdate.index,
|
|
|
|
ProblemSyncStatus.pendingDelete.index,
|
|
|
|
]);
|
|
|
|
} else {
|
|
|
|
whereClauses.add('syncStatus = ?');
|
|
|
|
whereArgs.add(ProblemSyncStatus.synced.index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 绑定状态筛选
|
|
|
|
if (bindStatus != null && bindStatus != '全部') {
|
|
|
|
if (bindStatus == '已绑定') {
|
|
|
|
whereClauses.add('bindData IS NOT NULL AND bindData != ""');
|
|
|
|
} else {
|
|
|
|
whereClauses.add('(bindData IS NULL OR bindData = "")');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
final results = await _database.query(
|
|
|
|
_tableName,
|
|
|
|
where: whereClauses.isNotEmpty ? whereClauses.join(' AND ') : null,
|
|
|
|
whereArgs: whereArgs.isEmpty ? null : whereArgs,
|
|
|
|
orderBy: 'creationTime DESC',
|
|
|
|
);
|
|
|
|
|
|
|
|
return results.map((json) => Problem.fromMap(json)).toList();
|
|
|
|
} catch (e) {
|
|
|
|
Get.log('获取问题列表失败:$e', isError: true);
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void onClose() {
|
|
|
|
_database.close();
|
|
|
|
Get.log('数据库连接已关闭');
|
|
|
|
super.onClose();
|
|
|
|
}
|
|
|
|
}
|