Browse Source

feat : 添加创建人id与修改时间utc毫秒比对

dev
徐振升 17 hours ago
parent
commit
bdfc6778f7
  1. 3
      lib/app/bindings/initial_binding.dart
  2. 14
      lib/data/models/problem_model.dart
  3. 36
      lib/data/models/problem_sync_status.dart
  4. 1
      lib/data/providers/sqlite_provider.dart
  5. 9
      lib/data/repositories/auth_repository.dart
  6. 5
      lib/data/repositories/problem_repository.dart
  7. 7
      lib/modules/auth/controllers/login_controller.dart
  8. 7
      lib/modules/home/bindings/home_binding.dart
  9. 4
      lib/modules/problem/bindings/problem_form_binding.dart
  10. 210
      lib/modules/problem/controllers/problem_controller.dart
  11. 6
      lib/modules/problem/controllers/problem_form_controller.dart

3
lib/app/bindings/initial_binding.dart

@ -8,6 +8,7 @@ import 'package:problem_check_system/data/repositories/file_repository.dart';
import 'package:problem_check_system/data/repositories/image_repository.dart';
import 'package:problem_check_system/data/repositories/image_repository_impl.dart';
import 'package:problem_check_system/data/repositories/problem_repository.dart';
import 'package:uuid/uuid.dart';
class InitialBinding implements Bindings {
@override
@ -19,6 +20,7 @@ class InitialBinding implements Bindings {
void _registerCoreServices() {
///
Get.put<GetStorage>(GetStorage(), permanent: true);
Get.put<Uuid>(Uuid(), permanent: true);
Get.put<HttpProvider>(HttpProvider());
Get.put<SQLiteProvider>(SQLiteProvider());
Get.put<ConnectivityProvider>(ConnectivityProvider());
@ -43,6 +45,7 @@ class InitialBinding implements Bindings {
sqliteProvider: Get.find<SQLiteProvider>(),
httpProvider: Get.find<HttpProvider>(),
connectivityProvider: Get.find<ConnectivityProvider>(),
authRepository: Get.find(),
),
);
}

14
lib/data/models/problem_model.dart

@ -21,6 +21,9 @@ class Problem {
///
final DateTime creationTime;
/// id
final String creatorId;
///
final ProblemSyncStatus syncStatus;
@ -42,6 +45,7 @@ class Problem {
required this.location,
required this.imageUrls,
required this.creationTime,
required this.creatorId,
required this.lastModifiedTime,
this.syncStatus = ProblemSyncStatus.pendingCreate,
this.censorTaskId,
@ -56,6 +60,7 @@ class Problem {
String? location,
List<ImageMetadata>? imageUrls,
DateTime? creationTime,
String? creatorId,
DateTime? lastModifiedTime,
ProblemSyncStatus? syncStatus,
bool? isDeleted,
@ -69,6 +74,7 @@ class Problem {
location: location ?? this.location,
imageUrls: imageUrls ?? this.imageUrls,
creationTime: creationTime ?? this.creationTime,
creatorId: creatorId ?? this.creatorId,
lastModifiedTime: lastModifiedTime ?? this.lastModifiedTime,
syncStatus: syncStatus ?? this.syncStatus,
censorTaskId: censorTaskId ?? this.censorTaskId,
@ -85,6 +91,7 @@ class Problem {
'location': location,
'imageUrls': json.encode(imageUrls.map((e) => e.toMap()).toList()),
'creationTime': creationTime.millisecondsSinceEpoch,
'creatorId': creatorId,
'lastModifiedTime': lastModifiedTime.millisecondsSinceEpoch,
'syncStatus': syncStatus.index,
'censorTaskId': censorTaskId,
@ -112,9 +119,14 @@ class Problem {
description: map['description'],
location: map['location'],
imageUrls: imageUrlsList,
creationTime: DateTime.fromMillisecondsSinceEpoch(map['creationTime']),
creationTime: DateTime.fromMillisecondsSinceEpoch(
map['creationTime'],
isUtc: true,
),
creatorId: map['creatorId'],
lastModifiedTime: DateTime.fromMillisecondsSinceEpoch(
map['lastModifiedTime'],
isUtc: true,
),
syncStatus: ProblemSyncStatus.values[map['syncStatus']],
censorTaskId: map['censorTaskId'],

36
lib/data/models/problem_sync_status.dart

@ -1,5 +1,7 @@
import 'package:get/get.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/repositories/auth_repository.dart';
import 'package:uuid/uuid.dart';
enum ProblemSyncStatus {
@ -20,29 +22,33 @@ enum ProblemSyncStatus {
}
/// - git add/git commit
class ProblemStateManager {
class ProblemStateManager extends GetxController {
/// uuid
static final Uuid _uuid = Uuid();
final Uuid uuid;
final AuthRepository authRepository;
ProblemStateManager({required this.uuid, required this.authRepository});
///
static Problem createNewProblem({
Problem createNewProblem({
required String description,
required String location,
required List<ImageMetadata> imageUrls,
}) {
return Problem(
id: _uuid.v4(),
id: uuid.v4(),
description: description,
location: location,
imageUrls: imageUrls,
creationTime: DateTime.now(),
lastModifiedTime: DateTime.now(),
creationTime: DateTime.now().toUtc(),
creatorId: authRepository.getUserId()!,
lastModifiedTime: DateTime.now().toUtc(),
syncStatus: ProblemSyncStatus.pendingCreate,
);
}
///
static Problem modifyProblem(Problem problem) {
Problem modifyProblem(Problem problem) {
final newStatus = problem.syncStatus == ProblemSyncStatus.synced
? ProblemSyncStatus
.pendingUpdate //
@ -50,25 +56,25 @@ class ProblemStateManager {
return problem.copyWith(
syncStatus: newStatus,
lastModifiedTime: DateTime.now(),
lastModifiedTime: DateTime.now().toUtc(),
);
}
///
static Problem markForDeletion(Problem problem) {
Problem markForDeletion(Problem problem) {
switch (problem.syncStatus) {
case ProblemSyncStatus.pendingCreate:
//
return problem.copyWith(
syncStatus: ProblemSyncStatus.untracked,
lastModifiedTime: DateTime.now(),
lastModifiedTime: DateTime.now().toUtc(),
);
case ProblemSyncStatus.synced:
case ProblemSyncStatus.pendingUpdate:
//
return problem.copyWith(
syncStatus: ProblemSyncStatus.pendingDelete,
lastModifiedTime: DateTime.now(),
lastModifiedTime: DateTime.now().toUtc(),
);
case ProblemSyncStatus.untracked:
case ProblemSyncStatus.pendingDelete:
@ -78,21 +84,21 @@ class ProblemStateManager {
}
/// git reset
static Problem undoDeletion(Problem problem) {
Problem undoDeletion(Problem problem) {
if (problem.syncStatus == ProblemSyncStatus.pendingDelete) {
return problem.copyWith(
syncStatus: ProblemSyncStatus.pendingUpdate,
lastModifiedTime: DateTime.now(),
lastModifiedTime: DateTime.now().toUtc(),
);
}
return problem;
}
/// git commit
static Problem markAsSynced(Problem problem) {
Problem markAsSynced(Problem problem) {
return problem.copyWith(
syncStatus: ProblemSyncStatus.synced,
lastModifiedTime: DateTime.now(),
lastModifiedTime: DateTime.now().toUtc(),
);
}
}

1
lib/data/providers/sqlite_provider.dart

@ -50,6 +50,7 @@ class SQLiteProvider extends GetxService {
location TEXT NOT NULL,
imageUrls TEXT NOT NULL,
creationTime INTEGER NOT NULL,
creatorId TEXT NOT NULL,
lastModifiedTime INTEGER NOT NULL,
syncStatus INTEGER NOT NULL,
censorTaskId TEXT,

9
lib/data/repositories/auth_repository.dart

@ -21,6 +21,7 @@ class AuthRepository extends GetxService {
static const String _refreshTokenKey = 'refresh_token';
static const String _loginKey = 'user';
static const String _rememberMe = 'remember_me';
static const String _userId = 'userId';
void saveToken(String token) {
storage.write(_tokenKey, token);
@ -38,6 +39,14 @@ class AuthRepository extends GetxService {
return storage.read(_refreshTokenKey);
}
void saveUserId(String id) {
storage.write(_userId, id);
}
String? getUserId() {
return storage.read(_userId);
}
void addLoginKey(LoginRequest login) {
storage.write(_loginKey, login.toJson());
}

5
lib/data/repositories/problem_repository.dart

@ -7,6 +7,7 @@ import 'package:problem_check_system/data/models/server_problem.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/auth_repository.dart';
///
///
@ -14,6 +15,7 @@ class ProblemRepository extends GetxService {
final SQLiteProvider sqliteProvider;
final HttpProvider httpProvider;
final ConnectivityProvider connectivityProvider;
final AuthRepository authRepository;
RxBool get isOnline => connectivityProvider.isOnline;
@ -21,6 +23,7 @@ class ProblemRepository extends GetxService {
required this.sqliteProvider,
required this.httpProvider,
required this.connectivityProvider,
required this.authRepository,
});
///
@ -54,7 +57,6 @@ class ProblemRepository extends GetxService {
await sqliteProvider.deleteProblem(problemId);
}
// TODO id,
// ProblemRepository中添加
Future<List<ServerProblem>> fetchProblemsFromServer({
DateTime? startTime,
@ -67,6 +69,7 @@ class ProblemRepository extends GetxService {
final response = await httpProvider.get(
ApiEndpoints.getProblems,
queryParameters: {
'creatorId': authRepository.getUserId(),
if (startTime != null)
'StartTime': startTime.toUtc().toIso8601String(),
if (endTime != null) 'EndTime': endTime.toUtc().toIso8601String(),

7
lib/modules/auth/controllers/login_controller.dart

@ -73,8 +73,11 @@ class LoginController extends GetxController {
_authRepository.removeLoginKey();
}
Get.offAllNamed(AppRoutes.home);
//
debugPrint('登录成功: $loginResponse');
// 访
var user = await _authRepository.getUserProfile();
if (user.id != null) {
_authRepository.saveUserId(user.id!);
}
} on DioException catch (e) {
// DioException
// Snackbar

7
lib/modules/home/bindings/home_binding.dart

@ -1,4 +1,5 @@
import 'package:get/get.dart';
import 'package:problem_check_system/data/models/problem_sync_status.dart';
import 'package:problem_check_system/data/repositories/auth_repository.dart';
import 'package:problem_check_system/data/repositories/problem_repository.dart';
import 'package:problem_check_system/modules/home/controllers/home_controller.dart';
@ -10,10 +11,14 @@ class HomeBinding implements Bindings {
void dependencies() {
///
Get.lazyPut<HomeController>(() => HomeController());
Get.put(ProblemStateManager(uuid: Get.find(), authRepository: Get.find()));
///
Get.lazyPut<ProblemController>(
() => ProblemController(problemRepository: Get.find<ProblemRepository>()),
() => ProblemController(
problemRepository: Get.find<ProblemRepository>(),
problemStateManager: Get.find<ProblemStateManager>(),
),
fenix: true,
);

4
lib/modules/problem/bindings/problem_form_binding.dart

@ -1,5 +1,6 @@
import 'package:get/get.dart';
import 'package:problem_check_system/data/models/problem_model.dart';
import 'package:problem_check_system/data/models/problem_sync_status.dart';
import 'package:problem_check_system/modules/problem/controllers/problem_form_controller.dart';
class ProblemFormBinding extends Bindings {
@ -12,9 +13,12 @@ class ProblemFormBinding extends Bindings {
if (arguments != null && arguments is Problem) {
problem = arguments;
}
Get.put(ProblemStateManager(uuid: Get.find(), authRepository: Get.find()));
Get.lazyPut<ProblemFormController>(
() => ProblemFormController(
problemRepository: Get.find(),
problemStateManager: Get.find(),
problem: problem,
isReadOnly: readOnly,
),

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

@ -25,6 +25,7 @@ class ProblemController extends GetxController
with GetSingleTickerProviderStateMixin {
///
final ProblemRepository problemRepository;
final ProblemStateManager problemStateManager;
final FileRepository fileRepository = Get.find<FileRepository>();
///
@ -103,7 +104,10 @@ class ProblemController extends GetxController
/// get
RxBool get isOnline => problemRepository.isOnline;
ProblemController({required this.problemRepository});
ProblemController({
required this.problemRepository,
required this.problemStateManager,
});
@override
void onInit() {
@ -457,7 +461,7 @@ class ProblemController extends GetxController
);
try {
const int totalSteps = 4;
const int totalSteps = 5;
syncProgress.startSync(totalSteps);
// 1.
@ -477,10 +481,11 @@ class ProblemController extends GetxController
);
// 4.
_downloadImagesForProblems(downloadedProblems);
syncProgress.updateProgress('正在下载图片...', 4);
await downloadImagesForProblems(downloadedProblems);
// 5.
syncProgress.updateProgress('正在重新加载数据...', 4);
syncProgress.updateProgress('正在重新加载数据...', 5);
await loadProblems();
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;
//
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,
);
final imageRepository = Get.find<ImageRepository>();
final List<Future<void>> downloadFutures = [];
for (final problem in problems) {
//
final downloadFuture = _downloadProblemImages(problem, imageRepository);
downloadFutures.add(downloadFuture);
}
//
await Future.wait(downloadFutures);
}
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,
///
Future<void> _downloadProblemImages(
Problem problem,
ImageRepository imageRepository,
) async {
try {
final List<ImageMetadata> downloadedImages = [];
final List<Future<void>> imageFutures = [];
for (final imageMeta in problem.imageUrls) {
if (imageMeta.remoteUrl != null && imageMeta.remoteUrl!.isNotEmpty) {
//
final imageFuture =
_downloadSingleImage(imageMeta, problem.id, imageRepository).then(
(downloadedImage) {
if (downloadedImage != null) {
downloadedImages.add(downloadedImage);
}
},
);
downloadedImages.add(downloadedImage);
}
}
//
if (downloadedImages.isNotEmpty) {
final updatedProblem = problem.copyWith(
imageUrls: downloadedImages,
);
await problemRepository.updateProblem(updatedProblem);
}
} catch (e) {
Get.log('下载问题 ${problem.id} 的图片失败: $e');
imageFutures.add(imageFuture);
}
}
});
//
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(
@ -607,7 +702,9 @@ class ProblemController extends GetxController
final serverUpdated = serverProblem.lastModificationTime;
final localUpdated = localProblem.lastModifiedTime;
if (serverUpdated != null && serverUpdated.isAfter(localUpdated)) {
if (serverUpdated != null &&
serverUpdated.millisecondsSinceEpoch >
localUpdated.millisecondsSinceEpoch) {
//
final updatedProblem = _convertServerProblemToLocal(serverProblem);
await problemRepository.updateProblem(updatedProblem);
@ -640,6 +737,7 @@ class ProblemController extends GetxController
location: serverProblem.location,
imageUrls: imageMetadatas,
creationTime: serverProblem.creationTime,
creatorId: serverProblem.creatorId,
lastModifiedTime: serverProblem.lastModificationTime ?? DateTime.now(),
syncStatus: ProblemSyncStatus.synced, //
censorTaskId: serverProblem.censorTaskId,
@ -836,7 +934,7 @@ class ProblemController extends GetxController
///
Future<void> deleteProblem(Problem problem) async {
try {
final deleteProblem = ProblemStateManager.markForDeletion(problem);
final deleteProblem = problemStateManager.markForDeletion(problem);
if (deleteProblem.syncStatus == ProblemSyncStatus.untracked) {
//
await problemRepository.deleteProblem(problem.id);

6
lib/modules/problem/controllers/problem_form_controller.dart

@ -20,6 +20,7 @@ class ProblemFormController extends GetxController {
final bool isReadOnly;
final ProblemRepository problemRepository;
final ProblemStateManager problemStateManager;
final TextEditingController descriptionController = TextEditingController();
final TextEditingController locationController = TextEditingController();
final RxList<XFile> selectedImages = <XFile>[].obs;
@ -28,6 +29,7 @@ class ProblemFormController extends GetxController {
// 使便
ProblemFormController({
required this.problemRepository,
required this.problemStateManager,
this.problem,
this.isReadOnly = false,
}) {
@ -162,12 +164,12 @@ class ProblemFormController extends GetxController {
imageUrls: imagePaths,
);
//
final modifyProblem = ProblemStateManager.modifyProblem(updatedProblem);
final modifyProblem = problemStateManager.modifyProblem(updatedProblem);
await problemRepository.updateProblem(modifyProblem);
} else {
//
final newProblem = ProblemStateManager.createNewProblem(
final newProblem = problemStateManager.createNewProblem(
description: descriptionController.text,
location: locationController.text,
imageUrls: imagePaths,

Loading…
Cancel
Save