diff --git a/lib/app/bindings/initial_binding.dart b/lib/app/bindings/initial_binding.dart index 664b607..4f60969 100644 --- a/lib/app/bindings/initial_binding.dart +++ b/lib/app/bindings/initial_binding.dart @@ -1,14 +1,14 @@ import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:problem_check_system/data/providers/connectivity_provider.dart'; -import 'package:problem_check_system/data/providers/dio_provider.dart'; +import 'package:problem_check_system/data/providers/http_provider.dart'; import 'package:problem_check_system/data/providers/local_database.dart'; class InitialBinding implements Bindings { @override void dependencies() { Get.put(GetStorage(), permanent: true); - Get.put(DioProvider(), permanent: true); + Get.put(HttpProvider(), permanent: true); Get.put(LocalDatabase(), permanent: true); Get.put(ConnectivityProvider(), permanent: true); } diff --git a/lib/data/providers/dio_provider.dart b/lib/data/providers/http_provider.dart similarity index 99% rename from lib/data/providers/dio_provider.dart rename to lib/data/providers/http_provider.dart index 51ea891..b973dfb 100644 --- a/lib/data/providers/dio_provider.dart +++ b/lib/data/providers/http_provider.dart @@ -7,7 +7,7 @@ import 'package:problem_check_system/modules/auth/controllers/auth_controller.da // DioProvider 是一个 GetxService,确保它在应用生命周期内是单例的。 // 它负责初始化和配置 Dio 实例,并添加所有拦截器。 -class DioProvider extends GetxService { +class HttpProvider extends GetxService { static const String _baseUrl = 'https://xh.anxincloud.cn'; late final Dio _dio; diff --git a/lib/data/providers/local_database.dart b/lib/data/providers/local_database.dart index f06700a..82d41e8 100644 --- a/lib/data/providers/local_database.dart +++ b/lib/data/providers/local_database.dart @@ -1,27 +1,35 @@ +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'; -class LocalDatabase { - static final LocalDatabase _instance = LocalDatabase._internal(); - factory LocalDatabase() => _instance; - static Database? _database; +/// LocalDatabase 是一个 GetxService,它负责管理本地 SQLite 数据库。 +/// GetxService 确保这个类在整个应用生命周期中都是一个单例,并且不会被自动销毁。 +class LocalDatabase extends GetxService { + static const String _dbName = 'problems.db'; static const String _tableName = 'problems'; - LocalDatabase._internal(); + /// 私有的数据库实例,只能在 LocalDatabase 类内部访问。 + late Database _database; - Future get database async { - if (_database != null) return _database!; - _database = await _initDatabase(); - return _database!; + /// onInit 是 GetxService 的生命周期方法,在服务首次被创建时调用。 + /// 它是执行异步初始化的最佳位置,确保在使用服务前,数据库已经准备就绪。 + @override + void onInit() { + super.onInit(); + _initDatabase(); } - Future _initDatabase() async { - String path = join(await getDatabasesPath(), 'problems.db'); - return await openDatabase(path, version: 1, onCreate: _onCreate); + /// 异步初始化数据库。它会打开数据库连接,如果数据库不存在则创建。 + 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( @@ -37,15 +45,17 @@ class LocalDatabase { '''); } + /// 插入一个新问题到数据库。在插入前,会为其生成一个唯一的 UUID。 + /// 返回插入的行数。 Future insertProblem(Problem problem) async { - final db = await database; problem.id = const Uuid().v4(); - return await db.insert(_tableName, problem.toMap()); + return await _database.insert(_tableName, problem.toMap()); } + /// 更新数据库中已存在的问题。 + /// 返回更新的行数。 Future updateProblem(Problem problem) async { - final db = await database; - return await db.update( + return await _database.update( _tableName, problem.toMap(), where: 'id = ?', @@ -53,48 +63,41 @@ class LocalDatabase { ); } + /// 根据 ID 删除数据库中的问题。 + /// 返回删除的行数。 Future deleteProblem(String id) async { - final db = await database; - return await db.delete(_tableName, where: 'id = ?', whereArgs: [id]); + return await _database.delete(_tableName, where: 'id = ?', whereArgs: [id]); } - /// 通用查询方法 - /// 根据可选的参数筛选问题。 - /// 时间范围、 - /// 上传状态 - /// 绑定状态 + /// 通用查询方法,根据可选的筛选条件获取问题列表。 + /// - `startDate`/`endDate`:筛选创建时间范围。 + /// - `uploadStatus`:筛选上传状态('已上传', '未上传', '全部')。 + /// - `bindStatus`:筛选绑定状态('已绑定', '未绑定', '全部')。 Future> getProblems({ DateTime? startDate, DateTime? endDate, - String? uploadStatus, // '已上传', '未上传', '全部' - String? bindStatus, // '已绑定', '未绑定', '全部' + String uploadStatus = '全部', + String bindStatus = '全部', }) async { - final db = await database; - - // 使用可变列表来构建筛选条件 final List whereClauses = []; final List whereArgs = []; - // 根据 startDate 添加筛选条件 if (startDate != null) { whereClauses.add('createdAt >= ?'); whereArgs.add(startDate.millisecondsSinceEpoch); } - // 根据 endDate 添加筛选条件 if (endDate != null) { whereClauses.add('createdAt <= ?'); whereArgs.add(endDate.millisecondsSinceEpoch); } - // 根据 uploadStatus 添加筛选条件 - if (uploadStatus != null && uploadStatus != '全部') { + if (uploadStatus != '全部') { whereClauses.add('isUploaded = ?'); whereArgs.add(uploadStatus == '已上传' ? 1 : 0); } - // 根据 bindStatus 添加筛选条件 - if (bindStatus != null && bindStatus != '全部') { + if (bindStatus != '全部') { if (bindStatus == '已绑定') { whereClauses.add('censorTaskId IS NOT NULL'); } else { @@ -102,10 +105,9 @@ class LocalDatabase { } } - // 将所有条件用 ' AND ' 连接起来 final String whereString = whereClauses.join(' AND '); - final List> maps = await db.query( + final List> maps = await _database.query( _tableName, where: whereString.isEmpty ? null : whereString, whereArgs: whereArgs.isEmpty ? null : whereArgs, @@ -116,4 +118,12 @@ class LocalDatabase { return Problem.fromMap(maps[i]); }); } + + /// onClose 是 GetxService 的生命周期方法,在服务被销毁前调用。 + /// 虽然 GetxService 默认是永久的,但养成关闭数据库连接的习惯是很好的实践。 + @override + void onClose() { + _database.close(); + super.onClose(); + } } diff --git a/lib/data/repositories/auth_repository.dart b/lib/data/repositories/auth_repository.dart index 8386862..18d15f2 100644 --- a/lib/data/repositories/auth_repository.dart +++ b/lib/data/repositories/auth_repository.dart @@ -5,15 +5,15 @@ import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:problem_check_system/data/models/auth_model.dart'; import 'package:problem_check_system/data/providers/connectivity_provider.dart'; -import 'package:problem_check_system/data/providers/dio_provider.dart'; +import 'package:problem_check_system/data/providers/http_provider.dart'; class AuthRepository extends GetxService { - final DioProvider dioProvider; + final HttpProvider httpProvider; final GetStorage storage; final ConnectivityProvider connectivityProvider; AuthRepository({ - required this.dioProvider, + required this.httpProvider, required this.storage, required this.connectivityProvider, }); @@ -88,7 +88,7 @@ class AuthRepository extends GetxService { /// Handles the user login process by calling the API and saving the response. Future login(LoginRequest request) async { try { - final response = await dioProvider.post( + final response = await httpProvider.post( '/api/Accounts/SignIn', data: request.toJson(), ); @@ -100,6 +100,23 @@ class AuthRepository extends GetxService { } } + /// 从 API 获取用户个人资料 + Future getUserProfile() async { + try { + final response = await httpProvider.get('/api/Accounts/Profile'); + + // 将 JSON 数据反序列化为 Profile 模型 + return Profile.fromJson(response.data); + } on DioException catch (e) { + // 专门处理网络错误,例如无网络连接、超时等 + // DioException 包含了更详细的错误信息 + throw Exception('Network error: ${e.message}'); + } catch (e) { + // 捕获所有其他未知错误 + rethrow; // 重新抛出异常,让调用者来处理 + } + } + /// Refreshes the authentication token using the refresh token. Future refreshToken() async { final refreshToken = getRefreshToken(); @@ -108,7 +125,7 @@ class AuthRepository extends GetxService { } try { - final response = await dioProvider.post( + final response = await httpProvider.post( '/auth/refresh', data: {'refresh_token': refreshToken}, ); @@ -127,7 +144,7 @@ class AuthRepository extends GetxService { Future logout() async { try { // Attempt to call the API, but even if it fails, continue to clear local data. - await dioProvider.post('/auth/logout'); + await httpProvider.post('/auth/logout'); } on DioException catch (e) { log('退出登录API调用失败: ${e.message}'); } finally { diff --git a/lib/data/repositories/my_repository.dart b/lib/data/repositories/my_repository.dart deleted file mode 100644 index 2bd97aa..0000000 --- a/lib/data/repositories/my_repository.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:problem_check_system/data/models/auth_model.dart'; -import 'package:problem_check_system/data/providers/dio_provider.dart'; - -class MyRepository { - final DioProvider dioProvider; - - MyRepository({required this.dioProvider}); - - /// 从 API 获取用户个人资料 - Future getUserProfile() async { - try { - final response = await dioProvider.get('/api/Accounts/Profile'); - - // 将 JSON 数据反序列化为 Profile 模型 - return Profile.fromJson(response.data); - } on DioException catch (e) { - // 专门处理网络错误,例如无网络连接、超时等 - // DioException 包含了更详细的错误信息 - throw Exception('Network error: ${e.message}'); - } catch (e) { - // 捕获所有其他未知错误 - rethrow; // 重新抛出异常,让调用者来处理 - } - } -} diff --git a/lib/modules/auth/bindings/auth_binding.dart b/lib/modules/auth/bindings/auth_binding.dart index 217da5f..df9f83f 100644 --- a/lib/modules/auth/bindings/auth_binding.dart +++ b/lib/modules/auth/bindings/auth_binding.dart @@ -1,25 +1,18 @@ import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:problem_check_system/data/providers/connectivity_provider.dart'; -import 'package:problem_check_system/data/providers/dio_provider.dart'; +import 'package:problem_check_system/data/providers/http_provider.dart'; import 'package:problem_check_system/data/repositories/auth_repository.dart'; import 'package:problem_check_system/modules/auth/controllers/auth_controller.dart'; class AuthBinding implements Bindings { @override void dependencies() { - // 1. 注入数据提供者 (AuthProvider),它依赖于 Dio - // 依赖注入通过构造函数完成,使代码更易于测试 - final DioProvider dioProvider = Get.find(); - final GetStorage storage = Get.find(); - final ConnectivityProvider connectivityProvider = - Get.find(); - Get.lazyPut( () => AuthRepository( - dioProvider: dioProvider, - storage: storage, - connectivityProvider: connectivityProvider, + httpProvider: Get.find(), + storage: Get.find(), + connectivityProvider: Get.find(), ), ); @@ -27,7 +20,6 @@ class AuthBinding implements Bindings { // 控制器通过 Get.find() 获取已注入的依赖 Get.lazyPut( () => AuthController(authRepository: Get.find()), - fenix: true, ); } } diff --git a/lib/modules/home/bindings/home_binding.dart b/lib/modules/home/bindings/home_binding.dart index dfe9049..90675b3 100644 --- a/lib/modules/home/bindings/home_binding.dart +++ b/lib/modules/home/bindings/home_binding.dart @@ -2,7 +2,6 @@ import 'package:get/get.dart'; import 'package:problem_check_system/data/providers/connectivity_provider.dart'; import 'package:problem_check_system/data/providers/local_database.dart'; import 'package:problem_check_system/data/repositories/auth_repository.dart'; -import 'package:problem_check_system/data/repositories/my_repository.dart'; import 'package:problem_check_system/modules/auth/controllers/auth_controller.dart'; import 'package:problem_check_system/modules/home/controllers/home_controller.dart'; import 'package:problem_check_system/modules/my/controllers/my_controller.dart'; @@ -21,22 +20,21 @@ class HomeBinding implements Bindings { localDatabase: database, connectivityProvider: connectivityProvider, - dioProvider: Get.find(), + httpProvider: Get.find(), ), fenix: true, ); - Get.lazyPut(() => MyRepository(dioProvider: Get.find())); - Get.lazyPut(() => MyController(myRepository: Get.find())); + Get.lazyPut( () => AuthRepository( - dioProvider: Get.find(), + httpProvider: Get.find(), storage: Get.find(), connectivityProvider: connectivityProvider, ), ); Get.lazyPut( () => AuthController(authRepository: Get.find()), - fenix: true, ); + Get.lazyPut(() => MyController(authRepository: Get.find())); } } diff --git a/lib/modules/my/bingdings/change_password_binding.dart b/lib/modules/my/bingdings/change_password_binding.dart index aa0323c..d31471a 100644 --- a/lib/modules/my/bingdings/change_password_binding.dart +++ b/lib/modules/my/bingdings/change_password_binding.dart @@ -7,7 +7,7 @@ class ChangePasswordBinding implements Bindings { void dependencies() { Get.lazyPut( () => AuthRepository( - dioProvider: Get.find(), + httpProvider: Get.find(), storage: Get.find(), connectivityProvider: Get.find(), ), diff --git a/lib/modules/my/bingdings/my_binding.dart b/lib/modules/my/bingdings/my_binding.dart index bc057b6..a07f1ae 100644 --- a/lib/modules/my/bingdings/my_binding.dart +++ b/lib/modules/my/bingdings/my_binding.dart @@ -1,6 +1,4 @@ import 'package:get/get.dart'; -import 'package:problem_check_system/data/repositories/my_repository.dart'; -import 'package:problem_check_system/modules/my/controllers/my_controller.dart'; class MyBinding implements Bindings { @override diff --git a/lib/modules/my/controllers/my_controller.dart b/lib/modules/my/controllers/my_controller.dart index da319b1..d6f02e1 100644 --- a/lib/modules/my/controllers/my_controller.dart +++ b/lib/modules/my/controllers/my_controller.dart @@ -1,10 +1,10 @@ import 'package:get/get.dart'; -import 'package:problem_check_system/data/repositories/my_repository.dart'; +import 'package:problem_check_system/data/repositories/auth_repository.dart'; class MyController extends GetxController { - final MyRepository myRepository; + final AuthRepository authRepository; - MyController({required this.myRepository}); + MyController({required this.authRepository}); // 响应式变量,用于存储用户信息 var userName = '张兰雪'.obs; @@ -19,7 +19,7 @@ class MyController extends GetxController { // 从本地存储或API加载用户信息 Future _loadUserInfo() async { - var userProfile = await myRepository.getUserProfile(); + var userProfile = await authRepository.getUserProfile(); userName.value = userProfile.name; userPhone.value = userProfile.email ?? '138****8547'; userImage.value = userProfile.signatureImage.toString(); diff --git a/lib/modules/problem/bindings/problem_binding.dart b/lib/modules/problem/bindings/problem_binding.dart index 4b0ec50..ca23404 100644 --- a/lib/modules/problem/bindings/problem_binding.dart +++ b/lib/modules/problem/bindings/problem_binding.dart @@ -12,7 +12,7 @@ class ProblemBinding implements Bindings { Get.lazyPut( () => ProblemController( localDatabase: Get.find(), - dioProvider: Get.find(), + httpProvider: Get.find(), connectivityProvider: Get.find(), ), ); diff --git a/lib/modules/problem/controllers/problem_controller.dart b/lib/modules/problem/controllers/problem_controller.dart index b15257b..cea3c89 100644 --- a/lib/modules/problem/controllers/problem_controller.dart +++ b/lib/modules/problem/controllers/problem_controller.dart @@ -5,7 +5,7 @@ import 'package:get/get.dart' hide MultipartFile, FormData; import 'package:flutter/material.dart'; import 'dart:io'; import 'package:path/path.dart' as path; -import 'package:problem_check_system/data/providers/dio_provider.dart'; +import 'package:problem_check_system/data/providers/http_provider.dart'; import 'package:problem_check_system/modules/problem/views/widgets/custom_data_range_dropdown.dart'; import 'package:problem_check_system/data/models/problem_model.dart'; import 'package:problem_check_system/data/providers/local_database.dart'; @@ -13,8 +13,10 @@ import 'package:problem_check_system/data/providers/connectivity_provider.dart'; class ProblemController extends GetxController with GetSingleTickerProviderStateMixin { - /// 本地数据库 - final LocalDatabase _localDatabase; + /// 依赖注入适配器 + final LocalDatabase localDatabase; + final HttpProvider httpProvider; + final ConnectivityProvider connectivityProvider; /// 最近问题列表 final RxList problems = [].obs; @@ -35,8 +37,6 @@ class ProblemController extends GetxController /// 是否加载中 final RxBool isLoading = false.obs; - final DioProvider _dioProvider; - final ConnectivityProvider _connectivityProvider; late TabController tabController; @@ -47,15 +47,13 @@ class ProblemController extends GetxController final fabUploadPosition = Offset(337.0, 703.7).obs; /// get 选中的 - RxBool get isOnline => _connectivityProvider.isOnline; + RxBool get isOnline => connectivityProvider.isOnline; ProblemController({ - required LocalDatabase localDatabase, - required DioProvider dioProvider, - required ConnectivityProvider connectivityProvider, - }) : _localDatabase = localDatabase, - _dioProvider = dioProvider, - _connectivityProvider = connectivityProvider; + required this.localDatabase, + required this.httpProvider, + required this.connectivityProvider, + }); @override void onInit() { @@ -190,7 +188,7 @@ class ProblemController extends GetxController : '全部'; // 只执行一次数据库查询 - final loadedProblems = await _localDatabase.getProblems( + final loadedProblems = await localDatabase.getProblems( startDate: startDate, endDate: endDate, uploadStatus: uploadStatus, @@ -235,7 +233,7 @@ class ProblemController extends GetxController isLoading.value = true; try { // 调用 _localDatabase.getProblems 并只筛选 '未上传' 的问题 - unUploadedProblems.value = await _localDatabase.getProblems( + unUploadedProblems.value = await localDatabase.getProblems( uploadStatus: '未上传', ); } catch (e) { @@ -247,7 +245,7 @@ class ProblemController extends GetxController Future addProblem(Problem problem) async { try { - await _localDatabase.insertProblem(problem); + await localDatabase.insertProblem(problem); loadProblems(); } catch (e) { Get.snackbar('错误', '保存问题失败: $e'); @@ -257,7 +255,7 @@ class ProblemController extends GetxController Future updateProblem(Problem problem) async { try { - await _localDatabase.updateProblem(problem); + await localDatabase.updateProblem(problem); loadProblems(); } catch (e) { Get.snackbar('错误', '更新问题失败: $e'); @@ -268,7 +266,7 @@ class ProblemController extends GetxController Future deleteProblem(Problem problem) async { try { if (problem.id != null) { - await _localDatabase.deleteProblem(problem.id!); + await localDatabase.deleteProblem(problem.id!); await _deleteProblemImages(problem); loadProblems(); } @@ -287,7 +285,7 @@ class ProblemController extends GetxController try { for (var problem in problemsToDelete) { if (problem.id != null) { - await _localDatabase.deleteProblem(problem.id!); + await localDatabase.deleteProblem(problem.id!); await _deleteProblemImages(problem); } } @@ -335,7 +333,7 @@ class ProblemController extends GetxController } } - final response = await _dioProvider.post( + final response = await httpProvider.post( 'https://your-server.com/api/problems', data: formData, options: Options(