26 changed files with 620 additions and 251 deletions
@ -0,0 +1,34 @@
|
||||
import 'package:get/get.dart'; |
||||
import 'package:problem_check_system/app/core/domain/entities/upload_result.dart'; |
||||
|
||||
/// 上传控制器接口 |
||||
/// |
||||
/// 定义了所有上传流程控制器必须提供的通用属性和方法。 |
||||
/// 这使得 UI 组件 (如 UploadProgressDialog) 可以与任何实现了此接口的控制器协作, |
||||
/// 而无需知道其具体类型(是企业上传还是问题上传)。 |
||||
abstract class IUploadController { |
||||
// --- 状态属性 (State Properties) --- |
||||
|
||||
/// 是否正在上传 |
||||
RxBool get isUploading; |
||||
|
||||
/// 上传进度 (0.0 to 1.0) |
||||
RxDouble get uploadProgress; |
||||
|
||||
/// 已上传的数量 |
||||
RxInt get uploadedCount; |
||||
|
||||
/// 总共需要上传的数量 |
||||
RxInt get totalToUpload; |
||||
|
||||
/// 上传结果 |
||||
Rx<UploadResult?> get uploadResult; |
||||
|
||||
// --- 操作方法 (Action Methods) --- |
||||
|
||||
/// 取消上传 |
||||
void cancelUpload(); |
||||
|
||||
/// 关闭上传对话框 (通常在上传完成后调用) |
||||
void closeUploadDialog(); |
||||
} |
||||
@ -1,11 +1,11 @@
|
||||
import 'package:flutter/material.dart'; |
||||
import 'package:get/get.dart'; |
||||
import 'package:problem_check_system/app/core/controllers/i_upload_controller.dart'; |
||||
import 'package:problem_check_system/app/core/domain/entities/upload_result.dart'; |
||||
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart'; |
||||
import 'package:problem_check_system/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart'; |
||||
|
||||
class UploadProgressDialog extends GetView<EnterpriseUploadController> { |
||||
const UploadProgressDialog({super.key}); |
||||
class UploadProgressDialog extends StatelessWidget { |
||||
final IUploadController controller; |
||||
const UploadProgressDialog({super.key, required this.controller}); |
||||
|
||||
@override |
||||
Widget build(BuildContext context) { |
||||
@ -1 +1,117 @@
|
||||
class ProblemRemoteDataSource {} |
||||
import 'package:dio/dio.dart'; |
||||
import 'package:problem_check_system/app/core/services/http_provider.dart'; |
||||
import 'package:problem_check_system/app/features/problem/data/model/problem_dto.dart'; |
||||
import 'package:problem_check_system/app/features/problem/data/model/problem_filter_dto.dart'; |
||||
|
||||
/// 问题远程数据源接口 |
||||
/// |
||||
/// 定义了与问题相关的远程 API 的所有操作。 |
||||
/// 这个接口处理的是 DTO (Data Transfer Objects),与服务器直接交互。 |
||||
abstract class ProblemRemoteDataSource { |
||||
// --- 查 (Read/Query) --- |
||||
|
||||
/// 根据筛选条件获取问题列表 |
||||
/// |
||||
/// [filter]: 包含所有筛选条件的 DTO |
||||
/// 返回一个 ProblemDto 列表的 Future |
||||
Future<List<ProblemDto>> getProblems(ProblemFilterDto filter); |
||||
|
||||
/// 根据 ID 获取单个问题的详细信息 |
||||
/// |
||||
/// [problemId]: 问题的唯一标识符 |
||||
/// 如果找到,返回一个 ProblemDto 的 Future;否则可能抛出异常 |
||||
Future<ProblemDto> getProblemById(String problemId); |
||||
|
||||
// --- 增 (Create) --- |
||||
|
||||
/// 创建一个新问题 |
||||
/// |
||||
/// [problem]: 包含新问题信息的 DTO |
||||
/// 服务器通常会返回创建成功后的完整问题对象(包含服务器生成的ID和时间戳) |
||||
Future<ProblemDto> createProblem(ProblemDto problem); |
||||
|
||||
// --- 改 (Update) --- |
||||
|
||||
/// 更新一个已存在的问题 |
||||
/// |
||||
/// [problem]: 包含要更新的问题信息的 DTO |
||||
/// 服务器通常会返回更新成功后的完整问题对象 |
||||
Future<ProblemDto> updateProblem(ProblemDto problem); |
||||
|
||||
// --- 删 (Delete) --- |
||||
|
||||
/// 根据 ID 删除一个问题 |
||||
/// |
||||
/// [problemId]: 要删除的问题的唯一标识符 |
||||
/// 删除操作通常成功后没有返回内容,所以使用 Future<void> |
||||
Future<void> deleteProblem(String problemId); |
||||
} |
||||
|
||||
class ProblemRemoteDataSourceImpl implements ProblemRemoteDataSource { |
||||
final HttpProvider http; |
||||
static const String problemsEndpoint = '/api/Memorandum'; |
||||
// 通过依赖注入传入 Dio 实例 |
||||
ProblemRemoteDataSourceImpl({required this.http}); |
||||
|
||||
@override |
||||
Future<List<ProblemDto>> getProblems(ProblemFilterDto filter) async { |
||||
try { |
||||
final response = await http.get( |
||||
problemsEndpoint, // 假设这是获取列表的端点 |
||||
queryParameters: filter.toJson(), |
||||
); |
||||
// 假设服务器返回的数据在 'data' 字段中,且是一个列表 |
||||
final List<dynamic> problemListJson = response.data['data']; |
||||
return problemListJson.map((json) => ProblemDto.fromJson(json)).toList(); |
||||
} on DioException catch (e) { |
||||
// 在这里处理网络错误,可以抛出一个自定义的异常 |
||||
throw Exception('获取问题列表失败: $e'); |
||||
} |
||||
} |
||||
|
||||
@override |
||||
Future<ProblemDto> getProblemById(String problemId) async { |
||||
try { |
||||
final response = await http.get('$problemsEndpoint/$problemId'); |
||||
return ProblemDto.fromJson(response.data); |
||||
} on DioException catch (e) { |
||||
throw Exception('获取问题失败: $e'); |
||||
} |
||||
} |
||||
|
||||
@override |
||||
Future<ProblemDto> createProblem(ProblemDto problem) async { |
||||
try { |
||||
final response = await http.post( |
||||
problemsEndpoint, |
||||
data: problem.toJson(), // 将 DTO 转换为 Map 发送 |
||||
); |
||||
return ProblemDto.fromJson(response.data); |
||||
} on DioException catch (e) { |
||||
throw Exception('创建问题失败: $e'); |
||||
} |
||||
} |
||||
|
||||
@override |
||||
Future<ProblemDto> updateProblem(ProblemDto problem) async { |
||||
try { |
||||
final response = await http.put( |
||||
'$problemsEndpoint/${problem.id}', // 通常 PUT 请求需要 ID 在 URL 中 |
||||
data: problem.toJson(), |
||||
); |
||||
return ProblemDto.fromJson(response.data); |
||||
} on DioException catch (e) { |
||||
throw Exception('更新问题失败: $e'); |
||||
} |
||||
} |
||||
|
||||
@override |
||||
Future<void> deleteProblem(String problemId) async { |
||||
try { |
||||
// 删除成功通常返回 204 No Content,Dio 会默认处理 |
||||
await http.delete('$problemsEndpoint/$problemId'); |
||||
} on DioException catch (e) { |
||||
throw Exception('删除问题失败: $e'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@ -1,115 +1,108 @@
|
||||
import 'dart:convert'; |
||||
|
||||
import 'package:problem_check_system/app/core/extensions/map_extensions.dart'; |
||||
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart'; |
||||
import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart'; |
||||
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart'; |
||||
import 'package:problem_check_system/app/core/extensions/map_extensions.dart'; |
||||
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart'; |
||||
|
||||
/// ProblemDto (Data Transfer Object) |
||||
/// |
||||
/// 这个模型专门用于与远程 API 进行数据交互。 |
||||
/// 它的字段和结构【严格匹配】服务器 API 定义的 JSON 格式。 |
||||
class ProblemDto { |
||||
final String? id; |
||||
final String id; |
||||
final String? title; |
||||
final String? location; |
||||
final String? censorTaskId; |
||||
final String? rowId; |
||||
final String? bindData; |
||||
final List<String?>? imageUrls; |
||||
final String? creationTime; |
||||
final List<String>? imageUrls; |
||||
final String creatorId; |
||||
final String creationTime; |
||||
final String? lastModifierId; |
||||
final String? lastModificationTime; |
||||
final String? companyId; |
||||
|
||||
ProblemDto({ |
||||
this.id, |
||||
required this.id, |
||||
this.title, |
||||
this.location, |
||||
this.censorTaskId, |
||||
this.rowId, |
||||
this.bindData, |
||||
this.imageUrls, |
||||
this.creationTime, |
||||
required this.creatorId, |
||||
required this.creationTime, |
||||
this.lastModifierId, |
||||
this.lastModificationTime, |
||||
this.companyId, |
||||
}); |
||||
|
||||
|
||||
factory ProblemDto.fromModel(ProblemModel model) { |
||||
factory ProblemDto.fromEntity(ProblemEntity entity) { |
||||
return ProblemDto( |
||||
id: model.id, |
||||
title: model.description, |
||||
location: model.location, |
||||
bindData: model.bindData, |
||||
imageUrls: List<String>.from(jsonDecode(model.imageUrls)), |
||||
creationTime: model.creationTime, |
||||
companyId: model.enterpriseId, |
||||
id: entity.id, |
||||
title: entity.description, |
||||
location: entity.location, |
||||
bindData: entity.bindData, |
||||
imageUrls: entity.imageUrls, |
||||
creationTime: entity.creationTime.toIso8601String(), |
||||
creatorId: entity.creatorId, |
||||
companyId: entity.enterpriseId, |
||||
); |
||||
} |
||||
ProblemModel toModel() { |
||||
return ProblemModel( |
||||
id: |
||||
|
||||
ProblemEntity toEntity() { |
||||
return ProblemEntity( |
||||
id: id, |
||||
description: title ?? '', |
||||
location: location ?? '', |
||||
imageUrls: imageUrls ?? [], |
||||
enterpriseId: companyId ?? '', |
||||
creatorId: creatorId, |
||||
creationTime: DateTime.tryParse(creationTime) ?? DateTime.now(), |
||||
lastModifierId: lastModifierId ?? creatorId, |
||||
lastModifiedTime: |
||||
DateTime.tryParse(lastModificationTime ?? '') ?? DateTime.now(), |
||||
syncStatus: SyncStatus.synced, |
||||
bindData: bindData, |
||||
); |
||||
} |
||||
|
||||
// ======================================================================= |
||||
// 新增方法 |
||||
// ======================================================================= |
||||
|
||||
/// [新增] fromJson 工厂构造函数:从 JSON Map 创建 EnterpriseDto 实例。 |
||||
/// |
||||
/// 当从服务器接收到 JSON 数据时,调用此方法将其转换为 Dart 对象。 |
||||
// ✅ FIX: fromJson - 对所有字段进行安全转换 |
||||
factory ProblemDto.fromJson(Map<String, dynamic> json) { |
||||
final creationTime = DateTime.parse(json['creationTime'] as String); |
||||
final creatorId = json['creatorId'] as String; |
||||
final lastModTimeStr = json['lastModificationTime'] as String?; |
||||
return ProblemDto( |
||||
// 必须存在的字段 |
||||
id: json['id'] as String, |
||||
creationTime: creationTime, |
||||
creatorId: creatorId, |
||||
lastModificationTime: lastModTimeStr != null |
||||
? DateTime.parse(lastModTimeStr) |
||||
: creationTime, |
||||
lastModifierId: json['lastModifierId'] as String? ?? creatorId, |
||||
companyName: json['companyName'] as String, |
||||
companyType: json['companyType'] as String? ?? "生产", |
||||
final creatorIdData = json['creatorId'] as String? ?? ''; |
||||
final creationTimeData = json['creationTime'] as String? ?? ''; |
||||
|
||||
// 可选字段 |
||||
companyScope: json['companyScope'] as String?, |
||||
mainPrincipalName: json['mainPrincipalName'] as String?, |
||||
mainPrincipalPhone: json['mainPrincipalPhone'] as String?, |
||||
securityPrincipalName: json['securityPrincipalName'] as String?, |
||||
securityPrincipalPhone: json['securityPrincipalPhone'] as String?, |
||||
companyAddress: json['companyAddress'] as String?, |
||||
majorHazard: json['majorHazard'] as String?, |
||||
return ProblemDto( |
||||
id: json['id'] as String? ?? '', |
||||
creationTime: creationTimeData, |
||||
creatorId: creatorIdData, |
||||
lastModificationTime: |
||||
json['lastModificationTime'] as String? ?? creationTimeData, |
||||
lastModifierId: json['lastModifierId'] as String? ?? creatorIdData, |
||||
title: json['title'] as String?, |
||||
location: json['location'] as String?, |
||||
bindData: json['bindData'] as String?, |
||||
imageUrls: (json['imageUrls'] as List? ?? []) |
||||
.whereType<String>() |
||||
.toList(), |
||||
companyId: json['companyId'] as String?, |
||||
censorTaskId: json['censorTaskId'] as String?, |
||||
rowId: json['rowId'] as String?, |
||||
); |
||||
} |
||||
|
||||
/// [新增] toJson 方法:将 EnterpriseDto 实例转换为 JSON Map。 |
||||
/// |
||||
/// 当需要将数据发送到服务器时,调用此方法将其转换为 JSON 格式。 |
||||
// 🟡 提醒: 请确认是否需要包含所有字段 |
||||
Map<String, dynamic> toJson() { |
||||
final jsonMap = { |
||||
'id': id, |
||||
// 使用 toIso8601String() 是将 DateTime 转换为标准化字符串的最佳实践 |
||||
'creationTime': creationTime.toIso8601String(), |
||||
'creatorId': creatorId, |
||||
'lastModificationTime': lastModificationTime?.toIso8601String(), |
||||
'lastModifierId': lastModifierId, |
||||
'companyName': companyName, |
||||
'companyType': companyType, |
||||
'companyScope': companyScope, |
||||
'mainPrincipalName': mainPrincipalName, |
||||
'mainPrincipalPhone': mainPrincipalPhone, |
||||
'securityPrincipalName': securityPrincipalName, |
||||
'securityPrincipalPhone': securityPrincipalPhone, |
||||
'companyAddress': companyAddress, |
||||
'majorHazard': majorHazard, |
||||
// 建议包含所有需要发送给服务器的字段 |
||||
"id": id, |
||||
"title": title, |
||||
"location": location, |
||||
"bindData": bindData, |
||||
"imageUrls": imageUrls, |
||||
"creatorId": creatorId, |
||||
"creationTime": creationTime, |
||||
"companyId": companyId, |
||||
"censorTaskId": censorTaskId, |
||||
"rowId": rowId, |
||||
"lastModifierId": lastModifierId, |
||||
"lastModificationTime": lastModificationTime, |
||||
}; |
||||
return jsonMap.withoutNullOrEmptyValues; |
||||
} |
||||
|
||||
/// [核心] 将 DTO (网络数据) 转换为 Model (本地/业务模型)。 |
||||
/// |
||||
/// 这个转换是数据流入应用内部的关键步骤。 |
||||
} |
||||
|
||||
@ -0,0 +1,3 @@
|
||||
class ProblemFilterDto { |
||||
toJson() {} |
||||
} |
||||
@ -1,35 +1,52 @@
|
||||
import 'package:get/get.dart'; |
||||
import 'package:problem_check_system/app/core/bindings/base_bindings.dart'; |
||||
import 'package:problem_check_system/app/core/services/database_service.dart'; |
||||
import 'package:problem_check_system/app/features/problem/data/datasources/problem_local_data_source.dart'; |
||||
import 'package:problem_check_system/app/features/problem/data/datasources/problem_remote_data_source.dart'; |
||||
import 'package:problem_check_system/app/features/problem/data/repositories/problem_repository_impl.dart'; |
||||
import 'package:problem_check_system/app/features/problem/domain/repositories/problem_repository.dart'; |
||||
import 'package:problem_check_system/app/features/problem/domain/usecases/get_all_problems_usecase.dart'; |
||||
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_list_controller.dart'; |
||||
|
||||
class ProblemListBinding extends Bindings { |
||||
class ProblemListBinding extends BaseBindings { |
||||
@override |
||||
void dependencies() { |
||||
// 数据 |
||||
Get.lazyPut<IProblemLocalDataSource>( |
||||
() => |
||||
ProblemLocalDataSource(databaseService: Get.find<DatabaseService>()), |
||||
void register1Services() { |
||||
// TODO: implement register1Services |
||||
} |
||||
|
||||
@override |
||||
void register2DataSource() { |
||||
Get.lazyPut<ProblemLocalDataSource>( |
||||
() => ProblemLocalDataSourceImpl( |
||||
databaseService: Get.find<DatabaseService>(), |
||||
), |
||||
); |
||||
// 仓库 |
||||
Get.lazyPut<IProblemRepository>( |
||||
() => ProblemRepository(Get.find<IProblemLocalDataSource>()), |
||||
Get.lazyPut<ProblemRemoteDataSource>( |
||||
() => ProblemRemoteDataSourceImpl(http: Get.find()), |
||||
); |
||||
// 用例 |
||||
Get.lazyPut<GetAllProblemsUsecase>( |
||||
() => GetAllProblemsUsecase( |
||||
problemRepository: Get.find<IProblemRepository>(), |
||||
} |
||||
|
||||
@override |
||||
void register3Repositories() { |
||||
Get.lazyPut<ProblemRepository>( |
||||
() => ProblemRepositoryImpl( |
||||
localDataSource: Get.find(), |
||||
remoteDataSource: Get.find(), |
||||
), |
||||
); |
||||
} |
||||
|
||||
@override |
||||
void register4Usecases() { |
||||
Get.lazyPut<GetAllProblemsUsecase>( |
||||
() => GetAllProblemsUsecase(problemRepository: Get.find()), |
||||
); |
||||
} |
||||
|
||||
/// 控制器 |
||||
@override |
||||
void register5Controllers() { |
||||
Get.lazyPut<ProblemListController>( |
||||
() => ProblemListController( |
||||
getAllProblemsUsecase: Get.find<GetAllProblemsUsecase>(), |
||||
), |
||||
() => ProblemListController(getAllProblemsUsecase: Get.find()), |
||||
); |
||||
} |
||||
} |
||||
|
||||
Loading…
Reference in new issue