liusuyi
2026-01-05 d2a65f0c13df21354ac83bf13b15f46937bd8ecb
将定时任务迁移到定时模块中
1 files added
5 files modified
486 ■■■■■ changed files
ard-device/src/main/java/com/ard/device/camera/service/impl/ArdCameraServiceImpl.java 83 ●●●●● patch | view | raw | blame | history
ard-device/src/main/java/com/ard/device/channel/service/impl/ArdChannelServiceImpl.java 106 ●●●●● patch | view | raw | blame | history
ard-quartz/pom.xml 4 ●●●●● patch | view | raw | blame | history
ard-quartz/src/main/java/com/ard/quartz/task/DeviceTask.java 258 ●●●●● patch | view | raw | blame | history
ard-vtdu/src/main/java/com/ard/vtdu/service/impl/ArdVtduServiceImpl.java 8 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/service/impl/ArdDroneInfoServiceImpl.java 27 ●●●●● patch | view | raw | blame | history
ard-device/src/main/java/com/ard/device/camera/service/impl/ArdCameraServiceImpl.java
@@ -373,87 +373,4 @@
        redisService.setCacheObject(getCacheKey(camera.getId()), camera);
    }
    /**
     * 同步设备通道与本地通道库信息
     *
     * @author 刘苏义
     * @date 2024/8/22 14:37
     */
    @Scheduled(fixedDelay = 5000)
    public void syncChannel() {
        try {
            log.debug("定时执行同步通道任务");
            List<ArdCamera> ardCameraList = this.selectArdCameraList(new ArdCamera());
            if (!ardCameraList.isEmpty()) {
                ardCameraList
                        .forEach(ardCamera -> {
                            //SDK获取实际通道
                            List<ArdChannel> ardChannelListSDK = cameraSDKService.getChannels(ardCamera);
                            //数据库获取通道
                            ArdChannel ardChannel = new ArdChannel();
                            ardChannel.setDeviceId(ardCamera.getId());
                            List<ArdChannel> ardChannelListDb = ardChannelService.selectArdChannelList(ardChannel);
                            //同步通道
                            asyncChannel(ardChannelListDb, ardChannelListSDK);
                        });
            }
        } catch (Exception ex) {
            log.error("定时执行同步通道任务异常:{}", ex.getMessage());
        }
    }
    /**
     * 定时执行登录失败相机重新登录
     *
     * @author 刘苏义
     * @date 2024/8/22 14:37
     */
    @Scheduled(fixedDelay = 10000)
    public void failedReconnect() {
        try {
            log.debug("定时执行失败相机重连任务");
            // 从数据库取全部设备
            List<ArdCamera> cameraList = selectArdCameraList(new ArdCamera());
            cameraList.stream()
                    // 只处理登录失败或离线的设备
                    .filter(camera -> camera.getLoginId() == null || camera.getLoginId() < 0 || "0".equals(camera.getState()))
                    .forEach(camera -> {
                        log.info("尝试重新登录设备 {}:{}", camera.getIp(), camera.getPort());
                        cameraSDKService.login(camera);
                    });
        } catch (Exception ex) {
            log.error("定时执行失败相机重连任务异常:{}", ex.getMessage());
        }
    }
    /**
     * 同步相机在线状态
     * 刘苏义
     * 2023/10/8 14:54:41
     */
    @Scheduled(fixedDelay = 5000)
    public void cameraState() {
        try {
            log.debug("定时执行同步相机在线状态任务");
            //调用查询方法
            List<ArdCamera> ardCameraList = selectArdCameraList(new ArdCamera());
            ardCameraList
                    .forEach(camera -> {
                        boolean onLineNew = cameraSDKService.isOnLine(camera);
                        boolean onLineOld = "1".equals(camera.getState());
                        if (onLineNew != onLineOld) {
                            if (onLineNew) {
                                camera.setState("1");
                            } else {
                                camera.setState("0");
                            }
                            camera.setUpdateTime(DateUtils.getNowDate());
                            ardCameraMapper.updateArdCamera(camera);
                        }
                    });
        } catch (Exception ex) {
            log.error("同步相机在线状态异常:{}", ex.getMessage());
        }
    }
}
ard-device/src/main/java/com/ard/device/channel/service/impl/ArdChannelServiceImpl.java
@@ -30,16 +30,9 @@
@Slf4j
@Service
public class ArdChannelServiceImpl implements IArdChannelService {
   // @Resource
    //private IArdCameraService ardCameraService;
    @Resource
    private ArdChannelMapper ardChannelMapper;
    @Resource
    private IArdVtduService ardVtduService;
    @Resource
    private MediamtxConfig mediamtxConfig;
    @Resource
    private RedisCache redisCache;
    /**
     * 查询通道管理
@@ -143,102 +136,5 @@
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }
    @Scheduled(fixedDelay = 5000)
    public void syncVtdu() {
        log.debug("定时同步Vtdu任务");
        List<ArdChannel> channels = this.selectArdChannelList(new ArdChannel());
        if (channels == null || channels.isEmpty()) {
            // log.warn("没有获取到通道数据,退出同步");
            return;
        }
        List<ArdVtdu> ardVtduList = ardVtduService.selectArdVtduList(new ArdVtdu());
        Set<String> validNames = channels.stream()
                .map(channel -> channel.getDeviceId() + "_" + channel.getChanNo())
                .collect(Collectors.toSet());
        // 2. 合并操作:增、删、改
        channels.forEach(channel -> {
            String name = channel.getDeviceId() + "_" + channel.getChanNo();  // 生成媒体表的 name
            String cameraId = channel.getDeviceId();  // cameraId 就是通道的 deviceId
            String rtspSourceUrl = getRtspSource(channel);
            String rtspUrl =
                    "rtsp://" + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getRtspPort() + "/" + name;  //
            // 拼接 webrtcUrl
            String rtmpUrl =
                    "rtmp://" + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getRtmpPort() + "/" + name;  // 拼接
            // webrtcUrl
            String protocol = mediamtxConfig.isHttps() ? "https://" : "http://";
            String webrtcUrl =
                    protocol + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getWebrtcPort() + "/" + name;
            // 拼接 webrtcUrl
            // 先检查媒体表是否已有此记录
            Optional<ArdVtdu> existingMedia = ardVtduList.stream()
                    .filter(media -> media.getName().equals(name))  // 查找是否已有对应的媒体记录
                    .findFirst();
            if (existingMedia.isPresent()) {
                // 如果已有,更新现有记录
                ArdVtdu vtdu = existingMedia.get();
                // 更新前后内容相同则不进行更新
                if (!vtdu.getRtspSource().equals(rtspSourceUrl) ||
                        !vtdu.getWebrtcUrl().equals(webrtcUrl) ||
                        !vtdu.getRtspUrl().equals(rtspUrl) ||
                        !vtdu.getRtmpUrl().equals(rtmpUrl)) {
                    vtdu.setRtspUrl(rtspUrl);
                    vtdu.setRtmpUrl(rtmpUrl);
                    vtdu.setWebrtcUrl(webrtcUrl);
                    ardVtduService.updateArdVtdu(vtdu);
                    log.info("更新媒体记录:{}", vtdu.getName());
                }
            } else {
                // 如果没有,插入新的记录
                ArdVtdu vtdu = new ArdVtdu();
                vtdu.setName(name);
                vtdu.setRtspSource(rtspSourceUrl);
                vtdu.setCameraId(cameraId);
                vtdu.setRtspUrl(rtspUrl);
                vtdu.setRtmpUrl(rtmpUrl);
                vtdu.setWebrtcUrl(webrtcUrl);
                ardVtduService.insertArdVtdu(vtdu);  // 保存到媒体表
                log.info("插入新媒体记录:{}", vtdu.getName());
            }
        });
        // 3. 删除操作:删除媒体表中多余的记录
        List<String> invalidNames = ardVtduList.stream()
                .filter(vtdu -> !validNames.contains(vtdu.getName()))
                .filter(vtdu -> !"publisher".equalsIgnoreCase(vtdu.getRtspSource())) // 不是机场流
                .map(ArdVtdu::getName)
                .collect(Collectors.toList());
        if (!invalidNames.isEmpty()) {
            // 将 List<String> 转换为 String[]
            String[] namesArray = invalidNames.toArray(new String[0]);
            ardVtduService.deleteArdVtduByNames(namesArray); // 传递数组
            log.info("删除不再存在的媒体记录:{}", Arrays.toString(namesArray));
        }
    }
    // 根据通道信息获取rtsp源地址
    private String getRtspSource(ArdChannel channel) {
        String rtspSource = "";
        ArdCamera ardCamera =  redisCache.getCacheObject(CacheConstants.CAMERA_LIST_KEY + channel.getDeviceId());
        if (ardCamera != null) {
            switch (ardCamera.getFactory()) {
                case "1":
                    rtspSource =
                            "rtsp://" + ardCamera.getUsername() + ":" + ardCamera.getPassword() + "@" + ardCamera.getIp() + ":" + ardCamera.getRtspPort() + "/h264/ch" + channel.getChanNo() + "/main/av_stream";
                    break;
                case "2":
                    rtspSource =
                            "rtsp://" + ardCamera.getUsername() + ":" + ardCamera.getPassword() + "@" + ardCamera.getIp() + ":" + ardCamera.getRtspPort() + "/cam/realmonitor?channel=" + channel.getChanNo() + "&subtype=0";
                    break;
            }
        }
        return rtspSource;
    }
}
ard-quartz/pom.xml
@@ -38,6 +38,10 @@
            <groupId>com.ard</groupId>
            <artifactId>ard-work</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ard</groupId>
            <artifactId>ard-device</artifactId>
        </dependency>
    </dependencies>
ard-quartz/src/main/java/com/ard/quartz/task/DeviceTask.java
New file
@@ -0,0 +1,258 @@
package com.ard.quartz.task;
import com.ard.common.constant.CacheConstants;
import com.ard.common.core.redis.RedisCache;
import com.ard.common.utils.DateUtils;
import com.ard.common.utils.file.MimeTypeUtils;
import com.ard.device.camera.domain.ArdCamera;
import com.ard.device.camera.service.IArdCameraService;
import com.ard.device.channel.domain.ArdChannel;
import com.ard.device.channel.service.IArdChannelService;
import com.ard.device.component.sdk.service.CameraSDKService;
import com.ard.oss.service.impl.OssServiceContext;
import com.ard.vtdu.config.MediamtxConfig;
import com.ard.vtdu.domain.ArdVtdu;
import com.ard.vtdu.service.IArdMediaService;
import com.ard.vtdu.service.IArdVtduService;
import com.ard.work.domain.entity.ArdMediaPic;
import com.ard.work.domain.entity.ArdMediaVideo;
import com.ard.work.mapper.ArdMediaPicMapper;
import com.ard.work.mapper.ArdMediaVideoMapper;
import com.ard.work.util.osSelect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
/**
 * 定时同步设备任务
 *
 * @author ard
 */
@Component("DeviceTask")
@Slf4j
public class DeviceTask {
    @Resource
    private RedisCache redisCache;
    @Resource
    private IArdChannelService ardChannelService;
    @Resource
    private IArdVtduService ardVtduService;
    @Resource
    private MediamtxConfig mediamtxConfig;
    @Resource
    private CameraSDKService cameraSDKService;
    @Resource
    private IArdCameraService ardCameraService;
    @Resource
    private IArdMediaService ardMediaService;
    /**
     * 定时执行登录失败相机重新登录
     */
    public void failedReconnect() {
        try {
            log.debug("定时执行失败相机重连任务");
            // 从数据库取全部设备
            ArdCamera ardCamera = new ArdCamera();
            ardCamera.setUseDataScope(false);
            List<ArdCamera> cameraList = ardCameraService.selectArdCameraList(ardCamera);
            cameraList.stream()
                    // 只处理登录失败或离线的设备
                    .filter(camera -> camera.getLoginId() == null || camera.getLoginId() < 0 || "0".equals(camera.getState()))
                    .forEach(camera -> {
                        log.info("尝试重新登录设备 {}:{}", camera.getIp(), camera.getPort());
                        cameraSDKService.login(camera);
                    });
        } catch (Exception ex) {
            log.error("定时执行失败相机重连任务异常:{}", ex.getMessage());
        }
    }
    /**
     * 同步相机在线状态
     */
    public void syncCameraState() {
        try {
            log.debug("定时执行同步相机在线状态任务");
            //调用查询方法
            ArdCamera ardCamera = new ArdCamera();
            ardCamera.setUseDataScope(false);
            List<ArdCamera> ardCameraList = ardCameraService.selectArdCameraList(ardCamera);
            ardCameraList
                    .forEach(camera -> {
                        boolean onLineNew = cameraSDKService.isOnLine(camera);
                        boolean onLineOld = "1".equals(camera.getState());
                        if (onLineNew != onLineOld) {
                            if (onLineNew) {
                                camera.setState("1");
                            } else {
                                camera.setState("0");
                            }
                            camera.setUpdateTime(DateUtils.getNowDate());
                            ardCameraService.updateArdCamera(camera);
                        }
                    });
        } catch (Exception ex) {
            log.error("同步相机在线状态异常:{}", ex.getMessage());
        }
    }
    /**
     * 同步相机通道
     */
    public void syncChannel() {
        try {
            log.debug("定时执行同步通道任务");
            ArdCamera camera = new ArdCamera();
            camera.setUseDataScope(false);
            List<ArdCamera> ardCameraList = ardCameraService.selectArdCameraList(camera);
            if (!ardCameraList.isEmpty()) {
                ardCameraList
                        .forEach(ardCamera -> {
                            //SDK获取实际通道
                            List<ArdChannel> ardChannelListSDK = cameraSDKService.getChannels(ardCamera);
                            //数据库获取通道
                            ArdChannel ardChannel = new ArdChannel();
                            ardChannel.setUseDataScope(false);
                            ardChannel.setDeviceId(ardCamera.getId());
                            List<ArdChannel> ardChannelListDb = ardChannelService.selectArdChannelList(ardChannel);
                            //同步通道
                            ardCameraService.asyncChannel(ardChannelListDb, ardChannelListSDK);
                        });
            }
        } catch (Exception ex) {
            log.error("定时执行同步通道任务异常:{}", ex.getMessage());
        }
    }
    /**
     * 同步通道流媒体
     */
    public void syncVtdu() {
        log.debug("定时同步Vtdu任务");
        ArdChannel ardChannel = new ArdChannel();
        ardChannel.setUseDataScope(false);
        List<ArdChannel> channels = ardChannelService.selectArdChannelList(ardChannel);
        if (channels == null || channels.isEmpty()) {
            // log.warn("没有获取到通道数据,退出同步");
            return;
        }
        List<ArdVtdu> ardVtduList = ardVtduService.selectArdVtduList(new ArdVtdu());
        Set<String> validNames = channels.stream()
                .map(channel -> channel.getDeviceId() + "_" + channel.getChanNo())
                .collect(Collectors.toSet());
        // 2. 合并操作:增、删、改
        channels.forEach(channel -> {
            String name = channel.getDeviceId() + "_" + channel.getChanNo();  // 生成媒体表的 name
            String cameraId = channel.getDeviceId();  // cameraId 就是通道的 deviceId
            String rtspSourceUrl = getRtspSource(channel);
            String rtspUrl =
                    "rtsp://" + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getRtspPort() + "/" + name;  //
            // 拼接 webrtcUrl
            String rtmpUrl =
                    "rtmp://" + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getRtmpPort() + "/" + name;  // 拼接
            // webrtcUrl
            String protocol = mediamtxConfig.isHttps() ? "https://" : "http://";
            String webrtcUrl =
                    protocol + mediamtxConfig.getPlayHost() + ":" + mediamtxConfig.getWebrtcPort() + "/" + name;
            // 拼接 webrtcUrl
            // 先检查媒体表是否已有此记录
            Optional<ArdVtdu> existingMedia = ardVtduList.stream()
                    .filter(media -> media.getName().equals(name))  // 查找是否已有对应的媒体记录
                    .findFirst();
            if (existingMedia.isPresent()) {
                // 如果已有,更新现有记录
                ArdVtdu vtdu = existingMedia.get();
                // 更新前后内容相同则不进行更新
                if (!vtdu.getRtspSource().equals(rtspSourceUrl) ||
                        !vtdu.getWebrtcUrl().equals(webrtcUrl) ||
                        !vtdu.getRtspUrl().equals(rtspUrl) ||
                        !vtdu.getRtmpUrl().equals(rtmpUrl)) {
                    vtdu.setRtspUrl(rtspUrl);
                    vtdu.setRtmpUrl(rtmpUrl);
                    vtdu.setWebrtcUrl(webrtcUrl);
                    ardVtduService.updateArdVtdu(vtdu);
                    log.info("更新媒体记录:{}", vtdu.getName());
                }
            } else {
                // 如果没有,插入新的记录
                ArdVtdu vtdu = new ArdVtdu();
                vtdu.setName(name);
                vtdu.setRtspSource(rtspSourceUrl);
                vtdu.setCameraId(cameraId);
                vtdu.setRtspUrl(rtspUrl);
                vtdu.setRtmpUrl(rtmpUrl);
                vtdu.setWebrtcUrl(webrtcUrl);
                ardVtduService.insertArdVtdu(vtdu);  // 保存到媒体表
                log.info("插入新媒体记录:{}", vtdu.getName());
            }
        });
        // 3. 删除操作:删除媒体表中多余的记录
        List<String> invalidNames = ardVtduList.stream()
                .filter(vtdu -> !validNames.contains(vtdu.getName()))
                .filter(vtdu -> !"publisher".equalsIgnoreCase(vtdu.getRtspSource())) // 不是机场流
                .map(ArdVtdu::getName)
                .collect(Collectors.toList());
        if (!invalidNames.isEmpty()) {
            // 将 List<String> 转换为 String[]
            String[] namesArray = invalidNames.toArray(new String[0]);
            ardVtduService.deleteArdVtduByNames(namesArray); // 传递数组
            log.info("删除不再存在的媒体记录:{}", Arrays.toString(namesArray));
        }
    }
    // 根据通道信息获取rtsp源地址
    private String getRtspSource(ArdChannel channel) {
        String rtspSource = "";
        ArdCamera ardCamera = redisCache.getCacheObject(CacheConstants.CAMERA_LIST_KEY + channel.getDeviceId());
        if (ardCamera != null) {
            switch (ardCamera.getFactory()) {
                case "1":
                    rtspSource =
                            "rtsp://" + ardCamera.getUsername() + ":" + ardCamera.getPassword() + "@" + ardCamera.getIp() + ":" + ardCamera.getRtspPort() + "/h264/ch" + channel.getChanNo() + "/main/av_stream";
                    break;
                case "2":
                    rtspSource =
                            "rtsp://" + ardCamera.getUsername() + ":" + ardCamera.getPassword() + "@" + ardCamera.getIp() + ":" + ardCamera.getRtspPort() + "/cam/realmonitor?channel=" + channel.getChanNo() + "&subtype=0";
                    break;
            }
        }
        return rtspSource;
    }
    /**
     * 同步mediamtx
     */
    public void syncMediamtx() {
        log.debug("定时同步MediaMTX任务");
        List<String> nameList = ardMediaService.getNameList();
        List<ArdVtdu> ardVtduList = ardVtduService.selectArdVtduList(new ArdVtdu());
        ardVtduService.asyncMediaMTX(ardVtduList, nameList);
    }
}
ard-vtdu/src/main/java/com/ard/vtdu/service/impl/ArdVtduServiceImpl.java
@@ -38,14 +38,6 @@
        mediaService.clearAll();
    }
    @Scheduled(fixedDelay = 5000)
    public void syncMediamtx() {
        log.debug("定时同步MediaMTX任务");
        List<String> nameList = mediaService.getNameList();
        List<ArdVtdu> ardVtduList = selectArdVtduList(new ArdVtdu());
        asyncMediaMTX(ardVtduList, nameList);
    }
    /**
     * 查询流媒体管理
     *
ard-work/src/main/java/com/ard/work/service/impl/ArdDroneInfoServiceImpl.java
@@ -362,32 +362,5 @@
    }
    /**
     * 更新 localBoxInfo 的字段,如果有变化返回 true,否则返回 false
     */
    private boolean updateChangedAttributes(ArdDroneInfo localBoxInfo, BoxInfoReceiver remoteArdDrone) {
        boolean changed = false;
        if (!Objects.equals(localBoxInfo.getBoxName(), remoteArdDrone.getBoxName())) {
            localBoxInfo.setBoxName(remoteArdDrone.getBoxName());
            changed = true;
        }
        if (!Objects.equals(localBoxInfo.getBoxVersion(), remoteArdDrone.getBoxVersion())) {
            localBoxInfo.setBoxVersion(remoteArdDrone.getBoxVersion());
            changed = true;
        }
        if (!Objects.equals(localBoxInfo.getLastTaskId(), remoteArdDrone.getLastTaskId())) {
            localBoxInfo.setLastTaskId(remoteArdDrone.getLastTaskId());
            changed = true;
        }
        if (!Objects.equals(localBoxInfo.getOnLine(), remoteArdDrone.getOnLine())) {
            localBoxInfo.setOnLine(remoteArdDrone.getOnLine());
            changed = true;
        }
        // TODO: 更新其他属性时,如果变化也设置 changed = true
        return changed;
    }
}