Browse Source

暂存:需要完善问题列表页面UI,卡片与列表

dev
徐振升 2 days ago
parent
commit
adea79b7e9
  1. 27
      lib/app/core/bindings/base_bindings.dart
  2. 0
      lib/app/core/domain/entities/sync_status.dart
  3. 47
      lib/app/core/models/image_metadata_model.dart
  4. 14
      lib/app/core/models/image_status.dart
  5. 104
      lib/app/core/models/problem_sync_status.dart
  6. 2
      lib/app/core/repositories/syncable_repository.dart
  7. 2
      lib/app/core/services/sync_service.dart
  8. 2
      lib/app/features/enterprise/data/datasources/enterprise_local_data_source.dart
  9. 2
      lib/app/features/enterprise/data/model/enterprise_dto.dart
  10. 2
      lib/app/features/enterprise/data/model/enterprise_model.dart
  11. 2
      lib/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart
  12. 2
      lib/app/features/enterprise/domain/entities/enterprise.dart
  13. 2
      lib/app/features/enterprise/domain/repositories/enterprise_repository.dart
  14. 2
      lib/app/features/enterprise/domain/usecases/add_enterprise_usecase.dart
  15. 2
      lib/app/features/enterprise/domain/usecases/editor_enterprise_usecase.dart
  16. 2
      lib/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart
  17. 99
      lib/app/features/problem/data/datasources/problem_local_data_source.dart
  18. 144
      lib/app/features/problem/data/model/problem_dto.dart
  19. 103
      lib/app/features/problem/data/model/problem_local_dto.dart
  20. 187
      lib/app/features/problem/data/model/problem_model.dart
  21. 144
      lib/app/features/problem/data/model/problem_remote_dto.dart
  22. 44
      lib/app/features/problem/data/repositories/problem_repository_impl.dart
  23. 4
      lib/app/features/problem/domain/entities/problem_entity.dart
  24. 30
      lib/app/features/problem/domain/entities/problem_filter_params.dart
  25. 15
      lib/app/features/problem/domain/entities/problem_list_item_entity.dart
  26. 9
      lib/app/features/problem/domain/repositories/problem_repository.dart
  27. 7
      lib/app/features/problem/domain/usecases/add_problem_usecase.dart
  28. 17
      lib/app/features/problem/domain/usecases/get_all_problems_usecase.dart
  29. 9
      lib/app/features/problem/domain/usecases/update_problem_usecase.dart
  30. 70
      lib/app/features/problem/presentation/bindings/problem_form_binding.dart
  31. 102
      lib/app/features/problem/presentation/controllers/problem_form_controller.dart
  32. 0
      lib/app/features/problem/presentation/models/problem_card_model.dart
  33. 97
      lib/app/features/problem/presentation/models/problem_card_state.dart
  34. 1
      lib/app/features/problem/presentation/pages/widgets/problem_card.dart

27
lib/app/core/bindings/base_bindings.dart

@ -0,0 +1,27 @@
import 'package:get/get.dart';
abstract class BaseBindings implements Bindings {
@override
void dependencies() {
register1Services();
register2DataSource();
register3Repositories();
register4Usecases();
register5Controllers();
}
///
void register1Services();
///
void register2DataSource();
///
void register3Repositories();
///
void register4Usecases();
///
void register5Controllers();
}

0
lib/app/core/models/sync_status.dart → lib/app/core/domain/entities/sync_status.dart

47
lib/app/core/models/image_metadata_model.dart

@ -1,47 +0,0 @@
// image_metadata_model.dart
import 'package:problem_check_system/app/core/models/image_status.dart';
class ImageMetadata {
final String localPath;
final String? remoteUrl;
final ImageStatus status;
ImageMetadata({
required this.localPath,
this.remoteUrl,
required this.status,
});
// For saving to SQL
Map<String, dynamic> toMap() {
return {
'localPath': localPath,
'remoteUrl': remoteUrl,
'status': status.index,
};
}
// For reading from SQL
factory ImageMetadata.fromMap(Map<String, dynamic> map) {
return ImageMetadata(
localPath: map['localPath'] as String,
remoteUrl: map['remoteUrl'] as String?,
status: ImageStatus.values[map['status'] as int],
);
}
/// Creates a new [ImageMetadata] instance with optional new values.
///
/// The original object remains unchanged.
ImageMetadata copyWith({
String? localPath,
String? remoteUrl,
ImageStatus? status,
}) {
return ImageMetadata(
localPath: localPath ?? this.localPath,
remoteUrl: remoteUrl ?? this.remoteUrl,
status: status ?? this.status,
);
}
}

14
lib/app/core/models/image_status.dart

@ -1,14 +0,0 @@
///
enum ImageStatus {
///
synced,
//
pendingUpload,
///
pendingDeleted,
///
pendingDownload,
}

104
lib/app/core/models/problem_sync_status.dart

@ -1,104 +0,0 @@
import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/image_metadata_model.dart';
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:uuid/uuid.dart';
enum ProblemSyncStatus {
/// -
untracked,
/// - git的unmodified
synced,
/// - git的untracked staged
pendingCreate,
/// - git的modified staged
pendingUpdate,
/// - git的deleted staged
pendingDelete,
}
/// - git add/git commit
class ProblemStateManager extends GetxController {
/// uuid
final Uuid uuid;
final AuthRepository authRepository;
ProblemStateManager({required this.uuid, required this.authRepository});
///
Problem createNewProblem({
required String description,
required String location,
required List<ImageMetadata> imageUrls,
}) {
return Problem(
id: uuid.v4(),
description: description,
location: location,
imageUrls: imageUrls,
creationTime: DateTime.now().toUtc(),
creatorId: authRepository.getUserId(),
lastModifiedTime: DateTime.now().toUtc(),
syncStatus: ProblemSyncStatus.pendingCreate,
);
}
///
Problem modifyProblem(Problem problem) {
final newStatus = problem.syncStatus == ProblemSyncStatus.synced
? ProblemSyncStatus
.pendingUpdate //
: problem.syncStatus; //
return problem.copyWith(
syncStatus: newStatus,
lastModifiedTime: DateTime.now().toUtc(),
);
}
///
Problem markForDeletion(Problem problem) {
switch (problem.syncStatus) {
case ProblemSyncStatus.pendingCreate:
//
return problem.copyWith(
syncStatus: ProblemSyncStatus.untracked,
lastModifiedTime: DateTime.now().toUtc(),
);
case ProblemSyncStatus.synced:
case ProblemSyncStatus.pendingUpdate:
//
return problem.copyWith(
syncStatus: ProblemSyncStatus.pendingDelete,
lastModifiedTime: DateTime.now().toUtc(),
);
case ProblemSyncStatus.untracked:
case ProblemSyncStatus.pendingDelete:
//
return problem;
}
}
/// git reset
Problem undoDeletion(Problem problem) {
if (problem.syncStatus == ProblemSyncStatus.pendingDelete) {
return problem.copyWith(
syncStatus: ProblemSyncStatus.pendingUpdate,
lastModifiedTime: DateTime.now().toUtc(),
);
}
return problem;
}
/// git commit
Problem markAsSynced(Problem problem) {
return problem.copyWith(
syncStatus: ProblemSyncStatus.synced,
lastModifiedTime: DateTime.now().toUtc(),
);
}
}

