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 '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<ImageMetadata> 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<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() { |
||||
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<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 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<String, dynamic> 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<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/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<ProblemEntity> call(ProblemEntity entity) async { |
||||
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