|
@@ -0,0 +1,217 @@
|
|
|
+package com.ozs.web.controller.shotschedule;
|
|
|
+
|
|
|
+import cn.hutool.core.date.DateTime;
|
|
|
+import cn.hutool.core.date.DateUnit;
|
|
|
+import cn.hutool.core.lang.Dict;
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.ozs.common.core.domain.entity.SysDictData;
|
|
|
+import com.ozs.common.utils.DictUtils;
|
|
|
+import com.ozs.service.entity.BaseCameraManagement;
|
|
|
+import com.ozs.service.entity.MsgAlarm;
|
|
|
+import com.ozs.service.entity.vo.BaseCameraVO;
|
|
|
+import com.ozs.service.mapper.BaseCameraManagementMapper;
|
|
|
+import com.ozs.service.mapper.MsgAlarmMapper;
|
|
|
+import com.ozs.service.service.RedisService;
|
|
|
+import com.ozs.system.service.ISysDictTypeService;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.context.annotation.Configuration;
|
|
|
+import org.springframework.data.redis.core.RedisTemplate;
|
|
|
+import org.springframework.scheduling.annotation.Async;
|
|
|
+import org.springframework.scheduling.annotation.EnableAsync;
|
|
|
+import org.springframework.scheduling.annotation.EnableScheduling;
|
|
|
+import org.springframework.scheduling.annotation.Scheduled;
|
|
|
+import org.springframework.util.CollectionUtils;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.time.Duration;
|
|
|
+import java.util.*;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author lyao
|
|
|
+ * @description 截图服务 - 调度任务
|
|
|
+ * @creat 2024/3/14
|
|
|
+ */
|
|
|
+@Configuration
|
|
|
+@EnableScheduling
|
|
|
+@EnableAsync
|
|
|
+@Slf4j
|
|
|
+public class ShotPictureTaskExecutors {
|
|
|
+
|
|
|
+ @Value("${shot.storeAddress}")
|
|
|
+ private String storeAddress;
|
|
|
+
|
|
|
+ @Value("${shot.isMaster}")
|
|
|
+ private Boolean isMaster;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Redis 缓存服务提供类
|
|
|
+ */
|
|
|
+ @Resource
|
|
|
+ private RedisService redisService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ public RedisTemplate<String, String> redisTemplate;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ISysDictTypeService iSysDictTypeService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private RedisPushService redisPushService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private MsgAlarmMapper msgAlarmMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private BaseCameraManagementMapper baseCameraManagementMapper;
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * REDIS 抓拍摄像头分布式锁
|
|
|
+ */
|
|
|
+ private final static String LOCK_CAMELA_NORMAL_KEY = "lock_camela_normal";
|
|
|
+
|
|
|
+ public static final String CAMERA_ALARM_HASH = "camera_alarm_hash";
|
|
|
+ public static final String CAMERA_NORMAL_HASH = "camera_normal_hash";
|
|
|
+
|
|
|
+ public final static AtomicInteger RUNNING_ATOMIC_INT = new AtomicInteger(0);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理非预警摄像头截图, 间隔: 60s
|
|
|
+ */
|
|
|
+ @Async
|
|
|
+ @Scheduled(fixedDelay = 60 * 1000, initialDelay = 3 * 1000)
|
|
|
+ public synchronized void initCameraConfig() {
|
|
|
+ if (!isMaster) return;
|
|
|
+ // 获取分布式锁
|
|
|
+ boolean lockSuccess = redisTemplate.opsForValue().setIfAbsent(LOCK_CAMELA_NORMAL_KEY, "execute", Duration.ofSeconds(60));
|
|
|
+ if (lockSuccess) {
|
|
|
+ // 查询未解除的报警摄像头(报警摄像头逻辑沿用wangyy代码)
|
|
|
+ List<String> lockedMsgAlarmCameraCodes = msgAlarmMapper.selectList(new QueryWrapper<MsgAlarm>().eq("is_lock", 2))
|
|
|
+ .stream().map(f -> f.getCameraCode()).distinct().collect(Collectors.toList());
|
|
|
+ try {
|
|
|
+ QueryWrapper<BaseCameraManagement> queryWrapper = new QueryWrapper<>();
|
|
|
+ queryWrapper.select("distinct camera_code, channel");
|
|
|
+ // 获取数据库中最新摄像头相关内容
|
|
|
+ List<BaseCameraManagement> dbAlarms = baseCameraManagementMapper.selectList(queryWrapper).stream().collect(Collectors.toList());
|
|
|
+ if (!CollectionUtils.isEmpty(dbAlarms)) {
|
|
|
+ Set<Object> hashAlarmKeys = redisService.redisTemplate.opsForHash().keys(CAMERA_ALARM_HASH);
|
|
|
+ Set<Object> hashNormalKeys = redisService.redisTemplate.opsForHash().keys(CAMERA_NORMAL_HASH);
|
|
|
+ // 数据库中存在的数据, 但是缓存中不存在, 则进行补全
|
|
|
+ for (BaseCameraManagement dbAlarm : dbAlarms) {
|
|
|
+ BaseCameraVO vo = new BaseCameraVO();
|
|
|
+ vo.setCameraCode(dbAlarm.getCameraCode());
|
|
|
+ vo.setChannel(dbAlarm.getChannel());
|
|
|
+ vo.setNextExecuteTime(DateTime.now().toString("yyyy-MM-dd HH:mm:ss"));
|
|
|
+ if (lockedMsgAlarmCameraCodes.contains(vo.getCameraCode()) && !hashAlarmKeys.contains(vo.getCameraCode())) {
|
|
|
+ vo.setIsAlarm(true);
|
|
|
+ redisService.addToHash(CAMERA_ALARM_HASH, vo.getCameraCode(), vo);
|
|
|
+ } else if (!lockedMsgAlarmCameraCodes.contains(vo.getCameraCode()) && !hashNormalKeys.contains(vo.getCameraCode())) {
|
|
|
+ vo.setIsAlarm(false);
|
|
|
+ redisService.addToHash(CAMERA_NORMAL_HASH, vo.getCameraCode(), vo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 排除缓存中未删除, 但是系统中删除的情况
|
|
|
+ for (Object hashAlarmKey : hashAlarmKeys) {
|
|
|
+ // 如果在数据库中都不存在, 则进行删除删除
|
|
|
+ if (!hashAlarmKeys.contains(hashAlarmKey) && !hashNormalKeys.contains(hashAlarmKey)) {
|
|
|
+ redisService.redisTemplate.opsForHash().delete(CAMERA_ALARM_HASH, hashAlarmKey);
|
|
|
+ redisService.redisTemplate.opsForHash().delete(CAMERA_NORMAL_HASH, hashAlarmKey);
|
|
|
+ } else if (hashAlarmKeys.contains(hashAlarmKey) && hashNormalKeys.contains(hashAlarmKey)) { // 如果存在于报警SET中, 则删除正常摄像头
|
|
|
+ redisService.redisTemplate.opsForHash().delete(CAMERA_NORMAL_HASH, hashAlarmKey);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 如果未找到数据库数据, 则清空抓图摄像头集合
|
|
|
+ redisService.redisTemplate.delete(CAMERA_ALARM_HASH);
|
|
|
+ redisService.redisTemplate.delete(CAMERA_NORMAL_HASH);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("缓存 [摄像头抓图] 调度任务配置出现异常, ", e);
|
|
|
+ } finally {
|
|
|
+ redisTemplate.delete(LOCK_CAMELA_NORMAL_KEY);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理非预警摄像头截图, 间隔: 60s
|
|
|
+ */
|
|
|
+ @Scheduled(fixedDelay = 5 * 1000, initialDelay = 10 * 1000)
|
|
|
+ public void batchProcess() {
|
|
|
+ if (!isMaster) return;
|
|
|
+ // 填充默认路径
|
|
|
+ String address = storeAddress;
|
|
|
+ // 获取字典值: 获取截图开关
|
|
|
+ List<SysDictData> dataList = DictUtils.getDictCache("shot_switch");
|
|
|
+ if (CollectionUtils.isEmpty(dataList)) {
|
|
|
+ DictUtils.setDictCache("shot_switch", dataList = iSysDictTypeService.selectDictDataByType("shot_switch"));
|
|
|
+ }
|
|
|
+ // 判断开关是否执行
|
|
|
+// boolean shotSwitch = CollectionUtils.isEmpty(dataList) && Boolean.parseBoolean(dataList.get(0).getDictLabel());
|
|
|
+// if (!shotSwitch) {
|
|
|
+// return;
|
|
|
+// }
|
|
|
+ // 获取字典值: 截图存放路径
|
|
|
+ List<SysDictData> addressDataList = DictUtils.getDictCache("shot_address");
|
|
|
+ if (CollectionUtils.isEmpty(addressDataList)) {
|
|
|
+ DictUtils.setDictCache("shot_address", addressDataList = iSysDictTypeService.selectDictDataByType("shot_address"));
|
|
|
+ }
|
|
|
+ // 设置路径地址为字典表最终值
|
|
|
+ if (!CollectionUtils.isEmpty(dataList) && Boolean.parseBoolean(dataList.get(0).getDictLabel())
|
|
|
+ && !CollectionUtils.isEmpty(addressDataList)) {
|
|
|
+ address = addressDataList.get(0).getDictLabel();
|
|
|
+ }
|
|
|
+ Map<Object, Object> entries = redisService.redisTemplate.opsForHash().entries(CAMERA_NORMAL_HASH);
|
|
|
+ if (!CollectionUtils.isEmpty(entries)) {
|
|
|
+ final String finalAddress = address;
|
|
|
+ for (Map.Entry<Object, Object> entry : entries.entrySet()) {
|
|
|
+ BaseCameraVO item = (BaseCameraVO) entry.getValue();
|
|
|
+ DateTime nextExecuteTime = DateTime.of(item.getNextExecuteTime(), "yyyy-MM-dd HH:mm:ss");
|
|
|
+ boolean after = item.getNextExecuteTime() == null ||
|
|
|
+ DateTime.now().isAfterOrEquals(nextExecuteTime) ||
|
|
|
+ (nextExecuteTime.between(DateTime.now(), DateUnit.MINUTE) > 5 && "running".equalsIgnoreCase(item.getStatus()));
|
|
|
+ if ((nextExecuteTime.between(DateTime.now(), DateUnit.MINUTE) > 5 && "running".equalsIgnoreCase(item.getStatus()))) {
|
|
|
+ item.setStatus("");
|
|
|
+ }
|
|
|
+ if (after && (StringUtils.isBlank(item.getStatus()) || "complete".equalsIgnoreCase(item.getStatus()))) {
|
|
|
+ Dict dict = new Dict();
|
|
|
+ dict.put("cameraCode", item.getCameraCode());
|
|
|
+ dict.put("channel", item.getChannel());
|
|
|
+ dict.put("address", finalAddress);
|
|
|
+ dict.put("isAlarm", false);
|
|
|
+ dict.put("nextExecuteTime", item.getNextExecuteTime());
|
|
|
+ redisPushService.pushMsg(JSONUtil.toJsonStr(dict));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Map<Object, Object> alarmEntries = redisService.redisTemplate.opsForHash().entries(CAMERA_ALARM_HASH);
|
|
|
+ if (!CollectionUtils.isEmpty(alarmEntries)) {
|
|
|
+ final String finalAddress = address;
|
|
|
+ for (Map.Entry<Object, Object> entry : alarmEntries.entrySet()) {
|
|
|
+ BaseCameraVO item = (BaseCameraVO) entry.getValue();
|
|
|
+ DateTime nextExecuteTime = DateTime.of(item.getNextExecuteTime(), "yyyy-MM-dd HH:mm:ss");
|
|
|
+ boolean after = item.getNextExecuteTime() == null ||
|
|
|
+ DateTime.now().isAfterOrEquals(nextExecuteTime) ||
|
|
|
+ (nextExecuteTime.between(DateTime.now(), DateUnit.MINUTE) > 5 && "running".equalsIgnoreCase(item.getStatus()));
|
|
|
+ if ((nextExecuteTime.between(DateTime.now(), DateUnit.MINUTE) > 5 && "running".equalsIgnoreCase(item.getStatus()))) {
|
|
|
+ item.setStatus("");
|
|
|
+ }
|
|
|
+ if (after && (StringUtils.isBlank(item.getStatus()) || "complete".equalsIgnoreCase(item.getStatus()))) {
|
|
|
+ Dict dict = new Dict();
|
|
|
+ dict.put("cameraCode", item.getCameraCode());
|
|
|
+ dict.put("channel", item.getChannel());
|
|
|
+ dict.put("address", finalAddress);
|
|
|
+ dict.put("isAlarm", true);
|
|
|
+ dict.put("nextExecuteTime", item.getNextExecuteTime());
|
|
|
+ redisPushService.pushMsg(JSONUtil.toJsonStr(dict));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|