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.
204 lines
6.9 KiB
204 lines
6.9 KiB
import 'dart:io'; |
|
|
|
import 'package:dio/dio.dart'; |
|
import 'package:get/get.dart' hide MultipartFile, FormData; |
|
import 'package:problem_check_system/data/models/enum_model.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<FileRepository>(); |
|
|
|
RxBool get isOnline => connectivityProvider.isOnline; |
|
|
|
ProblemRepository({ |
|
required this.sqliteProvider, |
|
required this.httpProvider, |
|
required this.connectivityProvider, |
|
}); |
|
|
|
/// 更新本地数据库中的一个问题。 |
|
/// 如果问题存在则更新,如果不存在则插入。 |
|
Future<void> 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`:筛选创建时间范围。 |
|
/// - `uploadStatus`:筛选上传状态('已上传', '未上传', '全部')。 |
|
/// - `bindStatus`:筛选绑定状态('已绑定', '未绑定', '全部')。 |
|
Future getProblems({ |
|
DateTime? startDate, |
|
DateTime? endDate, |
|
String uploadStatus = '全部', |
|
String bindStatus = '全部', |
|
}) async { |
|
return await sqliteProvider.getProblems( |
|
startDate: startDate, |
|
endDate: endDate, |
|
); |
|
} |
|
|
|
Future<void> insertProblem(Problem problem) async { |
|
await sqliteProvider.insertProblem(problem); |
|
} |
|
|
|
Future<void> deleteProblem(String id) async { |
|
await sqliteProvider.deleteProblem(id); |
|
} |
|
|
|
// * /api/Objects/association/${file.name} |
|
/// 上传单个问题及其所有关联的图片。 |
|
Future<Problem> 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<String> 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<String> 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<ImageMetadata> 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<List<Problem>> uploadProblems( |
|
List<Problem> problems, { |
|
required CancelToken cancelToken, |
|
required void Function(double progress) onProgress, |
|
}) async { |
|
final int totalProblems = problems.length; |
|
final List<Problem> 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; |
|
} |
|
} |
|
}
|
|
|