Files
flutterApp/lib/features/devices/presentation/bloc/device_task_cubit.dart

416 lines
14 KiB
Dart
Raw Normal View History

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,
2026-09-14 12:35:20 +08:00
// 🔥 活跃任务为空时必须显式清空,否则 copyWith 会保留旧任务导致卡片一直显示
clearCurrentTask: currentTask == null,
clearCurrentTaskId: currentTask == null,
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;
}
2026-07-11 16:46:53 +08:00
final params = CancelTaskParams(
deviceId: deviceId,
taskId: taskId,
orgId: orgId,
2026-07-11 16:46:53 +08:00
siteId: siteId,
forceCancel: forceCancel,
);
2026-07-11 16:46:53 +08:00
_logger.logWithLevel('[取消任务] 请求: POST /iot/deviceTask/cancelTask');
_logger.logWithLevel(
'[取消任务] 参数: deviceId=$deviceId, taskId=$taskId, orgId=$orgId, siteId=$siteId, forceCancel=$forceCancel',
);
2026-07-11 16:46:53 +08:00
final result = await _cancelTaskUseCase.call(params);
result.fold(
(failure) {
2026-07-11 16:46:53 +08:00
_logger.logWithLevel('[取消任务] 响应失败: ${failure.message}');
_logger.logWithLevel('❌ 取消任务失败: ${failure.message}');
emit(
state.copyWith(
isLoading: false,
errorMessage: ErrorHandler.getErrorMessage(failure.message),
operationType: DeviceTaskOperationType.none,
shouldShowError: true, // 🔥 标记需要显示错误弹窗
),
);
},
(success) {
2026-07-11 16:46:53 +08:00
_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;
}
2026-07-11 16:46:53 +08:00
final params2 = PauseTaskParams(
deviceId: deviceId,
taskId: taskId,
orgId: orgId,
2026-07-11 16:46:53 +08:00
siteId: siteId,
);
2026-07-11 16:46:53 +08:00
_logger.logWithLevel('[暂停任务] 请求: POST /iot/deviceTask/pauseTask');
_logger.logWithLevel(
'[暂停任务] 参数: deviceId=$deviceId, taskId=$taskId, orgId=$orgId, siteId=$siteId',
);
2026-07-11 16:46:53 +08:00
final result = await _pauseTaskUseCase.call(params2);
result.fold(
(failure) {
2026-07-11 16:46:53 +08:00
_logger.logWithLevel('[暂停任务] 响应失败: ${failure.message}');
_logger.logWithLevel('❌ 暂停任务失败: ${failure.message}');
emit(
state.copyWith(
isLoading: false,
errorMessage: ErrorHandler.getErrorMessage(failure.message),
operationType: DeviceTaskOperationType.none,
shouldShowError: true, // 🔥 标记需要显示错误弹窗
),
);
},
(success) {
2026-07-11 16:46:53 +08:00
_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;
}
2026-07-11 16:46:53 +08:00
final params3 = RecoveryTaskParams(
deviceId: deviceId,
taskId: taskId,
orgId: orgId,
2026-07-11 16:46:53 +08:00
siteId: siteId,
);
2026-07-11 16:46:53 +08:00
_logger.logWithLevel('[恢复任务] 请求: POST /iot/deviceTask/recoveryTask');
_logger.logWithLevel(
'[恢复任务] 参数: deviceId=$deviceId, taskId=$taskId, orgId=$orgId, siteId=$siteId',
);
2026-07-11 16:46:53 +08:00
final result = await _recoveryTaskUseCase.call(params3);
result.fold(
(failure) {
2026-07-11 16:46:53 +08:00
_logger.logWithLevel('[恢复任务] 响应失败: ${failure.message}');
_logger.logWithLevel('❌ 恢复任务失败: ${failure.message}');
emit(
state.copyWith(
isLoading: false,
errorMessage: ErrorHandler.getErrorMessage(failure.message),
operationType: DeviceTaskOperationType.none,
shouldShowError: true, // 🔥 标记需要显示错误弹窗
),
);
},
(data) {
2026-07-11 16:46:53 +08:00
_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() {
2026-09-14 12:35:20 +08:00
emit(state.copyWith(clearCurrentTask: true, clearCurrentTaskId: true));
_logger.logWithLevel('🧹 清除当前任务');
}
2026-08-07 08:49:29 +08:00
/// 🔥 退出登录时清空所有状态
void clearAll() {
if (!isClosed) {
emit(const DeviceTaskState());
}
_logger.logWithLevel('🧹 [DeviceTaskCubit] clearAll - 所有状态已重置');
}
}