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:sqflite/sqflite.dart'; |
||||
|
||||
abstract class EnterpriseLocalDataSource { |
||||
/// 新增企业 |
||||
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 { |
||||
final DatabaseService _databaseService; |
||||
|
||||
const EnterpriseLocalDataSourceImpl({ |
||||
required DatabaseService databaseService, |
||||
}) : _databaseService = databaseService; |
||||
|
||||
@override |
||||
Future<void> addEnterprise(EnterpriseModel enterprise) async { |
||||
// // 在这里实现将 Enterprise 保存到本地存储的逻辑 |
||||
// // 例如,使用 SQLite、SharedPreferences 或其他本地数据库 |
||||
// final db = await databaseService.database; |
||||
// await db.insert( |
||||
// 'enterprises', // 表名 |
||||
// enterprise.toMap(), |
||||
// conflictAlgorithm: ConflictAlgorithm.replace, |
||||
// ); |
||||
final db = await _databaseService.database; |
||||
await db.insert( |
||||
"enterprises", |
||||
enterprise.toMap(), |
||||
// conflictAlgorithm 用于定义当主键 (id) 冲突时的处理策略。 |
||||
// ConflictAlgorithm.replace 表示如果 ID 已存在,则用新数据替换旧数据。 |
||||
// 这在同步场景下通常比较有用。 |
||||
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/features/enterprise/domain/entities/enterprise_list_item.dart'; |
||||
|
||||
import '../entities/enterprise.dart'; |
||||
|
||||
abstract class EnterpriseRepository implements SyncableRepository<Enterprise> { |
||||
/// 将一个新的企业实体保存到数据层。 |
||||
/// 实现者应负责处理ID生成、时间戳和初始同步状态。 |
||||
/// 新增 |
||||
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