Browse Source

feat : 同步企业列表

dev
徐振升 1 week ago
parent
commit
afc5d19188
  1. 4
      lib/app/core/extensions/datetime_extension.dart
  2. 46
      lib/app/core/services/database_service.dart
  3. 75
      lib/app/features/enterprise/data/datasources/enterprise_local_data_source.dart
  4. 13
      lib/app/features/enterprise/data/datasources/enterprise_remote_data_source.dart
  5. 60
      lib/app/features/enterprise/data/model/enterprise_dto.dart
  6. 81
      lib/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart
  7. 16
      lib/app/features/enterprise/domain/entities/enterprise_conflict.dart
  8. 18
      lib/app/features/enterprise/domain/entities/sync_result.dart
  9. 6
      lib/app/features/enterprise/domain/repositories/enterprise_repository.dart
  10. 2
      lib/app/features/enterprise/domain/usecases/editor_enterprise_usecase.dart
  11. 12
      lib/app/features/enterprise/domain/usecases/resolve_conflict_usecase.dart
  12. 12
      lib/app/features/enterprise/domain/usecases/sync_enterprises_usecase.dart
  13. 2
      lib/app/features/enterprise/presentation/bindings/enterprise_form_binding.dart
  14. 13
      lib/app/features/enterprise/presentation/bindings/enterprise_list_binding.dart
  15. 2
      lib/app/features/enterprise/presentation/bindings/enterprise_upload_binding.dart
  16. 116
      lib/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart

4
lib/app/core/extensions/datetime_extension.dart

