package com.ard.work.inspect.service.impl; import java.time.LocalTime; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.stream.Collectors; import com.ard.common.core.constant.SecurityConstants; import com.ard.common.core.domain.R; import com.ard.common.core.utils.DateUtils; import com.ard.common.core.utils.StringUtils; import com.ard.common.core.utils.uuid.IdUtils; import com.ard.common.datascope.annotation.DataScope; import com.ard.system.api.RemoteConfigService; import com.ard.work.api.domian.ArdCamera; import com.ard.work.device.camera.service.IArdCameraService; import com.ard.work.inspect.domain.ArdVideoInspectTaskStep; import com.ard.work.inspect.dto.TimeSlot; import com.ard.work.inspect.mapper.ArdVideoInspectTaskStepMapper; import com.ard.work.inspect.task.TaskStatus; import com.ard.work.inspect.task.TaskWrapper; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import com.ard.work.inspect.mapper.ArdVideoInspectTaskMapper; import com.ard.work.inspect.domain.ArdVideoInspectTask; import com.ard.work.inspect.service.IArdVideoInspectTaskService; import org.springframework.transaction.annotation.Transactional; import jakarta.annotation.Resource; import static java.util.stream.Collectors.toList; /** * 视频巡检任务Service业务层处理 * * @author ruoyi * @date 2024-09-12 */ @Service @Slf4j public class ArdVideoInspectTaskServiceImpl implements IArdVideoInspectTaskService { @Resource private ArdVideoInspectTaskMapper ardVideoInspectTaskMapper; @Resource private ArdVideoInspectTaskStepMapper ardVideoInspectTaskStepMapper; @Resource private IArdCameraService ardCameraService; @Resource private RemoteConfigService remoteConfigService; private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(50); private final Map taskWrapperMap = new ConcurrentHashMap<>(); private final Map> pauseTaskMap = new ConcurrentHashMap<>();//暂停任务列表 /** * 查询视频巡检任务 * * @param id 视频巡检任务主键 * @return 视频巡检任务 */ @Override public ArdVideoInspectTask selectArdVideoInspectTaskById(String id) { ArdVideoInspectTask task = ardVideoInspectTaskMapper.selectArdVideoInspectTaskById(id); if (task != null) { ArdVideoInspectTaskStep step = new ArdVideoInspectTaskStep(); step.setTaskId(task.getId()); List steps = ardVideoInspectTaskStepMapper.selectArdVideoInspectTaskStepList(step); task.setArdVideoInspectTaskSteps(steps); TaskStatus taskStatus = getTaskStatus(task.getId()); task.setTaskStatus(taskStatus); } return task; } /** * 查询视频巡检任务列表 * * @param ardVideoInspectTask 视频巡检任务 * @return 视频巡检任务 */ @Override @DataScope(deptAlias = "d", userAlias = "u") public List selectArdVideoInspectTaskList(ArdVideoInspectTask ardVideoInspectTask) { List tasks = ardVideoInspectTaskMapper.selectArdVideoInspectTaskList(ardVideoInspectTask); tasks.forEach(task -> { ArdVideoInspectTaskStep step = new ArdVideoInspectTaskStep(); step.setTaskId(task.getId()); List steps = ardVideoInspectTaskStepMapper.selectArdVideoInspectTaskStepList(step); task.setArdVideoInspectTaskSteps(steps); TaskStatus taskStatus = getTaskStatus(task.getId()); task.setTaskStatus(taskStatus); }); return tasks; } /** * 新增视频巡检任务 * * @param ardVideoInspectTask 视频巡检任务 * @return 结果 */ @Override @Transactional public int insertArdVideoInspectTask(ArdVideoInspectTask ardVideoInspectTask) { ardVideoInspectTask.setId(IdUtils.simpleUUID()); ardVideoInspectTask.setCreateTime(DateUtils.getNowDate()); ardVideoInspectTask.getArdVideoInspectTaskSteps().forEach(step -> { step.setTaskId(ardVideoInspectTask.getId()); step.setId(IdUtils.simpleUUID()); ardVideoInspectTaskStepMapper.insertArdVideoInspectTaskStep(step); }); return ardVideoInspectTaskMapper.insertArdVideoInspectTask(ardVideoInspectTask); } /** * 修改视频巡检任务 * * @param ardVideoInspectTask 视频巡检任务 * @return 结果 */ @Override @Transactional public int updateArdVideoInspectTask(ArdVideoInspectTask ardVideoInspectTask) { ardVideoInspectTask.setUpdateTime(DateUtils.getNowDate()); //修改先删除任务关联的全部步骤再重新添加 ardVideoInspectTaskStepMapper.deleteArdVideoInspectTaskStepByTaskId(ardVideoInspectTask.getId()); if (ardVideoInspectTask.getArdVideoInspectTaskSteps() != null) { ardVideoInspectTask.getArdVideoInspectTaskSteps().forEach(step -> { step.setTaskId(ardVideoInspectTask.getId()); step.setId(IdUtils.simpleUUID()); ardVideoInspectTaskStepMapper.insertArdVideoInspectTaskStep(step); }); } return ardVideoInspectTaskMapper.updateArdVideoInspectTask(ardVideoInspectTask); } /** * 批量删除视频巡检任务 * * @param ids 需要删除的视频巡检任务主键 * @return 结果 */ @Override public int deleteArdVideoInspectTaskByIds(String[] ids) { Arrays.asList(ids).forEach(id -> { ardVideoInspectTaskStepMapper.deleteArdVideoInspectTaskStepByTaskId(id); }); return ardVideoInspectTaskMapper.deleteArdVideoInspectTaskByIds(ids); } /** * 删除视频巡检任务信息 * * @param id 视频巡检任务主键 * @return 结果 */ @Override public int deleteArdVideoInspectTaskById(String id) { ardVideoInspectTaskStepMapper.deleteArdVideoInspectTaskStepByTaskId(id); return ardVideoInspectTaskMapper.deleteArdVideoInspectTaskById(id); } /** * 获取单个相机的空闲时段 * * @param cameraId 相机ID * @return 返回空闲时段列表 */ public List getCameraIdleTimeList(String cameraId) { List idleTimeSlots = new LinkedList<>(); // 获取该相机的占用时段 List usedPeriods = ardVideoInspectTaskMapper.getTaskUsedCameraPeriods(cameraId); // 如果没有占用时间段,返回全天空闲 if (usedPeriods.isEmpty()) { idleTimeSlots.add(new TimeSlot(LocalTime.MIN, LocalTime.MAX)); // 全天空闲 return idleTimeSlots; } // 遍历已用的时间段并处理空闲时间段 LocalTime startOfDay = LocalTime.MIN; // 00:00:00 LocalTime endOfDay = LocalTime.MAX; // 23:59:59 LocalTime previousEndTime = startOfDay; for (TimeSlot usedPeriod : usedPeriods) { LocalTime currentStartTime = usedPeriod.getStartTime(); LocalTime currentEndTime = usedPeriod.getEndTime(); // 如果当前使用的开始时间在上一个结束时间之后,则存在空闲时间 if (previousEndTime.isBefore(currentStartTime)) { idleTimeSlots.add(new TimeSlot(previousEndTime, currentStartTime)); } // 更新前一个结束时间 previousEndTime = currentEndTime; } // 检查是否存在最后一个空闲时段(即从最后一个已用时间段结束到当天结束的时间段) if (previousEndTime.isBefore(endOfDay)) { idleTimeSlots.add(new TimeSlot(previousEndTime, endOfDay)); } return idleTimeSlots; } /** * 获取所有相机的空闲时段 */ public List> getOptions() { ArdCamera ardCamera = new ArdCamera("1"); List ardCameras = ardCameraService.selectArdCameraList(ardCamera); return ardCameras.stream() .map(camera -> { Map option = new HashMap<>(); option.put("value", camera.getId()); option.put("label", camera.getName()); option.put("channelList", Optional.ofNullable(camera.getChannelList()).orElse(Collections.emptyList())); // 防止 // channelList 为空 option.put("idleTimeList", getCameraIdleTimeList(camera.getId())); return option; }) .collect(toList()); } /** * 获取任务关联相机的其他任务 */ @Override public List getOtherTasks(String taskId) { return ardVideoInspectTaskMapper.getOtherTasks(taskId); } private final Object lock = new Object(); /** * 启动任务 */ @Override public Boolean startTask(String taskId) { if (taskWrapperMap.containsKey(taskId)) { log.warn("Task with ID {} is already running", taskId); return false; } ArdVideoInspectTask task = selectArdVideoInspectTaskById(taskId); if (task == null) { log.error("Task with ID {} not found", taskId); return false; } TaskWrapper taskWrapper = new TaskWrapper(task); taskWrapperMap.put(task.getId(), taskWrapper); // 存储 TaskWrapper 实例 scheduler.submit(taskWrapper); log.info("Task with ID {} has been started", taskId); return true; } /** * 暂停任务 */ @Override public boolean pauseTask(String taskId) { return Optional.ofNullable(taskWrapperMap.get(taskId)) .map(task -> { task.pause(); return true; }) .orElse(false); } /** * 恢复任务 */ @Override public boolean resumeTask(String taskId) { return Optional.ofNullable(taskWrapperMap.get(taskId)) .map(task -> { if (task.isPaused()) { // 假设 TaskWrapper 有 isPaused 方法 task.resume(); pauseOtherTasks(taskId); return true; } else { log.warn("Task with ID {} is already running.", taskId); return false; } }) .orElse(false); } /** * 取消任务 */ @Override public boolean cancelTask(String taskId) { log.info(taskId); if (StringUtils.isBlank(taskId)){ return false; } if (Objects.isNull(taskWrapperMap.get(taskId))) { return false; } return Optional.ofNullable(taskWrapperMap.get(taskId)) .map(task -> { task.cancel(); taskWrapperMap.remove(taskId); return true; }) .orElse(false); } @Override public void manualStartTask(String taskId) { startTask(taskId); pauseOtherTasks(taskId); } @Override public void manualStopTask(String taskId) { cancelTask(taskId); //恢复其他任务 List tasksToResume = pauseTaskMap.get(taskId); if (tasksToResume != null) { tasksToResume.forEach(this::resumeTask); } } //判断当前时间是否在任务的开始时间和结束时间之间 public static boolean isWithinTimeRange(ArdVideoInspectTask task) { // 使用 isAfter 和 isBefore 方法来判断 return !LocalTime.now().isBefore(task.getStartTime()) && !LocalTime.now().isAfter(task.getEndTime()); } public TaskStatus getTaskStatus(String taskId) { TaskStatus status = taskWrapperMap.get(taskId) != null ? taskWrapperMap.get(taskId).status : TaskStatus.NOT_RUNNING; return status; } //每5秒执行一次自动任务扫描,满足时间的自动启动任务 //容器启动后,先等待 10秒,然后每隔 5秒 执行一次 // @Scheduled(cron = "0/5 * * * * ?") @Scheduled(fixedRate = 5000, initialDelay = 10000) public void autoStartTask() { ArdVideoInspectTask ardTask = new ArdVideoInspectTask(); ardTask.setInspectMode("1"); List tasks = selectArdVideoInspectTaskList(ardTask); synchronized (lock) { // 加锁以防止多线程同时执行 tasks.forEach(task -> { //查询相机的其他任务是否有正在手动控制并且正在运行 List otherTasks = getOtherTasks(task.getId()); boolean isManualControl = otherTasks.stream().anyMatch( otherTask -> { TaskWrapper otherTaskWrapper = taskWrapperMap.get(otherTask.getId()); return otherTaskWrapper != null && otherTask.getInspectMode().equals("0") && otherTaskWrapper.status.equals(TaskStatus.RUNNING); } ); if (isManualControl) { return; } //遍历所有自动任务,对满足启动时间并且未启动的任务进行启动 boolean isTaskActive = taskWrapperMap.containsKey(task.getId()); boolean withinTimeRange = isWithinTimeRange(task); if (withinTimeRange && !isTaskActive) { startTask(task.getId()); } else if (!withinTimeRange && isTaskActive) { cancelTask(task.getId()); } }); } } //暂停当前任务相机的其他任务 public void pauseOtherTasks(String taskId) { //获取当前任务相机的其他任务 List otherTasks = getOtherTasks(taskId); // 将需要暂停的任务 ID 转换为列表 // 如果任务正在运行,直接暂停 List filteredTasks = otherTasks.stream() .filter(task -> taskWrapperMap.containsKey(task.getId())) // 过滤掉不在 taskWrapperMap 中的任务 .collect(Collectors.toList()); // 收集结果到 List 中 filteredTasks.forEach(task -> pauseTask(task.getId())); List pauseTaskList = filteredTasks.stream().map(ArdVideoInspectTask::getId).collect(Collectors.toList()); // 收集到列表 pauseTaskMap.put(taskId, pauseTaskList); } }