liusuyi
2025-11-18 23b1e3ac371e71536252415f777188607c291cf2
增量媒体库
6 files modified
220 ■■■■■ changed files
ard-work/src/main/java/com/ard/work/domain/entity/ArdFlightTask.java 17 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/mapper/ArdFlightTaskMapper.java 3 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/mapper/ArdMediaMapper.java 8 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java 161 ●●●●● patch | view | raw | blame | history
ard-work/src/main/resources/mapper/ArdFlightTaskMapper.xml 19 ●●●●● patch | view | raw | blame | history
ard-work/src/main/resources/mapper/ArdMediaMapper.xml 12 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/domain/entity/ArdFlightTask.java
@@ -1,6 +1,7 @@
package com.ard.work.domain.entity;
import com.ard.common.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
@@ -16,8 +17,22 @@
    private Integer photoCount;         // 图片数量
    private Integer videoCount;         // 录像数量
    private String videoPath;           // 视频路径
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date startTime;             // 任务开始时间
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date endTime;               // 任务结束时间
    private Long duration;              // 持续时长(单位:秒)
    private String serverId;            // 服务器ID
    private List<ArdMedia> mediaList;   // 媒体文件列表
    // 计算持续时长,单位:秒
    public Long getDuration() {
        if (startTime != null && endTime != null) {
            // 计算时间差(毫秒)
            long durationMillis = endTime.getTime() - startTime.getTime();
            // 将毫秒转换为秒
            return durationMillis / 1000;
        }
        return null;
    }
}
ard-work/src/main/java/com/ard/work/mapper/ArdFlightTaskMapper.java
@@ -10,6 +10,9 @@
    // 插入飞行任务
    int insertArdFlightTask(ArdFlightTask task);
    // 插入飞行任务
    int updateArdFlightTask(ArdFlightTask task);
    // 根据任务ID获取飞行任务
    ArdFlightTask getArdFlightTaskById(@Param("taskId") Integer taskId);
