liusuyi
2025-12-26 22f306496b583d3c25f2d3354441b0e29d6840cf
飞行历史同步视频从新接口获取
1 files modified
276 ■■■■■ changed files
ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceImpl.java 276 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceImpl.java
@@ -10,10 +10,12 @@
import com.ard.work.component.forest.TyClient;
import com.ard.work.domain.dto.BoxHistoryDTO;
import com.ard.work.domain.dto.BoxPhotosDTO;
import com.ard.work.domain.dto.BoxVideosDTO;
import com.ard.work.domain.entity.ArdFlightTask;
import com.ard.work.domain.entity.ArdMedia;
import com.ard.work.domain.receiver.BoxHistoryReceiver;
import com.ard.work.domain.receiver.BoxPhotosReceiver;
import com.ard.work.domain.receiver.BoxVideosReceiver;
import com.ard.work.domain.receiver.TyHttpCommonReceiver;
import com.ard.work.mapper.ArdFlightTaskMapper;
import com.ard.work.mapper.ArdMediaMapper;
@@ -80,7 +82,7 @@
    /**
     * 同步所有服务的飞行历史
     */
    @Scheduled(fixedDelay = 10000)
    @Scheduled(fixedDelay = 5000)
    public void syncAllFlightHistory() {
        List<String> serverIds = ardServerInfoService.getServerIdsList();
        for (String serverId : serverIds) {
@@ -158,10 +160,8 @@
            if (rows > 0) {
                // 删除旧的媒体记录,防止重复插入
                ardMediaMapper.deleteArdMediaByTaskId(flightTask.getTaskId());
                // 插入视频
                insertVideoToDatabase(flightTask, boxHistoryReceiver.getVideoPathList());
                insertVideoToDatabase(flightTask, serverId, boxHistoryReceiver.getBoxSn());
                // 插入图片
                insertPhotoToDatabase(flightTask, serverId, boxHistoryReceiver.getBoxSn());
            }
@@ -169,8 +169,8 @@
            // 如果存在,则更新记录
            log.debug("Updating task {}", boxHistoryReceiver.getTaskId());
            updateFlightTask(ardFlightTask, boxHistoryReceiver);
            // 增量更新
            updateVideoToDatabase(ardFlightTask, boxHistoryReceiver.getVideoPathList());
            // 增量更新视频
            updateVideoToDatabase(ardFlightTask, serverId, boxHistoryReceiver.getBoxSn());
            // 增量更新图片
            updatePhotoToDatabase(ardFlightTask, serverId, boxHistoryReceiver.getBoxSn());
@@ -214,171 +214,40 @@
        TyHttpCommonReceiver<List<BoxHistoryReceiver>> boxHistory = tyClient.getBoxHistory(serverId, boxHistoryDTO);
        return boxHistory.getContent();
    }
    //调用接口获取架次图片
    private List<BoxPhotosReceiver> getBoxPhotos(String serverId, BoxPhotosDTO boxPhotosDTO) {
        TyHttpCommonReceiver<List<BoxPhotosReceiver>> boxPhotos = tyClient.getBoxPhotos(serverId, boxPhotosDTO);
        return boxPhotos.getContent();
    }
    // 插入视频记录
    private void insertVideoToDatabase(ArdFlightTask flightTask, List<String> videoPathList) {
        if (videoPathList == null || videoPathList.isEmpty()) {
            return;
        }
        String boxSn = flightTask.getBoxSn();
        for (String videoPath : videoPathList) {
            //videoPath = "https://192.168.1.3:9001/record/DBM250974065008/8212fd8e89814d66b6c32f48d05f5101.mp4";
            ArdMedia media = ArdMedia.builder()
                    .id(IdUtils.simpleUUID())
                    .taskId(flightTask.getTaskId())
                    .mediaType(1)
                    .sourceUrl(videoPath)
                    .fileSize(getFileSizeByUrl(videoPath))
                    .build();
            // ---- 1. 视频上传(失败不抛异常,返回 null) ----
            CompletableFuture<String> videoFuture = CompletableFuture.supplyAsync(() -> {
                try (InputStream inputStream = openUrlInputStream(videoPath, 5000, 10000)) {
                    if (inputStream != null) {
                        return ossServiceContext.upload(
                                "record",
                                boxSn + "/" + media.getId() + ".mp4",
                                inputStream,
                                MimeTypeUtils.VIDEO_MP4
                        );
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }, threadPoolTaskExecutor);
            // ---- 2. 缩略图上传(失败不抛异常,返回 null) ----
            CompletableFuture<String> thumbFuture = CompletableFuture.supplyAsync(() -> {
                try {
                    try (ByteArrayInputStream thumbStream = generateThumbnail(videoPath, 1, 200)) {
                        if (thumbStream != null) {
                            return ossServiceContext.upload(
                                    "record",
                                    boxSn + "/" + media.getId() + ".jpg",
                                    thumbStream,
                                    MimeTypeUtils.IMAGE_JPEG
                            );
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }, threadPoolTaskExecutor);
            // 两个完成后写数据库(任意失败不阻断流程)
            videoFuture.thenCombine(thumbFuture, (vUrl, tUrl) -> {
                media.setMediaUrl(vUrl);
                media.setThumbnailUrl(tUrl);
                media.setCreateTime(new Date());
                return media;
            }).thenAcceptAsync(ardMediaMapper::insertArdMedia, threadPoolTaskExecutor);
        }
    // 调用接口获取架次视频
    private List<BoxVideosReceiver> getBoxVideos(String serverId, BoxVideosDTO boxVideosDTO) {
        TyHttpCommonReceiver<List<BoxVideosReceiver>> boxVideos = tyClient.getBoxVideos(serverId, boxVideosDTO);
        return boxVideos.getContent();
    }
    private void updateVideoToDatabase(ArdFlightTask flightTask, List<String> videoPathList) {
        if (videoPathList == null) {
            return;
        }
        // 需要插入新的视频
        List<ArdMedia> existingVideos = ardMediaMapper.selectArdMediaByTaskIdAndType(flightTask.getTaskId(), 1);
        Integer count = existingVideos.size();//获取本地库视频数量
        int size = videoPathList.size();//获取接口返回的视频数量
        if (count != null && size > count) {
            log.info("任务【{}】录像数量不一致,需要更新数据库", flightTask.getTaskId());
            // 提取现有的视频路径
            List<String> existingVideoPaths = existingVideos.stream()
                    .map(ArdMedia::getSourceUrl) // 获取每个视频的 URL
                    .collect(Collectors.toList());
            // 从接口返回的视频路径中找出新的视频
            List<String> newVideos = videoPathList.stream()
                    .filter(videoPath -> !existingVideoPaths.contains(videoPath)) // 过滤出新的视频
                    .collect(Collectors.toList());
            if (!newVideos.isEmpty()) {
                String boxSn = flightTask.getBoxSn();
                // 插入新的视频记录
                for (String videoPath : newVideos) {
                    ArdMedia ardMedia = ArdMedia.builder()
                            .id(IdUtils.simpleUUID())
                            .taskId(flightTask.getTaskId())
                            .mediaType(1) // 1表示视频
                            .fileSize(getFileSizeByUrl(videoPath))
                            .sourceUrl(videoPath)
                            .build();
                    // ---- 1. 视频上传(失败不抛异常,返回 null) ----
                    CompletableFuture<String> videoFuture = CompletableFuture.supplyAsync(() -> {
                        try (InputStream inputStream = openUrlInputStream(videoPath, 5000, 10000)) {
                            if (inputStream != null) {
                                return ossServiceContext.upload(
                                        "record",
                                        boxSn + "/" + ardMedia.getId() + ".mp4",
                                        inputStream,
                                        MimeTypeUtils.VIDEO_MP4
                                );
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        return null;
                    }, threadPoolTaskExecutor);
                    // ---- 2. 缩略图上传(失败不抛异常,返回 null) ----
                    CompletableFuture<String> thumbFuture = CompletableFuture.supplyAsync(() -> {
                        try {
                            try (ByteArrayInputStream thumbStream = generateThumbnail(videoPath, 1, 200)) {
                                if (thumbStream != null) {
                                    return ossServiceContext.upload(
                                            "record",
                                            boxSn + "/" + ardMedia.getId() + ".jpg",
                                            thumbStream,
                                            MimeTypeUtils.IMAGE_JPEG
                                    );
                                }
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        return null;
                    }, threadPoolTaskExecutor);
                    // 两个完成后写数据库(任意失败不阻断流程)
                    videoFuture.thenCombine(thumbFuture, (vUrl, tUrl) -> {
                        ardMedia.setMediaUrl(vUrl);
                        ardMedia.setThumbnailUrl(tUrl);
                        ardMedia.setCreateTime(new Date());
                        return ardMedia;
                    }).thenAcceptAsync(ardMediaMapper::insertArdMedia, threadPoolTaskExecutor);
                }
                // 更新任务的视频数量
                flightTask.setVideoCount(flightTask.getVideoCount() + newVideos.size());
                ardFlightTaskMapper.updateArdFlightTask(flightTask);
            }
        }
    }
    // 插入图片记录
    private void insertPhotoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn) {
        // 查询图片
        BoxPhotosDTO boxPhotosDTO = new BoxPhotosDTO();
        boxPhotosDTO.setTaskId(flightTask.getTaskId());
        boxPhotosDTO.setBoxSn(boxSn);
        boxPhotosDTO.setPageIndex(1);
        boxPhotosDTO.setPageSize(500);
        boxPhotosDTO.setPageSize(100);
        List<BoxPhotosReceiver> photoList = getBoxPhotos(serverId, boxPhotosDTO);
        if (photoList != null && !photoList.isEmpty()) {
            addPhotos(flightTask, photoList);
        }
    }
    private void insertVideoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn) {
        BoxVideosDTO boxVideosDTO = new BoxVideosDTO();
        boxVideosDTO.setTaskId(flightTask.getTaskId());
        boxVideosDTO.setBoxSn(boxSn);
        boxVideosDTO.setPageIndex(1);
        boxVideosDTO.setPageSize(100);
        List<BoxVideosReceiver> videoList = getBoxVideos(serverId, boxVideosDTO);
        if (videoList != null && !videoList.isEmpty()) {
            addVideos(flightTask, videoList);
        }
    }
    private void updatePhotoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn) {
        // 获取本地数据库中已存在的图片记录(类型为 0 表示图片)
@@ -417,7 +286,44 @@
            }
        }
    }
    private void updateVideoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn) {
        // 获取本地数据库中已存在的图片记录(类型为 0 表示图片)
        List<ArdMedia> existingVideos = ardMediaMapper.selectArdMediaByTaskIdAndType(flightTask.getTaskId(), 1);
        int count = existingVideos.size(); //获取本地库视频数量
        // 查询图片
        BoxVideosDTO boxVideosDTO = BoxVideosDTO.builder()
                .taskId(flightTask.getTaskId())
                .boxSn(boxSn)
                .pageIndex(1)
                .pageSize(500)
                .build();
        TyHttpCommonReceiver<List<BoxVideosReceiver>> boxVideos = tyClient.getBoxVideos(serverId, boxVideosDTO);
        List<BoxVideosReceiver> videoList = boxVideos.getContent();
        int size = videoList.size();
        if (size > count) {
            log.info("任务【{}】视频数量不一致,需要更新数据库", flightTask.getTaskId());
            // 需要插入新的视频
            // 提取现有的图片路径(根据 imgUrl 来判断是否重复)
            List<String> existingVideoUrls = existingVideos.stream()
                    .map(ArdMedia::getSourceUrl) // 提取 ArdMedia 中的图片 URL
                    .collect(Collectors.toList());
            // 筛选出 photoList 中没有出现在 existingPhotos 中的新图片
            List<BoxVideosReceiver> newVideos = videoList.stream()
                    .filter(photo -> !existingVideoUrls.contains(photo.getImgUrl())) // 过滤出新视频
                    .collect(Collectors.toList());
            if (!newVideos.isEmpty()) {
                // 插入新的视频记录
                addVideos(flightTask, newVideos);
                // 更新任务的视频数量
                flightTask.setVideoCount(flightTask.getPhotoCount() + newVideos.size());
                ardFlightTaskMapper.updateArdFlightTask(flightTask);
            }
        }
    }
    private void addPhotos(ArdFlightTask flightTask, List<BoxPhotosReceiver> photoList) {
        String boxSn = flightTask.getBoxSn();
        for (BoxPhotosReceiver photo : photoList) {
@@ -457,6 +363,63 @@
                    });
        }
    }
    private void addVideos(ArdFlightTask flightTask, List<BoxVideosReceiver> videoList) {
        String boxSn = flightTask.getBoxSn();
        for (BoxVideosReceiver video : videoList) {
            ArdMedia ardMedia = ArdMedia.builder()
                    .id(IdUtils.simpleUUID())
                    .taskId(flightTask.getTaskId())
                    .mediaType(1) // 1表示视频
                    .fileSize(getFileSizeByUrl(video.getImgUrl()))
                    .sourceUrl(video.getImgUrl())
                    .captureTime(DateUtils.parseDate(video.getStartTime()))
                    .build();
            // 异步上传文件到OSS,并在上传完成后插入数据库
            // ---- 1. 视频上传(失败不抛异常,返回 null) ----
            CompletableFuture<String> videoFuture = CompletableFuture.supplyAsync(() -> {
                try (InputStream inputStream = openUrlInputStream(ardMedia.getSourceUrl(), 5000, 10000)) {
                    if (inputStream != null) {
                        return ossServiceContext.upload(
                                "record",
                                boxSn + "/" + ardMedia.getId() + ".mp4",
                                inputStream,
                                MimeTypeUtils.VIDEO_MP4
                        );
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }, threadPoolTaskExecutor);
            // ---- 2. 缩略图上传(失败不抛异常,返回 null) ----
            CompletableFuture<String> thumbFuture = CompletableFuture.supplyAsync(() -> {
                try {
                    try (ByteArrayInputStream thumbStream = generateThumbnail(ardMedia.getSourceUrl(), 1, 200)) {
                        if (thumbStream != null) {
                            return ossServiceContext.upload(
                                    "record",
                                    boxSn + "/" + ardMedia.getId() + ".jpg",
                                    thumbStream,
                                    MimeTypeUtils.IMAGE_JPEG
                            );
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }, threadPoolTaskExecutor);
            // 两个完成后写数据库(任意失败不阻断流程)
            videoFuture.thenCombine(thumbFuture, (vUrl, tUrl) -> {
                ardMedia.setMediaUrl(vUrl);
                ardMedia.setThumbnailUrl(tUrl);
                ardMedia.setCreateTime(new Date());
                return ardMedia;
            }).thenAcceptAsync(ardMediaMapper::insertArdMedia, threadPoolTaskExecutor);
        }
    }
    // 获取文件大小
    private long getFileSizeByUrl(String fileUrl) {
@@ -470,12 +433,8 @@
            return -1;
        }
    }
    // 生成视频缩略图
    public ByteArrayInputStream generateThumbnail(
            String videoFile,
            int skipSeconds,
            int targetWidth) {
    public ByteArrayInputStream generateThumbnail(String videoFile, int skipSeconds, int targetWidth) {
        String ffmpegPath = "";
        if (osSelect.isWindows()) {
            ffmpegPath = "ffmpeg";
@@ -539,7 +498,6 @@
            throw new RuntimeException("FFmpeg 生成缩略图失败", e);
        }
    }
    /**
     * 将 URL 转成 InputStream
     *