liusuyi
2026-05-30 f4f4fc53260eb67483dce406a963628273786a61
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
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<String, TaskWrapper> taskWrapperMap = new ConcurrentHashMap<>();
    private final Map<String, List<String>> 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<ArdVideoInspectTaskStep> 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<ArdVideoInspectTask> selectArdVideoInspectTaskList(ArdVideoInspectTask ardVideoInspectTask) {
 
        List<ArdVideoInspectTask> tasks = ardVideoInspectTaskMapper.selectArdVideoInspectTaskList(ardVideoInspectTask);
        tasks.forEach(task -> {
            ArdVideoInspectTaskStep step = new ArdVideoInspectTaskStep();
            step.setTaskId(task.getId());
            List<ArdVideoInspectTaskStep> 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<TimeSlot> getCameraIdleTimeList(String cameraId) {
        List<TimeSlot> idleTimeSlots = new LinkedList<>();
 
        // 获取该相机的占用时段
        List<TimeSlot> 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<Map<String, Object>> getOptions() {
        ArdCamera ardCamera = new ArdCamera("1");
        List<ArdCamera> ardCameras = ardCameraService.selectArdCameraList(ardCamera);
        return ardCameras.stream()
                .map(camera -> {
                    Map<String, Object> 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<ArdVideoInspectTask> 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<String> 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<ArdVideoInspectTask> tasks = selectArdVideoInspectTaskList(ardTask);
        synchronized (lock) {  // 加锁以防止多线程同时执行
            tasks.forEach(task -> {
 
                //查询相机的其他任务是否有正在手动控制并且正在运行
                List<ArdVideoInspectTask> 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<ArdVideoInspectTask> otherTasks = getOtherTasks(taskId);
        // 将需要暂停的任务 ID 转换为列表
        // 如果任务正在运行,直接暂停
        List<ArdVideoInspectTask> filteredTasks = otherTasks.stream()
                .filter(task -> taskWrapperMap.containsKey(task.getId()))  // 过滤掉不在 taskWrapperMap 中的任务
                .collect(Collectors.toList());  // 收集结果到 List 中
        filteredTasks.forEach(task -> pauseTask(task.getId()));
        List<String> pauseTaskList =
                filteredTasks.stream().map(ArdVideoInspectTask::getId).collect(Collectors.toList());  // 收集到列表
        pauseTaskMap.put(taskId, pauseTaskList);
    }
 
 
}