diff --git a/lib/app/core/bindings/base_bindings.dart b/lib/app/core/bindings/base_bindings.dart new file mode 100644 index 0000000..0e85729 --- /dev/null +++ b/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(); +} diff --git a/lib/app/core/models/sync_status.dart b/lib/app/core/domain/entities/sync_status.dart similarity index 100% rename from lib/app/core/models/sync_status.dart rename to lib/app/core/domain/entities/sync_status.dart diff --git a/lib/app/core/models/image_metadata_model.dart b/lib/app/core/models/image_metadata_model.dart deleted file mode 100644 index 991a909..0000000 --- a/lib/app/core/models/image_metadata_model.dart +++ /dev/null @@ -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 toMap() { - return { - 'localPath': localPath, - 'remoteUrl': remoteUrl, - 'status': status.index, - }; - } - - // For reading from SQL - factory ImageMetadata.fromMap(Map 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, - ); - } -} diff --git a/lib/app/core/models/image_status.dart b/lib/app/core/models/image_status.dart deleted file mode 100644 index e037b95..0000000 --- a/lib/app/core/models/image_status.dart +++ /dev/null @@ -1,14 +0,0 @@ -/// 图片的同步状态 -enum ImageStatus { - /// 已同步 - synced, - - // 待上传 - pendingUpload, - - /// 待删除 - pendingDeleted, - - /// 待下载 - pendingDownload, -} diff --git a/lib/app/core/models/problem_sync_status.dart b/lib/app/core/models/problem_sync_status.dart deleted file mode 100644 index 6031ca7..0000000 --- a/lib/app/core/models/problem_sync_status.dart +++ /dev/null @@ -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 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(), - ); - } -} diff --git a/lib/app/core/repositories/syncable_repository.dart b/lib/app/core/repositories/syncable_repository.dart index ce388db..d0dec8a 100644 --- a/lib/app/core/repositories/syncable_repository.dart +++ b/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 { // --- 上传部分 (已有) --- diff --git a/lib/app/core/services/sync_service.dart b/lib/app/core/services/sync_service.dart index 5e6a80f..c58721d 100644 --- a/lib/app/core/services/sync_service.dart +++ b/lib/app/core/services/sync_service.dart @@ -1,6 +1,6 @@ import 'package:flutter/foundation.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/services/sync_metadata_service.dart'; diff --git a/lib/app/features/enterprise/data/datasources/enterprise_local_data_source.dart b/lib/app/features/enterprise/data/datasources/enterprise_local_data_source.dart index 38a3290..dff25d9 100644 --- a/lib/app/features/enterprise/data/datasources/enterprise_local_data_source.dart +++ b/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/features/enterprise/data/model/enterprise_list_item_model.dart'; import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart'; diff --git a/lib/app/features/enterprise/data/model/enterprise_dto.dart b/lib/app/features/enterprise/data/model/enterprise_dto.dart index 7596b02..63e66c2 100644 --- a/lib/app/features/enterprise/data/model/enterprise_dto.dart +++ b/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/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'; /// EnterpriseDto (Data Transfer Object) diff --git a/lib/app/features/enterprise/data/model/enterprise_model.dart b/lib/app/features/enterprise/data/model/enterprise_model.dart index 7811319..5033006 100644 --- a/lib/app/features/enterprise/data/model/enterprise_model.dart +++ b/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'; /// `EnterpriseModel` 是 `Enterprise` 实体在数据层的具体实现。 diff --git a/lib/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart b/lib/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart index f4e10a4..882291b 100644 --- a/lib/app/features/enterprise/data/repositories_impl/enterprise_repository_impl.dart +++ b/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/features/enterprise/data/datasources/enterprise_local_data_source.dart'; import 'package:problem_check_system/app/features/enterprise/data/datasources/enterprise_remote_data_source.dart'; diff --git a/lib/app/features/enterprise/domain/entities/enterprise.dart b/lib/app/features/enterprise/domain/entities/enterprise.dart index 2568513..e2e675a 100644 --- a/lib/app/features/enterprise/domain/entities/enterprise.dart +++ b/lib/app/features/enterprise/domain/entities/enterprise.dart @@ -1,5 +1,5 @@ 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` 实体,代表一个企业的核心业务对象。 /// 这是一个纯粹的、不可变的类,不包含任何与数据持久化相关的代码。 diff --git a/lib/app/features/enterprise/domain/repositories/enterprise_repository.dart b/lib/app/features/enterprise/domain/repositories/enterprise_repository.dart index 3a8dd3a..92cd1ef 100644 --- a/lib/app/features/enterprise/domain/repositories/enterprise_repository.dart +++ b/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/features/enterprise/domain/entities/enterprise_list_item.dart'; import 'package:problem_check_system/app/features/enterprise/domain/entities/sync_result.dart'; diff --git a/lib/app/features/enterprise/domain/usecases/add_enterprise_usecase.dart b/lib/app/features/enterprise/domain/usecases/add_enterprise_usecase.dart index 930d9b6..0475b75 100644 --- a/lib/app/features/enterprise/domain/usecases/add_enterprise_usecase.dart +++ b/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/features/enterprise/domain/entities/enterprise.dart'; import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart'; diff --git a/lib/app/features/enterprise/domain/usecases/editor_enterprise_usecase.dart b/lib/app/features/enterprise/domain/usecases/editor_enterprise_usecase.dart index b6c8484..1cd7ec9 100644 --- a/lib/app/features/enterprise/domain/usecases/editor_enterprise_usecase.dart +++ b/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/features/enterprise/domain/entities/enterprise.dart'; import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart'; diff --git a/lib/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart b/lib/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart index 09bac23..6fafca5 100644 --- a/lib/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart +++ b/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: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/core/models/sync_status.dart'; +import 'package:problem_check_system/app/core/domain/entities/sync_status.dart'; /// 上传操作的进度和结果模型 class UploadResult { diff --git a/lib/app/features/problem/data/datasources/problem_local_data_source.dart b/lib/app/features/problem/data/datasources/problem_local_data_source.dart index b442ec0..a8ca95e 100644 --- a/lib/app/features/problem/data/datasources/problem_local_data_source.dart +++ b/lib/app/features/problem/data/datasources/problem_local_data_source.dart @@ -1,6 +1,7 @@ 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/features/problem/domain/entities/problem_filter_params.dart'; import 'package:sqflite/sqflite.dart'; // 导入你的 DatabaseService /// IProblemLocalDataSource 定义了问题本地数据源的接口。 @@ -19,12 +20,7 @@ abstract class IProblemLocalDataSource { /// [endDate] - 筛选创建时间早于此日期的问题。 /// [syncStatus] - 筛选具有特定同步状态的问题。 /// [bindStatus] - (示例) 筛选具有特定绑定状态的问题。 - Future>> getAllProblems({ - DateTime? startDate, - DateTime? endDate, - String? syncStatus, - String? bindStatus, - }); + Future>> getAllProblems(ProblemFilterParams filter); /// 向数据库中添加一个新问题。 /// @@ -69,49 +65,70 @@ class ProblemLocalDataSource implements IProblemLocalDataSource { } @override - Future>> getAllProblems({ - DateTime? startDate, - DateTime? endDate, - String? syncStatus, - String? bindStatus, // 注意:你的表结构中没有 bindStatus,这里我们忽略它 - }) async { + Future>> getAllProblems( + ProblemFilterParams filter, + ) async { final db = await _databaseService.database; - - // 动态构建 WHERE 查询语句 + // 基础查询语句,使用 JOIN 连接问题表和企业表 + 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 whereClauses = []; - List whereArgs = []; + List 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) { - // 数据库中存储的是 INTEGER (毫秒时间戳) - whereClauses.add('creationTime >= ?'); - whereArgs.add(startDate.millisecondsSinceEpoch); + // 3. 结束时间筛选 + if (filter.endTime != null) { + whereClauses.add('p.creationTime <= ?'); + arguments.add(filter.endTime!.toIso8601String()); } - if (endDate != null) { - whereClauses.add('creationTime <= ?'); - whereArgs.add(endDate.millisecondsSinceEpoch); + + // 4. 同步状态筛选 + if (filter.syncStatus != null) { + whereClauses.add('p.syncStatus = ?'); + // 假设 SyncStatus 枚举在数据库中存储为字符串 + arguments.add(filter.syncStatus!.name); } - if (syncStatus != null) { - // 数据库中存储的是 INTEGER (枚举的 index) - // 我们需要将仓库层传来的字符串转回枚举的 index - try { - final status = SyncStatus.values.byName(syncStatus); - whereClauses.add('syncStatus = ?'); - whereArgs.add(status.index); - } catch (e) { - // 如果传来一个无效的 status 字符串,则忽略此过滤器 - Get.log('无效的 syncStatus 过滤器: $syncStatus', isError: true); + + // 5. 绑定状态筛选 + if (filter.isBound != null) { + if (filter.isBound!) { + whereClauses.add('p.bindData IS NOT NULL AND p.bindData != ""'); + } else { + whereClauses.add('p.bindData IS NULL OR p.bindData = ""'); } } - final String? whereString = whereClauses.isEmpty - ? null - : whereClauses.join(' AND '); + // 组合最终的查询语句 + String finalQuery = baseQuery; + if (whereClauses.isNotEmpty) { + finalQuery += ' WHERE ' + whereClauses.join(' AND '); + } - final List> maps = await db.query( - _tableName, - where: whereString, - whereArgs: whereArgs, - orderBy: 'creationTime DESC', // 通常按创建时间降序排列 + // 执行查询,使用 `?` 参数化查询来防止 SQL 注入 + final List> maps = await db.rawQuery( + finalQuery, + arguments, ); return maps; } diff --git a/lib/app/features/problem/data/model/problem_dto.dart b/lib/app/features/problem/data/model/problem_dto.dart new file mode 100644 index 0000000..6fa970e --- /dev/null +++ b/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 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? 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 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 map) { +// // 处理imageUrls的转换 +// List imageUrlsList = []; +// if (map['imageUrls'] != null) { +// try { +// final List 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, +// ); +// } +// } diff --git a/lib/app/features/problem/data/model/problem_local_dto.dart b/lib/app/features/problem/data/model/problem_local_dto.dart deleted file mode 100644 index 51d43df..0000000 --- a/lib/app/features/problem/data/model/problem_local_dto.dart +++ /dev/null @@ -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 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 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.from(jsonDecode(imageUrls)), - creatorId: creatorId, - creationTime: DateTime.parse(creationTime), - lastModifierId: lastModifierId, - lastModifiedTime: DateTime.parse(lastModifiedTime), - syncStatus: syncStatus, - bindData: bindData, - ); - } -} diff --git a/lib/app/features/problem/data/model/problem_model.dart b/lib/app/features/problem/data/model/problem_model.dart index 57c94ce..00130dd 100644 --- a/lib/app/features/problem/data/model/problem_model.dart +++ b/lib/app/features/problem/data/model/problem_model.dart @@ -1,152 +1,97 @@ 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'; -import 'package:problem_check_system/app/core/models/problem_sync_status.dart'; - -/// 问题的数据模型。 -/// 用于表示系统中的一个具体问题,包含了问题的描述、位置、图片等信息。 -class Problem { - /// 问题的唯一标识符,可空。 +class ProblemModel { final String id; - - /// 企业id - final String? companyId; - - /// 问题的详细描述。 + final String enterpriseId; final String description; - - /// 问题发生的位置。 final String location; - - /// 问题的图片元数据列表。 - final List imageUrls; - - /// 问题创建的时间。 - final DateTime creationTime; - - /// 问题创建id + final String imageUrls; final String creatorId; - - /// 问题的同步状态,默认为未同步。 - final ProblemSyncStatus syncStatus; - - /// 最后修改时间 - final DateTime lastModifiedTime; - - /// 相关的审查任务ID,可空。 - final String? censorTaskId; - - /// 绑定的附加数据,可空。 + final String creationTime; + final String lastModifierId; + final String lastModifiedTime; + final SyncStatus syncStatus; final String? bindData; - /// 问题是否已被检查,默认为false。 - final bool isChecked; - - Problem({ + ProblemModel({ required this.id, + required this.enterpriseId, required this.description, required this.location, required this.imageUrls, - required this.creationTime, required this.creatorId, + required this.creationTime, + required this.lastModifierId, required this.lastModifiedTime, - this.syncStatus = ProblemSyncStatus.pendingCreate, - this.censorTaskId, - this.bindData, - this.isChecked = false, - this.companyId, + required this.syncStatus, + required this.bindData, }); - /// copyWith 方法,用于创建对象的副本并修改指定字段 - Problem copyWith({ - String? id, - String? companyId, - String? description, - String? location, - List? 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, - ); - } - - /// 将对象转换为Map,用于SQLite存储 Map toMap() { return { 'id': id, - 'companyId': companyId, + 'enterpriseId': enterpriseId, 'description': description, 'location': location, - 'imageUrls': json.encode(imageUrls.map((e) => e.toMap()).toList()), - 'creationTime': creationTime.millisecondsSinceEpoch, + 'imageUrls': imageUrls, 'creatorId': creatorId, - 'lastModifiedTime': lastModifiedTime.millisecondsSinceEpoch, - 'syncStatus': syncStatus.index, - 'censorTaskId': censorTaskId, + 'creationTime': creationTime, + 'lastModifierId': lastModifierId, + 'lastModifiedTime': lastModifiedTime, + 'syncStatus': syncStatus.name, 'bindData': bindData, - 'isChecked': isChecked ? 1 : 0, }; } - /// 从Map创建对象,用于从SQLite读取 - factory Problem.fromMap(Map map) { - // 处理imageUrls的转换 - List imageUrlsList = []; - if (map['imageUrls'] != null) { - try { - final List imageList = json.decode(map['imageUrls']); - imageUrlsList = imageList.map((e) => ImageMetadata.fromMap(e)).toList(); - } catch (e) { - // 如果解析失败,保持空列表 - imageUrlsList = []; - } - } - - 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, + factory ProblemModel.fromMap(Map map) { + return ProblemModel( + 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?, ); } - /// 转换为JSON字符串 - String toJson() => json.encode(toMap()); + /// 从领域实体 ProblemEntity 创建 DTO 实例。 + 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字符串创建对象 - factory Problem.fromJson(String source) => - Problem.fromMap(json.decode(source)); + /// 将 DTO 实例转换回领域实体 ProblemEntity。 + ProblemEntity toEntity() { + return ProblemEntity( + id: id, + enterpriseId: enterpriseId, + description: description, + location: location, + imageUrls: List.from(jsonDecode(imageUrls)), + creatorId: creatorId, + creationTime: DateTime.parse(creationTime), + lastModifierId: lastModifierId, + lastModifiedTime: DateTime.parse(lastModifiedTime), + syncStatus: syncStatus, + bindData: bindData, + ); + } } diff --git a/lib/app/features/problem/data/model/problem_remote_dto.dart b/lib/app/features/problem/data/model/problem_remote_dto.dart deleted file mode 100644 index e03862e..0000000 --- a/lib/app/features/problem/data/model/problem_remote_dto.dart +++ /dev/null @@ -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 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? 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 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 map) { - // 处理imageUrls的转换 - List imageUrlsList = []; - if (map['imageUrls'] != null) { - try { - final List 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, - ); - } -} diff --git a/lib/app/features/problem/data/repositories/problem_repository_impl.dart b/lib/app/features/problem/data/repositories/problem_repository_impl.dart index f60b9e2..09f7f7b 100644 --- a/lib/app/features/problem/data/repositories/problem_repository_impl.dart +++ b/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/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_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'; /// 问题仓库,负责处理问题数据的本地持久化。 /// 它封装了底层数据库操作,为业务逻辑层提供一个简洁的接口。 -/// 它的核心工作是在领域实体 (ProblemEntity) 和数据传输对象 (ProblemLocalDto) 之间进行转换。 +/// 它的核心工作是在领域实体 (ProblemEntity) 和数据传输对象 (ProblemModel) 之间进行转换。 class ProblemRepository implements IProblemRepository { final IProblemLocalDataSource problemLocalDataSource; // 2. 依赖于数据源的抽象 @@ -14,7 +16,7 @@ class ProblemRepository implements IProblemRepository { @override Future addProblem(ProblemEntity problem) async { // 1. 将领域实体 (Entity) 转换为本地数据传输对象 (DTO) - final problemDto = ProblemLocalDto.fromEntity(problem); + final problemDto = ProblemModel.fromEntity(problem); // 2. 调用数据源的方法,将 DTO 转换为 Map 进行存储 await problemLocalDataSource.addProblem(problemDto.toMap()); @@ -30,27 +32,23 @@ class ProblemRepository implements IProblemRepository { } @override - Future> getAllProblems({ - DateTime? startDate, - DateTime? endDate, - String? syncStatus, - String? bindStatus, + Future> getAllProblemListItem({ + required ProblemFilterParams filter, }) async { // 1. 调用数据源获取所有问题的原始数据 (List of Maps) - final problemMaps = await problemLocalDataSource.getAllProblems( - // 将参数直接透传给数据源 - startDate: startDate, - endDate: endDate, - syncStatus: syncStatus, - bindStatus: bindStatus, - ); + final problemDataMaps = await problemLocalDataSource.getAllProblems(filter); - // 2. 将每个 Map 转换为 DTO,然后再转换为领域实体 (Entity) - final problems = problemMaps - .map((map) => ProblemLocalDto.fromMap(map).toEntity()) - .toList(); - - return problems; + // 2. 将查询结果的 Map 列表转换为 ProblemListItemEntity 列表 + // 这部分逻辑和之前类似,只是数据源方法变了 + return problemDataMaps.map((map) { + return ProblemListItemEntity( + id: map['problem_id'], + description: map['problem_description'], + location: map['problem_location'], + creationTime: DateTime.parse(map['problem_creationTime']), + enterpriseName: map['enterprise_name'] ?? '未知企业', + ); + }).toList(); } @override @@ -64,13 +62,13 @@ class ProblemRepository implements IProblemRepository { } // 3. 如果数据存在,则先转换为 DTO,再转换为领域实体并返回 - return ProblemLocalDto.fromMap(problemMap).toEntity(); + return ProblemModel.fromMap(problemMap).toEntity(); } @override Future updateProblem(ProblemEntity problem) async { // 1. 将领域实体 (Entity) 转换为本地数据传输对象 (DTO) - final problemDto = ProblemLocalDto.fromEntity(problem); + final problemDto = ProblemModel.fromEntity(problem); // 2. 调用数据源的方法,将 DTO 转换为 Map 进行更新 await problemLocalDataSource.updateProblem(problemDto.toMap()); diff --git a/lib/app/features/problem/domain/entities/problem_entity.dart b/lib/app/features/problem/domain/entities/problem_entity.dart index bd1802f..4417d88 100644 --- a/lib/app/features/problem/domain/entities/problem_entity.dart +++ b/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 { // 1. 将 getter 实现替换为 final 字段 @@ -14,7 +14,7 @@ class ProblemEntity implements SyncableEntity { @override final SyncStatus syncStatus; - /// 企业名称 + /// 企业Id final String enterpriseId; /// 问题描述 diff --git a/lib/app/features/problem/domain/entities/problem_filter_params.dart b/lib/app/features/problem/domain/entities/problem_filter_params.dart new file mode 100644 index 0000000..c007807 --- /dev/null +++ b/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(); +} diff --git a/lib/app/features/problem/domain/entities/problem_list_item_entity.dart b/lib/app/features/problem/domain/entities/problem_list_item_entity.dart new file mode 100644 index 0000000..840b9f8 --- /dev/null +++ b/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, + }); +} diff --git a/lib/app/features/problem/domain/repositories/problem_repository.dart b/lib/app/features/problem/domain/repositories/problem_repository.dart index 478547c..c3c4119 100644 --- a/lib/app/features/problem/domain/repositories/problem_repository.dart +++ b/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_filter_params.dart'; +import 'package:problem_check_system/app/features/problem/domain/entities/problem_list_item_entity.dart'; /// Problem 仓库的抽象接口 /// 定义了业务逻辑层需要的数据操作。 abstract class IProblemRepository { - Future> getAllProblems({ - DateTime? startDate, - DateTime? endDate, - String? syncStatus, - String? bindStatus, + Future> getAllProblemListItem({ + required ProblemFilterParams filter, }); Future getProblemById(String id); Future addProblem(ProblemEntity problem); diff --git a/lib/app/features/problem/domain/usecases/add_problem_usecase.dart b/lib/app/features/problem/domain/usecases/add_problem_usecase.dart index da8afa2..9f1daf1 100644 --- a/lib/app/features/problem/domain/usecases/add_problem_usecase.dart +++ b/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/features/problem/domain/entities/problem_entity.dart'; import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart'; @@ -16,7 +17,7 @@ class AddProblemUsecase { }); Future call({ - required String enterpriseId, + required Enterprise enterprise, required String description, required String location, required List imageUrls, @@ -31,7 +32,7 @@ class AddProblemUsecase { lastModifiedTime: nowUtc, lastModifierId: userId, syncStatus: SyncStatus.pendingCreate, - enterpriseId: enterpriseId, + enterpriseId: enterprise.id, creationTime: nowUtc, creatorId: userId, ); diff --git a/lib/app/features/problem/domain/usecases/get_all_problems_usecase.dart b/lib/app/features/problem/domain/usecases/get_all_problems_usecase.dart index 62c81e9..9579ad3 100644 --- a/lib/app/features/problem/domain/usecases/get_all_problems_usecase.dart +++ b/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'; class GetAllProblemsUsecase { @@ -6,17 +7,11 @@ class GetAllProblemsUsecase { GetAllProblemsUsecase({required this.problemRepository}); - Future> call({ - DateTime? startDate, - DateTime? endDate, - String? syncStatus, - String? bindStatus, + Future> call({ + ProblemFilterParams? filter, }) async { - return await problemRepository.getAllProblems( - startDate: startDate, - endDate: endDate, - syncStatus: syncStatus, - bindStatus: bindStatus, + return await problemRepository.getAllProblemListItem( + filter: filter ?? ProblemFilterParams.initial(), ); } } diff --git a/lib/app/features/problem/domain/usecases/update_problem.dart b/lib/app/features/problem/domain/usecases/update_problem_usecase.dart similarity index 77% rename from lib/app/features/problem/domain/usecases/update_problem.dart rename to lib/app/features/problem/domain/usecases/update_problem_usecase.dart index 8a1c4f4..76f575d 100644 --- a/lib/app/features/problem/domain/usecases/update_problem.dart +++ b/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/features/problem/domain/entities/problem_entity.dart'; import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart'; -class UpdateProblem { +class UpdateProblemUsecase { final IProblemRepository repository; final AuthRepository authRepository; - UpdateProblem({required this.repository, required this.authRepository}); + UpdateProblemUsecase({ + required this.repository, + required this.authRepository, + }); Future call(ProblemEntity entity) async { final nowUtc = DateTime.now().toUtc(); diff --git a/lib/app/features/problem/presentation/bindings/problem_form_binding.dart b/lib/app/features/problem/presentation/bindings/problem_form_binding.dart index 3223f22..dfae13b 100644 --- a/lib/app/features/problem/presentation/bindings/problem_form_binding.dart +++ b/lib/app/features/problem/presentation/bindings/problem_form_binding.dart @@ -1,4 +1,5 @@ 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/services/database_service.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/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/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'; -class ProblemFormBinding extends Bindings { +class ProblemFormBinding extends BaseBindings { @override - void dependencies() { - // final dynamic arguments = Get.arguments; - // 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?; - } - } + void register1Services() { + // TODO: implement register1Services + } + @override + void register2DataSource() { Get.put( EnterpriseLocalDataSourceImpl( databaseService: Get.find(), @@ -41,7 +29,10 @@ class ProblemFormBinding extends Bindings { Get.put( EnterpriseRemoteDataSourceImpl(http: Get.find()), ); + } + @override + void register3Repositories() { Get.put( EnterpriseRepositoryImpl( localDataSource: Get.find(), @@ -50,16 +41,53 @@ class ProblemFormBinding extends Bindings { uuid: Get.find(), ), ); + } + @override + void register4Usecases() { Get.put( GetEnterprisesUsecase(repository: Get.find()), ); + 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( problem: problem, formMode: formMode, getEnterprisesUsecase: Get.find(), + addProblemUsecase: Get.find(), + updateProblemUsecase: Get.find(), ), ); } diff --git a/lib/app/features/problem/presentation/controllers/problem_form_controller.dart b/lib/app/features/problem/presentation/controllers/problem_form_controller.dart index d88fe4f..b8a2e44 100644 --- a/lib/app/features/problem/presentation/controllers/problem_form_controller.dart +++ b/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:path_provider/path_provider.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/usecases/get_enterprises_usecase.dart'; 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/usecases/add_problem_usecase.dart'; +import 'package:problem_check_system/app/features/problem/domain/usecases/update_problem_usecase.dart'; import 'package:uuid/uuid.dart'; class ProblemFormController extends GetxController { @@ -40,13 +40,18 @@ class ProblemFormController extends GetxController { final TextEditingController descriptionController = TextEditingController(); final TextEditingController locationController = TextEditingController(); final RxList selectedImages = [].obs; + // 业务用例 final GetEnterprisesUsecase getEnterprisesUsecase; + final AddProblemUsecase addProblemUsecase; + final UpdateProblemUsecase updateProblemUsecase; // 使用依赖注入,便于测试 ProblemFormController({ this.problem, this.formMode = FormMode.view, required this.getEnterprisesUsecase, + required this.addProblemUsecase, + required this.updateProblemUsecase, }) { if (problem != null) { if (problem!.bindData != null) { @@ -105,12 +110,12 @@ class ProblemFormController extends GetxController { } } - // 5. 公有方法: 供UI调用以更新状态 + // 供UI调用以更新状态 void selectEnterprise(Enterprise? enterprise) { selectedEnterprise.value = enterprise; // 你可以在这里添加额外的逻辑,比如当企业被选中时执行某些操作 if (enterprise != null) { - print('Selected: ${enterprise.name}'); + Get.log('选择: ${enterprise.name}'); } } @@ -184,6 +189,11 @@ class ProblemFormController extends GetxController { // 验证表单 bool _validateForm() { + if (selectedEnterprise.value == null) { + Get.snackbar('提示', '请选择一个企业', snackPosition: SnackPosition.TOP); + return false; + } + if (descriptionController.text.isEmpty) { Get.snackbar('提示', '请填写问题描述', snackPosition: SnackPosition.TOP); return false; @@ -204,49 +214,45 @@ class ProblemFormController extends GetxController { /// 保存图片 Future saveProblem() async { - // if (!_validateForm()) { - // return; - // } - - // isLoading.value = true; - - // try { - // // 保存图片到本地 - // final List imagePaths = await _saveImagesToLocal(); - - // if (problem != null) { - // // 修改问题 - // final updatedProblem = problem!.copyWith( - // description: descriptionController.text, - // location: locationController.text, - // imageUrls: imagePaths, - // ); - // // 如果原问题是待创建的,修改后仍然应该是创建操作 - // final modifyProblem = problemStateManager.modifyProblem(updatedProblem); - - // await problemRepository.updateProblem(modifyProblem); - // } else { - // // 创建新问题 - // final newProblem = problemStateManager.createNewProblem( - // description: descriptionController.text, - // location: locationController.text, - // imageUrls: imagePaths, - // ); - - // await problemRepository.insertProblem(newProblem); - // } - // Get.back(result: true); // 返回成功结果 - // Get.snackbar('成功', '问题已更新'); - // } catch (e) { - // Get.snackbar('错误', '保存问题失败: $e'); - // } finally { - // isLoading.value = false; - // } + if (!_validateForm()) { + return; + } + + isLoading.value = true; + + try { + // 保存图片到本地 + final List imagePaths = await _saveImagesToLocal(); + + if (problem != null) { + // 修改问题 + final updatedProblem = problem!.copyWith( + description: descriptionController.text, + location: locationController.text, + imageUrls: imagePaths, + ); + await updateProblemUsecase(updatedProblem); + } else { + // 调用新增问题用例 + await addProblemUsecase( + enterprise: selectedEnterprise.value!, + description: descriptionController.text, + location: locationController.text, + imageUrls: imagePaths, + ); + } + Get.back(result: true); // 返回成功结果 + Get.snackbar('成功', '问题已更新'); + } catch (e) { + Get.snackbar('错误', '保存问题失败: $e'); + } finally { + isLoading.value = false; + } } // 保存图片到本地存储 - Future> _saveImagesToLocal() async { - final List imagePaths = []; + Future> _saveImagesToLocal() async { + final List imagePaths = []; final directory = await getApplicationDocumentsDirectory(); final imagesDir = Directory('${directory.path}/problem_images'); @@ -254,22 +260,18 @@ class ProblemFormController extends GetxController { if (!await imagesDir.exists()) { await imagesDir.create(recursive: true); } - + // 遍历选择图片 for (var image in selectedImages) { try { final String fileName = '${Uuid().v4()}_${path.basename(image.name)}'; final String imagePath = '${imagesDir.path}/$fileName'; - final ImageMetadata imageData = ImageMetadata( - localPath: imagePath, - status: ImageStatus.pendingUpload, - ); final File imageFile = File(imagePath); // 读取图片字节并写入文件 final imageBytes = await image.readAsBytes(); await imageFile.writeAsBytes(imageBytes); - imagePaths.add(imageData); + imagePaths.add(imagePath); } catch (e) { throw Exception(e); } diff --git a/lib/app/features/problem/presentation/models/problem_card_model.dart b/lib/app/features/problem/presentation/models/problem_card_model.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/app/features/problem/presentation/models/problem_card_state.dart b/lib/app/features/problem/presentation/models/problem_card_state.dart new file mode 100644 index 0000000..f89dc61 --- /dev/null +++ b/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 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}); +} diff --git a/lib/app/features/problem/presentation/pages/widgets/problem_card.dart b/lib/app/features/problem/presentation/pages/widgets/problem_card.dart index 259aa8a..c061180 100644 --- a/lib/app/features/problem/presentation/pages/widgets/problem_card.dart +++ b/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:intl/intl.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/presentation/pages/widgets/custom_button.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart';