23 changed files with 823 additions and 260 deletions
@ -1,3 +1,9 @@ |
|||||||
{ |
{ |
||||||
"cSpell.words": ["fenix", "Getx", "tdesign"] |
"cSpell.words": [ |
||||||
|
"fenix", |
||||||
|
"Getx", |
||||||
|
"tdesign", |
||||||
|
"usecase", |
||||||
|
"usecases" |
||||||
|
] |
||||||
} |
} |
||||||
|
|||||||
@ -0,0 +1,21 @@ |
|||||||
|
// lib/app/core/extensions/datetime_extension.dart |
||||||
|
import 'package:intl/intl.dart'; |
||||||
|
|
||||||
|
extension DateTimeFormatting on DateTime { |
||||||
|
/// 格式化为 'yyyy-MM-dd HH:mm:ss' |
||||||
|
String toDateTimeString() { |
||||||
|
return DateFormat('yyyy-MM-dd HH:mm:ss').format(this); |
||||||
|
} |
||||||
|
|
||||||
|
/// 格式化为 'yyyy-MM-dd' |
||||||
|
String toDateString() { |
||||||
|
return DateFormat('yyyy-MM-dd').format(this); |
||||||
|
} |
||||||
|
|
||||||
|
/// 格式化为 'HH:mm:ss' |
||||||
|
String toTimeString() { |
||||||
|
return DateFormat('HH:mm:ss').format(this); |
||||||
|
} |
||||||
|
|
||||||
|
// 你可以根据需要添加任意多的格式 |
||||||
|
} |
||||||
@ -1,20 +1,136 @@ |
|||||||
|
import 'package:problem_check_system/app/core/models/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'; |
import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart'; |
||||||
import 'package:sqflite/sqflite.dart'; |
import 'package:sqflite/sqflite.dart'; |
||||||
|
|
||||||
abstract class EnterpriseLocalDataSource { |
abstract class EnterpriseLocalDataSource { |
||||||
|
/// 新增企业 |
||||||
Future<void> addEnterprise(EnterpriseModel enterprise); |
Future<void> addEnterprise(EnterpriseModel enterprise); |
||||||
|
Future<void> updateEnterprise(EnterpriseModel enterprise); |
||||||
|
Future<List<EnterpriseListItemModel>> getEnterpriseListItems({ |
||||||
|
String? name, |
||||||
|
String? type, |
||||||
|
DateTime? startDate, |
||||||
|
DateTime? endDate, |
||||||
|
}); |
||||||
|
|
||||||
|
/// 查询企业列表 |
||||||
} |
} |
||||||
|
|
||||||
class EnterpriseLocalDataSourceImpl implements EnterpriseLocalDataSource { |
class EnterpriseLocalDataSourceImpl implements EnterpriseLocalDataSource { |
||||||
|
final DatabaseService _databaseService; |
||||||
|
|
||||||
|
const EnterpriseLocalDataSourceImpl({ |
||||||
|
required DatabaseService databaseService, |
||||||
|
}) : _databaseService = databaseService; |
||||||
|
|
||||||
@override |
@override |
||||||
Future<void> addEnterprise(EnterpriseModel enterprise) async { |
Future<void> addEnterprise(EnterpriseModel enterprise) async { |
||||||
// // 在这里实现将 Enterprise 保存到本地存储的逻辑 |
final db = await _databaseService.database; |
||||||
// // 例如,使用 SQLite、SharedPreferences 或其他本地数据库 |
await db.insert( |
||||||
// final db = await databaseService.database; |
"enterprises", |
||||||
// await db.insert( |
enterprise.toMap(), |
||||||
// 'enterprises', // 表名 |
// conflictAlgorithm 用于定义当主键 (id) 冲突时的处理策略。 |
||||||
// enterprise.toMap(), |
// ConflictAlgorithm.replace 表示如果 ID 已存在,则用新数据替换旧数据。 |
||||||
// conflictAlgorithm: ConflictAlgorithm.replace, |
// 这在同步场景下通常比较有用。 |
||||||
// ); |
conflictAlgorithm: ConflictAlgorithm.replace, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<void> updateEnterprise(EnterpriseModel enterprise) async { |
||||||
|
final db = await _databaseService.database; |
||||||
|
await db.update( |
||||||
|
'enterprises', |
||||||
|
enterprise.toMap(), |
||||||
|
where: 'id = ?', // 关键:通过 ID 找到要更新的行 |
||||||
|
whereArgs: [enterprise.id], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<List<EnterpriseListItemModel>> getEnterpriseListItems({ |
||||||
|
String? name, |
||||||
|
String? type, |
||||||
|
DateTime? startDate, |
||||||
|
DateTime? endDate, |
||||||
|
}) async { |
||||||
|
final db = await _databaseService.database; |
||||||
|
|
||||||
|
// pendingCreate 状态的索引值,通常是 0 |
||||||
|
final int pendingCreateIndex = SyncStatus.pendingCreate.index; |
||||||
|
|
||||||
|
// 动态构建 WHERE 子句和参数 |
||||||
|
final List<String> whereClauses = []; |
||||||
|
final List<dynamic> whereArgs = []; |
||||||
|
|
||||||
|
// 1. 添加名称查询条件 (模糊查询) |
||||||
|
if (name != null && name.isNotEmpty) { |
||||||
|
whereClauses.add('e.name LIKE ?'); |
||||||
|
whereArgs.add('%$name%'); |
||||||
|
} |
||||||
|
|
||||||
|
// 2. 添加类型查询条件 (精确匹配) |
||||||
|
if (type != null && type.isNotEmpty) { |
||||||
|
whereClauses.add('e.type = ?'); |
||||||
|
whereArgs.add(type); |
||||||
|
} |
||||||
|
|
||||||
|
// 3. 添加创建时间的起始日期条件 |
||||||
|
if (startDate != null) { |
||||||
|
whereClauses.add('e.creationTime >= ?'); |
||||||
|
whereArgs.add(startDate.millisecondsSinceEpoch); |
||||||
|
} |
||||||
|
|
||||||
|
// 4. [新增] 添加创建时间的结束日期条件 |
||||||
|
if (endDate != null) { |
||||||
|
// 为了包含 endDate 当天的所有数据,我们需要将时间设为当天的最后一刻 |
||||||
|
final endOfDay = DateTime( |
||||||
|
endDate.year, |
||||||
|
endDate.month, |
||||||
|
endDate.day, |
||||||
|
23, |
||||||
|
59, |
||||||
|
59, |
||||||
|
); |
||||||
|
whereClauses.add('e.creationTime <= ?'); |
||||||
|
whereArgs.add(endOfDay.millisecondsSinceEpoch); |
||||||
|
} |
||||||
|
// --- 修正部分结束 --- |
||||||
|
|
||||||
|
// 组合 WHERE 子句 |
||||||
|
final String whereString = whereClauses.isNotEmpty |
||||||
|
? 'WHERE ${whereClauses.join(' AND ')}' |
||||||
|
: ''; |
||||||
|
|
||||||
|
// 这是最核心的 SQL 查询语句 |
||||||
|
final String sql = |
||||||
|
''' |
||||||
|
SELECT |
||||||
|
e.*, |
||||||
|
COUNT(p.id) AS totalProblems, |
||||||
|
COUNT(CASE WHEN p.syncStatus != $pendingCreateIndex THEN 1 ELSE NULL END) AS uploadedProblems, |
||||||
|
COUNT(CASE WHEN p.syncStatus == $pendingCreateIndex THEN 1 ELSE NULL END) AS pendingProblems |
||||||
|
FROM |
||||||
|
enterprises e |
||||||
|
LEFT JOIN |
||||||
|
problems p ON e.id = p.enterpriseId |
||||||
|
$whereString |
||||||
|
GROUP BY |
||||||
|
e.id |
||||||
|
ORDER BY |
||||||
|
e.creationTime DESC |
||||||
|
'''; |
||||||
|
|
||||||
|
// 调试 |
||||||
|
// Get.log('查询 SQL: $sql'); |
||||||
|
// Get.log('参数: $whereArgs'); |
||||||
|
|
||||||
|
final List<Map<String, dynamic>> maps = await db.rawQuery(sql, whereArgs); |
||||||
|
|
||||||
|
return List.generate(maps.length, (i) { |
||||||
|
return EnterpriseListItemModel.fromMap(maps[i]); |
||||||
|
}); |
||||||
} |
} |
||||||
} |
} |
||||||
|
|||||||
@ -0,0 +1,24 @@ |
|||||||
|
import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart'; |
||||||
|
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart'; |
||||||
|
|
||||||
|
class EnterpriseListItemModel extends EnterpriseListItem { |
||||||
|
const EnterpriseListItemModel({ |
||||||
|
required super.enterprise, |
||||||
|
required super.totalProblems, |
||||||
|
required super.uploadedProblems, |
||||||
|
required super.pendingProblems, |
||||||
|
}); |
||||||
|
|
||||||
|
/// 从数据库查询返回的 Map 创建模型 |
||||||
|
/// 这个 map 将包含 JOIN 和 COUNT 后的结果 |
||||||
|
factory EnterpriseListItemModel.fromMap(Map<String, dynamic> map) { |
||||||
|
return EnterpriseListItemModel( |
||||||
|
// 核心:复用 EnterpriseModel 的 fromMap 方法来解析企业自身的数据 |
||||||
|
enterprise: EnterpriseModel.fromMap(map), |
||||||
|
// 直接从 map 中获取聚合计算出的计数值 |
||||||
|
totalProblems: map['totalProblems'] ?? 0, |
||||||
|
uploadedProblems: map['uploadedProblems'] ?? 0, |
||||||
|
pendingProblems: map['pendingProblems'] ?? 0, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,24 @@ |
|||||||
|
import 'package:equatable/equatable.dart'; |
||||||
|
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise.dart'; |
||||||
|
|
||||||
|
class EnterpriseListItem extends Equatable { |
||||||
|
final Enterprise enterprise; |
||||||
|
final int totalProblems; |
||||||
|
final int uploadedProblems; |
||||||
|
final int pendingProblems; |
||||||
|
|
||||||
|
const EnterpriseListItem({ |
||||||
|
required this.enterprise, |
||||||
|
required this.totalProblems, |
||||||
|
required this.uploadedProblems, |
||||||
|
required this.pendingProblems, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
List<Object?> get props => [ |
||||||
|
enterprise, |
||||||
|
totalProblems, |
||||||
|
uploadedProblems, |
||||||
|
pendingProblems, |
||||||
|
]; |
||||||
|
} |
||||||
@ -1,12 +1,20 @@ |
|||||||
import 'package:problem_check_system/app/core/repositories/syncable_repository.dart'; |
import 'package:problem_check_system/app/core/repositories/syncable_repository.dart'; |
||||||
|
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart'; |
||||||
|
|
||||||
import '../entities/enterprise.dart'; |
import '../entities/enterprise.dart'; |
||||||
|
|
||||||
abstract class EnterpriseRepository implements SyncableRepository<Enterprise> { |
abstract class EnterpriseRepository implements SyncableRepository<Enterprise> { |
||||||
/// 将一个新的企业实体保存到数据层。 |
/// 新增 |
||||||
/// 实现者应负责处理ID生成、时间戳和初始同步状态。 |
|
||||||
Future<void> addEnterprise(Enterprise enterprise); |
Future<void> addEnterprise(Enterprise enterprise); |
||||||
|
|
||||||
// --- 已有方法 --- |
/// 更新 |
||||||
// Future<List<EnterpriseListItem>> getEnterpriseListItems(); |
Future<void> updateEnterprise(Enterprise enterprise); |
||||||
|
|
||||||
|
/// 获取企业信息列表 |
||||||
|
Future<List<EnterpriseListItem>> getEnterpriseListItems({ |
||||||
|
String? name, |
||||||
|
String? type, |
||||||
|
DateTime? startDate, |
||||||
|
DateTime? endDate, |
||||||
|
}); |
||||||
} |
} |
||||||
|
|||||||
@ -1,12 +0,0 @@ |
|||||||
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'; |
|
||||||
|
|
||||||
class AddEnterprise { |
|
||||||
final EnterpriseRepository repository; |
|
||||||
|
|
||||||
AddEnterprise({required this.repository}); |
|
||||||
|
|
||||||
Future<void> call(Enterprise enterprise) async { |
|
||||||
await repository.addEnterprise(enterprise); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,36 @@ |
|||||||
|
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/features/enterprise/domain/repositories/enterprise_repository.dart'; |
||||||
|
import 'package:uuid/uuid.dart'; |
||||||
|
|
||||||
|
class AddEnterpriseUsecase { |
||||||
|
final EnterpriseRepository repository; |
||||||
|
final Uuid uuid; |
||||||
|
|
||||||
|
AddEnterpriseUsecase({required this.repository, required this.uuid}); |
||||||
|
|
||||||
|
Future<void> call({ |
||||||
|
required String name, |
||||||
|
required String type, |
||||||
|
String address = "", |
||||||
|
String scale = "", |
||||||
|
String contactPerson = "", |
||||||
|
String contactPhone = "", |
||||||
|
String majorHazardsDescription = "", |
||||||
|
}) async { |
||||||
|
final enterprise = Enterprise( |
||||||
|
id: uuid.v4(), |
||||||
|
name: name, |
||||||
|
type: type, |
||||||
|
address: address, |
||||||
|
scale: scale, |
||||||
|
contactPerson: contactPerson, |
||||||
|
contactPhone: contactPhone, |
||||||
|
majorHazardsDescription: majorHazardsDescription, |
||||||
|
lastModifiedTime: DateTime.now(), |
||||||
|
creationTime: DateTime.now(), |
||||||
|
syncStatus: SyncStatus.pendingCreate, |
||||||
|
); |
||||||
|
await repository.addEnterprise(enterprise); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
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/features/enterprise/domain/repositories/enterprise_repository.dart'; |
||||||
|
|
||||||
|
class EditEnterpriseUsecase { |
||||||
|
final EnterpriseRepository repository; |
||||||
|
|
||||||
|
EditEnterpriseUsecase({required this.repository}); |
||||||
|
|
||||||
|
/// [核心] 这个 Usecase 的业务逻辑 |
||||||
|
/// 它接收一个已经修改过的 Enterprise 对象 |
||||||
|
Future<void> call(Enterprise updatedEnterprise) async { |
||||||
|
// 1. 业务规则:更新“最后修改时间” |
||||||
|
final enterpriseToSave = updatedEnterprise.copyWith( |
||||||
|
lastModifiedTime: DateTime.now(), |
||||||
|
// 2. 业务规则:将同步状态标记为“待更新” |
||||||
|
// 我们需要先判断它原始的状态,避免覆盖“待创建” |
||||||
|
syncStatus: updatedEnterprise.syncStatus == SyncStatus.pendingCreate |
||||||
|
? SyncStatus.pendingCreate |
||||||
|
: SyncStatus.pendingUpdate, |
||||||
|
); |
||||||
|
|
||||||
|
// 3. 调用仓库进行持久化 |
||||||
|
await repository.updateEnterprise(enterpriseToSave); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
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'; |
||||||
|
|
||||||
|
class GetEnterpriseListUsecase { |
||||||
|
final EnterpriseRepository repository; |
||||||
|
|
||||||
|
const GetEnterpriseListUsecase({required this.repository}); |
||||||
|
|
||||||
|
Future<List<EnterpriseListItem>> call({ |
||||||
|
String? name, |
||||||
|
String? type, |
||||||
|
DateTime? startDate, |
||||||
|
DateTime? endDate, |
||||||
|
}) async { |
||||||
|
return await repository.getEnterpriseListItems( |
||||||
|
name: name, |
||||||
|
type: type, |
||||||
|
startDate: startDate, |
||||||
|
endDate: endDate, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue