zhaowenyu
2025-11-18 09323e7895ae85319cd8afc51409be43cde90e3d
Merge remote-tracking branch 'origin/dev' into dev
10 files added
9 files modified
1 files renamed
800 ■■■■■ changed files
ard-admin/src/main/java/com/ard/web/controller/work/UavBaseController.java 26 ●●●●● patch | view | raw | blame | history
ard-common/src/main/java/com/ard/common/utils/file/MimeTypeUtils.java 27 ●●●●● patch | view | raw | blame | history
ard-oss/src/main/java/com/ard/oss/service/impl/OssServiceContext.java 16 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/domain/dto/BoxHistoryDTO.java 3 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/domain/entity/ArdFlightTask.java 23 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/domain/entity/ArdMedia.java 16 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/domain/receiver/BoxHistoryReceiver.java 7 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/mapper/ArdFlightTaskMapper.java 26 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/mapper/ArdMediaMapper.java 32 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/mapper/ArdServerInfoMapper.java 6 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/service/IArdFlightTaskService.java 19 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/service/IArdMediaService.java 20 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/service/IArdServerInfoService.java 5 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java 273 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/service/impl/ArdServerInfoServiceImpl.java 5 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/service/impl/GimbalSpeedServiceImpl.java 2 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/work/service/impl/IArdMediaServiceImpl.java 31 ●●●●● patch | view | raw | blame | history
ard-work/src/main/resources/mapper/ArdFlightTaskMapper.xml 117 ●●●●● patch | view | raw | blame | history
ard-work/src/main/resources/mapper/ArdMediaMapper.xml 140 ●●●●● patch | view | raw | blame | history
ard-work/src/main/resources/mapper/ArdServerInfoMapper.xml 6 ●●●●● patch | view | raw | blame | history
ard-admin/src/main/java/com/ard/web/controller/work/UavBaseController.java
@@ -6,10 +6,15 @@
import com.ard.work.domain.dto.BoxHistoryDTO;
import com.ard.work.domain.dto.BoxPhotosDTO;
import com.ard.work.domain.dto.BoxSettingsDTO;
import com.ard.work.domain.entity.ArdDroneInfo;
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.BoxInfoReceiver;
import com.ard.work.domain.receiver.BoxStreamUrlReceiver;
import com.ard.work.service.IArdFlightTaskService;
import com.ard.work.service.IArdMediaService;
import com.ard.work.service.ITyService;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
@@ -35,6 +40,10 @@
    @Resource
    private ITyService tyService;
    @Resource
    private IArdFlightTaskService ardFlightTaskService;
    @Resource
    private IArdMediaService ardMediaService;
    @ApiOperation("获取边缘服务器token信息")
    @ApiOperationSupport(order = 1)
@@ -61,6 +70,23 @@
        List<BoxHistoryReceiver> boxHistory = tyService.getBoxHistory(boxHistoryDTO);
        return AjaxResult.success(boxHistory);
    }
    @ApiOperation("获取飞行架次")
    @ApiOperationSupport(order = 4)
    @GetMapping("/getFlightTask")
    public TableDataInfo getFlightTask(ArdFlightTask ardFlightTask) {
        startPage();
        List<ArdFlightTask> list = ardFlightTaskService.selectArdFlightTask(ardFlightTask);
        return getDataTable(list);
    }
    @ApiOperation("获取媒体数据")
    @ApiOperationSupport(order = 4)
    @GetMapping("/getMediaList")
    public TableDataInfo getMediaList(ArdMedia ardMedia) {
        startPage();
        List<ArdMedia> list = ardMediaService.selectArdMediaList(ardMedia);
        return getDataTable(list);
    }
    @ApiOperation("获取任务照片")
    @ApiOperationSupport(order = 5)
ard-common/src/main/java/com/ard/common/utils/file/MimeTypeUtils.java
@@ -2,11 +2,12 @@
/**
 * 媒体类型工具类
 *
 *
 * @author ard
 */
public class MimeTypeUtils
{
public class MimeTypeUtils {
    public static final String VIDEO_MP4 = "video/mp4";
    public static final String IMAGE_PNG = "image/png";
    public static final String IMAGE_JPG = "image/jpg";
@@ -16,15 +17,15 @@
    public static final String IMAGE_BMP = "image/bmp";
    public static final String IMAGE_GIF = "image/gif";
    public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" };
    public static final String[] FLASH_EXTENSION = { "swf", "flv" };
    public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
    public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
            "asf", "rm", "rmvb" };
    public static final String[] FLASH_EXTENSION = {"swf", "flv"};
    public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" };
    public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
            "asf", "rm", "rmvb"};
    public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"};
    public static final String[] DEFAULT_ALLOWED_EXTENSION = {
            // 图片
@@ -43,10 +44,10 @@
            "glb"
    };
    public static String getExtension(String prefix)
    {
        switch (prefix)
        {
    public static String getExtension(String prefix) {
        switch (prefix) {
            case VIDEO_MP4:
                return "mp4";
            case IMAGE_PNG:
                return "png";
            case IMAGE_JPG:
ard-oss/src/main/java/com/ard/oss/service/impl/OssServiceContext.java
@@ -12,6 +12,7 @@
import io.minio.PutObjectArgs;
import lombok.Cleanup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
@@ -167,7 +168,22 @@
            throw new RuntimeException("Upload failed: " + e.getMessage(), e);
        }
    }
    @Async("threadPoolTaskExecutor")
    public String upload(String bucket, String objectKey, InputStream input, String contentType) {
        try {
            // 上传文件时指定 Content-Type
            this.ossService.putObject(bucket, objectKey, input, contentType);
            // ✅ 构建永久访问 URL
            String baseUrl = OssConfiguration.endpoint;
            if (!baseUrl.endsWith("/")) {
                baseUrl += "/";
            }
            return baseUrl + bucket + "/" + objectKey;
        } catch (Exception e) {
            throw new RuntimeException("Upload failed: " + e.getMessage(), e);
        }
    }
    // 获取文件 MIME 类型
    public String getMimeTypeByExtension(String fileName) {
        // 获取文件扩展名
ard-work/src/main/java/com/ard/work/domain/dto/BoxHistoryDTO.java
@@ -1,11 +1,14 @@
package com.ard.work.domain.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@Builder
@AllArgsConstructor
ard-work/src/main/java/com/ard/work/domain/entity/ArdFlightTask.java
New file
@@ -0,0 +1,23 @@
package com.ard.work.domain.entity;
import com.ard.common.core.domain.BaseEntity;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
public class ArdFlightTask extends BaseEntity {
    private Integer taskId;             // 任务ID
    private String boxSn;               // 飞行器序列号
    private String boxName;             // 飞行器名称
    private String taskName;            // 任务名称
    private Integer photoCount;         // 图片数量
    private Integer videoCount;         // 录像数量
    private String videoPath;           // 视频路径
    private Date startTime;             // 任务开始时间
    private Date endTime;               // 任务结束时间
    private String serverId;            // 服务器ID
    private List<ArdMedia> mediaList;   // 媒体文件列表
}
ard-work/src/main/java/com/ard/work/domain/entity/ArdMedia.java
New file
@@ -0,0 +1,16 @@
package com.ard.work.domain.entity;
import com.ard.common.core.domain.BaseEntity;
import lombok.Data;
@Data
public class ArdMedia extends BaseEntity {
    private String id;                 // 媒体ID
    private Integer taskId;            // 任务ID(外键关联飞行任务)
    private Integer mediaType;         // 媒体类型(0"photo" 或 1"video")
    private Long fileSize;             // 文件大小
    private String fileMd5;            // 文件MD5值
    private String mediaUrl;           // 地址
    private String sourceUrl;          // 源地址
}
ard-work/src/main/java/com/ard/work/domain/receiver/BoxHistoryReceiver.java
@@ -37,6 +37,13 @@
    @JsonProperty("endTime")
    private String endTime;
    private Integer flightDuration;
    private Integer flightMileage;
    private Float aveSpeed;
    private Float maxSpeed;
    @JsonProperty("videoPathList")
    private List<String> videoPathList;
ard-work/src/main/java/com/ard/work/mapper/ArdFlightTaskMapper.java
New file
@@ -0,0 +1,26 @@
package com.ard.work.mapper;
import com.ard.work.domain.entity.ArdFlightTask;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
public interface ArdFlightTaskMapper {
    // 插入飞行任务
    int insertArdFlightTask(ArdFlightTask task);
    // 根据任务ID获取飞行任务
    ArdFlightTask getArdFlightTaskById(@Param("taskId") Integer taskId);
    // 获取所有飞行任务
    List<ArdFlightTask> selectArdFlightTask(ArdFlightTask ardFlightTask);
    // 获取某个时间戳后的任务
    List<ArdFlightTask> getTasksAfter(@Param("lastSyncTime") String lastSyncTime);
    // 获取最大 startTime
    Date findMaxStartTimeByServerId(String serverId);
}
ard-work/src/main/java/com/ard/work/mapper/ArdMediaMapper.java
New file
@@ -0,0 +1,32 @@
package com.ard.work.mapper;
import com.ard.work.domain.entity.ArdMedia;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
public interface ArdMediaMapper {
    /**
     * 查询媒体数据列表
     *
     * @param ardMedia 媒体数据
     * @return 媒体数据集合
     */
    List<ArdMedia> selectArdMedia(ArdMedia ardMedia);  // 动态查询
    List<ArdMedia> selectArdMediaByTaskId(Integer taskId);  // 根据任务ID查询
    /**
     * 插入媒体数据记录
     *
     * @param ardMedia 媒体数据
     */
    void insertArdMedia(ArdMedia ardMedia);  // 插入单个记录
    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/mapper/ArdServerInfoMapper.java
@@ -70,4 +70,10 @@
     */
    public int updateServerStatus(@Param("id") String id, @Param("status") String status);
    /**
     * 获取服务器ids
     *
     * @return 结果
     */
    List<String> getServerIdsList();
}
ard-work/src/main/java/com/ard/work/service/IArdFlightTaskService.java
New file
@@ -0,0 +1,19 @@
package com.ard.work.service;
import com.ard.work.domain.entity.ArdFlightTask;
import java.util.List;
/**
 * Description:
 * Author: lsy
 * Date: 2025年11月18日12:31
 **/
public interface IArdFlightTaskService {
    /**
     * 查询飞行架次列表
     *
     * @param ardFlightTask 飞行架次
     * @return 无人机飞行架次合
     */
    public List<ArdFlightTask> selectArdFlightTask(ArdFlightTask ardFlightTask);
}
ard-work/src/main/java/com/ard/work/service/IArdMediaService.java
New file
@@ -0,0 +1,20 @@
package com.ard.work.service;
import com.ard.work.domain.entity.ArdMedia;
import java.util.List;
/**
 * Description:
 * Author: lsy
 * Date: 2025年11月18日13:15
 **/
public interface IArdMediaService {
    /**
     * 查询媒体数据列表
     *
     * @param ardMedia 媒体数据
     * @return 媒体数据集合
     */
    public List<ArdMedia> selectArdMediaList(ArdMedia ardMedia);
}
ard-work/src/main/java/com/ard/work/service/IArdServerInfoService.java
@@ -70,4 +70,9 @@
     */
    public int updateServerStatus(String id,String status);
    /**
     * 获取服务器ID列表
     */
    public List<String> getServerIdsList();
}
ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java
New file
@@ -0,0 +1,273 @@
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;
import com.ard.work.domain.dto.BoxPhotosDTO;
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.TyHttpCommonReceiver;
import com.ard.work.mapper.ArdFlightTaskMapper;
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 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.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
@Service
public class ArdFlightTaskServiceServiceImpl implements IArdFlightTaskService {
    @Resource
    private ArdFlightTaskMapper ardFlightTaskMapper;
    @Resource
    private ArdMediaMapper ardMediaMapper;
    @Resource
    private IArdServerInfoService ardServerInfoService;
    @Resource
    private TyClient tyClient;
    @Resource
    private OssServiceContext ossServiceContext;
    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;  // 使用线程池执行异步任务
    @Override
    public List<ArdFlightTask> selectArdFlightTask(ArdFlightTask ardFlightTask) {
        return ardFlightTaskMapper.selectArdFlightTask(ardFlightTask);
    }
    // 同步所有服务的飞行历史
    @Scheduled(fixedDelay = 10000)
    public void syncAllFlightHistory() {
        List<String> serverIds = ardServerInfoService.getServerIdsList();
        for (String serverId : serverIds) {
            // 获取上次同步的时间(即数据库中最大 startTime)
            Date lastSyncTime = getLastSyncTimeForServer(serverId);
            // 根据上次同步时间同步飞行历史
            syncFlightHistory(serverId, lastSyncTime);
        }
    }
    // 获取上次同步时间(获取最大 startTime)
    private Date getLastSyncTimeForServer(String serverId) {
        Date maxStartTime = ardFlightTaskMapper.findMaxStartTimeByServerId(serverId);
        return maxStartTime != null ? maxStartTime : new Date(0);  // 如果没有数据,返回 Unix 纪元时间作为起点
    }
    // 调用接口获取飞行历史数据
    // 根据上次同步时间同步飞行历史
    public void syncFlightHistory(String serverId, Date lastSyncTime) {
        // 创建查询条件 BoxHistoryDTO,并设置 startTime 作为过滤条件
        BoxHistoryDTO boxHistoryDTO = new BoxHistoryDTO();
        boxHistoryDTO.setStartTime(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, lastSyncTime));  // 传递 lastSyncTime 参数
        // 获取飞行历史
        List<BoxHistoryReceiver> historyList = getBoxHistory(serverId, boxHistoryDTO);
        // 如果获取到数据,处理并同步到本地数据库
        if (historyList != null && !historyList.isEmpty()) {
            for (BoxHistoryReceiver boxHistory : historyList) {
                syncToLocalDatabase(serverId, boxHistory);
            }
        }
    }
    // 调用接口获取飞行历史数据
    private List<BoxHistoryReceiver> getBoxHistory(String serverId, BoxHistoryDTO boxHistoryDTO) {
        TyHttpCommonReceiver<List<BoxHistoryReceiver>> boxHistory = tyClient.getBoxHistory(serverId, boxHistoryDTO);
        return boxHistory.getContent();
    }
    // 将数据同步到本地数据库
    private void syncToLocalDatabase(String serverId, BoxHistoryReceiver boxHistoryReceiver) {
        // 创建FlightTask实体对象并填充数据
        ArdFlightTask flightTask = new ArdFlightTask();
        flightTask.setTaskId(boxHistoryReceiver.getTaskId());
        flightTask.setBoxSn(boxHistoryReceiver.getBoxSn());
        flightTask.setBoxName(boxHistoryReceiver.getBoxName());
        flightTask.setTaskName(boxHistoryReceiver.getTaskName());
        Integer photoCount = Integer.parseInt(boxHistoryReceiver.getPhotoCount());
        flightTask.setPhotoCount(photoCount);
        Integer videoCount = (boxHistoryReceiver.getVideoPathList() != null) ?
                boxHistoryReceiver.getVideoPathList().size() : 0;
        flightTask.setVideoCount(videoCount);
        flightTask.setVideoPath(boxHistoryReceiver.getVideoPath());
        flightTask.setServerId(serverId);
        Date startTime = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, boxHistoryReceiver.getStartTime());
        flightTask.setStartTime(startTime);
        Date endTime = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, boxHistoryReceiver.getEndTime());
        flightTask.setEndTime(endTime);
        flightTask.setCreateTime(DateUtils.getNowDate());
        // 检查是否已经存在相同任务ID的记录
        ArdFlightTask ardFlightTaskById = ardFlightTaskMapper.getArdFlightTaskById(flightTask.getTaskId());
        if (ardFlightTaskById != null) {
            return;
        }
        System.out.println("Syncing task " + boxHistoryReceiver.getTaskId());
        // 插入新记录
        int rows = ardFlightTaskMapper.insertArdFlightTask(flightTask);
        if (rows > 0) {
            // 删除旧的媒体记录,防止重复插入
            ardMediaMapper.deleteArdMediaByTaskId(flightTask.getTaskId());
            // 插入视频
            syncVideoToDatabase(flightTask, boxHistoryReceiver.getVideoPathList());
            // 插入图片
            syncPhotoToDatabase(flightTask, serverId, boxHistoryReceiver.getBoxSn());
        }
    }
    // 插入视频记录
    private void syncVideoToDatabase(ArdFlightTask flightTask, List<String> videoPathList) {
        if (videoPathList != null && !videoPathList.isEmpty()) {
            for (String videoPath : videoPathList) {
                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);  // 插入数据库
                        });
            }
        }
    }
    // 插入图片记录
    private void syncPhotoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn) {
        // 查询图片
        BoxPhotosDTO boxPhotosDTO = new BoxPhotosDTO();
        boxPhotosDTO.setTaskId(flightTask.getTaskId());
        boxPhotosDTO.setBoxSn(boxSn);
        boxPhotosDTO.setPageIndex(1);
        boxPhotosDTO.setPageSize(1000);
        TyHttpCommonReceiver<List<BoxPhotosReceiver>> boxPhotos = tyClient.getBoxPhotos(serverId, boxPhotosDTO);
        List<BoxPhotosReceiver> photoList = boxPhotos.getContent();
        if (photoList != null && !photoList.isEmpty()) {
            for (BoxPhotosReceiver photo : photoList) {
                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);  // 插入数据库
                        });
            }
        }
    }
    // 通用的上传方法,支持视频和图片等文件上传
    public String uploadFileToOss(String boxSn, String fileUrl, String bucket, String contentType) {
        InputStream fileInputStream = null;
        String fileOssUrl = "";
        try {
            // 创建信任所有证书的SSLContext
            TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }
                        @Override
                        public void checkClientTrusted(
                                java.security.cert.X509Certificate[] certs, String authType) {
                        }
                        @Override
                        public void checkServerTrusted(
                                java.security.cert.X509Certificate[] certs, String authType) {
                        }
                    }
            };
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
            // 创建所有主机名都信任的HostnameVerifier
            HostnameVerifier allHostsValid = new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            };
            HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
            // 现在可以正常访问
            // 从URL创建InputStream
            URL url = new URL(fileUrl);
            fileInputStream = url.openStream();
            // 文件名
            String fileName = boxSn + "/" + IdUtils.fastSimpleUUID() + "." + MimeTypeUtils.getExtension(contentType);
            // 调用OSS服务上下文进行文件上传
            fileOssUrl = ossServiceContext.upload(bucket, fileName, fileInputStream, contentType);
        } catch (Exception e) {
            e.printStackTrace();
            // 异常处理,可以选择记录日志或者抛出业务异常
        } finally {
            // 关闭InputStream
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return fileOssUrl; // 返回上传后的文件OSS URL
    }
}
ard-work/src/main/java/com/ard/work/service/impl/ArdServerInfoServiceImpl.java
@@ -150,6 +150,11 @@
        return ardServerInfoMapper.updateServerStatus(id,status);
    }
    @Override
    public List<String> getServerIdsList() {
        return ardServerInfoMapper.getServerIdsList();
    }
    private String getEncryptStr(ArdServerInfo serverInfo, long timeStamp) {
        String sb = serverInfo.getAccessKeyId() + serverInfo.getAccessKeySecret() + timeStamp;
        return DigestUtils.md5DigestAsHex(sb.getBytes());
ard-work/src/main/java/com/ard/work/service/impl/GimbalSpeedServiceImpl.java
File was renamed from ard-work/src/main/java/com/ard/work/service/impl/GimbalSpeedService.java
@@ -25,7 +25,7 @@
 */
@Service
@Slf4j
public class GimbalSpeedService implements IGimbalSpeedService {
public class GimbalSpeedServiceImpl implements IGimbalSpeedService {
    private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    private final Map<String, ScheduledFuture<?>> tasks = new ConcurrentHashMap<>();
ard-work/src/main/java/com/ard/work/service/impl/IArdMediaServiceImpl.java
New file
@@ -0,0 +1,31 @@
package com.ard.work.service.impl;
import com.ard.work.domain.entity.ArdMedia;
import com.ard.work.mapper.ArdMediaMapper;
import com.ard.work.service.IArdMediaService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
 * Description:
 * Author: lsy
 * Date: 2025年11月18日13:17
 **/
@Service
public class IArdMediaServiceImpl implements IArdMediaService {
    @Resource
    private ArdMediaMapper ardMediaMapper;
    /**
     * 查询媒体数据列表
     *
     * @param ardMedia 媒体数据
     * @return 媒体数据集合
     */
    @Override
    public List<ArdMedia> selectArdMediaList(ArdMedia ardMedia) {
        return ardMediaMapper.selectArdMedia(ardMedia);
    }
}
ard-work/src/main/resources/mapper/ArdFlightTaskMapper.xml
New file
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ard.work.mapper.ArdFlightTaskMapper">
    <!-- 结果映射 -->
    <resultMap id="ArdFlightTaskResult" type="ArdFlightTask">
        <result property="taskId" column="task_id"/>
        <result property="boxSn" column="box_sn"/>
        <result property="boxName" column="box_name"/>
        <result property="taskName" column="task_name"/>
        <result property="videoCount" column="video_count"/>
        <result property="photoCount" column="photo_count"/>
        <result property="videoPath" column="video_path"/>
        <result property="startTime" column="start_time"/>
        <result property="endTime" column="end_time"/>
        <result property="serverId" column="server_id"/>
        <result property="createBy" column="create_by"/>
        <result property="createTime" column="create_time"/>
        <result property="updateBy" column="update_by"/>
        <result property="updateTime" column="update_time"/>
        <result property="remark" column="remark"/>
    </resultMap>
    <!-- 查询任务记录并联接图片和视频 -->
    <select id="selectArdFlightTask" resultMap="ArdFlightTaskResult">
        SELECT
        aft.task_id,
        aft.box_sn,
        aft.box_name,
        aft.task_name,
        aft.video_count,
        aft.photo_count,
        aft.video_path,
        aft.start_time,
        aft.end_time,
        aft.server_id,
        aft.create_by,
        aft.create_time,
        aft.update_by,
        aft.update_time,
        aft.remark
        FROM ard_flight_task aft
        <where>
            <trim prefix="" suffix="" suffixOverrides="AND,OR">
                <if test="taskId != null">AND aft.task_id = #{taskId}</if>
                <if test="boxSn != null">AND aft.box_sn = #{boxSn}</if>
                <if test="boxName != null">AND aft.box_name LIKE CONCAT('%', #{boxName}, '%')</if>
                <if test="taskName != null">AND aft.task_name LIKE CONCAT('%', #{taskName}, '%')</if>
                <if test="startTime != null">AND aft.start_time &gt;= #{startTime}</if>
                <if test="endTime != null">AND aft.end_time &lt;= #{endTime}</if>
                <if test="serverId != null">AND aft.server_id = #{serverId}</if>
            </trim>
        </where>
        ORDER BY aft.task_id DESC
    </select>
    <!-- 插入飞行任务 -->
    <insert id="insertArdFlightTask" parameterType="com.ard.work.domain.entity.ArdFlightTask">
        insert into ard_flight_task
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="taskId != null">task_id,</if>
            <if test="boxSn != null">box_sn,</if>
            <if test="boxName != null">box_name,</if>
            <if test="taskName != null">task_name,</if>
            <if test="videoCount != null">video_count,</if>
            <if test="photoCount != null">photo_count,</if>
            <if test="videoPath != null">video_path,</if>
            <if test="startTime != null">start_time,</if>
            <if test="endTime != null">end_time,</if>
            <if test="serverId != null">server_id,</if>
            <if test="createBy != null">create_by,</if>
            <if test="createTime != null">create_time,</if>
            <if test="updateBy != null">update_by,</if>
            <if test="updateTime != null">update_time,</if>
            <if test="remark != null">remark,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="taskId != null">#{taskId},</if>
            <if test="boxSn != null">#{boxSn},</if>
            <if test="boxName != null">#{boxName},</if>
            <if test="taskName != null">#{taskName},</if>
            <if test="videoCount != null">#{videoCount},</if>
            <if test="photoCount != null">#{photoCount},</if>
            <if test="videoPath != null">#{videoPath},</if>
            <if test="startTime != null">#{startTime},</if>
            <if test="endTime != null">#{endTime},</if>
            <if test="serverId != null">#{serverId},</if>
            <if test="createBy != null">#{createBy},</if>
            <if test="createTime != null">#{createTime},</if>
            <if test="updateBy != null">#{updateBy},</if>
            <if test="updateTime != null">#{updateTime},</if>
            <if test="remark != null">#{remark},</if>
        </trim>
    </insert>
    <!-- 根据任务ID获取飞行任务 -->
    <select id="getArdFlightTaskById" resultType="ArdFlightTask">
        SELECT *
        FROM ard_flight_task
        WHERE task_id = #{taskId}
    </select>
    <!-- 获取某个时间戳后的任务 -->
    <select id="getTasksAfter" resultType="ArdFlightTask">
        SELECT *
        FROM ard_flight_task
        WHERE end_time > #{lastSyncTime}
    </select>
    <select id="findMaxStartTimeByServerId" resultType="java.util.Date">
        SELECT MAX(start_time)
        FROM ard_flight_task
        WHERE server_id = #{serverId}
    </select>
</mapper>
ard-work/src/main/resources/mapper/ArdMediaMapper.xml
New file
@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ard.work.mapper.ArdMediaMapper">
    <!-- 结果映射 -->
    <resultMap id="ArdMediaResult" type="com.ard.work.domain.entity.ArdMedia">
        <result property="id" column="id"/>
        <result property="taskId" column="task_id"/>
        <result property="mediaType" column="media_type"/>
        <result property="fileSize" column="file_size"/>
        <result property="fileMd5" column="file_md5"/>
        <result property="mediaUrl" column="media_url"/>
        <result property="sourceUrl" column="source_url"/>
        <result property="createTime" column="create_time"/>
    </resultMap>
    <!-- 查询媒体列表:根据条件动态生成SQL -->
    <select id="selectArdMedia" resultMap="ArdMediaResult">
        SELECT id, task_id, media_type, file_size, file_md5, media_url, source_url,create_time
        FROM ard_media
        <where>
            <!-- 动态查询条件:任务ID -->
            <if test="taskId != null">AND task_id = #{taskId}</if>
            <!-- 动态查询条件:媒体类型 -->
            <if test="mediaType != null">AND media_type = #{mediaType}</if>
            <!-- 动态查询条件:文件大小 -->
            <if test="fileSize != null">AND file_size = #{fileSize}</if>
            <!-- 动态查询条件:文件MD5 -->
            <if test="fileMd5 != null">AND file_md5 = #{fileMd5}</if>
            <!-- 动态查询条件:媒体URL -->
            <if test="mediaUrl != null">AND media_url = #{mediaUrl}</if>
            <!-- 动态查询条件:时间戳 -->
            <if test="createTime != null">AND create_time = #{createTime}</if>
        </where>
    </select>
    <!-- 根据任务ID查询媒体 -->
    <select id="selectArdMediaByTaskId" resultMap="ArdMediaResult">
        SELECT id, task_id, media_type, file_size, file_md5, media_url, create_time
        FROM ard_media
        WHERE task_id = #{taskId}
    </select>
    <!-- 插入媒体记录 -->
    <insert id="insertArdMedia" parameterType="com.ard.work.domain.entity.ArdMedia">
        INSERT INTO ard_media
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="id != null">id,</if>
            <if test="taskId != null">task_id,</if>
            <if test="mediaType != null">media_type,</if>
            <if test="fileSize != null">file_size,</if>
            <if test="fileMd5 != null">file_md5,</if>
            <if test="mediaUrl != null">media_url,</if>
            <if test="sourceUrl != null">source_url,</if>
            <if test="createTime != null">create_time,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="id != null">#{id},</if>
            <if test="taskId != null">#{taskId},</if>
            <if test="mediaType != null">#{mediaType},</if>
            <if test="fileSize != null">#{fileSize},</if>
            <if test="fileMd5 != null">#{fileMd5},</if>
            <if test="mediaUrl != null">#{mediaUrl},</if>
            <if test="sourceUrl != null">#{sourceUrl},</if>
            <if test="createTime != null">#{createTime},</if>
        </trim>
    </insert>
    <!-- 批量插入媒体记录 -->
    <insert id="insertArdMediaList" parameterType="java.util.List">
        INSERT INTO ard_media
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="list != null and !list.isEmpty()">
                <foreach item="item" collection="list" separator=",">
                    <trim prefix="(" suffix=")" suffixOverrides=",">
                        <if test="item.taskId != null">task_id,</if>
                        <if test="item.mediaType != null">media_type,</if>
                        <if test="item.fileSize != null">file_size,</if>
                        <if test="item.fileMd5 != null">file_md5,</if>
                        <if test="item.mediaUrl != null">media_url,</if>
                        <if test="item.sourceUrl != null">source_url,</if>
                        <if test="item.createTime != null">create_time,</if>
                    </trim>
                </foreach>
            </if>
        </trim>
        <trim prefix="values " suffix=")" suffixOverrides=",">
            <foreach item="item" collection="list" separator=",">
                <trim prefix="(" suffix=")" suffixOverrides=",">
                    <if test="item.taskId != null">#{item.taskId},</if>
                    <if test="item.mediaType != null">#{item.mediaType},</if>
                    <if test="item.fileSize != null">#{item.fileSize},</if>
                    <if test="item.fileMd5 != null">#{item.fileMd5},</if>
                    <if test="item.mediaUrl != null">#{item.mediaUrl},</if>
                    <if test="item.sourceUrl != null">#{item.sourceUrl},</if>
                    <if test="item.createTime != null">#{item.createTime},</if>
                </trim>
            </foreach>
        </trim>
    </insert>
    <!-- 更新媒体记录 -->
    <update id="updateArdMedia" parameterType="com.ard.work.domain.entity.ArdMedia">
        UPDATE ard_media
        <set>
            <if test="mediaType != null">media_type = #{mediaType},</if>
            <if test="fileSize != null">file_size = #{fileSize},</if>
            <if test="fileMd5 != null">file_md5 = #{fileMd5},</if>
            <if test="mediaUrl != null">media_url = #{mediaUrl},</if>
            <if test="createTime != null">create_time = #{createTime},</if>
        </set>
        WHERE id = #{id}
    </update>
    <!-- 删除媒体记录 -->
    <delete id="deleteArdMediaById" parameterType="Integer">
        DELETE FROM ard_media WHERE id = #{id}
    </delete>
    <!-- 批量删除媒体记录 -->
    <delete id="deleteArdMediaByIds" parameterType="java.util.List">
        DELETE FROM ard_media WHERE id IN
        <foreach item="id" collection="list" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>
    <delete id="deleteArdMediaByTaskId" parameterType="java.lang.Integer">
        DELETE FROM ard_media WHERE task_id = #{taskId}
    </delete>
</mapper>
ard-work/src/main/resources/mapper/ArdServerInfoMapper.xml
@@ -148,4 +148,10 @@
            #{id}
        </foreach>
    </delete>
    <select id="getServerIdsList" resultType="String">
        SELECT id
        FROM ard_server
        where server_status = "1"
    </select>
</mapper>