From b8b5a3895e741d5348ecdd39ad2e5cbd443f8e89 Mon Sep 17 00:00:00 2001
From: zhaowenyu <35589726@qq.com>
Date: Wed, 01 Apr 2026 12:17:04 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev' into dev
---
ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceImpl.java | 431 ++++-------------------------------------------------
1 files changed, 39 insertions(+), 392 deletions(-)
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceImpl.java b/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceImpl.java
index 5d97e6a..35a510a 100644
--- a/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceImpl.java
+++ b/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceImpl.java
@@ -4,49 +4,23 @@
import com.ard.common.core.domain.entity.SysUser;
import com.ard.common.core.redis.RedisCache;
import com.ard.common.utils.DateUtils;
-import com.ard.common.utils.file.MimeTypeUtils;
-import com.ard.common.utils.uuid.IdUtils;
-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.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;
import com.ard.work.service.IArdFlightTaskService;
+import com.ard.work.service.IArdMediaService;
import com.ard.work.service.IArdServerInfoService;
import com.ard.work.service.control.UavControlService;
-import com.ard.work.util.osSelect;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
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.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLConnection;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
import java.util.Date;
import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.stream.Collectors;
@Slf4j
@Service
@@ -55,19 +29,16 @@
@Resource
private ArdFlightTaskMapper ardFlightTaskMapper;
@Resource
- private ArdMediaMapper ardMediaMapper;
- @Resource
private IArdServerInfoService ardServerInfoService;
@Resource
- private TyClient tyClient;
- @Resource
- private OssServiceContext ossServiceContext;
- @Resource
- private ThreadPoolTaskExecutor threadPoolTaskExecutor; // 使用线程池执行异步任务
- @Resource
private UavControlService uavControlService;
- @Autowired
+ @Resource
+ private IArdMediaService ardMediaService;
+ @Resource
private RedisCache redisCache;
+ @Resource
+ private TyClient tyClient;
+
/**
* 查询飞行架次列表
*
@@ -84,12 +55,16 @@
*/
@Scheduled(fixedDelay = 5000)
public void syncAllFlightHistory() {
- List<String> serverIds = ardServerInfoService.getServerIdsList();
- for (String serverId : serverIds) {
- // 获取上次同步的时间(即数据库中最大 startTime)
- Date lastSyncTime = getLastSyncTimeForServer(serverId);
- // 根据上次同步时间同步飞行历史
- syncFlightHistory(serverId, lastSyncTime);
+ try {
+ List<String> serverIds = ardServerInfoService.getServerIdsList();
+ for (String serverId : serverIds) {
+ // 获取上次同步的时间(即数据库中最大 startTime)
+ Date lastSyncTime = getLastSyncTimeForServer(serverId);
+ // 根据上次同步时间同步飞行历史
+ syncFlightHistory(serverId, lastSyncTime);
+ }
+ } catch (Exception e) {
+ log.error("同步飞行历史失败", e);
}
}
@@ -99,10 +74,18 @@
return maxStartTime != null ? maxStartTime : new Date(0); // 如果没有数据,返回 Unix 纪元时间作为起点
}
+ // 调用接口获取飞行历史数据
+ private List<BoxHistoryReceiver> getBoxHistory(String serverId, BoxHistoryDTO boxHistoryDTO) {
+ TyHttpCommonReceiver<List<BoxHistoryReceiver>> boxHistory = tyClient.getBoxHistory(serverId, boxHistoryDTO);
+ return boxHistory.getContent();
+ }
+
// 根据上次同步时间同步飞行历史
public void syncFlightHistory(String serverId, Date startTime) {
// 创建查询条件 BoxHistoryDTO,并设置 startTime 作为过滤条件
BoxHistoryDTO boxHistoryDTO = BoxHistoryDTO.builder()
+ .pageIndex(1)
+ .pageSize(20)
.startTime(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, startTime))
.build();
@@ -123,7 +106,7 @@
ArdFlightTask ardFlightTask = ardFlightTaskMapper.getArdFlightTaskById(boxHistoryReceiver.getTaskId());
if (ardFlightTask == null) {
// 如果不存在,则插入新记录
- log.debug("insert task {}", boxHistoryReceiver.getTaskId());
+ log.info("insert task {}", boxHistoryReceiver.getTaskId());
Date endTime = null;
if (boxHistoryReceiver.getEndTime() != null) {
endTime = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, boxHistoryReceiver.getEndTime());
@@ -149,7 +132,7 @@
Long flyUserId = uavControlService.getCurrentController(flightTask.getBoxSn(), "fly");
if (flyUserId != null) {
- SysUser flyUser = redisCache.getCacheObject(CacheConstants.SYS_USER_KEY + flyUserId);
+ SysUser flyUser = redisCache.getCacheMapValue(CacheConstants.SYS_USER, flyUserId.toString());
if (flyUser != null && flyUser.getNickName() != null) {
flightTask.setCreateBy(flyUser.getNickName());
}
@@ -158,24 +141,22 @@
// 插入新记录
int rows = ardFlightTaskMapper.insertArdFlightTask(flightTask);
if (rows > 0) {
- // 删除旧的媒体记录,防止重复插入
- ardMediaMapper.deleteArdMediaByTaskId(flightTask.getTaskId());
// 插入视频
- insertVideoToDatabase(flightTask, serverId, boxHistoryReceiver.getBoxSn());
+ ardMediaService.insertVideoToDatabase(flightTask, serverId, boxHistoryReceiver.getBoxSn());
// 插入图片
- insertPhotoToDatabase(flightTask, serverId, boxHistoryReceiver.getBoxSn());
+ ardMediaService.insertPhotoToDatabase(flightTask, serverId, boxHistoryReceiver.getBoxSn());
}
} else {
// 如果存在,则更新记录
- log.debug("Updating task {}", boxHistoryReceiver.getTaskId());
+ log.debug("sync task {}", boxHistoryReceiver.getTaskId());
updateFlightTask(ardFlightTask, boxHistoryReceiver);
- // 增量更新视频
- updateVideoToDatabase(ardFlightTask, serverId, boxHistoryReceiver.getBoxSn());
- // 增量更新图片
- updatePhotoToDatabase(ardFlightTask, serverId, boxHistoryReceiver.getBoxSn());
-
+ // 同步任务图片
+ ardMediaService.syncPicToDatabase(ardFlightTask, serverId, boxHistoryReceiver.getBoxSn());
+ // 同步任务视频
+ ardMediaService.syncVideoToDatabase(ardFlightTask, serverId, boxHistoryReceiver.getBoxSn());
}
}
+
private void updateFlightTask(ArdFlightTask local, BoxHistoryReceiver remote) {
boolean needUpdate = false;
@@ -185,6 +166,10 @@
Date newEnd = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, remote.getEndTime());
if (local.getEndTime() == null || !local.getEndTime().equals(newEnd)) {
local.setEndTime(newEnd);
+ local.setAveSpeed(remote.getAveSpeed());
+ local.setMaxSpeed(remote.getMaxSpeed());
+ local.setFlightDuration(remote.getFlightDuration());
+ local.setFlightMileage(remote.getFlightMileage());
needUpdate = true;
}
}
@@ -205,344 +190,6 @@
if (needUpdate) {
ardFlightTaskMapper.updateArdFlightTask(local);
- }
- }
-
-
- // 调用接口获取飞行历史数据
- private List<BoxHistoryReceiver> getBoxHistory(String serverId, BoxHistoryDTO boxHistoryDTO) {
- 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 List<BoxVideosReceiver> getBoxVideos(String serverId, BoxVideosDTO boxVideosDTO) {
- TyHttpCommonReceiver<List<BoxVideosReceiver>> boxVideos = tyClient.getBoxVideos(serverId, boxVideosDTO);
- return boxVideos.getContent();
- }
-
- 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(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 表示图片)
- List<ArdMedia> existingPhotos = ardMediaMapper.selectArdMediaByTaskIdAndType(flightTask.getTaskId(), 0);
- int count = existingPhotos.size(); //获取本地库图片数量
- // 查询图片
- BoxPhotosDTO boxPhotosDTO = BoxPhotosDTO.builder()
- .taskId(flightTask.getTaskId())
- .boxSn(boxSn)
- .pageIndex(1)
- .pageSize(500)
- .build();
- TyHttpCommonReceiver<List<BoxPhotosReceiver>> boxPhotos = tyClient.getBoxPhotos(serverId, boxPhotosDTO);
- List<BoxPhotosReceiver> photoList = boxPhotos.getContent();
- int size = photoList.size();
-
- if (size > count) {
- log.info("任务【{}】图片数量不一致,需要更新数据库", flightTask.getTaskId());
- // 需要插入新的视频
- // 提取现有的图片路径(根据 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());
- if (!newPhotos.isEmpty()) {
- // 插入新的视频记录
- addPhotos(flightTask, newPhotos);
-
- // 更新任务的视频数量
- flightTask.setPhotoCount(flightTask.getPhotoCount() + newPhotos.size());
- ardFlightTaskMapper.updateArdFlightTask(flightTask);
- }
- }
- }
- 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) {
- ArdMedia ardMedia = ArdMedia.builder()
- .id(IdUtils.simpleUUID())
- .taskId(flightTask.getTaskId())
- .mediaType(0) // 0表示图片
- .fileSize(photo.getFileSize())
- .fileMd5(photo.getFileMd5())
- .sourceUrl(photo.getImgUrl())
- .photoId(photo.getPhotoId())
- .longitude(photo.getLng())
- .latitude(photo.getLat())
- .height(photo.getHeight())
- .captureTime(DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, photo.getTimeStamp()))
- .build();
- // 异步上传文件到OSS,并在上传完成后插入数据库
- CompletableFuture.supplyAsync(() -> {
- try (InputStream inputStream = openUrlInputStream(photo.getImgUrl(), 5000, 10000)) {
- if (inputStream != null) {
- return ossServiceContext.upload(
- "pic",
- boxSn + "/" + ardMedia.getId() + ".jpg",
- inputStream,
- MimeTypeUtils.IMAGE_JPG
- );
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }, threadPoolTaskExecutor)
- .thenAccept(url -> {
- ardMedia.setMediaUrl(url);
- ardMedia.setCreateTime(new Date());
- ardMediaMapper.insertArdMedia(ardMedia);
- });
- }
- }
- 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) {
- try {
- URL url = new URL(fileUrl);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setRequestMethod("HEAD");
- conn.connect();
- return conn.getContentLengthLong(); // 返回字节大小
- } catch (Exception e) {
- return -1;
- }
- }
- // 生成视频缩略图
- public ByteArrayInputStream generateThumbnail(String videoFile, int skipSeconds, int targetWidth) {
- String ffmpegPath = "";
- if (osSelect.isWindows()) {
- ffmpegPath = "ffmpeg";
- } else if (osSelect.isLinux()) {
- ffmpegPath = "/usr/local/ffmpeg/bin/ffmpeg";
- }
- try {
- List<String> command = new ArrayList<>();
- command.add(ffmpegPath);
-
- command.add("-timeout");
- command.add("3000000");
- command.add("-rw_timeout");
- command.add("3000000");
-
- command.add("-ss");
- command.add(String.format("00:00:%02d", skipSeconds));
- command.add("-i");
- command.add(videoFile);
- command.add("-vframes");
- command.add("1");
-
- // 等比缩放
- command.add("-vf");
- command.add("scale=" + targetWidth + ":-1");
-
- // **确保输出 JPEG**
- command.add("-f");
- command.add("mjpeg"); // 关键点:输出格式固定为 JPEG
- command.add("-vcodec");
- command.add("mjpeg");
-
- // 输出到管道
- command.add("pipe:1");
-
- // ⭐ 打印最终命令(可复制)
- log.info("ffmpeg command: {}", String.join(" ", command));
-
- ProcessBuilder pb = new ProcessBuilder(command);
- pb.redirectErrorStream(false);
- Process process = pb.start();
-
- // 读取 FFmpeg 输出(JPEG 数据)到内存
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try (InputStream is = process.getInputStream()) {
- byte[] buffer = new byte[8192];
- int len;
- while ((len = is.read(buffer)) != -1) {
- baos.write(buffer, 0, len);
- }
- }
-
- int exitCode = process.waitFor();
- if (exitCode != 0) {
- throw new RuntimeException("FFmpeg 执行失败,退出码: " + exitCode);
- }
-
- return new ByteArrayInputStream(baos.toByteArray());
-
- } catch (IOException | InterruptedException e) {
- throw new RuntimeException("FFmpeg 生成缩略图失败", e);
- }
- }
- /**
- * 将 URL 转成 InputStream
- *
- * @param fileUrl 文件 URL
- * @param connectTimeoutMillis 连接超时(毫秒)
- * @param readTimeoutMillis 读取超时(毫秒)
- * @return InputStream,如果失败返回 null
- */
- public InputStream openUrlInputStream(String fileUrl, int connectTimeoutMillis, int readTimeoutMillis) {
- try {
- URL url = new URL(fileUrl);
-
- // 支持 HTTPS 信任所有证书(如果需要访问自签名证书)
- if ("https".equalsIgnoreCase(url.getProtocol())) {
- TrustManager[] trustAllCerts = new TrustManager[]{
- new X509TrustManager() {
- @Override
- public void checkClientTrusted(X509Certificate[] chain, String authType) {
- }
-
- @Override
- public void checkServerTrusted(X509Certificate[] chain, String authType) {
- }
-
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return null;
- }
- }
- };
- SSLContext sc = SSLContext.getInstance("SSL");
- sc.init(null, trustAllCerts, new java.security.SecureRandom());
- HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
- HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
- }
-
- URLConnection connection = url.openConnection();
- connection.setConnectTimeout(connectTimeoutMillis);
- connection.setReadTimeout(readTimeoutMillis);
-
- return connection.getInputStream();
-
- } catch (Exception e) {
- e.printStackTrace();
- return null;
}
}
--
Gitblit v1.9.3