2
lib/app/core/repositories/syncable_repository.dart

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
abstract class SyncableRepository<T extends SyncableEntity> { abstract class SyncableRepository<T extends SyncableEntity> {
// --- () --- // --- () ---

2
lib/app/core/services/sync_service.dart

@ -1,6 +1,6 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/repositories/syncable_repository.dart'; import 'package:problem_check_system/app/core/repositories/syncable_repository.dart';
import 'package:problem_check_system/app/core/services/sync_metadata_service.dart'; import 'package:problem_check_system/app/core/services/sync_metadata_service.dart';

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

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/services/database_service.dart'; import 'package:problem_check_system/app/core/services/database_service.dart';
import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_list_item_model.dart'; import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_list_item_model.dart';
import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart'; import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart';

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

@ -1,5 +1,5 @@
import 'package:problem_check_system/app/core/extensions/map_extensions.dart'; 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/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart'; import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart';
/// EnterpriseDto (Data Transfer Object) /// EnterpriseDto (Data Transfer Object)

2
lib/app/features/enterprise/data/model/enterprise_model.dart

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart'; import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
/// `EnterpriseModel` `Enterprise` /// `EnterpriseModel` `Enterprise`

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

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/services/network_status_service.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_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/datasources/enterprise_remote_data_source.dart';

2
lib/app/features/enterprise/domain/entities/enterprise.dart

@ -1,5 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
/// `Enterprise` /// `Enterprise`
/// ///

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

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/repositories/syncable_repository.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/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/entities/sync_result.dart';

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

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart'; import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart'; import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart'; import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart';

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

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart'; import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart'; import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart'; import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart';

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

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:get/get.dart'; // GetX import 'package:get/get.dart'; // GetX
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart'; import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart'; import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
/// ///
class UploadResult { class UploadResult {

99
lib/app/features/problem/data/datasources/problem_local_data_source.dart

@ -1,6 +1,7 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:problem_check_system/app/core/models/sync_status.dart'; // SyncStatus import 'package:problem_check_system/app/core/domain/entities/sync_status.dart'; // SyncStatus
import 'package:problem_check_system/app/core/services/database_service.dart'; import 'package:problem_check_system/app/core/services/database_service.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_filter_params.dart';
import 'package:sqflite/sqflite.dart'; // DatabaseService import 'package:sqflite/sqflite.dart'; // DatabaseService
/// IProblemLocalDataSource /// IProblemLocalDataSource
@ -19,12 +20,7 @@ abstract class IProblemLocalDataSource {
/// [endDate] - /// [endDate] -
/// [syncStatus] - /// [syncStatus] -
/// [bindStatus] - () /// [bindStatus] - ()
Future<List<Map<String, dynamic>>> getAllProblems({ Future<List<Map<String, dynamic>>> getAllProblems(ProblemFilterParams filter);
DateTime? startDate,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
});
/// ///
/// ///
@ -69,49 +65,70 @@ class ProblemLocalDataSource implements IProblemLocalDataSource {
} }
@override @override
Future<List<Map<String, dynamic>>> getAllProblems({ Future<List<Map<String, dynamic>>> getAllProblems(
DateTime? startDate, ProblemFilterParams filter,
DateTime? endDate, ) async {
String? syncStatus,
String? bindStatus, // bindStatus
}) async {
final db = await _databaseService.database; final db = await _databaseService.database;
// 使 JOIN
// WHERE final baseQuery = '''
SELECT
p.id as problem_id,
p.description as problem_description,
p.location as problem_location,
p.creationTime as problem_creationTime,
c.name as enterprise_name
FROM problems p
LEFT JOIN enterprises c ON p.enterpriseId = c.id
''';
// WHERE
List<String> whereClauses = []; List<String> whereClauses = [];
List<dynamic> whereArgs = []; List<dynamic> arguments = [];
// 1. ()
if (filter.enterpriseName != null && filter.enterpriseName!.isNotEmpty) {
whereClauses.add('c.name LIKE ?');
arguments.add('%${filter.enterpriseName}%');
}
// 2.
if (filter.startTime != null) {
whereClauses.add('p.creationTime >= ?');
//
arguments.add(filter.startTime!.toIso8601String());
}
if (startDate != null) { // 3.
// INTEGER () if (filter.endTime != null) {
whereClauses.add('creationTime >= ?'); whereClauses.add('p.creationTime <= ?');
whereArgs.add(startDate.millisecondsSinceEpoch); arguments.add(filter.endTime!.toIso8601String());
} }
if (endDate != null) {
whereClauses.add('creationTime <= ?'); // 4.
whereArgs.add(endDate.millisecondsSinceEpoch); if (filter.syncStatus != null) {
whereClauses.add('p.syncStatus = ?');
// SyncStatus
arguments.add(filter.syncStatus!.name);
} }
if (syncStatus != null) {
// INTEGER ( index) // 5.
// index if (filter.isBound != null) {
try { if (filter.isBound!) {
final status = SyncStatus.values.byName(syncStatus); whereClauses.add('p.bindData IS NOT NULL AND p.bindData != ""');
whereClauses.add('syncStatus = ?'); } else {
whereArgs.add(status.index); whereClauses.add('p.bindData IS NULL OR p.bindData = ""');
} catch (e) {
// status
Get.log('无效的 syncStatus 过滤器: $syncStatus', isError: true);
} }
} }
final String? whereString = whereClauses.isEmpty //
? null String finalQuery = baseQuery;
: whereClauses.join(' AND '); if (whereClauses.isNotEmpty) {
finalQuery += ' WHERE ' + whereClauses.join(' AND ');
}
final List<Map<String, dynamic>> maps = await db.query( // 使 `?` SQL
_tableName, final List<Map<String, dynamic>> maps = await db.rawQuery(
where: whereString, finalQuery,
whereArgs: whereArgs, arguments,
orderBy: 'creationTime DESC', //
); );
return maps; return maps;
} }

144
lib/app/features/problem/data/model/problem_dto.dart

@ -0,0 +1,144 @@
// import 'dart:convert';
// import 'package:problem_check_system/app/core/extensions/map_extensions.dart';
// import 'package:problem_check_system/app/core/models/image_metadata_model.dart';
// import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
// ///
// ///
// class ProblemRemoteDto {
// ///
// final String id;
// /// id
// final String? enterpriseId;
// ///
// final String description;
// ///
// final String location;
// ///
// final List<String> imageUrls;
// ///
// final DateTime creationTime;
// /// id
// final String creatorId;
// ///
// final ProblemSyncStatus syncStatus;
// ///
// final DateTime lastModifiedTime;
// /// ID
// final String? censorTaskId;
// ///
// final String? bindData;
// /// false
// final bool isChecked;
// ProblemRemoteDto({
// required this.id,
// required this.description,
// required this.location,
// required this.imageUrls,
// required this.creationTime,
// required this.creatorId,
// required this.lastModifiedTime,
// this.syncStatus = ProblemSyncStatus.pendingCreate,
// this.censorTaskId,
// this.bindData,
// this.isChecked = false,
// this.enterpriseId,
// });
// /// copyWith
// ProblemRemoteDto copyWith({
// String? id,
// String? enterpriseId,
// String? description,
// String? location,
// List<ImageMetadata>? imageUrls,
// DateTime? creationTime,
// String? creatorId,
// DateTime? lastModifiedTime,
// ProblemSyncStatus? syncStatus,
// bool? isDeleted,
// String? censorTaskId,
// String? bindData,
// bool? isChecked,
// }) {
// return ProblemRemoteDto(
// id: id ?? this.id,
// enterpriseId: enterpriseId ?? this.enterpriseId,
// description: description ?? this.description,
// 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,
// bindData: bindData ?? this.bindData,
// isChecked: isChecked ?? this.isChecked,
// );
// }
// /// JSON字符串
// Map<String, dynamic> toJson() {
// return {
// 'id': id,
// 'enterpriseId': enterpriseId,
// 'description': description,
// 'location': location,
// 'imageUrls': json.encode(imageUrls.map((e) => e.toMap()).toList()),
// 'creationTime': creationTime.toIso8601String(),
// 'creatorId': creatorId,
// 'lastModifiedTime': lastModifiedTime.toIso8601String(),
// 'censorTaskId': censorTaskId,
// 'bindData': bindData,
// }.withoutNullOrEmptyValues;
// }
// /// Map创建对象SQLite读取
// factory ProblemRemoteDto.fromJson(Map<String, dynamic> map) {
// // imageUrls的转换
// List<ImageMetadata> imageUrlsList = [];
// if (map['imageUrls'] != null) {
// try {
// final List<dynamic> imageList = json.decode(map['imageUrls']);
// imageUrlsList = imageList.map((e) => ImageMetadata.fromMap(e)).toList();
// } catch (e) {
// //
// imageUrlsList = [];
// }
// }
// return ProblemRemoteDto(
// id: map['id'],
// enterpriseId: map['companyId'],
// description: map['description'],
// location: map['location'],
// imageUrls: imageUrlsList,
// 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'],
// bindData: map['bindData'],
// isChecked: map['isChecked'] == 1,
// );
// }
// }

103
lib/app/features/problem/data/model/problem_local_dto.dart

@ -1,103 +0,0 @@
import 'dart:convert';
import 'package:problem_check_system/app/core/models/sync_status.dart';
import '../../domain/entities/problem_entity.dart';
/// ProblemLocalDto ()
///
/// `problems`
/// (Entity) Map
/// TEXT
class ProblemLocalDto {
final String id;
final String enterpriseId;
final String description;
final String location;
final String imageUrls;
final String creatorId;
final String creationTime;
final String lastModifierId;
final String lastModifiedTime;
final SyncStatus syncStatus;
final String? bindData;
ProblemLocalDto({
required this.id,
required this.enterpriseId,
required this.description,
required this.location,
required this.imageUrls,
required this.creatorId,
required this.creationTime,
required this.lastModifierId,
required this.lastModifiedTime,
required this.syncStatus,
required this.bindData,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'enterpriseId': enterpriseId,
'description': description,
'location': location,
'imageUrls': imageUrls,
'creatorId': creatorId,
'creationTime': creationTime,
'lastModifierId': lastModifierId,
'lastModifiedTime': lastModifiedTime,
'syncStatus': syncStatus.name,
'bindData': bindData,
};
}
factory ProblemLocalDto.fromMap(Map<String, dynamic> map) {
return ProblemLocalDto(
id: map['id'] as String,
enterpriseId: map['enterpriseId'] as String,
description: map['description'] as String,
location: map['location'] as String,
imageUrls: map['imageUrls'] as String,
creatorId: map['creatorId'] as String,
creationTime: map['creationTime'] as String,
lastModifierId: map['lastModifierId'],
lastModifiedTime: map['lastModifiedTime'] as String,
syncStatus: SyncStatus.values.byName(map['syncStatus'] as String),
bindData: map['bindData'] as String?,
);
}
/// ProblemEntity DTO
factory ProblemLocalDto.fromEntity(ProblemEntity entity) {
return ProblemLocalDto(
id: entity.id,
enterpriseId: entity.enterpriseId,
description: entity.description,
location: entity.location,
imageUrls: jsonEncode(entity.imageUrls),
creatorId: entity.creatorId,
creationTime: entity.creationTime.toUtc().toIso8601String(),
lastModifierId: entity.lastModifierId,
lastModifiedTime: entity.lastModifiedTime.toUtc().toIso8601String(),
syncStatus: entity.syncStatus,
bindData: entity.bindData,
);
}
/// DTO ProblemEntity
ProblemEntity toEntity() {
return ProblemEntity(
id: id,
enterpriseId: enterpriseId,
description: description,
location: location,
imageUrls: List<String>.from(jsonDecode(imageUrls)),
creatorId: creatorId,
creationTime: DateTime.parse(creationTime),
lastModifierId: lastModifierId,
lastModifiedTime: DateTime.parse(lastModifiedTime),
syncStatus: syncStatus,
bindData: bindData,
);
}
}

187
lib/app/features/problem/data/model/problem_model.dart

@ -1,152 +1,97 @@
import 'dart:convert'; import 'dart:convert';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/core/models/image_metadata_model.dart'; class ProblemModel {
import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
///
///
class Problem {
///
final String id; final String id;
final String enterpriseId;
/// id
final String? companyId;
///
final String description; final String description;
///
final String location; final String location;
final String imageUrls;
///
final List<ImageMetadata> imageUrls;
///
final DateTime creationTime;
/// id
final String creatorId; final String creatorId;
final String creationTime;
/// final String lastModifierId;
final ProblemSyncStatus syncStatus; final String lastModifiedTime;
final SyncStatus syncStatus;
///
final DateTime lastModifiedTime;
/// ID
final String? censorTaskId;
///
final String? bindData; final String? bindData;
/// false ProblemModel({
final bool isChecked;
Problem({
required this.id, required this.id,
required this.enterpriseId,
required this.description, required this.description,
required this.location, required this.location,
required this.imageUrls, required this.imageUrls,
required this.creationTime,
required this.creatorId, required this.creatorId,
required this.creationTime,
required this.lastModifierId,
required this.lastModifiedTime, required this.lastModifiedTime,
this.syncStatus = ProblemSyncStatus.pendingCreate, required this.syncStatus,
this.censorTaskId, required this.bindData,
this.bindData,
this.isChecked = false,
this.companyId,
}); });
/// copyWith
Problem copyWith({
String? id,
String? companyId,
String? description,
String? location,
List<ImageMetadata>? imageUrls,
DateTime? creationTime,
String? creatorId,
DateTime? lastModifiedTime,
ProblemSyncStatus? syncStatus,
bool? isDeleted,
String? censorTaskId,
String? bindData,
bool? isChecked,
}) {
return Problem(
id: id ?? this.id,
companyId: companyId ?? this.companyId,
description: description ?? this.description,
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,
bindData: bindData ?? this.bindData,
isChecked: isChecked ?? this.isChecked,
);
}
/// MapSQLite存储
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return { return {
'id': id, 'id': id,
'companyId': companyId, 'enterpriseId': enterpriseId,
'description': description, 'description': description,
'location': location, 'location': location,
'imageUrls': json.encode(imageUrls.map((e) => e.toMap()).toList()), 'imageUrls': imageUrls,
'creationTime': creationTime.millisecondsSinceEpoch,
'creatorId': creatorId, 'creatorId': creatorId,
'lastModifiedTime': lastModifiedTime.millisecondsSinceEpoch, 'creationTime': creationTime,
'syncStatus': syncStatus.index, 'lastModifierId': lastModifierId,
'censorTaskId': censorTaskId, 'lastModifiedTime': lastModifiedTime,
'syncStatus': syncStatus.name,
'bindData': bindData, 'bindData': bindData,
'isChecked': isChecked ? 1 : 0,
}; };
} }
/// Map创建对象SQLite读取 factory ProblemModel.fromMap(Map<String, dynamic> map) {
factory Problem.fromMap(Map<String, dynamic> map) { return ProblemModel(
// imageUrls的转换 id: map['id'] as String,
List<ImageMetadata> imageUrlsList = []; enterpriseId: map['enterpriseId'] as String,
if (map['imageUrls'] != null) { description: map['description'] as String,
try { location: map['location'] as String,
final List<dynamic> imageList = json.decode(map['imageUrls']); imageUrls: map['imageUrls'] as String,
imageUrlsList = imageList.map((e) => ImageMetadata.fromMap(e)).toList(); creatorId: map['creatorId'] as String,
} catch (e) { creationTime: map['creationTime'] as String,
// lastModifierId: map['lastModifierId'],
imageUrlsList = []; lastModifiedTime: map['lastModifiedTime'] as String,
} syncStatus: SyncStatus.values.byName(map['syncStatus'] as String),
} bindData: map['bindData'] as String?,
return Problem(
id: map['id'],
companyId: map['companyId'],
description: map['description'],
location: map['location'],
imageUrls: imageUrlsList,
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'],
bindData: map['bindData'],
isChecked: map['isChecked'] == 1,
); );
} }
/// JSON字符串 /// ProblemEntity DTO
String toJson() => json.encode(toMap()); factory ProblemModel.fromEntity(ProblemEntity entity) {
return ProblemModel(
id: entity.id,
enterpriseId: entity.enterpriseId,
description: entity.description,
location: entity.location,
imageUrls: jsonEncode(entity.imageUrls),
creatorId: entity.creatorId,
creationTime: entity.creationTime.toUtc().toIso8601String(),
lastModifierId: entity.lastModifierId,
lastModifiedTime: entity.lastModifiedTime.toUtc().toIso8601String(),
syncStatus: entity.syncStatus,
bindData: entity.bindData,
);
}
/// JSON字符串创建对象 /// DTO ProblemEntity
factory Problem.fromJson(String source) => ProblemEntity toEntity() {
Problem.fromMap(json.decode(source)); return ProblemEntity(
id: id,
enterpriseId: enterpriseId,
description: description,
location: location,
imageUrls: List<String>.from(jsonDecode(imageUrls)),
creatorId: creatorId,
creationTime: DateTime.parse(creationTime),
lastModifierId: lastModifierId,
lastModifiedTime: DateTime.parse(lastModifiedTime),
syncStatus: syncStatus,
bindData: bindData,
);
}
} }

144
lib/app/features/problem/data/model/problem_remote_dto.dart

@ -1,144 +0,0 @@
import 'dart:convert';
import 'package:problem_check_system/app/core/extensions/map_extensions.dart';
import 'package:problem_check_system/app/core/models/image_metadata_model.dart';
import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
///
///
class ProblemRemoteDto {
///
final String id;
/// id
final String? enterpriseId;
///
final String description;
///
final String location;
///
final List<ImageMetadata> imageUrls;
///
final DateTime creationTime;
/// id
final String creatorId;
///
final ProblemSyncStatus syncStatus;
///
final DateTime lastModifiedTime;
/// ID
final String? censorTaskId;
///
final String? bindData;
/// false
final bool isChecked;
ProblemRemoteDto({
required this.id,
required this.description,
required this.location,
required this.imageUrls,
required this.creationTime,
required this.creatorId,
required this.lastModifiedTime,
this.syncStatus = ProblemSyncStatus.pendingCreate,
this.censorTaskId,
this.bindData,
this.isChecked = false,
this.enterpriseId,
});
/// copyWith
ProblemRemoteDto copyWith({
String? id,
String? enterpriseId,
String? description,
String? location,
List<ImageMetadata>? imageUrls,
DateTime? creationTime,
String? creatorId,
DateTime? lastModifiedTime,
ProblemSyncStatus? syncStatus,
bool? isDeleted,
String? censorTaskId,
String? bindData,
bool? isChecked,
}) {
return ProblemRemoteDto(
id: id ?? this.id,
enterpriseId: enterpriseId ?? this.enterpriseId,
description: description ?? this.description,
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,
bindData: bindData ?? this.bindData,
isChecked: isChecked ?? this.isChecked,
);
}
/// JSON字符串
Map<String, dynamic> toJson() {
return {
'id': id,
'enterpriseId': enterpriseId,
'description': description,
'location': location,
'imageUrls': json.encode(imageUrls.map((e) => e.toMap()).toList()),
'creationTime': creationTime.toIso8601String(),
'creatorId': creatorId,
'lastModifiedTime': lastModifiedTime.toIso8601String(),
'censorTaskId': censorTaskId,
'bindData': bindData,
}.withoutNullOrEmptyValues;
}
/// Map创建对象SQLite读取
factory ProblemRemoteDto.fromJson(Map<String, dynamic> map) {
// imageUrls的转换
List<ImageMetadata> imageUrlsList = [];
if (map['imageUrls'] != null) {
try {
final List<dynamic> imageList = json.decode(map['imageUrls']);
imageUrlsList = imageList.map((e) => ImageMetadata.fromMap(e)).toList();
} catch (e) {
//
imageUrlsList = [];
}
}
return ProblemRemoteDto(
id: map['id'],
enterpriseId: map['companyId'],
description: map['description'],
location: map['location'],
imageUrls: imageUrlsList,
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'],
bindData: map['bindData'],
isChecked: map['isChecked'] == 1,
);
}
}

44
lib/app/features/problem/data/repositories/problem_repository_impl.dart

@ -1,11 +1,13 @@
import 'package:problem_check_system/app/features/problem/data/datasources/problem_local_data_source.dart'; import 'package:problem_check_system/app/features/problem/data/datasources/problem_local_data_source.dart';
import 'package:problem_check_system/app/features/problem/data/model/problem_local_dto.dart'; import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart'; import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_filter_params.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_list_item_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart'; import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
/// ///
/// ///
/// (ProblemEntity) (ProblemLocalDto) /// (ProblemEntity) (ProblemModel)
class ProblemRepository implements IProblemRepository { class ProblemRepository implements IProblemRepository {
final IProblemLocalDataSource problemLocalDataSource; // 2. final IProblemLocalDataSource problemLocalDataSource; // 2.
@ -14,7 +16,7 @@ class ProblemRepository implements IProblemRepository {
@override @override
Future<ProblemEntity> addProblem(ProblemEntity problem) async { Future<ProblemEntity> addProblem(ProblemEntity problem) async {
// 1. (Entity) (DTO) // 1. (Entity) (DTO)
final problemDto = ProblemLocalDto.fromEntity(problem); final problemDto = ProblemModel.fromEntity(problem);
// 2. DTO Map // 2. DTO Map
await problemLocalDataSource.addProblem(problemDto.toMap()); await problemLocalDataSource.addProblem(problemDto.toMap());
@ -30,27 +32,23 @@ class ProblemRepository implements IProblemRepository {
} }
@override @override
Future<List<ProblemEntity>> getAllProblems({ Future<List<ProblemListItemEntity>> getAllProblemListItem({
DateTime? startDate, required ProblemFilterParams filter,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
}) async { }) async {
// 1. (List of Maps) // 1. (List of Maps)
final problemMaps = await problemLocalDataSource.getAllProblems( final problemDataMaps = await problemLocalDataSource.getAllProblems(filter);
//
startDate: startDate,
endDate: endDate,
syncStatus: syncStatus,
bindStatus: bindStatus,
);
// 2. Map DTO (Entity) // 2. Map ProblemListItemEntity
final problems = problemMaps //
.map((map) => ProblemLocalDto.fromMap(map).toEntity()) return problemDataMaps.map((map) {
.toList(); return ProblemListItemEntity(
id: map['problem_id'],
return problems; description: map['problem_description'],
location: map['problem_location'],
creationTime: DateTime.parse(map['problem_creationTime']),
enterpriseName: map['enterprise_name'] ?? '未知企业',
);
}).toList();
} }
@override @override
@ -64,13 +62,13 @@ class ProblemRepository implements IProblemRepository {
} }
// 3. DTO // 3. DTO
return ProblemLocalDto.fromMap(problemMap).toEntity(); return ProblemModel.fromMap(problemMap).toEntity();
} }
@override @override
Future<ProblemEntity> updateProblem(ProblemEntity problem) async { Future<ProblemEntity> updateProblem(ProblemEntity problem) async {
// 1. (Entity) (DTO) // 1. (Entity) (DTO)
final problemDto = ProblemLocalDto.fromEntity(problem); final problemDto = ProblemModel.fromEntity(problem);
// 2. DTO Map // 2. DTO Map
await problemLocalDataSource.updateProblem(problemDto.toMap()); await problemLocalDataSource.updateProblem(problemDto.toMap());

4
lib/app/features/problem/domain/entities/problem_entity.dart

@ -1,4 +1,4 @@
import 'package:problem_check_system/app/core/models/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
class ProblemEntity implements SyncableEntity { class ProblemEntity implements SyncableEntity {
// 1. getter final // 1. getter final
@ -14,7 +14,7 @@ class ProblemEntity implements SyncableEntity {
@override @override
final SyncStatus syncStatus; final SyncStatus syncStatus;
/// /// Id
final String enterpriseId; final String enterpriseId;
/// ///

30
lib/app/features/problem/domain/entities/problem_filter_params.dart

@ -0,0 +1,30 @@
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
///
class ProblemFilterParams {
/// ()
final String? enterpriseName;
/// -
final DateTime? startTime;
/// -
final DateTime? endTime;
/// ()
final SyncStatus? syncStatus;
/// (true: , false: , null: )
final bool? isBound;
ProblemFilterParams({
this.enterpriseName,
this.startTime,
this.endTime,
this.syncStatus,
this.isBound,
});
/// '无筛选'
factory ProblemFilterParams.initial() => ProblemFilterParams();
}

15
lib/app/features/problem/domain/entities/problem_list_item_entity.dart

@ -0,0 +1,15 @@
class ProblemListItemEntity {
final String id;
final String description;
final String location;
final DateTime creationTime;
final String enterpriseName;
ProblemListItemEntity({
required this.id,
required this.description,
required this.location,
required this.creationTime,
required this.enterpriseName,
});
}

9
lib/app/features/problem/domain/repositories/problem_repository.dart

@ -1,13 +1,12 @@
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart'; import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_filter_params.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_list_item_entity.dart';
/// Problem /// Problem
/// ///
abstract class IProblemRepository { abstract class IProblemRepository {
Future<List<ProblemEntity>> getAllProblems({ Future<List<ProblemListItemEntity>> getAllProblemListItem({
DateTime? startDate, required ProblemFilterParams filter,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
}); });
Future<ProblemEntity?> getProblemById(String id); Future<ProblemEntity?> getProblemById(String id);
Future<ProblemEntity> addProblem(ProblemEntity problem); Future<ProblemEntity> addProblem(ProblemEntity problem);

7
lib/app/features/problem/domain/usecases/add_problem_usecase.dart

@ -1,4 +1,5 @@
import 'package:problem_check_system/app/core/models/sync_status.dart'; import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart'; import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart'; import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart'; import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
@ -16,7 +17,7 @@ class AddProblemUsecase {
}); });
Future<ProblemEntity> call({ Future<ProblemEntity> call({
required String enterpriseId, required Enterprise enterprise,
required String description, required String description,
required String location, required String location,
required List<String> imageUrls, required List<String> imageUrls,
@ -31,7 +32,7 @@ class AddProblemUsecase {
lastModifiedTime: nowUtc, lastModifiedTime: nowUtc,
lastModifierId: userId, lastModifierId: userId,
syncStatus: SyncStatus.pendingCreate, syncStatus: SyncStatus.pendingCreate,
enterpriseId: enterpriseId, enterpriseId: enterprise.id,
creationTime: nowUtc, creationTime: nowUtc,
creatorId: userId, creatorId: userId,
); );

17
lib/app/features/problem/domain/usecases/get_all_problems_usecase.dart

@ -1,4 +1,5 @@
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart'; import 'package:problem_check_system/app/features/problem/domain/entities/problem_filter_params.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_list_item_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart'; import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
class GetAllProblemsUsecase { class GetAllProblemsUsecase {
@ -6,17 +7,11 @@ class GetAllProblemsUsecase {
GetAllProblemsUsecase({required this.problemRepository}); GetAllProblemsUsecase({required this.problemRepository});
Future<List<ProblemEntity>> call({ Future<List<ProblemListItemEntity>> call({
DateTime? startDate, ProblemFilterParams? filter,
DateTime? endDate,
String? syncStatus,
String? bindStatus,
}) async { }) async {
return await problemRepository.getAllProblems( return await problemRepository.getAllProblemListItem(
startDate: startDate, filter: filter ?? ProblemFilterParams.initial(),
endDate: endDate,
syncStatus: syncStatus,
bindStatus: bindStatus,
); );
} }
} }

9
lib/app/features/problem/domain/usecases/update_problem.dart → lib/app/features/problem/domain/usecases/update_problem_usecase.dart

@ -1,13 +1,16 @@
import 'package:problem_check_system/app/core/models/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/core/repositories/auth_repository.dart'; import 'package:problem_check_system/app/core/repositories/auth_repository.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart'; import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart'; import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart';
class UpdateProblem { class UpdateProblemUsecase {
final IProblemRepository repository; final IProblemRepository repository;
final AuthRepository authRepository; final AuthRepository authRepository;
UpdateProblem({required this.repository, required this.authRepository}); UpdateProblemUsecase({
required this.repository,
required this.authRepository,
});
Future<ProblemEntity> call(ProblemEntity entity) async { Future<ProblemEntity> call(ProblemEntity entity) async {
final nowUtc = DateTime.now().toUtc(); final nowUtc = DateTime.now().toUtc();

70
lib/app/features/problem/presentation/bindings/problem_form_binding.dart

@ -1,4 +1,5 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:problem_check_system/app/core/bindings/base_bindings.dart';
import 'package:problem_check_system/app/core/models/form_mode.dart'; import 'package:problem_check_system/app/core/models/form_mode.dart';
import 'package:problem_check_system/app/core/services/database_service.dart'; import 'package:problem_check_system/app/core/services/database_service.dart';
import 'package:problem_check_system/app/core/services/http_provider.dart'; import 'package:problem_check_system/app/core/services/http_provider.dart';
@ -8,31 +9,18 @@ import 'package:problem_check_system/app/features/enterprise/data/repositories_i
import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.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_enterprises_usecase.dart'; import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprises_usecase.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart'; import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/usecases/add_problem_usecase.dart';
import 'package:problem_check_system/app/features/problem/domain/usecases/update_problem_usecase.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_form_controller.dart'; import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_form_controller.dart';
class ProblemFormBinding extends Bindings { class ProblemFormBinding extends BaseBindings {
@override @override
void dependencies() { void register1Services() {
// final dynamic arguments = Get.arguments; // TODO: implement register1Services
// final bool readOnly = Get.parameters['isReadOnly'] == 'true'; }
ProblemEntity? problem;
FormMode formMode = FormMode.view;
if (Get.arguments is Map) {
final arguments = Get.arguments as Map;
//
if (arguments.containsKey('mode')) {
formMode = arguments['mode'] as FormMode;
}
//
if (arguments.containsKey('data')) {
problem = arguments['data'] as ProblemEntity?;
}
}
@override
void register2DataSource() {
Get.put<EnterpriseLocalDataSource>( Get.put<EnterpriseLocalDataSource>(
EnterpriseLocalDataSourceImpl( EnterpriseLocalDataSourceImpl(
databaseService: Get.find<DatabaseService>(), databaseService: Get.find<DatabaseService>(),
@ -41,7 +29,10 @@ class ProblemFormBinding extends Bindings {
Get.put<EnterpriseRemoteDataSource>( Get.put<EnterpriseRemoteDataSource>(
EnterpriseRemoteDataSourceImpl(http: Get.find<HttpProvider>()), EnterpriseRemoteDataSourceImpl(http: Get.find<HttpProvider>()),
); );
}
@override
void register3Repositories() {
Get.put<EnterpriseRepository>( Get.put<EnterpriseRepository>(
EnterpriseRepositoryImpl( EnterpriseRepositoryImpl(
localDataSource: Get.find<EnterpriseLocalDataSource>(), localDataSource: Get.find<EnterpriseLocalDataSource>(),
@ -50,16 +41,53 @@ class ProblemFormBinding extends Bindings {
uuid: Get.find(), uuid: Get.find(),
), ),
); );
}
@override
void register4Usecases() {
Get.put<GetEnterprisesUsecase>( Get.put<GetEnterprisesUsecase>(
GetEnterprisesUsecase(repository: Get.find<EnterpriseRepository>()), GetEnterprisesUsecase(repository: Get.find<EnterpriseRepository>()),
); );
Get.lazyPut(
() => AddProblemUsecase(
problemRepository: Get.find(),
authRepository: Get.find(),
uuid: Get.find(),
),
);
Get.lazyPut(
() => UpdateProblemUsecase(
repository: Get.find(),
authRepository: Get.find(),
),
);
}
@override
void register5Controllers() {
ProblemEntity? problem;
FormMode formMode = FormMode.view;
if (Get.arguments is Map) {
final arguments = Get.arguments as Map;
//
if (arguments.containsKey('mode')) {
formMode = arguments['mode'] as FormMode;
}
//
if (arguments.containsKey('data')) {
problem = arguments['data'] as ProblemEntity?;
}
}
Get.lazyPut<ProblemFormController>( Get.lazyPut<ProblemFormController>(
() => ProblemFormController( () => ProblemFormController(
problem: problem, problem: problem,
formMode: formMode, formMode: formMode,
getEnterprisesUsecase: Get.find<GetEnterprisesUsecase>(), getEnterprisesUsecase: Get.find<GetEnterprisesUsecase>(),
addProblemUsecase: Get.find(),
updateProblemUsecase: Get.find(),
), ),
); );
} }

102
lib/app/features/problem/presentation/controllers/problem_form_controller.dart

@ -7,12 +7,12 @@ import 'package:path/path.dart' as path;
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:problem_check_system/app/core/models/form_mode.dart'; import 'package:problem_check_system/app/core/models/form_mode.dart';
import 'package:problem_check_system/app/core/models/image_status.dart';
import 'package:problem_check_system/app/core/models/image_metadata_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.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprises_usecase.dart'; import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprises_usecase.dart';
import 'dart:io'; import 'dart:io';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart'; import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/usecases/add_problem_usecase.dart';
import 'package:problem_check_system/app/features/problem/domain/usecases/update_problem_usecase.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
class ProblemFormController extends GetxController { class ProblemFormController extends GetxController {
@ -40,13 +40,18 @@ class ProblemFormController extends GetxController {
final TextEditingController descriptionController = TextEditingController(); final TextEditingController descriptionController = TextEditingController();
final TextEditingController locationController = TextEditingController(); final TextEditingController locationController = TextEditingController();
final RxList<XFile> selectedImages = <XFile>[].obs; final RxList<XFile> selectedImages = <XFile>[].obs;
//
final GetEnterprisesUsecase getEnterprisesUsecase; final GetEnterprisesUsecase getEnterprisesUsecase;
final AddProblemUsecase addProblemUsecase;
final UpdateProblemUsecase updateProblemUsecase;
// 使便 // 使便
ProblemFormController({ ProblemFormController({
this.problem, this.problem,
this.formMode = FormMode.view, this.formMode = FormMode.view,
required this.getEnterprisesUsecase, required this.getEnterprisesUsecase,
required this.addProblemUsecase,
required this.updateProblemUsecase,
}) { }) {
if (problem != null) { if (problem != null) {
if (problem!.bindData != null) { if (problem!.bindData != null) {
@ -105,12 +110,12 @@ class ProblemFormController extends GetxController {
} }
} }
// 5. : UI调用以更新状态 // UI调用以更新状态
void selectEnterprise(Enterprise? enterprise) { void selectEnterprise(Enterprise? enterprise) {
selectedEnterprise.value = enterprise; selectedEnterprise.value = enterprise;
// //
if (enterprise != null) { if (enterprise != null) {
print('Selected: ${enterprise.name}'); Get.log('选择: ${enterprise.name}');
} }
} }
@ -184,6 +189,11 @@ class ProblemFormController extends GetxController {
// //
bool _validateForm() { bool _validateForm() {
if (selectedEnterprise.value == null) {
Get.snackbar('提示', '请选择一个企业', snackPosition: SnackPosition.TOP);
return false;
}
if (descriptionController.text.isEmpty) { if (descriptionController.text.isEmpty) {
Get.snackbar('提示', '请填写问题描述', snackPosition: SnackPosition.TOP); Get.snackbar('提示', '请填写问题描述', snackPosition: SnackPosition.TOP);
return false; return false;
@ -204,49 +214,45 @@ class ProblemFormController extends GetxController {
/// ///
Future<void> saveProblem() async { Future<void> saveProblem() async {
// if (!_validateForm()) { if (!_validateForm()) {
// return; return;
// } }
// isLoading.value = true; isLoading.value = true;
// try { try {
// // //
// final List<ImageMetadata> imagePaths = await _saveImagesToLocal(); final List<String> imagePaths = await _saveImagesToLocal();
// if (problem != null) { if (problem != null) {
// // //
// final updatedProblem = problem!.copyWith( final updatedProblem = problem!.copyWith(
// description: descriptionController.text, description: descriptionController.text,
// location: locationController.text, location: locationController.text,
// imageUrls: imagePaths, imageUrls: imagePaths,
// ); );
// // await updateProblemUsecase(updatedProblem);
// final modifyProblem = problemStateManager.modifyProblem(updatedProblem); } else {
//
// await problemRepository.updateProblem(modifyProblem); await addProblemUsecase(
// } else { enterprise: selectedEnterprise.value!,
// // description: descriptionController.text,
// final newProblem = problemStateManager.createNewProblem( location: locationController.text,
// description: descriptionController.text, imageUrls: imagePaths,
// location: locationController.text, );
// imageUrls: imagePaths, }
// ); Get.back(result: true); //
Get.snackbar('成功', '问题已更新');
// await problemRepository.insertProblem(newProblem); } catch (e) {
// } Get.snackbar('错误', '保存问题失败: $e');
// Get.back(result: true); // } finally {
// Get.snackbar('成功', '问题已更新'); isLoading.value = false;
// } catch (e) { }
// Get.snackbar('错误', '保存问题失败: $e');
// } finally {
// isLoading.value = false;
// }
} }
// //
Future<List<ImageMetadata>> _saveImagesToLocal() async { Future<List<String>> _saveImagesToLocal() async {
final List<ImageMetadata> imagePaths = []; final List<String> imagePaths = [];
final directory = await getApplicationDocumentsDirectory(); final directory = await getApplicationDocumentsDirectory();
final imagesDir = Directory('${directory.path}/problem_images'); final imagesDir = Directory('${directory.path}/problem_images');
@ -254,22 +260,18 @@ class ProblemFormController extends GetxController {
if (!await imagesDir.exists()) { if (!await imagesDir.exists()) {
await imagesDir.create(recursive: true); await imagesDir.create(recursive: true);
} }
//
for (var image in selectedImages) { for (var image in selectedImages) {
try { try {
final String fileName = '${Uuid().v4()}_${path.basename(image.name)}'; final String fileName = '${Uuid().v4()}_${path.basename(image.name)}';
final String imagePath = '${imagesDir.path}/$fileName'; final String imagePath = '${imagesDir.path}/$fileName';
final ImageMetadata imageData = ImageMetadata(
localPath: imagePath,
status: ImageStatus.pendingUpload,
);
final File imageFile = File(imagePath); final File imageFile = File(imagePath);
// //
final imageBytes = await image.readAsBytes(); final imageBytes = await image.readAsBytes();
await imageFile.writeAsBytes(imageBytes); await imageFile.writeAsBytes(imageBytes);
imagePaths.add(imageData); imagePaths.add(imagePath);
} catch (e) { } catch (e) {
throw Exception(e); throw Exception(e);
} }

0
lib/app/features/problem/presentation/models/problem_card_model.dart

97
lib/app/features/problem/presentation/models/problem_card_state.dart

@ -0,0 +1,97 @@
// presentation/states/problem_card_state.dart
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
class ProblemCardState extends Equatable {
final String problemId; //
final String? previewImageUrl; // []
final String description; // []
final String enterpriseName; // []
final String location; // []
final String creationTime; // []
final StatusTagState uploadStatus; // []
final StatusTagState bindingStatus; // []
const ProblemCardState({
required this.problemId,
this.previewImageUrl,
required this.description,
required this.enterpriseName,
required this.location,
required this.creationTime,
required this.uploadStatus,
required this.bindingStatus,
});
// []
factory ProblemCardState.fromEntities({
required ProblemEntity problem,
required Enterprise enterprise,
}) {
// 1. null
final String? previewImageUrl = problem.imageUrls.isNotEmpty
? problem.imageUrls.first
: null;
// 2. DateTime
final String formattedTime =
'${problem.creationTime.year}-${problem.creationTime.month.toString().padLeft(2, '0')}-${problem.creationTime.day.toString().padLeft(2, '0')}';
// 3. SyncStatus UI的 StatusTagState
StatusTagState uploadStatusState;
switch (problem.syncStatus) {
case SyncStatus.synced:
uploadStatusState = StatusTagState(text: '已上传', color: Colors.green);
break;
case SyncStatus.pendingCreate:
case SyncStatus.pendingUpdate:
case SyncStatus.pendingDelete:
uploadStatusState = StatusTagState(text: '待上传', color: Colors.orange);
break;
case SyncStatus.untracked:
default:
uploadStatusState = StatusTagState(text: '未上传', color: Colors.grey);
break;
}
// 4. bindData
final StatusTagState bindingStatusState =
(problem.bindData != null && problem.bindData!.isNotEmpty)
? StatusTagState(text: '已绑定', color: Colors.blue)
: StatusTagState(text: '未绑定', color: Colors.red);
return ProblemCardState(
problemId: problem.id,
previewImageUrl: previewImageUrl,
description: problem.description,
enterpriseName: enterprise.name,
location: problem.location,
creationTime: formattedTime,
uploadStatus: uploadStatusState,
bindingStatus: bindingStatusState,
);
}
@override
List<Object?> get props => [
problemId,
previewImageUrl,
description,
enterpriseName,
location,
creationTime,
uploadStatus,
bindingStatus,
];
}
//
class StatusTagState {
final String text;
final Color color;
StatusTagState({required this.text, required this.color});
}

1
lib/app/features/problem/presentation/pages/widgets/problem_card.dart

@ -3,7 +3,6 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:problem_check_system/app/core/routes/app_routes.dart'; import 'package:problem_check_system/app/core/routes/app_routes.dart';
import 'package:problem_check_system/app/core/models/problem_sync_status.dart';
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart'; import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
import 'package:problem_check_system/app/features/problem/presentation/pages/widgets/custom_button.dart'; import 'package:problem_check_system/app/features/problem/presentation/pages/widgets/custom_button.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart';

Loading…
Cancel
Save