ard-work/src/main/java/com/ard/work/mapper/ArdMediaMapper.java
@@ -2,6 +2,8 @@
import com.ard.work.domain.entity.ArdMedia;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@@ -14,7 +16,10 @@
     * @return 媒体数据集合
     */
    List<ArdMedia> selectArdMedia(ArdMedia ardMedia);  // 动态查询
    List<ArdMedia> selectArdMediaByTaskId(Integer taskId);  // 根据任务ID查询
    List<ArdMedia> selectArdMediaByTaskIdAndType(@Param("taskId") Integer taskId, @Param("mediaType") Integer mediaType);  //
    /**
     * 插入媒体数据记录
@@ -26,7 +31,10 @@
    void insertArdMediaList(List<ArdMedia> ardMediaList);  // 插入批量记录
    void updateArdMedia(ArdMedia ardMedia);  // 更新记录
    void deleteArdMediaById(Integer id);  // 根据ID删除记录
    void deleteArdMediaByIds(List<Integer> mediaIds);  // 批量删除记录
    void deleteArdMediaByTaskId(Integer taskId);  // 批量删除记录
}
ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java
@@ -1,10 +1,8 @@
package com.ard.work.service.impl;
import com.ard.common.utils.DateUtils;
import com.ard.common.utils.StringUtils;
import com.ard.common.utils.file.MimeTypeUtils;
import com.ard.common.utils.uuid.IdUtils;
import com.ard.oss.service.IOssService;
import com.ard.oss.service.impl.OssServiceContext;
import com.ard.work.component.forest.TyClient;
import com.ard.work.domain.dto.BoxHistoryDTO;
@@ -18,23 +16,22 @@
import com.ard.work.mapper.ArdMediaMapper;
import com.ard.work.service.IArdFlightTaskService;
import com.ard.work.service.IArdServerInfoService;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.net.ssl.*;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Slf4j
@Service
public class ArdFlightTaskServiceServiceImpl implements IArdFlightTaskService {
@@ -127,24 +124,30 @@
        flightTask.setCreateTime(DateUtils.getNowDate());
        // 检查是否已经存在相同任务ID的记录
        ArdFlightTask ardFlightTaskById = ardFlightTaskMapper.getArdFlightTaskById(flightTask.getTaskId());
        if (ardFlightTaskById != null) {
            return;
        }
        ArdFlightTask ardFlightTask = ardFlightTaskMapper.getArdFlightTaskById(flightTask.getTaskId());
        if (ardFlightTask == null) {
            // 如果不存在,则插入新记录
            log.info("Syncing task {}", boxHistoryReceiver.getTaskId());
            // 插入新记录
            int rows = ardFlightTaskMapper.insertArdFlightTask(flightTask);
            if (rows > 0) {
                // 删除旧的媒体记录,防止重复插入
                ardMediaMapper.deleteArdMediaByTaskId(flightTask.getTaskId());
        System.out.println("Syncing task " + boxHistoryReceiver.getTaskId());
                // 插入视频
                syncVideoToDatabase(flightTask, boxHistoryReceiver.getVideoPathList());
        // 插入新记录
        int rows = ardFlightTaskMapper.insertArdFlightTask(flightTask);
        if (rows > 0) {
            // 删除旧的媒体记录,防止重复插入
            ardMediaMapper.deleteArdMediaByTaskId(flightTask.getTaskId());
                // 插入图片
                syncPhotoToDatabase(flightTask, serverId, boxHistoryReceiver.getBoxSn());
            }
        } else {
            // 如果存在,则更新记录
            log.info("Updating task {}", boxHistoryReceiver.getTaskId());
            // 增量更新
            updateVideoToDatabase(ardFlightTask, boxHistoryReceiver.getVideoPathList());
            // 增量更新图片
            updatePhotoToDatabase(ardFlightTask, serverId, boxHistoryReceiver.getBoxSn());
            // 插入视频
            syncVideoToDatabase(flightTask, boxHistoryReceiver.getVideoPathList());
            // 插入图片
            syncPhotoToDatabase(flightTask, serverId, boxHistoryReceiver.getBoxSn());
        }
    }
@@ -170,6 +173,58 @@
        }
    }
    private void updateVideoToDatabase(ArdFlightTask flightTask, List<String> videoPathList) {
        if (videoPathList == null) {
            return;
        }
        Integer count = flightTask.getVideoCount();//获取本地库视频数量
        int size = videoPathList.size();//获取接口返回的视频数量
        if (count != null && size > count) {
            log.info("录像数量不一致,需要更新数据库");
            // 需要插入新的视频
            // 获取本地数据库中已存在的视频记录
            List<ArdMedia> existingVideos = ardMediaMapper.selectArdMediaByTaskIdAndType(flightTask.getTaskId(), 1);
            List<String> newVideos = getNewVideos(existingVideos, videoPathList);
            if (!newVideos.isEmpty()) {
                // 插入新的视频记录
                for (String videoPath : newVideos) {
                    ArdMedia ardMedia = new ArdMedia();
                    ardMedia.setId(IdUtils.simpleUUID());
                    ardMedia.setTaskId(flightTask.getTaskId());
                    ardMedia.setMediaType(1); // 1表示视频
                    ardMedia.setSourceUrl(videoPath);
                    // 异步上传文件到OSS,并在上传完成后插入数据库
                    CompletableFuture.supplyAsync(() -> uploadFileToOss(flightTask.getBoxSn(), videoPath, "record",
                                    MimeTypeUtils.VIDEO_MP4), threadPoolTaskExecutor)
                            .thenAccept(url -> {
                                ardMedia.setMediaUrl(url);  // 获取上传后的文件 URL
                                ardMedia.setCreateTime(new Date());
                                ardMediaMapper.insertArdMedia(ardMedia);  // 插入数据库
                            });
                }
                // 更新任务的视频数量
                flightTask.setVideoCount(flightTask.getVideoCount() + newVideos.size());
                ardFlightTaskMapper.updateArdFlightTask(flightTask);
            }
        }
    }
    // 获取需要插入的新视频路径
    private List<String> getNewVideos(List<ArdMedia> existingVideos, List<String> videoPathList) {
        // 提取现有的视频路径
        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());
        return newVideos;
    }
    // 插入图片记录
    private void syncPhotoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn) {
        // 查询图片
@@ -177,7 +232,7 @@
        boxPhotosDTO.setTaskId(flightTask.getTaskId());
        boxPhotosDTO.setBoxSn(boxSn);
        boxPhotosDTO.setPageIndex(1);
        boxPhotosDTO.setPageSize(1000);
        boxPhotosDTO.setPageSize(500);
        TyHttpCommonReceiver<List<BoxPhotosReceiver>> boxPhotos = tyClient.getBoxPhotos(serverId, boxPhotosDTO);
        List<BoxPhotosReceiver> photoList = boxPhotos.getContent();
@@ -203,6 +258,66 @@
        }
    }
    private void updatePhotoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn) {
        int count = flightTask.getPhotoCount(); //获取本地库图片数量
        // 查询图片
        BoxPhotosDTO boxPhotosDTO = new BoxPhotosDTO();
        boxPhotosDTO.setTaskId(flightTask.getTaskId());
        boxPhotosDTO.setBoxSn(boxSn);
        boxPhotosDTO.setPageIndex(1);
        boxPhotosDTO.setPageSize(500);
        TyHttpCommonReceiver<List<BoxPhotosReceiver>> boxPhotos = tyClient.getBoxPhotos(serverId, boxPhotosDTO);
        List<BoxPhotosReceiver> photoList = boxPhotos.getContent();
        int size = photoList.size();
        if (size > count) {
            log.info("图片数量不一致,需要更新数据库");
            // 获取本地数据库中已存在的图片记录(类型为 0 表示图片)
            List<ArdMedia> existingPhotos = ardMediaMapper.selectArdMediaByTaskIdAndType(flightTask.getTaskId(), 0);
            // 需要插入新的视频
            List<BoxPhotosReceiver> newPhotos = getNewPhotos(existingPhotos, photoList);
            if (!newPhotos.isEmpty()) {
                // 插入新的视频记录
                for (BoxPhotosReceiver photo : newPhotos) {
                    ArdMedia ardMedia = new ArdMedia();
                    ardMedia.setId(IdUtils.simpleUUID());
                    ardMedia.setTaskId(flightTask.getTaskId());
                    ardMedia.setMediaType(0); // 0表示图片
                    ardMedia.setFileSize(photo.getFileSize());
                    ardMedia.setFileMd5(photo.getFileMd5());
                    ardMedia.setSourceUrl(photo.getImgUrl());
                    // 异步上传文件到OSS,并在上传完成后插入数据库
                    CompletableFuture.supplyAsync(() -> uploadFileToOss(flightTask.getBoxSn(), photo.getImgUrl(), "pic",
                                    MimeTypeUtils.IMAGE_JPG), threadPoolTaskExecutor)
                            .thenAccept(url -> {
                                ardMedia.setMediaUrl(url);  // 获取上传后的文件 URL
                                ardMedia.setCreateTime(new Date());
                                ardMediaMapper.insertArdMedia(ardMedia);  // 插入数据库
                            });
                }
                // 更新任务的视频数量
                flightTask.setPhotoCount(flightTask.getPhotoCount() + newPhotos.size());
                ardFlightTaskMapper.updateArdFlightTask(flightTask);
            }
        }
    }
    // 获取需要插入的新图片
    private List<BoxPhotosReceiver> getNewPhotos(List<ArdMedia> existingPhotos, List<BoxPhotosReceiver> photoList) {
        // 提取现有的图片路径(根据 imgUrl 来判断是否重复)
        List<String> existingPhotoUrls = existingPhotos.stream()
                .map(ArdMedia::getSourceUrl) // 提取 ArdMedia 中的图片 URL
                .collect(Collectors.toList());
        // 筛选出 photoList 中没有出现在 existingPhotos 中的新图片
        List<BoxPhotosReceiver> newPhotos = photoList.stream()
                .filter(photo -> !existingPhotoUrls.contains(photo.getImgUrl())) // 过滤出新图片
                .collect(Collectors.toList());
        return newPhotos; // 返回新增的图片列表
    }
    // 通用的上传方法,支持视频和图片等文件上传
    public String uploadFileToOss(String boxSn, String fileUrl, String bucket, String contentType) {
        InputStream fileInputStream = null;
ard-work/src/main/resources/mapper/ArdFlightTaskMapper.xml
@@ -93,6 +93,25 @@
        </trim>
    </insert>
    <update id="updateArdFlightTask" parameterType="com.ard.work.domain.entity.ArdFlightTask">
        update ard_flight_task
        <set>
            <if test="taskId != null">task_id = #{taskId},</if>
            <if test="boxSn != null">box_sn = #{boxSn},</if>
            <if test="boxName != null">box_name = #{boxName},</if>
            <if test="taskName != null">task_name = #{taskName},</if>
            <if test="videoCount != null">video_count = #{videoCount},</if>
            <if test="photoCount != null">photo_count = #{photoCount},</if>
            <if test="videoPath != null">video_path = #{videoPath},</if>
            <if test="startTime != null">start_time = #{startTime},</if>
            <if test="endTime != null">end_time = #{endTime},</if>
            <if test="serverId != null">server_id = #{serverId},</if>
            <if test="updateBy != null">update_by = #{updateBy},</if>
            <if test="updateTime != null">update_time = #{updateTime},</if>
            <if test="remark != null">remark = #{remark},</if>
        </set>
        where task_id = #{taskId}
    </update>
    <!-- 根据任务ID获取飞行任务 -->
    <select id="getArdFlightTaskById" resultType="ArdFlightTask">
ard-work/src/main/resources/mapper/ArdMediaMapper.xml
@@ -34,6 +34,9 @@
            <!-- 动态查询条件:媒体URL -->
            <if test="mediaUrl != null">AND media_url = #{mediaUrl}</if>
            <!-- 动态查询条件:媒体URL -->
            <if test="sourceUrl != null">AND source_url = #{sourceUrl}</if>
            <!-- 动态查询条件:时间戳 -->
            <if test="createTime != null">AND create_time = #{createTime}</if>
        </where>
@@ -41,11 +44,16 @@
    <!-- 根据任务ID查询媒体 -->
    <select id="selectArdMediaByTaskId" resultMap="ArdMediaResult">
        SELECT id, task_id, media_type, file_size, file_md5, media_url, create_time
        SELECT id, task_id, media_type, file_size, file_md5, media_url,source_url, create_time
        FROM ard_media
        WHERE task_id = #{taskId}
    </select>
    <!-- 根据任务ID和媒体类型查询媒体 -->
    <select id="selectArdMediaByTaskIdAndType" resultMap="ArdMediaResult">
        SELECT id, task_id, media_type, file_size, file_md5, media_url,source_url, create_time
        FROM ard_media
        WHERE task_id = #{taskId} and media_type = #{mediaType}
    </select>
    <!-- 插入媒体记录 -->
    <insert id="insertArdMedia" parameterType="com.ard.work.domain.entity.ArdMedia">
        INSERT INTO ard_media