|
|
|
@ -169,7 +169,7 @@ class ProblemController extends GetxController
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
Get.back(); |
|
|
|
|
Get.snackbar('成功', '所有问题已成功上传!', snackPosition: SnackPosition.BOTTOM); |
|
|
|
|
Get.snackbar('成功', '所有问题已成功上传!', snackPosition: SnackPosition.TOP); |
|
|
|
|
|
|
|
|
|
// 上传成功后清空选中状态 |
|
|
|
|
clearSelection(); |
|
|
|
@ -180,17 +180,17 @@ class ProblemController extends GetxController
|
|
|
|
|
} on DioException catch (e) { |
|
|
|
|
Get.back(); |
|
|
|
|
if (CancelToken.isCancel(e)) { |
|
|
|
|
Get.snackbar('提示', '上传已取消', snackPosition: SnackPosition.BOTTOM); |
|
|
|
|
Get.snackbar('提示', '上传已取消', snackPosition: SnackPosition.TOP); |
|
|
|
|
} else { |
|
|
|
|
Get.snackbar( |
|
|
|
|
'上传失败', |
|
|
|
|
'错误: ${e.message}', |
|
|
|
|
snackPosition: SnackPosition.BOTTOM, |
|
|
|
|
snackPosition: SnackPosition.TOP, |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} catch (e) { |
|
|
|
|
Get.back(); |
|
|
|
|
Get.snackbar('上传失败', '发生未知错误', snackPosition: SnackPosition.BOTTOM); |
|
|
|
|
Get.snackbar('上传失败', '发生未知错误', snackPosition: SnackPosition.TOP); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -366,12 +366,14 @@ class ProblemController extends GetxController
|
|
|
|
|
|
|
|
|
|
// 4. 处理服务器响应 |
|
|
|
|
if (response.isSuccess) { |
|
|
|
|
final problem = Problem.fromJson(response.data); |
|
|
|
|
// 更新图片状态(仅对创建和更新操作) |
|
|
|
|
final updatedImageMetadata = |
|
|
|
|
problem.syncStatus != ProblemSyncStatus.pendingDelete |
|
|
|
|
? _updateImageMetadata(problem.imageUrls, remoteUrls) |
|
|
|
|
: problem.imageUrls; |
|
|
|
|
|
|
|
|
|
Get.log(problem.lastModifiedTime.toUtc().toIso8601String()); |
|
|
|
|
// 返回同步完成的对象,操作类型重置为none |
|
|
|
|
return problem.copyWith( |
|
|
|
|
syncStatus: problem.syncStatus != ProblemSyncStatus.pendingDelete |
|
|
|
@ -435,6 +437,89 @@ class ProblemController extends GetxController
|
|
|
|
|
|
|
|
|
|
// #endregion |
|
|
|
|
|
|
|
|
|
// #region 问题同步 |
|
|
|
|
|
|
|
|
|
Future<void> pullDataFromServer() async { |
|
|
|
|
isLoading.value = true; |
|
|
|
|
try { |
|
|
|
|
// 1. 从服务器获取最新数据 |
|
|
|
|
final List<Problem> serverProblems = await problemRepository |
|
|
|
|
.fetchProblemsFromServer(); |
|
|
|
|
|
|
|
|
|
// 2. 获取本地数据 |
|
|
|
|
final List<Problem> localProblems = await problemRepository.getProblems(); |
|
|
|
|
|
|
|
|
|
// 3. 同步策略:以服务器数据为准,合并本地未上传的更改 |
|
|
|
|
await _syncProblems(serverProblems, localProblems); |
|
|
|
|
|
|
|
|
|
// 4. 重新加载本地问题列表 |
|
|
|
|
await loadProblems(); |
|
|
|
|
|
|
|
|
|
Get.snackbar('成功', '数据同步完成', snackPosition: SnackPosition.TOP); |
|
|
|
|
} catch (e) { |
|
|
|
|
Get.snackbar('同步失败', '错误: $e', snackPosition: SnackPosition.TOP); |
|
|
|
|
} finally { |
|
|
|
|
isLoading.value = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// 同步服务器和本地数据 |
|
|
|
|
Future<void> _syncProblems( |
|
|
|
|
List<Problem> serverProblems, |
|
|
|
|
List<Problem> localProblems, |
|
|
|
|
) async { |
|
|
|
|
// 创建映射以便快速查找 |
|
|
|
|
final Map<String, Problem> serverProblemsMap = { |
|
|
|
|
for (var problem in serverProblems.where((p) => p.id != null)) |
|
|
|
|
problem.id!: problem, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
final Map<String, Problem> localProblemsMap = { |
|
|
|
|
for (var problem in localProblems.where((p) => p.id != null)) |
|
|
|
|
problem.id!: problem, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// 处理服务器有但本地没有的数据(新增) |
|
|
|
|
for (final serverProblem in serverProblems) { |
|
|
|
|
if (serverProblem.id != null && |
|
|
|
|
!localProblemsMap.containsKey(serverProblem.id)) { |
|
|
|
|
// 服务器新增的问题,添加到本地 |
|
|
|
|
await problemRepository.insertProblem(serverProblem); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 处理本地有但服务器没有的数据(删除) |
|
|
|
|
for (final localProblem in localProblems) { |
|
|
|
|
if (localProblem.id != null && |
|
|
|
|
!serverProblemsMap.containsKey(localProblem.id)) { |
|
|
|
|
// 服务器已删除的问题,从本地删除 |
|
|
|
|
await problemRepository.deleteProblem(localProblem.id!); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 处理双方都有的数据(更新) |
|
|
|
|
for (final serverProblem in serverProblems) { |
|
|
|
|
if (serverProblem.id != null && |
|
|
|
|
localProblemsMap.containsKey(serverProblem.id)) { |
|
|
|
|
final localProblem = localProblemsMap[serverProblem.id]!; |
|
|
|
|
|
|
|
|
|
// 只有当本地数据已同步时才更新(避免覆盖本地未上传的更改) |
|
|
|
|
if (localProblem.syncStatus == ProblemSyncStatus.synced) { |
|
|
|
|
// 比较更新时间,使用最新的数据 |
|
|
|
|
final serverUpdated = serverProblem.lastModifiedTime; |
|
|
|
|
final localUpdated = localProblem.lastModifiedTime; |
|
|
|
|
|
|
|
|
|
if (serverUpdated.isAfter(localUpdated)) { |
|
|
|
|
// 服务器数据更新,更新本地数据 |
|
|
|
|
await problemRepository.updateProblem(serverProblem); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// 如果本地有未同步的更改,保留本地更改(下次上传时会同步到服务器) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// #endregion |
|
|
|
|
|
|
|
|
|
// #region 悬浮按钮 |
|
|
|
|
/// floatingButton更新位置 |
|
|
|
|
void updateFabUploadPosition(Offset delta) { |
|
|
|
|