|
|
@ -25,6 +25,7 @@ class ProblemController extends GetxController |
|
|
|
with GetSingleTickerProviderStateMixin { |
|
|
|
with GetSingleTickerProviderStateMixin { |
|
|
|
/// 依赖问题数据 |
|
|
|
/// 依赖问题数据 |
|
|
|
final ProblemRepository problemRepository; |
|
|
|
final ProblemRepository problemRepository; |
|
|
|
|
|
|
|
final ProblemStateManager problemStateManager; |
|
|
|
final FileRepository fileRepository = Get.find<FileRepository>(); |
|
|
|
final FileRepository fileRepository = Get.find<FileRepository>(); |
|
|
|
|
|
|
|
|
|
|
|
/// 最近问题列表 |
|
|
|
/// 最近问题列表 |
|
|
@ -103,7 +104,10 @@ class ProblemController extends GetxController |
|
|
|
/// get 选中的 |
|
|
|
/// get 选中的 |
|
|
|
RxBool get isOnline => problemRepository.isOnline; |
|
|
|
RxBool get isOnline => problemRepository.isOnline; |
|
|
|
|
|
|
|
|
|
|
|
ProblemController({required this.problemRepository}); |
|
|
|
ProblemController({ |
|
|
|
|
|
|
|
required this.problemRepository, |
|
|
|
|
|
|
|
required this.problemStateManager, |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
@override |
|
|
|
void onInit() { |
|
|
|
void onInit() { |
|
|
@ -457,7 +461,7 @@ class ProblemController extends GetxController |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
const int totalSteps = 4; |
|
|
|
const int totalSteps = 5; |
|
|
|
syncProgress.startSync(totalSteps); |
|
|
|
syncProgress.startSync(totalSteps); |
|
|
|
|
|
|
|
|
|
|
|
// 1. 从服务器获取最新数据 |
|
|
|
// 1. 从服务器获取最新数据 |
|
|
@ -477,10 +481,11 @@ class ProblemController extends GetxController |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// 4. 启动图片下载任务 |
|
|
|
// 4. 启动图片下载任务 |
|
|
|
_downloadImagesForProblems(downloadedProblems); |
|
|
|
syncProgress.updateProgress('正在下载图片...', 4); |
|
|
|
|
|
|
|
await downloadImagesForProblems(downloadedProblems); |
|
|
|
|
|
|
|
|
|
|
|
// 5. 重新加载本地问题列表 |
|
|
|
// 5. 重新加载本地问题列表 |
|
|
|
syncProgress.updateProgress('正在重新加载数据...', 4); |
|
|
|
syncProgress.updateProgress('正在重新加载数据...', 5); |
|
|
|
await loadProblems(); |
|
|
|
await loadProblems(); |
|
|
|
|
|
|
|
|
|
|
|
syncProgress.completeSync(); |
|
|
|
syncProgress.completeSync(); |
|
|
@ -500,64 +505,154 @@ class ProblemController extends GetxController |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 异步下载问题的图片 |
|
|
|
/// 异步下载问题的图片,返回下载完成的任务 |
|
|
|
void _downloadImagesForProblems(List<Problem> problems) { |
|
|
|
Future<void> downloadImagesForProblems(List<Problem> problems) async { |
|
|
|
if (problems.isEmpty) return; |
|
|
|
if (problems.isEmpty) return; |
|
|
|
|
|
|
|
|
|
|
|
// 在后台执行图片下载 |
|
|
|
final imageRepository = Get.find<ImageRepository>(); |
|
|
|
Future(() async { |
|
|
|
final List<Future<void>> downloadFutures = []; |
|
|
|
final imageRepository = Get.find<ImageRepository>(); // 使用GetX获取实例 |
|
|
|
|
|
|
|
|
|
|
|
for (final problem in problems) { |
|
|
|
for (final problem in problems) { |
|
|
|
// 为每个问题创建下载任务 |
|
|
|
try { |
|
|
|
final downloadFuture = _downloadProblemImages(problem, imageRepository); |
|
|
|
final List<ImageMetadata> downloadedImages = []; |
|
|
|
downloadFutures.add(downloadFuture); |
|
|
|
|
|
|
|
} |
|
|
|
for (final imageMeta in problem.imageUrls) { |
|
|
|
|
|
|
|
if (imageMeta.remoteUrl != null && |
|
|
|
// 等待所有问题图片下载完成 |
|
|
|
imageMeta.remoteUrl!.isNotEmpty) { |
|
|
|
await Future.wait(downloadFutures); |
|
|
|
// 检查是否已下载 |
|
|
|
} |
|
|
|
final bool isDownloaded = await imageRepository.isImageDownloaded( |
|
|
|
|
|
|
|
imageMeta.remoteUrl!, |
|
|
|
|
|
|
|
problem.id, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String localPath; |
|
|
|
/// 下载单个问题的所有图片 |
|
|
|
if (isDownloaded) { |
|
|
|
Future<void> _downloadProblemImages( |
|
|
|
// 如果已下载,获取本地路径 |
|
|
|
Problem problem, |
|
|
|
localPath = (await imageRepository.getLocalImagePath( |
|
|
|
ImageRepository imageRepository, |
|
|
|
imageMeta.remoteUrl!, |
|
|
|
) async { |
|
|
|
problem.id, |
|
|
|
try { |
|
|
|
))!; |
|
|
|
final List<ImageMetadata> downloadedImages = []; |
|
|
|
} else { |
|
|
|
final List<Future<void>> imageFutures = []; |
|
|
|
// 下载图片到本地 |
|
|
|
|
|
|
|
localPath = await imageRepository.downloadImage( |
|
|
|
for (final imageMeta in problem.imageUrls) { |
|
|
|
imageMeta.remoteUrl!, |
|
|
|
if (imageMeta.remoteUrl != null && imageMeta.remoteUrl!.isNotEmpty) { |
|
|
|
problem.id, |
|
|
|
// 为每张图片创建下载任务 |
|
|
|
); |
|
|
|
final imageFuture = |
|
|
|
} |
|
|
|
_downloadSingleImage(imageMeta, problem.id, imageRepository).then( |
|
|
|
|
|
|
|
(downloadedImage) { |
|
|
|
// 更新图片元数据 |
|
|
|
if (downloadedImage != null) { |
|
|
|
final downloadedImage = imageMeta.copyWith( |
|
|
|
downloadedImages.add(downloadedImage); |
|
|
|
localPath: localPath, |
|
|
|
} |
|
|
|
status: ImageStatus.synced, |
|
|
|
}, |
|
|
|
); |
|
|
|
); |
|
|
|
downloadedImages.add(downloadedImage); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 更新问题的图片数据 |
|
|
|
imageFutures.add(imageFuture); |
|
|
|
if (downloadedImages.isNotEmpty) { |
|
|
|
|
|
|
|
final updatedProblem = problem.copyWith( |
|
|
|
|
|
|
|
imageUrls: downloadedImages, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
await problemRepository.updateProblem(updatedProblem); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
|
|
Get.log('下载问题 ${problem.id} 的图片失败: $e'); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// 等待当前问题的所有图片下载完成 |
|
|
|
|
|
|
|
await Future.wait(imageFutures); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 更新问题的图片数据 |
|
|
|
|
|
|
|
if (downloadedImages.isNotEmpty) { |
|
|
|
|
|
|
|
final updatedProblem = problem.copyWith(imageUrls: downloadedImages); |
|
|
|
|
|
|
|
await problemRepository.updateProblem(updatedProblem); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
|
|
Get.log('下载问题 ${problem.id} 的图片失败: $e'); |
|
|
|
|
|
|
|
rethrow; // 重新抛出异常,让调用方知道失败 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// 下载单张图片 |
|
|
|
|
|
|
|
Future<ImageMetadata?> _downloadSingleImage( |
|
|
|
|
|
|
|
ImageMetadata imageMeta, |
|
|
|
|
|
|
|
String problemId, |
|
|
|
|
|
|
|
ImageRepository imageRepository, |
|
|
|
|
|
|
|
) async { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
final bool isDownloaded = await imageRepository.isImageDownloaded( |
|
|
|
|
|
|
|
imageMeta.remoteUrl!, |
|
|
|
|
|
|
|
problemId, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String localPath; |
|
|
|
|
|
|
|
if (isDownloaded) { |
|
|
|
|
|
|
|
localPath = (await imageRepository.getLocalImagePath( |
|
|
|
|
|
|
|
imageMeta.remoteUrl!, |
|
|
|
|
|
|
|
problemId, |
|
|
|
|
|
|
|
))!; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
localPath = await imageRepository.downloadImage( |
|
|
|
|
|
|
|
imageMeta.remoteUrl!, |
|
|
|
|
|
|
|
problemId, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return imageMeta.copyWith( |
|
|
|
|
|
|
|
localPath: localPath, |
|
|
|
|
|
|
|
status: ImageStatus.synced, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
|
|
Get.log('下载图片 ${imageMeta.remoteUrl} 失败: $e'); |
|
|
|
|
|
|
|
return null; // 单张图片失败不影响其他图片 |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// /// 异步下载问题的图片 |
|
|
|
|
|
|
|
// void _downloadImagesForProblems(List<Problem> problems) { |
|
|
|
|
|
|
|
// if (problems.isEmpty) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // 在后台执行图片下载 |
|
|
|
|
|
|
|
// Future(() async { |
|
|
|
|
|
|
|
// final imageRepository = Get.find<ImageRepository>(); // 使用GetX获取实例 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// for (final problem in problems) { |
|
|
|
|
|
|
|
// try { |
|
|
|
|
|
|
|
// final List<ImageMetadata> downloadedImages = []; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// for (final imageMeta in problem.imageUrls) { |
|
|
|
|
|
|
|
// if (imageMeta.remoteUrl != null && |
|
|
|
|
|
|
|
// imageMeta.remoteUrl!.isNotEmpty) { |
|
|
|
|
|
|
|
// // 检查是否已下载 |
|
|
|
|
|
|
|
// final bool isDownloaded = await imageRepository.isImageDownloaded( |
|
|
|
|
|
|
|
// imageMeta.remoteUrl!, |
|
|
|
|
|
|
|
// problem.id, |
|
|
|
|
|
|
|
// ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// String localPath; |
|
|
|
|
|
|
|
// if (isDownloaded) { |
|
|
|
|
|
|
|
// // 如果已下载,获取本地路径 |
|
|
|
|
|
|
|
// localPath = (await imageRepository.getLocalImagePath( |
|
|
|
|
|
|
|
// imageMeta.remoteUrl!, |
|
|
|
|
|
|
|
// problem.id, |
|
|
|
|
|
|
|
// ))!; |
|
|
|
|
|
|
|
// } else { |
|
|
|
|
|
|
|
// // 下载图片到本地 |
|
|
|
|
|
|
|
// localPath = await imageRepository.downloadImage( |
|
|
|
|
|
|
|
// imageMeta.remoteUrl!, |
|
|
|
|
|
|
|
// problem.id, |
|
|
|
|
|
|
|
// ); |
|
|
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // 更新图片元数据 |
|
|
|
|
|
|
|
// final downloadedImage = imageMeta.copyWith( |
|
|
|
|
|
|
|
// localPath: localPath, |
|
|
|
|
|
|
|
// status: ImageStatus.synced, |
|
|
|
|
|
|
|
// ); |
|
|
|
|
|
|
|
// downloadedImages.add(downloadedImage); |
|
|
|
|
|
|
|
// } |
|
|
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // 更新问题的图片数据 |
|
|
|
|
|
|
|
// if (downloadedImages.isNotEmpty) { |
|
|
|
|
|
|
|
// final updatedProblem = problem.copyWith( |
|
|
|
|
|
|
|
// imageUrls: downloadedImages, |
|
|
|
|
|
|
|
// ); |
|
|
|
|
|
|
|
// await problemRepository.updateProblem(updatedProblem); |
|
|
|
|
|
|
|
// } |
|
|
|
|
|
|
|
// } catch (e) { |
|
|
|
|
|
|
|
// Get.log('下载问题 ${problem.id} 的图片失败: $e'); |
|
|
|
|
|
|
|
// } |
|
|
|
|
|
|
|
// } |
|
|
|
|
|
|
|
// }); |
|
|
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
/// 同步服务器和本地数据,返回需要下载图片的问题列表 |
|
|
|
/// 同步服务器和本地数据,返回需要下载图片的问题列表 |
|
|
|
Future<List<Problem>> _syncProblems( |
|
|
|
Future<List<Problem>> _syncProblems( |
|
|
@ -607,7 +702,9 @@ class ProblemController extends GetxController |
|
|
|
final serverUpdated = serverProblem.lastModificationTime; |
|
|
|
final serverUpdated = serverProblem.lastModificationTime; |
|
|
|
final localUpdated = localProblem.lastModifiedTime; |
|
|
|
final localUpdated = localProblem.lastModifiedTime; |
|
|
|
|
|
|
|
|
|
|
|
if (serverUpdated != null && serverUpdated.isAfter(localUpdated)) { |
|
|
|
if (serverUpdated != null && |
|
|
|
|
|
|
|
serverUpdated.millisecondsSinceEpoch > |
|
|
|
|
|
|
|
localUpdated.millisecondsSinceEpoch) { |
|
|
|
// 服务器数据更新,更新本地数据 |
|
|
|
// 服务器数据更新,更新本地数据 |
|
|
|
final updatedProblem = _convertServerProblemToLocal(serverProblem); |
|
|
|
final updatedProblem = _convertServerProblemToLocal(serverProblem); |
|
|
|
await problemRepository.updateProblem(updatedProblem); |
|
|
|
await problemRepository.updateProblem(updatedProblem); |
|
|
@ -640,6 +737,7 @@ class ProblemController extends GetxController |
|
|
|
location: serverProblem.location, |
|
|
|
location: serverProblem.location, |
|
|
|
imageUrls: imageMetadatas, |
|
|
|
imageUrls: imageMetadatas, |
|
|
|
creationTime: serverProblem.creationTime, |
|
|
|
creationTime: serverProblem.creationTime, |
|
|
|
|
|
|
|
creatorId: serverProblem.creatorId, |
|
|
|
lastModifiedTime: serverProblem.lastModificationTime ?? DateTime.now(), |
|
|
|
lastModifiedTime: serverProblem.lastModificationTime ?? DateTime.now(), |
|
|
|
syncStatus: ProblemSyncStatus.synced, // 来自服务器的数据标记为已同步 |
|
|
|
syncStatus: ProblemSyncStatus.synced, // 来自服务器的数据标记为已同步 |
|
|
|
censorTaskId: serverProblem.censorTaskId, |
|
|
|
censorTaskId: serverProblem.censorTaskId, |
|
|
@ -836,7 +934,7 @@ class ProblemController extends GetxController |
|
|
|
/// 控制器中可以添加逻辑 |
|
|
|
/// 控制器中可以添加逻辑 |
|
|
|
Future<void> deleteProblem(Problem problem) async { |
|
|
|
Future<void> deleteProblem(Problem problem) async { |
|
|
|
try { |
|
|
|
try { |
|
|
|
final deleteProblem = ProblemStateManager.markForDeletion(problem); |
|
|
|
final deleteProblem = problemStateManager.markForDeletion(problem); |
|
|
|
if (deleteProblem.syncStatus == ProblemSyncStatus.untracked) { |
|
|
|
if (deleteProblem.syncStatus == ProblemSyncStatus.untracked) { |
|
|
|
// 直接删除问题和图片 |
|
|
|
// 直接删除问题和图片 |
|
|
|
await problemRepository.deleteProblem(problem.id); |
|
|
|
await problemRepository.deleteProblem(problem.id); |
|
|
|