413 lines
13 KiB
Dart
413 lines
13 KiB
Dart
import 'package:flutter_bloc/flutter_bloc.dart';
|
||
import 'package:get_it/get_it.dart';
|
||
import '../../../../core/app/app_user_cubit.dart';
|
||
import '../../../../core/logging/i_logger_service.dart';
|
||
import '../../../../core/network/error_handler.dart';
|
||
import '../../../../features/v2/site/presentation/cubit/site_cubit.dart';
|
||
import '../../domain/entities/device_task_entity.dart'; // 🔥 新增
|
||
import '../../domain/usecases/cancel_task_usecase.dart';
|
||
import '../../domain/usecases/get_device_task_pool_usecase.dart';
|
||
import '../../domain/usecases/pause_task_usecase.dart';
|
||
import '../../domain/usecases/recovery_task_usecase.dart';
|
||
import 'device_task_state.dart';
|
||
|
||
class DeviceTaskCubit extends Cubit<DeviceTaskState> {
|
||
final GetDeviceTaskPoolUseCase _getDeviceTaskPoolUseCase;
|
||
final CancelTaskUseCase _cancelTaskUseCase;
|
||
final PauseTaskUseCase _pauseTaskUseCase;
|
||
final RecoveryTaskUseCase _recoveryTaskUseCase;
|
||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||
|
||
DeviceTaskCubit(
|
||
this._getDeviceTaskPoolUseCase,
|
||
this._cancelTaskUseCase,
|
||
this._pauseTaskUseCase,
|
||
this._recoveryTaskUseCase,
|
||
) : super(const DeviceTaskState());
|
||
|
||
/// 获取任务池并过滤出当前设备的任务
|
||
Future<void> fetchAndFilterTask(String deviceId) async {
|
||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||
|
||
try {
|
||
// 获取用户信息
|
||
final userCubit = GetIt.I<AppUserCubit>();
|
||
final user = userCubit.state.user;
|
||
if (user == null) {
|
||
emit(state.copyWith(isLoading: false, errorMessage: '用户未登录'));
|
||
return;
|
||
}
|
||
|
||
// 获取场站信息(siteId 和 orgId 都从当前场站取)
|
||
final siteCubit = GetIt.I<SiteCubit>();
|
||
final selectedSite = siteCubit.state.selectedSite;
|
||
final siteId = selectedSite?.id;
|
||
final orgId = selectedSite?.orgId ?? 0;
|
||
if (siteId == null) {
|
||
emit(state.copyWith(isLoading: false, errorMessage: '未选择场站'));
|
||
return;
|
||
}
|
||
|
||
// 调用接口获取任务池
|
||
final result = await _getDeviceTaskPoolUseCase.call(
|
||
GetDeviceTaskPoolParams(
|
||
userId: user.userId ?? '',
|
||
siteId: siteId,
|
||
orgId: orgId,
|
||
),
|
||
);
|
||
|
||
result.fold(
|
||
(failure) {
|
||
_logger.logWithLevel('❌ 获取任务池失败: ${failure.message}');
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
errorMessage: ErrorHandler.getErrorMessage(failure.message),
|
||
shouldShowError: true, // 🔥 标记需要显示错误弹窗
|
||
),
|
||
);
|
||
},
|
||
(taskList) {
|
||
// 🔥 过滤出当前设备 + 活跃状态的任务(NEW, EXECUTING, PAUSE, CANCELING, FAILED)
|
||
final activeTasks = taskList.where((task) {
|
||
// 1. 设备号匹配
|
||
if (task.deviceId != deviceId) return false;
|
||
|
||
// 2. 状态过滤:保留新建、执行中、暂停中、取消中、执行失败的任务
|
||
final status = task.taskStatus;
|
||
return status == 'NEW' || // 新建
|
||
status == 'EXECUTING' || // 执行中
|
||
status == 'PAUSE' || // 暂停中
|
||
status == 'CANCELING' || // 取消中(用户可处理)
|
||
status == 'FAILED'; // 执行失败(用户可恢复或取消)
|
||
}).toList();
|
||
|
||
_logger.logWithLevel(
|
||
'✅ [fetchAndFilterTask] deviceId=$deviceId, 任务池总数=${taskList.length}, 过滤后活跃任务数=${activeTasks.length}',
|
||
);
|
||
for (final t in taskList) {
|
||
_logger.logWithLevel(
|
||
' - taskId=${t.id}, deviceId=${t.deviceId}, status=${t.taskStatus}',
|
||
);
|
||
}
|
||
|
||
// 🔥 如果有多个任务,保存所有选项供用户选择
|
||
// 如果只有1个,直接选中
|
||
final currentTask = activeTasks.isNotEmpty ? activeTasks.first : null;
|
||
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
taskPool: taskList,
|
||
currentTask: currentTask,
|
||
currentTaskId: currentTask?.id,
|
||
activeTasks: activeTasks,
|
||
),
|
||
);
|
||
},
|
||
);
|
||
} catch (e) {
|
||
_logger.logWithLevel('❌ 获取任务池异常: $e');
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
errorMessage: ErrorHandler.getErrorMessage(e),
|
||
shouldShowError: true, // 🔥 标记需要显示错误弹窗
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 取消任务
|
||
Future<void> cancelTask(String deviceId, {bool forceCancel = true}) async {
|
||
final taskId = state.currentTaskId;
|
||
if (taskId == null) {
|
||
emit(state.copyWith(errorMessage: '无可用任务'));
|
||
return;
|
||
}
|
||
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: true,
|
||
operationType: DeviceTaskOperationType.cancel,
|
||
),
|
||
);
|
||
|
||
try {
|
||
final userCubit = GetIt.I<AppUserCubit>();
|
||
final user = userCubit.state.user;
|
||
final siteCubit = GetIt.I<SiteCubit>();
|
||
final selectedSite = siteCubit.state.selectedSite;
|
||
final siteId = selectedSite?.id;
|
||
final orgId = selectedSite?.orgId ?? 0;
|
||
|
||
if (user == null || siteId == null) {
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
errorMessage: '参数不完整',
|
||
operationType: DeviceTaskOperationType.none,
|
||
),
|
||
);
|
||
return;
|
||
}
|
||
|
||
final params = CancelTaskParams(
|
||
deviceId: deviceId,
|
||
taskId: taskId,
|
||
orgId: orgId,
|
||
siteId: siteId,
|
||
forceCancel: forceCancel,
|
||
);
|
||
_logger.logWithLevel('[取消任务] 请求: POST /iot/deviceTask/cancelTask');
|
||
_logger.logWithLevel(
|
||
'[取消任务] 参数: deviceId=$deviceId, taskId=$taskId, orgId=$orgId, siteId=$siteId, forceCancel=$forceCancel',
|
||
);
|
||
final result = await _cancelTaskUseCase.call(params);
|
||
|
||
result.fold(
|
||
(failure) {
|
||
_logger.logWithLevel('[取消任务] 响应失败: ${failure.message}');
|
||
_logger.logWithLevel('❌ 取消任务失败: ${failure.message}');
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
errorMessage: ErrorHandler.getErrorMessage(failure.message),
|
||
operationType: DeviceTaskOperationType.none,
|
||
shouldShowError: true, // 🔥 标记需要显示错误弹窗
|
||
),
|
||
);
|
||
},
|
||
(success) {
|
||
_logger.logWithLevel('[取消任务] 响应成功: $success');
|
||
_logger.logWithLevel('✅ 取消任务成功');
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
operationType: DeviceTaskOperationType.none,
|
||
),
|
||
);
|
||
},
|
||
);
|
||
} catch (e) {
|
||
_logger.logWithLevel('❌ 取消任务异常: $e');
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
errorMessage: ErrorHandler.getErrorMessage(e),
|
||
operationType: DeviceTaskOperationType.none,
|
||
shouldShowError: true, // 🔥 标记需要显示错误弹窗
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 暂停任务
|
||
Future<void> pauseTask(String deviceId) async {
|
||
final taskId = state.currentTaskId;
|
||
if (taskId == null) {
|
||
emit(state.copyWith(errorMessage: '无可用任务'));
|
||
return;
|
||
}
|
||
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: true,
|
||
operationType: DeviceTaskOperationType.pause,
|
||
),
|
||
);
|
||
|
||
try {
|
||
final userCubit = GetIt.I<AppUserCubit>();
|
||
final user = userCubit.state.user;
|
||
final siteCubit = GetIt.I<SiteCubit>();
|
||
final selectedSite = siteCubit.state.selectedSite;
|
||
final siteId = selectedSite?.id;
|
||
final orgId = selectedSite?.orgId ?? 0;
|
||
|
||
if (user == null || siteId == null) {
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
errorMessage: '参数不完整',
|
||
operationType: DeviceTaskOperationType.none,
|
||
),
|
||
);
|
||
return;
|
||
}
|
||
|
||
final params2 = PauseTaskParams(
|
||
deviceId: deviceId,
|
||
taskId: taskId,
|
||
orgId: orgId,
|
||
siteId: siteId,
|
||
);
|
||
_logger.logWithLevel('[暂停任务] 请求: POST /iot/deviceTask/pauseTask');
|
||
_logger.logWithLevel(
|
||
'[暂停任务] 参数: deviceId=$deviceId, taskId=$taskId, orgId=$orgId, siteId=$siteId',
|
||
);
|
||
final result = await _pauseTaskUseCase.call(params2);
|
||
|
||
result.fold(
|
||
(failure) {
|
||
_logger.logWithLevel('[暂停任务] 响应失败: ${failure.message}');
|
||
_logger.logWithLevel('❌ 暂停任务失败: ${failure.message}');
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
errorMessage: ErrorHandler.getErrorMessage(failure.message),
|
||
operationType: DeviceTaskOperationType.none,
|
||
shouldShowError: true, // 🔥 标记需要显示错误弹窗
|
||
),
|
||
);
|
||
},
|
||
(success) {
|
||
_logger.logWithLevel('[暂停任务] 响应成功: $success');
|
||
_logger.logWithLevel('✅ 暂停任务成功');
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
operationType: DeviceTaskOperationType.none,
|
||
),
|
||
);
|
||
},
|
||
);
|
||
} catch (e) {
|
||
_logger.logWithLevel('❌ 暂停任务异常: $e');
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
errorMessage: ErrorHandler.getErrorMessage(e),
|
||
operationType: DeviceTaskOperationType.none,
|
||
shouldShowError: true, // 🔥 标记需要显示错误弹窗
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 恢复任务
|
||
Future<void> recoveryTask(String deviceId) async {
|
||
final taskId = state.currentTaskId;
|
||
if (taskId == null) {
|
||
emit(state.copyWith(errorMessage: '无可用任务'));
|
||
return;
|
||
}
|
||
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: true,
|
||
operationType: DeviceTaskOperationType.recovery,
|
||
),
|
||
);
|
||
|
||
try {
|
||
final userCubit = GetIt.I<AppUserCubit>();
|
||
final user = userCubit.state.user;
|
||
final siteCubit = GetIt.I<SiteCubit>();
|
||
final selectedSite = siteCubit.state.selectedSite;
|
||
final siteId = selectedSite?.id;
|
||
final orgId = selectedSite?.orgId ?? 0;
|
||
|
||
if (user == null || siteId == null) {
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
errorMessage: '参数不完整',
|
||
operationType: DeviceTaskOperationType.none,
|
||
),
|
||
);
|
||
return;
|
||
}
|
||
|
||
final params3 = RecoveryTaskParams(
|
||
deviceId: deviceId,
|
||
taskId: taskId,
|
||
orgId: orgId,
|
||
siteId: siteId,
|
||
);
|
||
_logger.logWithLevel('[恢复任务] 请求: POST /iot/deviceTask/recoveryTask');
|
||
_logger.logWithLevel(
|
||
'[恢复任务] 参数: deviceId=$deviceId, taskId=$taskId, orgId=$orgId, siteId=$siteId',
|
||
);
|
||
final result = await _recoveryTaskUseCase.call(params3);
|
||
|
||
result.fold(
|
||
(failure) {
|
||
_logger.logWithLevel('[恢复任务] 响应失败: ${failure.message}');
|
||
_logger.logWithLevel('❌ 恢复任务失败: ${failure.message}');
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
errorMessage: ErrorHandler.getErrorMessage(failure.message),
|
||
operationType: DeviceTaskOperationType.none,
|
||
shouldShowError: true, // 🔥 标记需要显示错误弹窗
|
||
),
|
||
);
|
||
},
|
||
(data) {
|
||
_logger.logWithLevel('[恢复任务] 响应成功: $data');
|
||
_logger.logWithLevel('✅ 恢复任务成功: $data');
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
operationType: DeviceTaskOperationType.none,
|
||
),
|
||
);
|
||
},
|
||
);
|
||
} catch (e) {
|
||
_logger.logWithLevel('❌ 恢复任务异常: $e');
|
||
emit(
|
||
state.copyWith(
|
||
isLoading: false,
|
||
errorMessage: ErrorHandler.getErrorMessage(e),
|
||
operationType: DeviceTaskOperationType.none,
|
||
shouldShowError: true, // 🔥 标记需要显示错误弹窗
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 更新当前任务ID(当选择新航线时调用)
|
||
void updateCurrentTaskId(int taskId) {
|
||
// 🔥 只有 activeTasks 非空时才设置 currentTask,避免信息栏一直显示
|
||
DeviceTaskEntity? taskEntity;
|
||
if (state.activeTasks.isNotEmpty) {
|
||
taskEntity = state.activeTasks.firstWhere(
|
||
(t) => t.id == taskId,
|
||
orElse: () => DeviceTaskEntity(
|
||
id: taskId,
|
||
deviceId: '',
|
||
taskStatus: 'EXECUTING',
|
||
taskStatusTranslate: '执行中',
|
||
),
|
||
);
|
||
}
|
||
emit(state.copyWith(currentTaskId: taskId, currentTask: taskEntity));
|
||
_logger.logWithLevel(
|
||
'🔄 更新当前任务ID: $taskId, activeTasks: ${state.activeTasks.length}, currentTask: ${taskEntity?.taskStatus ?? "null"}',
|
||
);
|
||
}
|
||
|
||
/// 🔥 手动选择任务(用户从弹窗中选择)
|
||
void selectTask(DeviceTaskEntity task) {
|
||
_logger.logWithLevel('✅ 用户选择任务ID: ${task.id}, 状态: ${task.taskStatus}');
|
||
emit(state.copyWith(currentTask: task, currentTaskId: task.id));
|
||
}
|
||
|
||
/// 清除当前任务
|
||
void clearCurrentTask() {
|
||
emit(state.copyWith(currentTask: null, currentTaskId: null));
|
||
_logger.logWithLevel('🧹 清除当前任务');
|
||
}
|
||
|
||
/// 🔥 退出登录时清空所有状态
|
||
void clearAll() {
|
||
if (!isClosed) {
|
||
emit(const DeviceTaskState());
|
||
}
|
||
_logger.logWithLevel('🧹 [DeviceTaskCubit] clearAll - 所有状态已重置');
|
||
}
|
||
}
|