import 'dart:io'; import 'package:dio/dio.dart'; import 'package:get/get.dart' hide MultipartFile, FormData; import 'package:problem_check_system/data/models/image_status.dart'; import 'package:problem_check_system/data/models/sync_status.dart'; import 'package:problem_check_system/data/models/image_metadata_model.dart'; import 'package:problem_check_system/data/models/problem_model.dart'; import 'package:problem_check_system/data/providers/connectivity_provider.dart'; import 'package:problem_check_system/data/providers/http_provider.dart'; import 'package:problem_check_system/data/providers/sqlite_provider.dart'; import 'package:problem_check_system/data/repositories/file_repository.dart'; /// 问题仓库,负责处理问题数据的本地持久化。 /// 它封装了底层数据库操作,为业务逻辑层提供一个简洁的接口。 class ProblemRepository extends GetxService { final SQLiteProvider sqliteProvider; final HttpProvider httpProvider; final ConnectivityProvider connectivityProvider; final FileRepository fileRepository = Get.find(); RxBool get isOnline => connectivityProvider.isOnline; ProblemRepository({ required this.sqliteProvider, required this.httpProvider, required this.connectivityProvider, }); /// 更新本地数据库中的一个问题。 /// 如果问题存在则更新,如果不存在则插入。 Future updateProblem(Problem problem) async { // 检查问题是否存在,通常通过ID判断 final existingProblem = await sqliteProvider.getProblemById(problem.id!); if (existingProblem != null) { // 问题已存在,执行更新操作 await sqliteProvider.updateProblem(problem); } else { // 问题不存在,执行插入操作 await sqliteProvider.insertProblem(problem); } } /// 通用查询方法,根据可选的筛选条件获取问题列表。 /// - `startDate`/`endDate`:筛选创建时间范围。 /// - `syncStatus`:筛选上传状态('已上传', '未上传', '全部')。 /// - `bindStatus`:筛选绑定状态('已绑定', '未绑定', '全部')。 Future getProblems({ DateTime? startDate, DateTime? endDate, String? syncStatus, String? bindStatus, }) async { return await sqliteProvider.getProblems( startDate: startDate, endDate: endDate, syncStatus: syncStatus, bindStatus: bindStatus, ); } Future insertProblem(Problem problem) async { await sqliteProvider.insertProblem(problem); } Future deleteProblem(String id) async { await sqliteProvider.deleteProblem(id); } // * /api/Objects/association/${file.name} /// 上传单个问题及其所有关联的图片。 Future uploadProblem( Problem problem, { required CancelToken cancelToken, required void Function(double progress) onProgress, }) async { try { final newImages = problem.imageUrls .where((img) => img.status == ImageStatus.local) .toList(); final totalFilesToUpload = newImages.length; int filesUploadedCount = 0; // 1. 上传所有状态为 ImageStatus.local 的新图片 final List remoteUrls = []; for (var image in newImages) { // 修正:当上传被取消时,抛出异常而不是无返回值的 return if (cancelToken.isCancelled) { throw DioException( requestOptions: RequestOptions(path: ''), type: DioExceptionType.cancel, error: '上传已取消', ); } final imageFile = File(image.localPath); final url = await fileRepository.uploadImage( imageFile, cancelToken: cancelToken, onSendProgress: (sent, total) { double overallProgress = (filesUploadedCount + (sent / total)) / totalFilesToUpload; onProgress(overallProgress); }, ); remoteUrls.add(url); filesUploadedCount++; } onProgress(1.0); // 确保图片上传进度为100% // 2. 构建 API payload final List finalRemoteUrls = []; int newImageIndex = 0; for (var image in problem.imageUrls) { if (image.status == ImageStatus.synced) { finalRemoteUrls.add(image.remoteUrl!); } else if (image.status == ImageStatus.local) { finalRemoteUrls.add(remoteUrls[newImageIndex]); newImageIndex++; } } final apiPayload = { 'id': problem.id, 'description': problem.description, 'location': problem.location, 'imageUrls': finalRemoteUrls, // 使用整合后的网络URL列表 'createdAt': problem.creationTime.toIso8601String(), // ... 其他字段 }; // 3. 发送给服务器 final response = await httpProvider.post( '/api/problem', data: apiPayload, cancelToken: cancelToken, ); // 4. 处理服务器响应,并更新本地模型状态 if (response.statusCode == 200) { final List updatedImageMetadata = []; int uploadedUrlIndex = 0; for (var image in problem.imageUrls) { if (image.status == ImageStatus.local) { updatedImageMetadata.add( ImageMetadata( localPath: image.localPath, remoteUrl: remoteUrls[uploadedUrlIndex], status: ImageStatus.synced, ), ); uploadedUrlIndex++; } else { updatedImageMetadata.add(image); } } // 返回一个包含新同步状态和更新后图片列表的对象 return problem.copyWith( syncStatus: SyncStatus.synced, imageUrls: updatedImageMetadata, ); } else { throw Exception('问题上传失败,状态码: ${response.statusCode}'); } } on DioException { rethrow; } } /// 新增:上传问题列表。 /// 遍历问题列表,并计算总进度。 Future> uploadProblems( List problems, { required CancelToken cancelToken, required void Function(double progress) onProgress, }) async { final int totalProblems = problems.length; final List updatedProblems = []; try { for (int i = 0; i < totalProblems; i++) { // 如果取消令牌被触发,停止并返回 if (cancelToken.isCancelled) { break; } final problemToUpload = problems[i]; // 传递一个子进度回调,用于计算单个问题的进度 final updatedProblem = await uploadProblem( problemToUpload, cancelToken: cancelToken, onProgress: (progress) { // 计算总体进度:(已完成的问题数 + 当前问题的进度) / 总问题数 final overallProgress = (i + progress) / totalProblems; onProgress(overallProgress); }, ); updatedProblems.add(updatedProblem); } return updatedProblems; } on DioException { rethrow; } } getProblemsForSync() { return sqliteProvider.getProblemsForSync(); } }