Browse Source

feat : 下拉同步服务器数据

dev
徐振升 12 hours ago
parent
commit
db8594f83a
  1. 2
      lib/core/utils/constants/api_endpoints.dart
  2. 36
      lib/data/repositories/problem_repository.dart
  3. 93
      lib/modules/problem/controllers/problem_controller.dart
  4. 35
      lib/modules/problem/views/problem_list_page.dart
  5. 2
      pubspec.lock
  6. 1
      pubspec.yaml

2
lib/core/utils/constants/api_endpoints.dart

@ -9,7 +9,7 @@ abstract class ApiEndpoints {
static const String patchPassword = '/api/Accounts/ChangePassword';
// Memorandum
static const String getProblem = '/api/Memorandum';
static const String getProblems = '/api/Memorandum';
static const String postProblem = '/api/Memorandum';
static String putProblemById(String id) => '/api/Memorandum/$id';
static String deleteProblemById(String id) => '/api/Memorandum/$id';

36
lib/data/repositories/problem_repository.dart

@ -1,5 +1,6 @@
import 'package:dio/dio.dart';
import 'package:get/get.dart' hide MultipartFile, FormData, Response;
import 'package:problem_check_system/core/extensions/http_response_extension.dart';
import 'package:problem_check_system/core/utils/constants/api_endpoints.dart';
import 'package:problem_check_system/data/models/problem_model.dart';
import 'package:problem_check_system/data/providers/connectivity_provider.dart';
@ -52,10 +53,37 @@ class ProblemRepository extends GetxService {
await sqliteProvider.deleteProblem(problemId);
}
/// getAll
Future<Response> getAll() async {
final response = await httpProvider.get(ApiEndpoints.getProblem);
return response;
// ProblemRepository中添加
Future<List<Problem>> fetchProblemsFromServer({
DateTime? startTime,
DateTime? endTime,
int? pageNumber,
int? pageSize,
CancelToken? cancelToken,
}) async {
try {
final response = await httpProvider.get(
ApiEndpoints.getProblems,
queryParameters: {
if (startTime != null)
'StartTime': startTime.toUtc().toIso8601String(),
if (endTime != null) 'EndTime': endTime.toUtc().toIso8601String(),
if (pageNumber != null) 'pageNumber': pageNumber,
if (pageSize != null) 'pageSize': pageSize,
},
cancelToken: cancelToken,
);
if (response.isSuccess) {
// Problem对象的列表
final List<dynamic> data = response.data;
return data.map((json) => Problem.fromJson(json)).toList();
} else {
throw Exception('拉取问题失败: ${response.statusCode}');
}
} on DioException catch (e) {
throw Exception('拉取问题失败: $e');
}
}
/// post

93
lib/modules/problem/controllers/problem_controller.dart

@ -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) {

35
lib/modules/problem/views/problem_list_page.dart

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:easy_refresh/easy_refresh.dart';
import 'package:problem_check_system/data/models/problem_sync_status.dart';
import 'package:problem_check_system/modules/problem/controllers/problem_controller.dart';
import 'package:problem_check_system/data/models/problem_model.dart';
@ -23,16 +24,32 @@ class ProblemListPage extends GetView<ProblemController> {
return const Center(child: CircularProgressIndicator());
}
return ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 17.w),
itemCount: problemsToShow.length,
itemBuilder: (context, index) {
// if (index == problemsToShow.length) {
// return SizedBox(height: 79.5.h);
// }
final problem = problemsToShow[index];
return _buildSwipeableProblemCard(problem);
return EasyRefresh(
header: ClassicHeader(
dragText: '下拉刷新'.tr,
armedText: '释放开始'.tr,
readyText: '刷新中...'.tr,
processingText: '刷新中...'.tr,
processedText: '成功了'.tr,
noMoreText: 'No more'.tr,
failedText: '失败'.tr,
messageText: '最后更新于 %T'.tr,
),
onRefresh: () async {
//
await controller.pullDataFromServer();
},
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 17.w),
itemCount: problemsToShow.length,
itemBuilder: (context, index) {
// if (index == problemsToShow.length) {
// return SizedBox(height: 79.5.h);
// }
final problem = problemsToShow[index];
return _buildSwipeableProblemCard(problem);
},
),
);
});
}

2
pubspec.lock

@ -106,7 +106,7 @@ packages:
source: hosted
version: "2.1.1"
easy_refresh:
dependency: transitive
dependency: "direct main"
description:
name: easy_refresh
sha256: "486e30abfcaae66c0f2c2798a10de2298eb9dc5e0bb7e1dba9328308968cae0c"

1
pubspec.yaml

@ -10,6 +10,7 @@ dependencies:
connectivity_plus: ^6.1.5
crypto: ^3.0.6
dio: ^5.9.0
easy_refresh: ^3.4.0
flutter:
sdk: flutter
flutter_localizations:

Loading…
Cancel
Save