@ -7,6 +7,10 @@ extension DateTimeFormatting on DateTime {
return DateFormat('yyyy-MM-dd HH:mm:ss').format(this);
}
String toDateTimeString2() {
return DateFormat("yyyy-MM-dd HH:mm:ss.SSS'Z'").format(toLocal());
}
/// 'yyyy-MM-dd'
String toDateString() {
return DateFormat('yyyy-MM-dd').format(this);

46
lib/app/core/services/database_service.dart

@ -26,7 +26,9 @@ const String _createEnterprisesTable = '''
id TEXT PRIMARY KEY,
syncStatus INTEGER NOT NULL,
lastModifiedTime INTEGER NOT NULL,
lastModifierId TEXT NOT NULL,
creationTime INTEGER NOT NULL,
creatorId TEXT NOT NULL,
name TEXT NOT NULL,
type TEXT NOT NULL,
address TEXT,
@ -41,7 +43,7 @@ const String _createEnterprisesTable = '''
///
class DatabaseService extends GetxService {
static const String _dbName = 'database.db';
static const int _dbVersion = 1;
static const int _dbVersion = 3;
Database? _database;
@ -88,15 +90,41 @@ class DatabaseService extends GetxService {
///
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
Get.log('正在将数据库从版本 $oldVersion 升级到 $newVersion...');
//
if (oldVersion < 2) {
// 1 2
// problems enterpriseId
await db.execute('''
ALTER TABLE problems ADD COLUMN enterpriseId TEXT
// 使
await db.transaction((txn) async {
// 1
if (oldVersion < 2) {
Get.log('数据库升级 V1 -> V2:');
// 1
await txn.execute('ALTER TABLE enterprises ADD COLUMN creatorId TEXT');
Get.log(' - `enterprises` 表已成功添加 `creatorId` 字段');
await txn.execute(
'ALTER TABLE enterprises ADD COLUMN lastModifierId TEXT',
);
Get.log(' - `enterprises` 表已成功添加 `lastModifierId` 字段');
}
//
if (oldVersion < 3) {
// 2 3
await txn.execute('''
UPDATE enterprises
SET
creatorId = '65a8dfa687a982050c224135',
lastModifierId = '65a8dfa687a982050c224135'
WHERE
creatorId IS NULL OR lastModifierId IS NULL;
''');
Get.log('`problems` 表已成功添加 `enterpriseId` 字段');
}
Get.log(
' - `enterprises` 表中旧数据的 `creatorId` 和 `lastModifierId` 字段已填充默认值',
);
}
});
Get.log('数据库升级完成');
}
@override

75
lib/app/features/enterprise/data/datasources/enterprise_local_data_source.dart

@ -6,7 +6,7 @@ import 'package:sqflite/sqflite.dart';
abstract class EnterpriseLocalDataSource {
///
Future<void> addEnterprise(EnterpriseModel enterprise);
// Future<void> addEnterprise(EnterpriseModel enterprise);
Future<void> updateEnterprise(EnterpriseModel enterprise);
Future<List<EnterpriseListItemModel>> getEnterpriseListItems({
String? name,
@ -17,6 +17,12 @@ abstract class EnterpriseLocalDataSource {
});
Future<void> updateSyncStatus(String enterpriseId, SyncStatus newStatus);
// []
Future<List<EnterpriseModel>> getAllEnterprises();
// []
Future<void> upsertEnterprise(EnterpriseModel enterprise);
Future<void> cacheEnterprises(List<EnterpriseModel> enterprises);
}
class EnterpriseLocalDataSourceImpl implements EnterpriseLocalDataSource {
@ -28,18 +34,18 @@ class EnterpriseLocalDataSourceImpl implements EnterpriseLocalDataSource {
static const String _tableName = 'enterprises';
@override
Future<void> addEnterprise(EnterpriseModel enterprise) async {
final db = await _databaseService.database;
await db.insert(
_tableName,
enterprise.toMap(),
// conflictAlgorithm (id)
// ConflictAlgorithm.replace ID
//
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
// @override
// Future<void> addEnterprise(EnterpriseModel enterprise) async {
// final db = await _databaseService.database;
// await db.insert(
// _tableName,
// enterprise.toMap(),
// // conflictAlgorithm (id)
// // ConflictAlgorithm.replace ID
// //
// conflictAlgorithm: ConflictAlgorithm.replace,
// );
// }
@override
Future<void> updateEnterprise(EnterpriseModel enterprise) async {
@ -161,4 +167,47 @@ class EnterpriseLocalDataSourceImpl implements EnterpriseLocalDataSource {
whereArgs: [enterpriseId],
);
}
/// []
///
@override
Future<void> cacheEnterprises(List<EnterpriseModel> enterprises) async {
if (enterprises.isEmpty) {
return; //
}
final db = await _databaseService.database;
// 1. Batch
final batch = db.batch();
// 2. insert batch
for (final enterprise in enterprises) {
// 使 insert ConflictAlgorithm.replace id "upsert"
batch.insert(
_tableName,
enterprise.toMap(), // EnterpriseModel toMap()
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
// 3. batch
await batch.commit(noResult: true);
}
@override
Future<List<EnterpriseModel>> getAllEnterprises() async {
final db = await _databaseService.database;
final maps = await db.query('enterprises');
return maps.map((json) => EnterpriseModel.fromMap(json)).toList();
}
@override
Future<void> upsertEnterprise(EnterpriseModel enterprise) async {
final db = await _databaseService.database;
await db.insert(
'enterprises',
enterprise.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace, // replace
);
}
}

13
lib/app/features/enterprise/data/datasources/enterprise_remote_data_source.dart

@ -1,3 +1,4 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/services/http_provider.dart';
import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_dto.dart';
import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart';
@ -28,7 +29,10 @@ class EnterpriseRemoteDataSourceImpl implements EnterpriseRemoteDataSource {
try {
final enterpriseDto = EnterpriseDto.fromModel(enterprise);
final data = enterpriseDto.toJson();
await http.post(enterprisesEndpoint, data: data);
await http.post(
'$enterprisesEndpoint/CreateWithId?Id=${enterprise.id}',
data: data,
);
} catch (e) {
rethrow;
}
@ -59,10 +63,11 @@ class EnterpriseRemoteDataSourceImpl implements EnterpriseRemoteDataSource {
Future<List<EnterpriseDto>> getEnterprises() async {
try {
final response = await http.get(enterprisesEndpoint);
return (response.data as List)
.map((json) => EnterpriseDto.fromJson(json))
.toList();
final Map<String, dynamic> data = response.data;
final List<dynamic> enterprises = data['items'];
return enterprises.map((json) => EnterpriseDto.fromJson(json)).toList();
} catch (e) {
Get.log('$e');
rethrow;
}
}

60
lib/app/features/enterprise/data/model/enterprise_dto.dart

@ -1,4 +1,5 @@
import 'package:problem_check_system/app/core/extensions/map_extensions.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart';
/// EnterpriseDto (Data Transfer Object)
@ -9,8 +10,8 @@ class EnterpriseDto {
final String id;
final DateTime creationTime;
final String creatorId;
final DateTime lastModificationTime;
final String lastModifierId;
final DateTime? lastModificationTime;
final String? lastModifierId;
final String companyName;
final String companyType;
final String? companyScope;
@ -19,7 +20,7 @@ class EnterpriseDto {
final String? securityPrincipalName;
final String? securityPrincipalPhone;
final String? companyAddress;
final String? detail;
final String? majorHazard;
const EnterpriseDto({
required this.id,
@ -35,7 +36,7 @@ class EnterpriseDto {
this.securityPrincipalName,
this.securityPrincipalPhone,
this.companyAddress,
this.detail,
this.majorHazard,
});
/// [] `EnterpriseModel` DTO
@ -45,16 +46,16 @@ class EnterpriseDto {
return EnterpriseDto(
id: model.id,
creationTime: model.creationTime,
creatorId: "", //todo
creatorId: model.creatorId,
lastModificationTime: model.lastModifiedTime,
lastModifierId: "", // todo需要在企业模型中添修改用户id
lastModifierId: model.lastModifierId,
companyName: model.name,
companyType: model.type,
companyScope: model.scale,
mainPrincipalName: model.contactPerson,
mainPrincipalPhone: model.contactPhone,
companyAddress: model.address,
detail: model.majorHazardsDescription,
majorHazard: model.majorHazardsDescription,
);
}
@ -66,17 +67,20 @@ class EnterpriseDto {
///
/// JSON Dart
factory EnterpriseDto.fromJson(Map<String, dynamic> json) {
final creationTime = DateTime.parse(json['creationTime'] as String);
final creatorId = json['creatorId'] as String;
final lastModTimeStr = json['lastModificationTime'] as String?;
return EnterpriseDto(
//
id: json['id'] as String,
creationTime: DateTime.parse(json['creationTime'] as String),
creatorId: json['creatorId'] as String,
lastModificationTime: DateTime.parse(
json['lastModificationTime'] as String,
),
lastModifierId: json['lastModifierId'] as String,
creationTime: creationTime,
creatorId: creatorId,
lastModificationTime: lastModTimeStr != null
? DateTime.parse(lastModTimeStr)
: creationTime,
lastModifierId: json['lastModifierId'] as String? ?? creatorId,
companyName: json['companyName'] as String,
companyType: json['companyType'] as String,
companyType: json['companyType'] as String? ?? "生产",
//
companyScope: json['companyScope'] as String?,
@ -85,7 +89,7 @@ class EnterpriseDto {
securityPrincipalName: json['securityPrincipalName'] as String?,
securityPrincipalPhone: json['securityPrincipalPhone'] as String?,
companyAddress: json['companyAddress'] as String?,
detail: json['detail'] as String?,
majorHazard: json['majorHazard'] as String?,
);
}
@ -98,7 +102,7 @@ class EnterpriseDto {
// 使 toIso8601String() DateTime
'creationTime': creationTime.toIso8601String(),
'creatorId': creatorId,
'lastModificationTime': lastModificationTime.toIso8601String(),
'lastModificationTime': lastModificationTime?.toIso8601String(),
'lastModifierId': lastModifierId,
'companyName': companyName,
'companyType': companyType,
@ -108,8 +112,30 @@ class EnterpriseDto {
'securityPrincipalName': securityPrincipalName,
'securityPrincipalPhone': securityPrincipalPhone,
'companyAddress': companyAddress,
'detail': detail,
'majorHazard': majorHazard,
};
return jsonMap.withoutNullOrEmptyValues;
}
/// [] DTO () Model (/)
///
///
EnterpriseModel toModel() {
return EnterpriseModel(
id: id,
// DTO syncStatus
syncStatus: SyncStatus.synced,
lastModifiedTime: lastModificationTime ?? creationTime,
lastModifierId: lastModifierId ?? creatorId,
creationTime: creationTime,
creatorId: creatorId,
name: companyName,
type: companyType,
address: companyAddress,
scale: companyScope,
contactPerson: mainPrincipalName,
contactPhone: mainPrincipalPhone,
majorHazardsDescription: majorHazard,
);
}
}

81
lib/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart

@ -1,27 +1,33 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/services/network_status_service.dart';
import 'package:problem_check_system/app/features/enterprise/data/datasources/enterprise_local_data_source.dart';
import 'package:problem_check_system/app/features/enterprise/data/datasources/enterprise_remote_data_source.dart';
import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_conflict.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/sync_result.dart';
import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart';
import 'package:uuid/uuid.dart';
class EnterpriseRepositoryImpl implements EnterpriseRepository {
final EnterpriseLocalDataSource localDataSource;
final EnterpriseRemoteDataSource remoteDataSource; //
final Uuid uuid = Uuid();
final EnterpriseRemoteDataSource remoteDataSource;
final NetworkStatusService networkStatusService;
final Uuid uuid;
EnterpriseRepositoryImpl({
required this.localDataSource,
required this.remoteDataSource,
required this.networkStatusService,
required this.uuid,
});
@override
Future<void> addEnterprise(Enterprise enterprise) async {
final enterpriseModel = EnterpriseModel.fromEntity(enterprise);
await localDataSource.addEnterprise(enterpriseModel);
await localDataSource.upsertEnterprise(enterpriseModel);
}
@override
@ -108,4 +114,73 @@ class EnterpriseRepositoryImpl implements EnterpriseRepository {
//
await localDataSource.updateSyncStatus(enterpriseId, newStatus);
}
// []
@override
Future<SyncResult> syncWithServer() async {
if (!networkStatusService.isOnline.value) {
// 线
return const SyncResult();
}
// 1.
final remoteEnterpriseDtos = await remoteDataSource.getEnterprises();
// [使 toModel] DTO Model
final remoteEnterprises = remoteEnterpriseDtos
.map((dto) => dto.toModel())
.toList();
final localEnterprises = await localDataSource.getAllEnterprises();
// 2. 使 Map
final localEnterpriseMap = {for (var e in localEnterprises) e.id: e};
final List<EnterpriseModel> newEnterprises = [];
final List<EnterpriseConflict> conflicts = [];
// 3.
for (final remoteEnterprise in remoteEnterprises) {
final localEnterprise = localEnterpriseMap[remoteEnterprise.id];
if (localEnterprise == null) {
//
newEnterprises.add(remoteEnterprise);
} else {
//
final remoteMillis =
remoteEnterprise.lastModifiedTime.millisecondsSinceEpoch;
final localMillis =
localEnterprise.lastModifiedTime.millisecondsSinceEpoch;
if (remoteMillis != localMillis) {
//
conflicts.add(
EnterpriseConflict(
localVersion: localEnterprise.toEntity(), // Model Entity
serverVersion: remoteEnterprise.toEntity(),
),
);
}
//
}
}
// 4.
if (newEnterprises.isNotEmpty) {
await localDataSource.cacheEnterprises(newEnterprises);
}
// 5.
return SyncResult(
newItemsFromServer: newEnterprises.length,
conflicts: conflicts,
);
}
// []
@override
Future<void> resolveConflictAndUpdate(Enterprise chosenEnterprise) {
//
final enterpriseModel = EnterpriseModel.fromEntity(chosenEnterprise);
return localDataSource.upsertEnterprise(enterpriseModel);
}
}

16
lib/app/features/enterprise/domain/entities/enterprise_conflict.dart

@ -0,0 +1,16 @@
import 'package:equatable/equatable.dart';
import 'enterprise.dart'; // Enterprise
///
class EnterpriseConflict extends Equatable {
final Enterprise localVersion;
final Enterprise serverVersion;
const EnterpriseConflict({
required this.localVersion,
required this.serverVersion,
});
@override
List<Object?> get props => [localVersion, serverVersion];
}

18
lib/app/features/enterprise/domain/entities/sync_result.dart

@ -0,0 +1,18 @@
import 'package:equatable/equatable.dart';
import 'enterprise_conflict.dart';
class SyncResult extends Equatable {
///
final int newItemsFromServer;
///
final List<EnterpriseConflict> conflicts;
const SyncResult({this.newItemsFromServer = 0, this.conflicts = const []});
///
bool get hasConflicts => conflicts.isNotEmpty;
@override
List<Object?> get props => [newItemsFromServer, conflicts];
}

6
lib/app/features/enterprise/domain/repositories/enterprise_repository.dart

@ -1,6 +1,7 @@
import 'package:problem_check_system/app/core/models/sync_status.dart';
import 'package:problem_check_system/app/core/repositories/syncable_repository.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/sync_result.dart';
import '../entities/enterprise.dart';
@ -23,4 +24,9 @@ abstract class EnterpriseRepository implements SyncableRepository<Enterprise> {
Future<void> syncEnterpriseToServer(Enterprise enterprise) async {}
Future<void> updateEnterpriseSyncStatus(String id, SyncStatus synced) async {}
// []
Future<SyncResult> syncWithServer();
// []
Future<void> resolveConflictAndUpdate(Enterprise chosenEnterprise);
}

2
lib/app/features/enterprise/domain/usecases/editor_enterprise_usecase.dart

@ -16,7 +16,7 @@ class EditEnterpriseUsecase {
Future<void> call(Enterprise updatedEnterprise) async {
// 1.
final enterpriseToSave = updatedEnterprise.copyWith(
lastModifiedTime: DateTime.now(),
lastModifiedTime: DateTime.now().toUtc(),
lastModifierId: authRepository.getUserId(),
// 2.
//

12
lib/app/features/enterprise/domain/usecases/resolve_conflict_usecase.dart

@ -0,0 +1,12 @@
import '../entities/enterprise.dart';
import '../repositories/enterprise_repository.dart';
class ResolveConflictUsecase {
final EnterpriseRepository repository;
ResolveConflictUsecase({required this.repository});
Future<void> call(Enterprise chosenEnterprise) async {
return repository.resolveConflictAndUpdate(chosenEnterprise);
}
}

12
lib/app/features/enterprise/domain/usecases/sync_enterprises_usecase.dart

@ -0,0 +1,12 @@
import '../entities/sync_result.dart';
import '../repositories/enterprise_repository.dart';
class SyncEnterprisesUsecase {
final EnterpriseRepository repository;
SyncEnterprisesUsecase({required this.repository});
Future<SyncResult> call() async {
return repository.syncWithServer();
}
}

2
lib/app/features/enterprise/presentation/bindings/enterprise_form_binding.dart

@ -22,6 +22,8 @@ class EnterpriseFormBinding extends Bindings {
(() => EnterpriseRepositoryImpl(
localDataSource: Get.find(),
remoteDataSource: Get.find(),
networkStatusService: Get.find(),
uuid: Get.find(),
)),
);
Get.lazyPut<AddEnterpriseUsecase>(

13
lib/app/features/enterprise/presentation/bindings/enterprise_list_binding.dart

@ -6,6 +6,8 @@ import 'package:problem_check_system/app/features/enterprise/data/datasources/en
import 'package:problem_check_system/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart';
import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprise_list_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/resolve_conflict_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/sync_enterprises_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart';
class EnterpriseListBinding extends Bindings {
@ -23,14 +25,25 @@ class EnterpriseListBinding extends Bindings {
EnterpriseRepositoryImpl(
localDataSource: Get.find<EnterpriseLocalDataSource>(),
remoteDataSource: Get.find<EnterpriseRemoteDataSource>(),
networkStatusService: Get.find(),
uuid: Get.find(),
),
);
Get.put<SyncEnterprisesUsecase>(
SyncEnterprisesUsecase(repository: Get.find<EnterpriseRepository>()),
);
Get.put<ResolveConflictUsecase>(
ResolveConflictUsecase(repository: Get.find<EnterpriseRepository>()),
);
Get.put<GetEnterpriseListUsecase>(
GetEnterpriseListUsecase(repository: Get.find<EnterpriseRepository>()),
);
Get.lazyPut<EnterpriseListController>(
() => EnterpriseListController(
getEnterpriseListUsecase: Get.find<GetEnterpriseListUsecase>(),
syncEnterprisesUsecase: Get.find<SyncEnterprisesUsecase>(),
resolveConflictUsecase: Get.find<ResolveConflictUsecase>(),
),
);
}

2
lib/app/features/enterprise/presentation/bindings/enterprise_upload_binding.dart

@ -25,6 +25,8 @@ class EnterpriseUploadBinding extends Bindings {
EnterpriseRepositoryImpl(
localDataSource: Get.find<EnterpriseLocalDataSource>(),
remoteDataSource: Get.find<EnterpriseRemoteDataSource>(),
networkStatusService: Get.find(),
uuid: Get.find(),
),
);

116
lib/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart

@ -2,11 +2,15 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/extensions/datetime_extension.dart';
import 'package:problem_check_system/app/core/models/company_enum.dart';
import 'package:problem_check_system/app/core/routes/app_routes.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_conflict.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprise_list_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/resolve_conflict_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/sync_enterprises_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_form_controller.dart';
/// -----------------------------------------------------------------------------
@ -23,12 +27,20 @@ import 'package:problem_check_system/app/features/enterprise/presentation/contro
///
class EnterpriseListController extends GetxController {
final GetEnterpriseListUsecase getEnterpriseListUsecase;
final SyncEnterprisesUsecase syncEnterprisesUsecase; //
final ResolveConflictUsecase resolveConflictUsecase; //
EnterpriseListController({required this.getEnterpriseListUsecase});
EnterpriseListController({
required this.getEnterpriseListUsecase,
required this.syncEnterprisesUsecase,
required this.resolveConflictUsecase,
});
// --- ---
final enterpriseList = <EnterpriseListItem>[].obs;
final isLoading = false.obs;
final isSyncing = false.obs;
final nameController = TextEditingController();
final selectedType = Rx<CompanyType?>(null);
final startDate = Rx<DateTime?>(null);
@ -38,8 +50,9 @@ class EnterpriseListController extends GetxController {
@override
void onInit() {
// -
loadAndSyncEnterprises();
super.onInit();
fetchEnterprises(); //
}
@override
@ -50,26 +63,30 @@ class EnterpriseListController extends GetxController {
}
// --- ---
//
Future<void> loadAndSyncEnterprises() async {
try {
isLoading(true);
isSyncing(true);
void search() {
fetchEnterprises();
}
// 1:
final syncResult = await syncEnterprisesUsecase();
void clearFilters() {
nameController.clear();
selectedType.value = null;
startDate.value = null;
endDate.value = null;
fetchEnterprises();
}
// 2:
if (syncResult.hasConflicts) {
//
for (final conflict in syncResult.conflicts) {
final chosenVersion = await _showConflictDialog(conflict);
if (chosenVersion != null) {
//
await resolveConflictUsecase(chosenVersion);
}
}
}
// --- EnterpriseListController ---
isSyncing(false);
///
Future<void> fetchEnterprises() async {
expansibleController.collapse();
isLoading.value = true;
try {
// 3:
final result = await getEnterpriseListUsecase.call(
name: nameController.text,
type: selectedType.value?.displayText,
@ -78,12 +95,71 @@ class EnterpriseListController extends GetxController {
);
enterpriseList.assignAll(result);
} catch (e) {
Get.snackbar('错误', '加载企业列表失败: $e');
Get.snackbar('错误', '操作失败: $e');
} finally {
isLoading.value = false;
isLoading(false);
isSyncing(false);
}
}
//
Future<Enterprise?> _showConflictDialog(EnterpriseConflict conflict) {
return Get.dialog<Enterprise>(
AlertDialog(
title: Text('数据冲突: ${conflict.localVersion.name}'),
// [ 1] content
content: Column(
// mainAxisSize.min Column
mainAxisSize: MainAxisSize.min,
// crossAxisAlignment.stretch
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text('服务器上的数据与本地数据不一致,请选择要保留的版本。'),
const SizedBox(height: 24), //
//
ElevatedButton(
child: Text(
'使用客户端版本\n(修改于: ${conflict.localVersion.lastModifiedTime.toUtc().toDateTimeString2()})',
textAlign: TextAlign.center,
),
onPressed: () => Get.back(result: conflict.localVersion),
),
const SizedBox(height: 8),
//
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Get.theme.colorScheme.primary,
foregroundColor: Get.theme.colorScheme.onPrimary,
),
child: Text(
'使用服务器版本\n(修改于: ${conflict.serverVersion.lastModifiedTime.toUtc().toDateTimeString2()})',
textAlign: TextAlign.center,
),
onPressed: () => Get.back(result: conflict.serverVersion),
),
],
),
// [ 2] actions
// actions: [ ... ],
),
);
}
void search() {
loadAndSyncEnterprises();
}
void clearFilters() {
nameController.clear();
selectedType.value = null;
startDate.value = null;
endDate.value = null;
loadAndSyncEnterprises();
}
///
Future<void> navigateToEditForm(Enterprise enterprise) async {
final result = await Get.toNamed(

Loading…
Cancel
Save