34 changed files with 592 additions and 704 deletions
@ -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(); |
||||||
|
} |
||||||
@ -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, |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,14 +0,0 @@ |
|||||||
/// 图片的同步状态 |
|
||||||
enum ImageStatus { |
|
||||||
/// 已同步 |
|
||||||
synced, |
|
||||||
|
|
||||||
// 待上传 |
|
||||||
pendingUpload, |
|
||||||
|
|
||||||
/// 待删除 |
|
||||||
pendingDeleted, |
|
||||||
|
|
||||||
/// 待下载 |
|
||||||
pendingDownload, |
|
||||||
} |
|
||||||
@ -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(), |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -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, |
||||||
|
// ); |
||||||
|
// } |
||||||
|
// } |
||||||
@ -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, |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -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, |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/// 将对象转换为Map,用于SQLite存储 |
|
||||||
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, |
||||||
|
); |
||||||
|
} |
||||||
} |
} |
||||||
|
|||||||
@ -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, |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -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(); |
||||||
|
} |
||||||
@ -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, |
||||||
|
}); |
||||||
|
} |
||||||
@ -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(); |
||||||
@ -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}); |
||||||
|
} |
||||||
Loading…
Reference in new issue