From 4e8a1c02b45f72f6377603778e8c8746ed82f374 Mon Sep 17 00:00:00 2001
From: zhaowenyu <35589726@qq.com>
Date: Tue, 06 Jan 2026 10:04:33 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev' into dev

---
 ard-admin/src/main/java/com/ard/web/controller/data/ArdNoFlyZoneController.java           |    7 
 ard-system/src/main/resources/mapper/data/ArdModelMapper.xml                              |    5 
 ard-system/src/main/resources/mapper/data/ArdMapMapper.xml                                |    5 
 ard-admin/src/main/java/com/ard/web/controller/work/UavBaseController.java                |   30 
 ard-system/src/main/java/com/ard/data/model/mapper/ArdModelMapper.java                    |   10 
 ard-route/src/main/java/com/ard/route/util/RouteFileUtils.java                            |    6 
 ard-work/src/main/java/com/ard/work/mapper/ArdMediaPicMapper.java                         |   60 +
 ard-device/src/main/java/com/ard/device/camera/service/impl/ArdCameraServiceImpl.java     |   86 -
 ard-work/src/main/java/com/ard/work/service/impl/ArdServerInfoServiceImpl.java            |   24 
 ard-admin/src/main/java/com/ard/web/controller/work/ArdServerInfoController.java          |   12 
 ard-admin/src/main/java/com/ard/web/controller/data/ArdModelController.java               |    7 
 ard-admin/src/main/java/com/ard/web/controller/data/ArdTileController.java                |    7 
 ard-framework/src/main/java/com/ard/framework/config/ThreadPoolConfig.java                |   28 
 ard-system/src/main/java/com/ard/data/map/service/impl/ArdMapServiceImpl.java             |   21 
 ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskPartServiceImpl.java        |   14 
 ard-device/src/main/java/com/ard/device/camera/domain/ArdCamera.java                      |    2 
 ard-system/src/main/resources/mapper/data/ArdNoFlyZoneMapper.xml                          |    5 
 ard-work/src/main/java/com/ard/work/domain/entity/ArdMediaPic.java                        |   28 
 ard-system/src/main/java/com/ard/data/map/mapper/ArdMapMapper.java                        |   10 
 ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceImpl.java            |  473 --------
 ard-work/src/main/java/com/ard/work/mapper/ArdMediaVideoMapper.java                       |   59 +
 ard-sample/src/main/java/com/ard/sample/manage/service/impl/LiveStreamServiceImpl.java    |    1 
 ard-work/src/main/resources/mapper/ArdMediaPicMapper.xml                                  |  115 ++
 ard-admin/src/main/java/com/ard/web/controller/data/ArdMapController.java                 |    7 
 ard-work/src/main/java/com/ard/work/service/IArdMediaService.java                         |   20 
 ard-work/src/main/java/com/ard/work/service/IArdServerInfoService.java                    |    2 
 ard-device/src/main/java/com/ard/device/channel/service/impl/ArdChannelServiceImpl.java   |  107 --
 ard-system/src/main/java/com/ard/data/tile/service/IArdTileService.java                   |    9 
 ard-device/src/main/java/com/ard/device/component/sdk/hik/service/impl/HikVisionSDK.java  |   40 
 ard-device/src/main/java/com/ard/device/component/sdk/service/CameraSDK.java              |    6 
 ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java                       |    5 
 ard-admin/src/main/java/com/ard/web/controller/work/ArdFlightTaskController.java          |   78 +
 ard-system/src/main/java/com/ard/data/model/service/impl/ArdModelServiceImpl.java         |   21 
 ard-system/src/main/java/com/ard/data/model/service/IArdModelService.java                 |    9 
 ard-work/src/main/java/com/ard/work/domain/entity/ArdMediaVideo.java                      |   30 
 ard-system/src/main/java/com/ard/data/map/service/IArdMapService.java                     |    9 
 ard-work/src/main/java/com/ard/work/service/IArdDroneInfoService.java                     |    2 
 ard-system/src/main/java/com/ard/data/tile/mapper/ArdTileMapper.java                      |   10 
 ard-quartz/src/main/java/com/ard/quartz/task/MediaTask.java                               |  353 +++++++
 ard-system/src/main/java/com/ard/data/noflyzone/service/impl/ArdNoFlyZoneServiceImpl.java |   55 
 ard-quartz/pom.xml                                                                        |    8 
 ard-system/src/main/java/com/ard/data/noflyzone/mapper/ArdNoFlyZoneMapper.java            |    9 
 ard-device/src/main/java/com/ard/device/component/sdk/service/CameraSDKService.java       |   36 
 ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java           |   39 
 ard-system/src/main/java/com/ard/data/noflyzone/service/IArdNoFlyZoneService.java         |    8 
 ard-system/src/main/resources/mapper/data/ArdTileMapper.xml                               |    5 
 ard-work/src/main/java/com/ard/work/service/impl/ArdMediaServiceImpl.java                 |  275 +++++
 ard-quartz/src/main/java/com/ard/quartz/task/DeviceTask.java                              |  258 +++++
 /dev/null                                                                                 |  202 ----
 ard-work/src/main/resources/mapper/ArdFlightTaskMapper.xml                                |    2 
 ard-vtdu/src/main/java/com/ard/vtdu/service/impl/ArdVtduServiceImpl.java                  |    8 
 ard-work/src/main/java/com/ard/work/service/impl/ArdDroneInfoServiceImpl.java             |   78 
 ard-work/src/main/resources/mapper/ArdMediaVideoMapper.xml                                |  108 ++
 ard-device/src/main/java/com/ard/device/component/sdk/dh/service/impl/DaHuaSDK.java       |   39 
 ard-system/src/main/java/com/ard/data/tile/service/impl/ArdTileServiceImpl.java           |   21 
 ard-admin/src/main/java/com/ard/web/controller/work/ArdDroneInfoController.java           |    8 
 ard-device/src/main/java/com/ard/device/component/sdk/common/GlobalVariable.java          |    3 
 ard-work/src/main/java/com/ard/work/service/ITyService.java                               |    2 
 58 files changed, 1,788 insertions(+), 1,099 deletions(-)

diff --git a/ard-admin/src/main/java/com/ard/web/controller/data/ArdMapController.java b/ard-admin/src/main/java/com/ard/web/controller/data/ArdMapController.java
index 5a97336..537ac14 100644
--- a/ard-admin/src/main/java/com/ard/web/controller/data/ArdMapController.java
+++ b/ard-admin/src/main/java/com/ard/web/controller/data/ArdMapController.java
@@ -3,6 +3,7 @@
 import java.util.List;
 import javax.servlet.http.HttpServletResponse;
 
+import com.ard.common.constant.DeviceConstants;
 import com.ard.data.map.domain.ArdMap;
 import com.ard.data.map.service.IArdMapService;
 import io.swagger.annotations.Api;
@@ -81,6 +82,9 @@
     @Log(title = "地图管理", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody ArdMap ardMap) {
+        if (DeviceConstants.NOT_UNIQUE.equals(ardMapService.checkArdMapNameUnique(ardMap))) {
+            return error("新增地图'" + ardMap.getMapName() + "'失败,已存在");
+        }
         return toAjax(ardMapService.insertArdMap(ardMap));
     }
 
@@ -92,6 +96,9 @@
     @Log(title = "地图管理", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody ArdMap ardMap) {
+        if (DeviceConstants.NOT_UNIQUE.equals(ardMapService.checkArdMapNameUnique(ardMap))) {
+            return error("修改地图'" + ardMap.getMapName() + "'失败,已存在");
+        }
         return toAjax(ardMapService.updateArdMap(ardMap));
     }
 
diff --git a/ard-admin/src/main/java/com/ard/web/controller/data/ArdModelController.java b/ard-admin/src/main/java/com/ard/web/controller/data/ArdModelController.java
index d9e6612..3db3520 100644
--- a/ard-admin/src/main/java/com/ard/web/controller/data/ArdModelController.java
+++ b/ard-admin/src/main/java/com/ard/web/controller/data/ArdModelController.java
@@ -3,6 +3,7 @@
 import java.util.List;
 import javax.servlet.http.HttpServletResponse;
 
+import com.ard.common.constant.DeviceConstants;
 import com.ard.data.model.domain.ArdModel;
 import com.ard.data.model.service.IArdModelService;
 import io.swagger.annotations.Api;
@@ -81,6 +82,9 @@
     @Log(title = "模型管理", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody ArdModel ardModel) {
+        if (DeviceConstants.NOT_UNIQUE.equals(ardModelService.checkArdModelNameUnique(ardModel))) {
+            return error("新增模型'" + ardModel.getModelName() + "'失败,已存在");
+        }
         return toAjax(ardModelService.insertArdModel(ardModel));
     }
 
@@ -92,6 +96,9 @@
     @Log(title = "模型管理", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody ArdModel ardModel) {
+        if (DeviceConstants.NOT_UNIQUE.equals(ardModelService.checkArdModelNameUnique(ardModel))) {
+            return error("修改模型'" + ardModel.getModelName() + "'失败,已存在");
+        }
         return toAjax(ardModelService.updateArdModel(ardModel));
     }
 
diff --git a/ard-admin/src/main/java/com/ard/web/controller/data/ArdNoFlyZoneController.java b/ard-admin/src/main/java/com/ard/web/controller/data/ArdNoFlyZoneController.java
index b1aa9a4..8e8b4ae 100644
--- a/ard-admin/src/main/java/com/ard/web/controller/data/ArdNoFlyZoneController.java
+++ b/ard-admin/src/main/java/com/ard/web/controller/data/ArdNoFlyZoneController.java
@@ -1,6 +1,7 @@
 package com.ard.web.controller.data;
 
 import com.alibaba.fastjson2.JSON;
+import com.ard.common.constant.DeviceConstants;
 import com.ard.common.utils.uuid.IdUtils;
 import com.ard.data.noflyzone.domain.FeatureCollection;
 import io.swagger.annotations.Api;
@@ -82,6 +83,9 @@
     @PostMapping
     public AjaxResult add(@RequestBody ArdNoFlyZone ardNoFlyZone)
     {
+        if (DeviceConstants.NOT_UNIQUE.equals(ardNoFlyZoneService.checkArdNoFlyZoneNameUnique(ardNoFlyZone))) {
+            return error("新增禁飞区'" + ardNoFlyZone.getName() + "'失败,已存在");
+        }
         return toAjax(ardNoFlyZoneService.insertArdNoFlyZone(ardNoFlyZone));
     }
 
@@ -94,6 +98,9 @@
     @PutMapping
     public AjaxResult edit(@RequestBody ArdNoFlyZone ardNoFlyZone)
     {
+        if (DeviceConstants.NOT_UNIQUE.equals(ardNoFlyZoneService.checkArdNoFlyZoneNameUnique(ardNoFlyZone))) {
+            return error("修改禁飞区'" + ardNoFlyZone.getName() + "'失败,已存在");
+        }
         return toAjax(ardNoFlyZoneService.updateArdNoFlyZone(ardNoFlyZone));
     }
 
diff --git a/ard-admin/src/main/java/com/ard/web/controller/data/ArdTileController.java b/ard-admin/src/main/java/com/ard/web/controller/data/ArdTileController.java
index dc69330..4f57755 100644
--- a/ard-admin/src/main/java/com/ard/web/controller/data/ArdTileController.java
+++ b/ard-admin/src/main/java/com/ard/web/controller/data/ArdTileController.java
@@ -3,6 +3,7 @@
 import java.util.List;
 import javax.servlet.http.HttpServletResponse;
 
+import com.ard.common.constant.DeviceConstants;
 import com.ard.data.tile.domain.ArdTile;
 import com.ard.data.tile.service.IArdTileService;
 import io.swagger.annotations.Api;
@@ -81,6 +82,9 @@
     @Log(title = "tile管理", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody ArdTile ardTile) {
+        if (DeviceConstants.NOT_UNIQUE.equals(ardTileService.checkArdTileNameUnique(ardTile))) {
+            return error("新增tile'" + ardTile.getTilesName() + "'失败,已存在");
+        }
         return toAjax(ardTileService.insertArdTile(ardTile));
     }
 
@@ -92,6 +96,9 @@
     @Log(title = "tile管理", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody ArdTile ardTile) {
+        if (DeviceConstants.NOT_UNIQUE.equals(ardTileService.checkArdTileNameUnique(ardTile))) {
+            return error("修改tile'" + ardTile.getTilesName() + "'失败,已存在");
+        }
         return toAjax(ardTileService.updateArdTile(ardTile));
     }
 
diff --git a/ard-admin/src/main/java/com/ard/web/controller/work/ArdDroneInfoController.java b/ard-admin/src/main/java/com/ard/web/controller/work/ArdDroneInfoController.java
index 51e538a..b2bf7a3 100644
--- a/ard-admin/src/main/java/com/ard/web/controller/work/ArdDroneInfoController.java
+++ b/ard-admin/src/main/java/com/ard/web/controller/work/ArdDroneInfoController.java
@@ -112,10 +112,12 @@
     @ApiOperation("批量新增无人机盒子")
     @PreAuthorize("@ss.hasPermi('work:drone:insertArdDroneLs')")
     @Log(title = "无人机管理", businessType = BusinessType.DELETE)
-    @PostMapping("insertArdDroneLs/{boxSn}/{deptId}")
-    public AjaxResult insertArdDroneLs(@PathVariable String[] boxSn, @PathVariable Long deptId) {
+    @PostMapping("insertArdDroneLs/{boxSn}/{deptId}/{serverId}")
+    public AjaxResult insertArdDroneLs(@PathVariable String[] boxSn,
+                                       @PathVariable Long deptId,
+                                       @PathVariable String serverId) {
         try {
-            int result = ardDroneInfoService.insertArdDroneLs(boxSn, deptId);
+            int result = ardDroneInfoService.insertArdDroneLs(boxSn,deptId,serverId);
             if (result > 0) {
                 return AjaxResult.success("成功新增 " + result + " 个无人机盒子");
             } else {
diff --git a/ard-admin/src/main/java/com/ard/web/controller/work/ArdFlightTaskController.java b/ard-admin/src/main/java/com/ard/web/controller/work/ArdFlightTaskController.java
new file mode 100644
index 0000000..008e125
--- /dev/null
+++ b/ard-admin/src/main/java/com/ard/web/controller/work/ArdFlightTaskController.java
@@ -0,0 +1,78 @@
+package com.ard.web.controller.work;
+
+import com.ard.common.core.controller.BaseController;
+import com.ard.common.core.page.TableDataInfo;
+import com.ard.work.domain.dto.*;
+import com.ard.work.domain.entity.ArdFlightTask;
+import com.ard.work.domain.entity.ArdFlightTaskPart;
+import com.ard.work.domain.entity.ArdMediaPic;
+import com.ard.work.domain.entity.ArdMediaVideo;
+import com.ard.work.service.IArdFlightTaskPartService;
+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;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * 飞行架次管理接口
+ * ArdFlightController
+ *
+ * @author liusuyi
+ * @date 2024年10月21日13:52
+ **/
+@Api(tags = "飞行架次管理接口")
+@ApiSort(value = 2)
+@RequestMapping("/uav/base")
+@RestController
+public class ArdFlightTaskController extends BaseController {
+
+    @Resource
+    private IArdFlightTaskService ardFlightTaskService;
+    @Resource
+    private IArdFlightTaskPartService ardFlightTaskPartService;
+    @Resource
+    private IArdMediaService ardMediaService;
+
+
+    @ApiOperation("获取飞行架次")
+    @ApiOperationSupport(order = 1)
+    @GetMapping("/getFlightTask")
+    public TableDataInfo getFlightTask(ArdFlightTask ardFlightTask) {
+        startPage();
+        List<ArdFlightTask> list = ardFlightTaskService.selectArdFlightTask(ardFlightTask);
+        return getDataTable(list);
+    }
+
+    @ApiOperation("获取架次飞行轨迹")
+    @ApiOperationSupport(order = 2)
+    @GetMapping("/getCompleteFlightData")
+    public TableDataInfo getCompleteFlightData(ArdFlightTaskPart ardFlightTaskPart) {
+        List<UavFlightStateDTO> completeFlightData = ardFlightTaskPartService.getCompleteFlightData(ardFlightTaskPart);
+        return getDataTable(completeFlightData);
+    }
+
+    @ApiOperation("获取架次图片数据")
+    @ApiOperationSupport(order = 3)
+    @GetMapping("/getMediaPicList")
+    public TableDataInfo getMediaPicList(ArdMediaPic ardMedia) {
+        startPage();
+        List<ArdMediaPic> list = ardMediaService.selectArdMediaPicList(ardMedia);
+        return getDataTable(list);
+    }
+
+    @ApiOperation("获取架次录像数据")
+    @ApiOperationSupport(order = 4)
+    @GetMapping("/getMediaVideoList")
+    public TableDataInfo getMediaVideoList(ArdMediaVideo ardMedia) {
+        startPage();
+        List<ArdMediaVideo> list = ardMediaService.selectArdMediaVideoList(ardMedia);
+        return getDataTable(list);
+    }
+}
\ No newline at end of file
diff --git a/ard-admin/src/main/java/com/ard/web/controller/work/ArdServerInfoController.java b/ard-admin/src/main/java/com/ard/web/controller/work/ArdServerInfoController.java
index 7c14a8d..8a531ba 100644
--- a/ard-admin/src/main/java/com/ard/web/controller/work/ArdServerInfoController.java
+++ b/ard-admin/src/main/java/com/ard/web/controller/work/ArdServerInfoController.java
@@ -51,18 +51,6 @@
     }
 
     /**
-     * 查询服务器管理列表
-     */
-    @ApiOperation("查询服务器管理列表")
-    @PreAuthorize("@ss.hasPermi('work:server:listNo')")
-    @GetMapping("/listNo")
-    public TableDataInfo listNo(ArdServerInfo ardServerInfo) {
-        startPage();
-        List<ArdServerInfo> list = ardServerInfoService.selectArdServerInfoListNo(ardServerInfo);
-        return getDataTable(list);
-    }
-
-    /**
      * 导出服务器管理列表
      */
     @ApiOperation("导出服务器管理列表")
diff --git a/ard-admin/src/main/java/com/ard/web/controller/work/UavBaseController.java b/ard-admin/src/main/java/com/ard/web/controller/work/UavBaseController.java
index bc41a9c..0904ee9 100644
--- a/ard-admin/src/main/java/com/ard/web/controller/work/UavBaseController.java
+++ b/ard-admin/src/main/java/com/ard/web/controller/work/UavBaseController.java
@@ -5,9 +5,7 @@
 import com.ard.common.core.page.TableDataInfo;
 import com.ard.work.domain.dto.*;
 import com.ard.work.domain.dto.telemetry.TelemetryData;
-import com.ard.work.domain.entity.ArdFlightTask;
-import com.ard.work.domain.entity.ArdFlightTaskPart;
-import com.ard.work.domain.entity.ArdMedia;
+import com.ard.work.domain.entity.*;
 import com.ard.work.domain.receiver.*;
 import com.ard.work.service.IArdFlightTaskPartService;
 import com.ard.work.service.IArdFlightTaskService;
@@ -70,32 +68,6 @@
     public AjaxResult getBoxHistory(@RequestBody BoxHistoryDTO boxHistoryDTO) {
         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("/getCompleteFlightData")
-    public TableDataInfo getCompleteFlightData(ArdFlightTaskPart ardFlightTaskPart) {
-        List<UavFlightStateDTO> completeFlightData = ardFlightTaskPartService.getCompleteFlightData(ardFlightTaskPart);
-        return getDataTable(completeFlightData);
-    }
-
-    @ApiOperation("获取媒体数据")
-    @ApiOperationSupport(order = 4)
-    @GetMapping("/getMediaList")
-    public TableDataInfo getMediaList(ArdMedia ardMedia) {
-        startPage();
-        List<ArdMedia> list = ardMediaService.selectArdMediaList(ardMedia);
-        return getDataTable(list);
     }
 
     @ApiOperation("获取任务照片")
diff --git a/ard-device/src/main/java/com/ard/device/camera/domain/ArdCamera.java b/ard-device/src/main/java/com/ard/device/camera/domain/ArdCamera.java
index 135810d..b7c379c 100644
--- a/ard-device/src/main/java/com/ard/device/camera/domain/ArdCamera.java
+++ b/ard-device/src/main/java/com/ard/device/camera/domain/ArdCamera.java
@@ -4,6 +4,7 @@
 import com.ard.common.core.domain.BaseEntity;
 import com.ard.device.channel.domain.ArdChannel;
 import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import java.util.List;
@@ -17,6 +18,7 @@
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
+@Builder
 public class ArdCamera extends BaseEntity {
     private static final long serialVersionUID = 1L;
 
diff --git a/ard-device/src/main/java/com/ard/device/camera/service/impl/ArdCameraServiceImpl.java b/ard-device/src/main/java/com/ard/device/camera/service/impl/ArdCameraServiceImpl.java
index b8f40d8..e55b8cc 100644
--- a/ard-device/src/main/java/com/ard/device/camera/service/impl/ArdCameraServiceImpl.java
+++ b/ard-device/src/main/java/com/ard/device/camera/service/impl/ArdCameraServiceImpl.java
@@ -163,7 +163,7 @@
         ardCamera.setUpdateBy(SecurityUtils.getUsername());
         int res = ardCameraMapper.updateArdCamera(ardCamera);
         if (res > 0) {
-            cameraSDKService.logout(ardCamera.getId());//sdk注销
+            cameraSDKService.logout(ardCamera);//sdk注销
             cameraSDKService.login(ardCamera);//sdk登录获取通道信息
         }
         return res;
@@ -186,7 +186,7 @@
                 //删除当前相机的所有通道
                 ardChannelService.deleteArdChannelByDeviceId(id);
                 //sdk注销
-                cameraSDKService.logout(id);
+                cameraSDKService.logout(camera);
             }
         }
         return ardCameraMapper.deleteArdCameraByIds(ids);
@@ -207,7 +207,7 @@
             //删除当前相机的所有通道
             ardChannelService.deleteArdChannelByDeviceId(id);
             //sdk注销
-            cameraSDKService.logout(id);
+            cameraSDKService.logout(ArdCamera.builder().id(id).build());
         }
         return i;
     }
@@ -373,84 +373,4 @@
         redisService.setCacheObject(getCacheKey(camera.getId()), camera);
     }
 
-    /**
-     * 同步设备通道与本地通道库信息
-     *
-     * @author 刘苏义
-     * @date 2024/8/22 14:37
-     */
-    @Scheduled(cron = "0/10 * * * * ?")
-    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(cron = "0/30 * * * * ?")
-    public void failedReconnect() {
-        try {
-            log.debug("定时执行失败相机重连任务");
-            List<Object> ardCameras = redisService.getListKey(CacheConstants.CAMERA_LIST_KEY);
-            ardCameras.stream()
-                    .map(object -> (ArdCamera) object) // 将Object转换为ArdCameras
-                    .filter(camera -> !GlobalVariable.loginMap.containsKey(camera.getId()))
-                    .forEach(camera -> cameraSDKService.login(camera));
-        } catch (Exception ex) {
-            log.error("定时执行失败相机重连任务异常:{}", ex.getMessage());
-        }
-    }
-
-    /**
-     * 同步相机在线状态
-     * 刘苏义
-     * 2023/10/8 14:54:41
-     */
-    @Scheduled(cron = "0/10 * * * * ?")
-    public void cameraState() {
-        try {
-            log.debug("定时执行同步相机在线状态任务");
-            //调用查询方法
-            List<ArdCamera> ardCameraList = selectArdCameraList(new ArdCamera());
-            ardCameraList
-                    .forEach(camera -> {
-                        CameraCmd cmd = new CameraCmd();
-                        cmd.setCameraId(camera.getId());
-                        boolean onLineNew = cameraSDKService.isOnLine(cmd);
-                        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());
-        }
-    }
 }
\ No newline at end of file
diff --git a/ard-device/src/main/java/com/ard/device/channel/service/impl/ArdChannelServiceImpl.java b/ard-device/src/main/java/com/ard/device/channel/service/impl/ArdChannelServiceImpl.java
index 2c9f997..6c00d76 100644
--- a/ard-device/src/main/java/com/ard/device/channel/service/impl/ArdChannelServiceImpl.java
+++ b/ard-device/src/main/java/com/ard/device/channel/service/impl/ArdChannelServiceImpl.java
@@ -1,6 +1,8 @@
 package com.ard.device.channel.service.impl;
 
+import com.ard.common.constant.CacheConstants;
 import com.ard.common.constant.UserConstants;
+import com.ard.common.core.redis.RedisCache;
 import com.ard.common.utils.uuid.IdUtils;
 import com.ard.device.camera.domain.ArdCamera;
 import com.ard.device.camera.service.IArdCameraService;
@@ -11,6 +13,7 @@
 import com.ard.vtdu.domain.ArdVtdu;
 import com.ard.vtdu.service.IArdVtduService;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 
@@ -27,14 +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;
 
     /**
      * 查询通道管理
@@ -138,102 +136,5 @@
             return UserConstants.NOT_UNIQUE;
         }
         return UserConstants.UNIQUE;
-    }
-
-
-    @Scheduled(cron = "0/5 * * * * ?")
-    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 = ardCameraService.selectArdCameraById(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;
     }
 }
\ No newline at end of file
diff --git a/ard-device/src/main/java/com/ard/device/component/sdk/common/GlobalVariable.java b/ard-device/src/main/java/com/ard/device/component/sdk/common/GlobalVariable.java
index f76ae07..ec95a86 100644
--- a/ard-device/src/main/java/com/ard/device/component/sdk/common/GlobalVariable.java
+++ b/ard-device/src/main/java/com/ard/device/component/sdk/common/GlobalVariable.java
@@ -16,8 +16,7 @@
  **/
 @Data
 public class GlobalVariable {
-    //保存相机登录信息(cameraId:loginId)
-    public static Map<String, Object> loginMap = new ConcurrentHashMap<>();
+
     //保存相机登录信息(loginId:Camera)
     public static Map<Integer, ArdCamera> hikLoginMap = new ConcurrentHashMap<>();
 }
diff --git a/ard-device/src/main/java/com/ard/device/component/sdk/dh/service/impl/DaHuaSDK.java b/ard-device/src/main/java/com/ard/device/component/sdk/dh/service/impl/DaHuaSDK.java
index 54fa4d5..84468ce 100644
--- a/ard-device/src/main/java/com/ard/device/component/sdk/dh/service/impl/DaHuaSDK.java
+++ b/ard-device/src/main/java/com/ard/device/component/sdk/dh/service/impl/DaHuaSDK.java
@@ -17,6 +17,7 @@
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Service;
+
 import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
 import java.io.*;
@@ -102,7 +103,8 @@
     public void login(ArdCamera camera) {
         try {
             NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();
-            NetSDKLib.LLong loginId = LoginModule.login(camera.getIp(), camera.getPort(), camera.getUsername(), camera.getPassword(), m_stDeviceInfo);
+            NetSDKLib.LLong loginId = LoginModule.login(camera.getIp(), camera.getPort(), camera.getUsername(),
+                    camera.getPassword(), m_stDeviceInfo);
             if (loginId.longValue() <= 0) {
                 camera.setChanNum(0);
                 camera.setLoginId(-1L);
@@ -116,7 +118,7 @@
             camera.setChanNum(m_stDeviceInfo.byChanNum);
             camera.setStartChan(1);
             camera.setLoginId(loginId.longValue());
-            GlobalVariable.loginMap.put(camera.getId(), loginId);
+
             //获取最新通道
             List<ArdChannel> ardChannelList = getChannels(camera);
             if (!ardChannelList.isEmpty()) {
@@ -135,11 +137,8 @@
      * 2023/10/17 8:28:13
      */
     @Override
-    public void logout(String cameraId) {
-        if (!GlobalVariable.loginMap.containsKey(cameraId)) {
-            return;
-        }
-        NetSDKLib.LLong loginId = (NetSDKLib.LLong) GlobalVariable.loginMap.get(cameraId);
+    public void logout(ArdCamera ardCamera) {
+        NetSDKLib.LLong loginId = new NetSDKLib.LLong(ardCamera.getLoginId());
         LoginModule.logout(loginId);
     }
 
@@ -149,13 +148,9 @@
      * 2023/10/17 8:28:13
      */
     @Override
-    public boolean isOnLine(CameraCmd cmd) {
+    public boolean isOnLine(ArdCamera ardCamera) {
         try {
-            String cameraId = cmd.getCameraId();
-            if (!GlobalVariable.loginMap.containsKey(cameraId)) {
-                return false;
-            }
-            NetSDKLib.LLong loginId = (NetSDKLib.LLong) GlobalVariable.loginMap.get(cameraId);
+            NetSDKLib.LLong loginId = new NetSDKLib.LLong(ardCamera.getLoginId());
             NetSDKLib.NET_WORKSTATE dh_ptz_location_info = new NetSDKLib.NET_WORKSTATE();
             boolean b = ConfigModule.queryDevState(loginId, NET_DEVSTATE_ONLINE, dh_ptz_location_info);
             if (!b) {
@@ -185,8 +180,8 @@
             channel.setDeviceId(camera.getId());
             channel.setChanNo(i);
             //获取视频编码
-            CameraCmd cameraCmd = new CameraCmd(camera.getId(), channel.getChanNo());
-            VideoCompressionCfg videoCompressionCfg = getVideoCompressionCfg(cameraCmd);
+            camera.setChanNo(i);
+            VideoCompressionCfg videoCompressionCfg = getVideoCompressionCfg(camera);
             if (StringUtils.isNotNull(videoCompressionCfg)) {
                 channel.setVideoCode(videoCompressionCfg.getVideoEncType());
             }
@@ -202,16 +197,12 @@
     }
 
     @Override
-    public VideoCompressionCfg getVideoCompressionCfg(CameraCmd cmd) {
-        String cameraId = cmd.getCameraId();
-        Integer chanNo = cmd.getChanNo();
-        if (!GlobalVariable.loginMap.containsKey(cameraId)) {
-            return null;
-        }
-        NetSDKLib.LLong loginId = (NetSDKLib.LLong) GlobalVariable.loginMap.get(cameraId);
+    public VideoCompressionCfg getVideoCompressionCfg(ArdCamera ardCamera) {
+
+        NetSDKLib.LLong loginId = new NetSDKLib.LLong(ardCamera.getLoginId());
         NetSDKLib.CFG_ENCODE_INFO cfg_encode_info = new NetSDKLib.CFG_ENCODE_INFO();
-        cfg_encode_info.nChannelID = chanNo - 1;
-        boolean b = ConfigModule.GetNewDevConfig(loginId, chanNo - 1, CFG_CMD_ENCODE, cfg_encode_info);
+        cfg_encode_info.nChannelID = ardCamera.getChanNo() - 1;
+        boolean b = ConfigModule.GetNewDevConfig(loginId, ardCamera.getChanNo() - 1, CFG_CMD_ENCODE, cfg_encode_info);
         if (b) {
             int emCompression = cfg_encode_info.stuMainStream[0].stuVideoFormat.emCompression;//视频格式
             String videoEncType;
diff --git a/ard-device/src/main/java/com/ard/device/component/sdk/hik/service/impl/HikVisionSDK.java b/ard-device/src/main/java/com/ard/device/component/sdk/hik/service/impl/HikVisionSDK.java
index c146539..728245a 100644
--- a/ard-device/src/main/java/com/ard/device/component/sdk/hik/service/impl/HikVisionSDK.java
+++ b/ard-device/src/main/java/com/ard/device/component/sdk/hik/service/impl/HikVisionSDK.java
@@ -9,6 +9,7 @@
 import com.ard.device.component.sdk.common.ResolutionEnum;
 import com.ard.device.component.sdk.common.SdkErrorCodeEnum;
 import com.ard.device.component.sdk.common.osSelect;
+import com.ard.device.component.sdk.dh.lib.NetSDKLib;
 import com.ard.device.component.sdk.event.LoginEvent;
 import com.ard.device.component.sdk.hik.lib.ExceptionCallBack;
 import com.ard.device.component.sdk.hik.lib.HCNetSDK;
@@ -109,8 +110,6 @@
                 return;
             }
             log.info("Login Success 【 {}:{} 】", camera.getIp(), camera.getPort());
-
-            GlobalVariable.loginMap.put(camera.getId(), loginId);
             GlobalVariable.hikLoginMap.put(loginId, camera);
             camera.setLoginId((long) loginId);
             camera.setState("1");
@@ -138,26 +137,15 @@
      * 注销
      */
     @Override
-    public void logout(String cameraId) {
-        if (!GlobalVariable.loginMap.containsKey(cameraId)) {
-            return;
-        }
-        Integer userId = (Integer) GlobalVariable.loginMap.get(cameraId);
-        boolean b = hCNetSDK.NET_DVR_Logout(userId);
-        if (b) {
-            GlobalVariable.loginMap.remove(cameraId);
-        }
+    public void logout(ArdCamera ardCamera) {
+
+        hCNetSDK.NET_DVR_Logout(ardCamera.getLoginId().intValue());
     }
 
     @Override
-    public boolean isOnLine(CameraCmd cmd) {
+    public boolean isOnLine(ArdCamera ardCamera) {
         try {
-            String cameraId = cmd.getCameraId();
-            if (!GlobalVariable.loginMap.containsKey(cameraId)) {
-                return false;
-            }
-            Integer userId = (Integer) GlobalVariable.loginMap.get(cameraId);
-            return hCNetSDK.NET_DVR_RemoteControl(userId, NET_DVR_CHECK_USER_STATUS, null, 0);
+            return hCNetSDK.NET_DVR_RemoteControl(ardCamera.getLoginId().intValue(), NET_DVR_CHECK_USER_STATUS, null, 0);
         } catch (Exception ex) {
             log.error("检测在线异常:{}", ex.getMessage());
             return false;
@@ -169,19 +157,13 @@
      *
      */
     @Override
-    public VideoCompressionCfg getVideoCompressionCfg(CameraCmd cmd) {
-        String cameraId = cmd.getCameraId();
-        Integer chanNo = cmd.getChanNo();
-        if (!GlobalVariable.loginMap.containsKey(cameraId)) {
-            return null;
-        }
-        Integer userId = (Integer) GlobalVariable.loginMap.get(cameraId);
-
+    public VideoCompressionCfg getVideoCompressionCfg(ArdCamera ardCamera) {
+        Integer chanNo = ardCamera.getChanNo();
+        int userId = ardCamera.getLoginId().intValue();
         HCNetSDK.NET_DVR_COMPRESSIONCFG_V30 compressionCfg = new HCNetSDK.NET_DVR_COMPRESSIONCFG_V30();
         compressionCfg.write();
         Pointer point = compressionCfg.getPointer();
         IntByReference ibrBytesReturned = new IntByReference(0);
-
         boolean bool = hCNetSDK.NET_DVR_GetDVRConfig(userId, HCNetSDK.NET_DVR_GET_COMPRESSCFG_V30, chanNo, point,
                 compressionCfg.size(), ibrBytesReturned);
         if (bool) {
@@ -432,8 +414,8 @@
                     channel.setName(channelName);
                     channel.setChanNo(chanNo);
                     //获取视频编码
-                    CameraCmd cameraCmd = new CameraCmd(camera.getId(), chanNo);
-                    VideoCompressionCfg videoCompressionCfg = getVideoCompressionCfg(cameraCmd);
+                    camera.setChanNo(chanNo);
+                    VideoCompressionCfg videoCompressionCfg = getVideoCompressionCfg(camera);
                     if (StringUtils.isNotNull(videoCompressionCfg)) {
                         channel.setVideoCode(videoCompressionCfg.getVideoEncType());
                     }
diff --git a/ard-device/src/main/java/com/ard/device/component/sdk/service/CameraSDK.java b/ard-device/src/main/java/com/ard/device/component/sdk/service/CameraSDK.java
index 5195736..91b2e37 100644
--- a/ard-device/src/main/java/com/ard/device/component/sdk/service/CameraSDK.java
+++ b/ard-device/src/main/java/com/ard/device/component/sdk/service/CameraSDK.java
@@ -20,13 +20,13 @@
     void login(ArdCamera cameras);
 
     //注销
-    void logout(String cameraId);
+    void logout(ArdCamera ardCamera);
 
     //在线状态
-    boolean isOnLine(CameraCmd cmd);
+    boolean isOnLine(ArdCamera ardCamera);
 
     //获取码流压缩参数
-    VideoCompressionCfg getVideoCompressionCfg(CameraCmd cmd);
+    VideoCompressionCfg getVideoCompressionCfg(ArdCamera ardCamera);
 
     //获取相机通道信息
     List<ArdChannel> getChannels(ArdCamera camera);
diff --git a/ard-device/src/main/java/com/ard/device/component/sdk/service/CameraSDKService.java b/ard-device/src/main/java/com/ard/device/component/sdk/service/CameraSDKService.java
index 4ca608e..5de1b57 100644
--- a/ard-device/src/main/java/com/ard/device/component/sdk/service/CameraSDKService.java
+++ b/ard-device/src/main/java/com/ard/device/component/sdk/service/CameraSDKService.java
@@ -55,15 +55,12 @@
 
     //注销
     @Override
-    public void logout(String cameraId) {
+    public void logout(ArdCamera ardCamera) {
         try {
-            ArdCamera ardCamera = redisService.getCacheObject(CacheConstants.CAMERA_LIST_KEY + cameraId);
-            if (ardCamera != null) {
-                String factory = ardCamera.getFactory();
-                CameraSDK cameraSDK = cameraSDKFactory.createCameraSDK(factory);
-                cameraSDK.logout(cameraId);
-                GlobalVariable.loginMap.remove(cameraId);//移除相机登录id缓存
-            }
+
+            String factory = ardCamera.getFactory();
+            CameraSDK cameraSDK = cameraSDKFactory.createCameraSDK(factory);
+            cameraSDK.logout(ardCamera);
         } catch (Exception ex) {
             log.error("注销异常:{}", ex.getMessage());
         }
@@ -71,17 +68,11 @@
 
     //在线检测
     @Override
-    public boolean isOnLine(CameraCmd cmd) {
+    public boolean isOnLine(ArdCamera ardCamera) {
         try {
-            boolean onLine = false;
-            String cameraId = cmd.getCameraId();
-            ArdCamera ardCamera = redisService.getCacheObject(CacheConstants.CAMERA_LIST_KEY + cameraId);
-            if (ardCamera != null) {
-                String factory = ardCamera.getFactory();
-                CameraSDK cameraSDK = cameraSDKFactory.createCameraSDK(factory);
-                onLine = cameraSDK.isOnLine(cmd);
-            }
-            return onLine;
+            String factory = ardCamera.getFactory();
+            CameraSDK cameraSDK = cameraSDKFactory.createCameraSDK(factory);
+            return cameraSDK.isOnLine(ardCamera);
         } catch (Exception ex) {
             log.error("检测在线异常:{}", ex.getMessage());
             return false;
@@ -90,16 +81,11 @@
 
     @Override
     //获取码流压缩参数
-    public VideoCompressionCfg getVideoCompressionCfg(CameraCmd cmd) {
+    public VideoCompressionCfg getVideoCompressionCfg(ArdCamera ardCamera) {
         try {
-            String cameraId = cmd.getCameraId();
-            ArdCamera ardCamera = redisService.getCacheObject(CacheConstants.CAMERA_LIST_KEY + cameraId);
-            if (ardCamera == null) {
-                return null;
-            }
             String factory = ardCamera.getFactory();
             CameraSDK cameraSDK = cameraSDKFactory.createCameraSDK(factory);
-            return cameraSDK.getVideoCompressionCfg(cmd);
+            return cameraSDK.getVideoCompressionCfg(ardCamera);
         } catch (Exception ex) {
             log.error("获取码流压缩参数异常:{}", ex.getMessage());
             return null;
diff --git a/ard-framework/src/main/java/com/ard/framework/config/ThreadPoolConfig.java b/ard-framework/src/main/java/com/ard/framework/config/ThreadPoolConfig.java
index 44329d0..76c3a93 100644
--- a/ard-framework/src/main/java/com/ard/framework/config/ThreadPoolConfig.java
+++ b/ard-framework/src/main/java/com/ard/framework/config/ThreadPoolConfig.java
@@ -62,4 +62,32 @@
             }
         };
     }
+    @Bean("mediaVideoExecutor")
+    public ThreadPoolTaskExecutor mediaVideoExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(10);    // 核心线程数:同时处理视频的核心线程数
+        executor.setMaxPoolSize(10);     // 最大线程数:线程池不会扩展超过这个数量
+        executor.setQueueCapacity(50);   // 队列容量:等待执行的任务数量,之前20太小可能会拒绝任务
+        executor.setThreadNamePrefix("media-video-");
+
+        // 当线程池和队列都满时的拒绝策略
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+
+        executor.initialize();
+        return executor;
+    }
+    @Bean("mediaPicExecutor")
+    public ThreadPoolTaskExecutor mediaPicExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(10);    // 核心线程数:同时处理视频的核心线程数
+        executor.setMaxPoolSize(10);     // 最大线程数:线程池不会扩展超过这个数量
+        executor.setQueueCapacity(50);   // 队列容量:等待执行的任务数量,之前20太小可能会拒绝任务
+        executor.setThreadNamePrefix("media-pic-");
+
+        // 当线程池和队列都满时的拒绝策略
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+
+        executor.initialize();
+        return executor;
+    }
 }
diff --git a/ard-quartz/pom.xml b/ard-quartz/pom.xml
index cb73cbd..fbff55e 100644
--- a/ard-quartz/pom.xml
+++ b/ard-quartz/pom.xml
@@ -34,6 +34,14 @@
             <groupId>com.ard</groupId>
             <artifactId>ard-common</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.ard</groupId>
+            <artifactId>ard-work</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ard</groupId>
+            <artifactId>ard-device</artifactId>
+        </dependency>
 
     </dependencies>
 
diff --git a/ard-quartz/src/main/java/com/ard/quartz/task/DeviceTask.java b/ard-quartz/src/main/java/com/ard/quartz/task/DeviceTask.java
new file mode 100644
index 0000000..b51d24a
--- /dev/null
+++ b/ard-quartz/src/main/java/com/ard/quartz/task/DeviceTask.java
@@ -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);
+    }
+}
diff --git a/ard-quartz/src/main/java/com/ard/quartz/task/MediaTask.java b/ard-quartz/src/main/java/com/ard/quartz/task/MediaTask.java
new file mode 100644
index 0000000..1e5258a
--- /dev/null
+++ b/ard-quartz/src/main/java/com/ard/quartz/task/MediaTask.java
@@ -0,0 +1,353 @@
+package com.ard.quartz.task;
+
+import com.ard.common.utils.StringUtils;
+import com.ard.common.utils.file.MimeTypeUtils;
+import com.ard.oss.service.impl.OssServiceContext;
+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.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * 定时同步媒体任务
+ *
+ * @author ard
+ */
+@Component("MediaTask")
+@Slf4j
+public class MediaTask {
+    @Resource
+    private ArdMediaPicMapper ardMediaPicMapper;
+    @Resource
+    private ArdMediaVideoMapper ardMediaVideoMapper;
+    @Resource
+    private OssServiceContext ossServiceContext;
+    @Resource(name = "mediaVideoExecutor")
+    private ThreadPoolTaskExecutor mediaVideoExecutor;
+    @Resource(name = "mediaPicExecutor")
+    private ThreadPoolTaskExecutor mediaPicExecutor;
+
+    private final AtomicBoolean runningVideo = new AtomicBoolean(false);
+    private final AtomicBoolean runningPic = new AtomicBoolean(false);
+
+    public void syncVideoMediaUrl() {
+        log.warn("开始同步视频媒体");
+        if (!runningVideo.compareAndSet(false, true)) {
+            log.warn("上一次同步还没完成,跳过本次调度");
+            return;
+        }
+        try {
+            while (true) {
+                List<ArdMediaVideo> videos = ardMediaVideoMapper.selectPendingVideos(10);
+                if (videos.isEmpty()) {
+                    break; // 没有待处理视频,退出循环
+                }
+                for (ArdMediaVideo video : videos) {
+                    int updated = ardMediaVideoMapper.markProcessing(video.getId());
+                    if (updated != 1) continue;
+                    mediaVideoExecutor.execute(() -> processOneVideo(video));
+                }
+                Thread.sleep(200);
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        } finally {
+            runningVideo.set(false);
+        }
+    }
+
+    public void syncPicMediaUrl() {
+        log.warn("开始同步图片媒体");
+        if (!runningPic.compareAndSet(false, true)) {
+            log.warn("上一次同步还没完成,跳过本次调度");
+            return;
+        }
+        try {
+            while (true) {
+                List<ArdMediaPic> pics = ardMediaPicMapper.selectPendingPics(10); // 查询待处理的图片
+                if (pics.isEmpty()) {
+                    break; // 没有待处理图片,退出循环
+                }
+                for (ArdMediaPic pic : pics) {
+                    // 🔒 原子抢占:0 → 1
+                    int updated = ardMediaPicMapper.markProcessing(pic.getId());
+                    if (updated != 1) {
+                        continue; // 已被别的任务抢占
+                    }
+                    mediaPicExecutor.execute(() -> processOnePic(pic));
+                }
+                // 短暂 sleep,避免无限循环CPU空转
+                Thread.sleep(200);
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        } finally {
+            runningPic.set(false);
+        }
+    }
+
+    private void processOneVideo(ArdMediaVideo video) {
+        int maxRetries = 3;       // 最大重试次数
+        int retryDelay = 3000;    // 每次重试等待 3 秒
+        int attempt = 0;
+
+        while (attempt < maxRetries) {
+            attempt++;
+            try (InputStream is = openUrlInputStream(video.getSourceUrl())) {
+                if (is != null) {
+                    String mediaUrl = ossServiceContext.upload(
+                            "record",
+                            video.getTaskId() + "/" + video.getId() + ".mp4",
+                            is,
+                            MimeTypeUtils.VIDEO_MP4
+                    );
+                    video.setMediaUrl(mediaUrl);
+
+                    // 生成视频缩略图
+                    processVideoThumbnail(video);
+
+                    video.setSyncStatus(2); // 成功
+                    break; // 上传成功,退出循环
+                } else {
+                    log.warn("视频尚未生成,尝试重试 {}/{}", attempt, maxRetries);
+                }
+            } catch (Exception e) {
+                log.error("视频同步异常 videoId={}, 尝试 {}/{}", video.getId(), attempt, maxRetries, e);
+            }
+
+            if (attempt < maxRetries) {
+                try {
+                    Thread.sleep(retryDelay);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    break;
+                }
+            } else {
+                video.setSyncStatus(3); // 达到最大重试仍失败
+            }
+        }
+
+        video.setUpdateTime(new Date());
+        try {
+            ardMediaVideoMapper.updateArdMediaVideo(video);
+        } catch (Exception e) {
+            log.error("更新视频状态失败 videoId={}", video.getId(), e);
+        }
+    }
+
+    private void processOnePic(ArdMediaPic pic) {
+        int maxRetries = 3; // 最大重试次数
+        int retryDelay = 2000; // 每次重试等待 2 秒
+        int attempt = 0;
+
+        while (attempt < maxRetries) {
+            attempt++;
+            try (InputStream inputStream = openUrlInputStream(pic.getSourceUrl())) {
+                if (inputStream != null) {
+                    String url = ossServiceContext.upload(
+                            "pic",
+                            pic.getTaskId() + "/" + pic.getId() + ".jpg",
+                            inputStream,
+                            MimeTypeUtils.IMAGE_JPG
+                    );
+                    pic.setMediaUrl(url);
+                    pic.setSyncStatus(2); // 成功
+                    break; // 上传成功,退出循环
+                } else {
+                    log.warn("图片尚未生成,尝试重试 {}/{}", attempt, maxRetries);
+                }
+            } catch (Exception e) {
+                log.error("图片同步异常 taskId={}, photoId={}, 尝试 {}/{}", pic.getTaskId(), pic.getId(), attempt, maxRetries, e);
+            }
+
+            if (attempt < maxRetries) {
+                try {
+                    Thread.sleep(retryDelay);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    break;
+                }
+            } else {
+                pic.setSyncStatus(3); // 达到最大重试仍失败
+            }
+        }
+
+        pic.setUpdateTime(new Date());
+        try {
+            ardMediaPicMapper.updatePic(pic);
+        } catch (Exception e) {
+            log.error("更新图片状态失败 photoId={}", pic.getId(), e);
+        }
+    }
+
+    /**
+     * 处理视频缩略图
+     */
+    private void processVideoThumbnail(ArdMediaVideo video) {
+        // 生成缩略图
+        try (ByteArrayInputStream in = generateThumbnail(video.getSourceUrl())) {
+            String thumbUrl = ossServiceContext.upload(
+                    "record",
+                    video.getTaskId() + "/" + video.getId() + ".jpg",
+                    in,
+                    MimeTypeUtils.IMAGE_JPEG
+            );
+            video.setThumbnailUrl(thumbUrl);
+        } catch (Exception e) {
+            log.warn("视频缩略图生成或上传失败 taskId={}, videoId={}", video.getTaskId(), video.getId(), e);
+        }
+        // 获取文件大小
+        try {
+            long videoSize = getFileSizeFromUrl(video.getSourceUrl());
+            video.setFileSize(videoSize);
+        } catch (Exception e) {
+            log.warn("获取视频大小失败 url={}", video.getSourceUrl(), e);
+        }
+    }
+
+    private long getFileSizeFromUrl(String fileUrl) throws IOException {
+        URL url = new URL(fileUrl);
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("HEAD");
+        conn.setConnectTimeout(5000);
+        conn.setReadTimeout(5000);
+        conn.connect();
+        long size = conn.getContentLengthLong(); // 返回字节数
+        conn.disconnect();
+        return size;
+    }
+
+    private ByteArrayInputStream generateThumbnail(String videoFile) {
+
+        String ffmpeg = osSelect.isWindows()
+                ? "ffmpeg"
+                : "/usr/local/ffmpeg/bin/ffmpeg";
+
+        Path tempImg = null;
+        try {
+            tempImg = Files.createTempFile("thumb_", ".jpg");
+
+            List<String> cmd = new ArrayList<>();
+            cmd.add(ffmpeg);
+            cmd.add("-y");
+            cmd.add("-loglevel");
+            cmd.add("error");
+            cmd.add("-i");
+            cmd.add(videoFile);
+            cmd.add("-ss");
+            cmd.add(String.valueOf(1));
+            cmd.add("-vframes");
+            cmd.add("1");
+            cmd.add("-vf");
+            cmd.add("scale=" + 200 + ":-1");
+            cmd.add(tempImg.toAbsolutePath().toString());
+
+            log.info("ffmpeg cmd: {}", String.join(" ", cmd));
+
+            Process process = new ProcessBuilder(cmd).start();
+
+            drain(process.getErrorStream());
+
+            boolean finished = process.waitFor(15, TimeUnit.SECONDS); // 超时保护
+            if (!finished) {
+                process.destroyForcibly();
+                throw new RuntimeException("ffmpeg timeout");
+            }
+
+            if (process.exitValue() != 0) {
+                throw new RuntimeException("ffmpeg exit=" + process.exitValue());
+            }
+
+            byte[] data = Files.readAllBytes(tempImg);
+            return new ByteArrayInputStream(data);
+
+        } catch (Exception e) {
+            throw new RuntimeException("生成缩略图失败", e);
+        } finally {
+            if (tempImg != null) {
+                try {
+                    Files.deleteIfExists(tempImg);
+                } catch (IOException ignore) {
+                }
+            }
+        }
+    }
+
+    private static void drain(InputStream is) {
+        new Thread(() -> {
+            try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
+                while (br.readLine() != null) {
+                }
+            } catch (IOException ignore) {
+            }
+        }, "ffmpeg-stderr").start();
+    }
+
+    /**
+     * 将 URL 转成 InputStream
+     *
+     * @param fileUrl 文件 URL
+     * @return InputStream,如果失败返回 null
+     */
+    private InputStream openUrlInputStream(String fileUrl) {
+        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(5000);
+            connection.setReadTimeout(10000);
+            return connection.getInputStream();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+}
diff --git a/ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java b/ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java
index 5a0b49c..1bc4a7a 100644
--- a/ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java
+++ b/ard-route/src/main/java/com/ard/route/service/impl/ArdRouteInfoServiceImpl.java
@@ -81,8 +81,10 @@
     public ArdRouteInfo selectArdRouteInfoById(String id) {
         ArdRouteInfo ardRouteInfo = ardRouteInfoMapper.selectArdRouteInfoById(id);
         try {
-            if (ardRouteInfo.getRouteData() != null){
-                ardRouteInfo.setUavRouteReq((UavRouteReq) deserialize(ardRouteInfo.getRouteData()));
+            if (ardRouteInfo != null) {
+                if (ardRouteInfo.getRouteData() != null){
+                    ardRouteInfo.setUavRouteReq((UavRouteReq) deserialize(ardRouteInfo.getRouteData()));
+                }
             }
         } catch (IOException | ClassNotFoundException e) {
             throw new RuntimeException(e);
@@ -144,18 +146,7 @@
         }
         ardRouteInfo.getUavRouteReq().setDroneType(subStringDroneType(ardRouteInfo.getAircraft()));
         KmzResultVo kmzResult = routeService.buildKmz(ardRouteInfo.getUavRouteReq(),ardRouteInfo.getName());
-        if (M350_RTK.getValue().equals(ardRouteInfo.getUavRouteReq().getDroneType())) {
-            // 变焦数值加工
-            for (RoutePointReq routePointReq : ardRouteInfo.getUavRouteReq().getRoutePointList()) {
-                for (ActionGroupReq actionGroupReq : routePointReq.getActionGroupList()) {
-                    for (PointActionReq action : actionGroupReq.getActions()) {
-                        if (action.getZoom() != null){
-                            action.setZoom(divided_23(action.getZoom()));
-                        }
-                    }
-                }
-            }
-        }
+        zoomProcessing(ardRouteInfo);
         ardRouteInfo.setPath(kmzResult.getPath());
         ardRouteInfo.setReadOnly("0");
         ardRouteInfoMapper.insertArdRouteInfo(dataProcessing(ardRouteInfo));
@@ -179,6 +170,8 @@
             // 先删除航线
             delOssService(route);
             // 再生成航线
+            ardRouteInfo.getUavRouteReq().setDroneType(subStringDroneType(ardRouteInfo.getAircraft()));
+            zoomProcessing(ardRouteInfo);
             kmzResult = routeService.buildKmz(ardRouteInfo.getUavRouteReq(), ardRouteInfo.getName());
             if ("更新航线kmz文件失败".equals(kmzResult.getData())) {
                 return 0;
@@ -186,6 +179,22 @@
             ardRouteInfo.setPath(kmzResult.getPath());
         }
         return ardRouteInfoMapper.updateArdRouteInfo(dataProcessing(ardRouteInfo));
+    }
+
+    // 变倍加工方法
+    private void zoomProcessing(ArdRouteInfo ardRouteInfo) {
+        if (M350_RTK.getValue().equals(ardRouteInfo.getUavRouteReq().getDroneType())) {
+            // 变焦数值加工
+            for (RoutePointReq routePointReq : ardRouteInfo.getUavRouteReq().getRoutePointList()) {
+                for (ActionGroupReq actionGroupReq : routePointReq.getActionGroupList()) {
+                    for (PointActionReq action : actionGroupReq.getActions()) {
+                        if (action.getZoom() != null){
+                            action.setZoom(divided_23(action.getZoom()));
+                        }
+                    }
+                }
+            }
+        }
     }
 
     // 新增信息加工
@@ -413,7 +422,7 @@
     public Boolean routeBegins(String id,String boxSn) {
         ArdRouteInfo route = ardRouteInfoMapper.selectArdRouteInfoById(id);
         // 判断是否循环航线
-        if ("1".equals(route.getIsLoop())) {
+        if (null != route && "0".equals(route.getIsLoop())){
             deleteArdRouteInfoById(id);
         }
         WayLineBeginDTO wayLineBeginDTO = new WayLineBeginDTO(boxSn);
diff --git a/ard-route/src/main/java/com/ard/route/util/RouteFileUtils.java b/ard-route/src/main/java/com/ard/route/util/RouteFileUtils.java
index 955a11b..ceeb180 100644
--- a/ard-route/src/main/java/com/ard/route/util/RouteFileUtils.java
+++ b/ard-route/src/main/java/com/ard/route/util/RouteFileUtils.java
@@ -541,7 +541,11 @@
             }else {
                 kmlActionActuatorFuncParam.setFileSuffix("panoramicPhoto_360");
             }
-            kmlActionActuatorFuncParam.setUseGlobalPayloadLensIndex(String.valueOf(pointActionReq.getUseGlobalImageFormat()));
+            if (pointActionReq.getUseGlobalImageFormat() != null) {
+                kmlActionActuatorFuncParam.setUseGlobalPayloadLensIndex(String.valueOf(pointActionReq.getUseGlobalImageFormat()));
+            }else {
+                kmlActionActuatorFuncParam.setUseGlobalPayloadLensIndex("0");
+            }
             if (ObjectUtil.equals(pointActionReq.getUseGlobalImageFormat(), 0)) {
                 kmlActionActuatorFuncParam.setPayloadLensIndex(pointActionReq.getImageFormat());
             } else {
diff --git a/ard-sample/src/main/java/com/ard/sample/manage/service/impl/LiveStreamServiceImpl.java b/ard-sample/src/main/java/com/ard/sample/manage/service/impl/LiveStreamServiceImpl.java
index 0940bbd..8a6d437 100644
--- a/ard-sample/src/main/java/com/ard/sample/manage/service/impl/LiveStreamServiceImpl.java
+++ b/ard-sample/src/main/java/com/ard/sample/manage/service/impl/LiveStreamServiceImpl.java
@@ -21,7 +21,6 @@
 import com.ard.sample.manage.service.ILiveStreamService;
 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 lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
diff --git a/ard-system/src/main/java/com/ard/data/map/mapper/ArdMapMapper.java b/ard-system/src/main/java/com/ard/data/map/mapper/ArdMapMapper.java
index ae12f02..3aaaeaf 100644
--- a/ard-system/src/main/java/com/ard/data/map/mapper/ArdMapMapper.java
+++ b/ard-system/src/main/java/com/ard/data/map/mapper/ArdMapMapper.java
@@ -1,6 +1,7 @@
 package com.ard.data.map.mapper;
 
 import com.ard.data.map.domain.ArdMap;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -59,4 +60,13 @@
      * @return 结果
      */
     public int deleteArdMapByIds(String[] ids);
+
+    /**
+     * 校验地图管理信息名称是否唯一
+     *
+     * @param mapName 名称
+     * @return 结果
+     */
+    public ArdMap checkArdMapNameUnique(@Param("mapName") String mapName);
+
 }
diff --git a/ard-system/src/main/java/com/ard/data/map/service/IArdMapService.java b/ard-system/src/main/java/com/ard/data/map/service/IArdMapService.java
index 028ab9b..996eba2 100644
--- a/ard-system/src/main/java/com/ard/data/map/service/IArdMapService.java
+++ b/ard-system/src/main/java/com/ard/data/map/service/IArdMapService.java
@@ -59,4 +59,13 @@
      * @return 结果
      */
     public int deleteArdMapById(String id);
+
+    /**
+     * 校验地图管理信息名称是否唯一
+     *
+     * @param ardMap 地图管理
+     * @return 结果
+     */
+    public String checkArdMapNameUnique(ArdMap ardMap);
+
 }
diff --git a/ard-system/src/main/java/com/ard/data/map/service/impl/ArdMapServiceImpl.java b/ard-system/src/main/java/com/ard/data/map/service/impl/ArdMapServiceImpl.java
index 869117b..362b632 100644
--- a/ard-system/src/main/java/com/ard/data/map/service/impl/ArdMapServiceImpl.java
+++ b/ard-system/src/main/java/com/ard/data/map/service/impl/ArdMapServiceImpl.java
@@ -1,5 +1,6 @@
 package com.ard.data.map.service.impl;
 
+import com.ard.common.constant.DeviceConstants;
 import com.ard.common.utils.uuid.IdUtils;
 import com.ard.data.map.domain.ArdMap;
 import com.ard.data.map.mapper.ArdMapMapper;
@@ -87,4 +88,24 @@
     public int deleteArdMapById(String id) {
         return ardMapMapper.deleteArdMapById(id);
     }
+
+    /**
+     * 校验地图管理信息名称是否唯一
+     *
+     * @param ardMap 地图管理
+     * @return 结果
+     */
+    @Override
+    public String checkArdMapNameUnique(ArdMap ardMap) {
+        String id = ardMap.getId();
+        String mapName = ardMap.getMapName();
+
+        ArdMap map = ardMapMapper.checkArdMapNameUnique(mapName);
+
+        if (map != null && (id == null || !id.equals(map.getId()))) {
+            return DeviceConstants.NOT_UNIQUE;
+        }
+        return DeviceConstants.UNIQUE;
+    }
+
 }
diff --git a/ard-system/src/main/java/com/ard/data/model/mapper/ArdModelMapper.java b/ard-system/src/main/java/com/ard/data/model/mapper/ArdModelMapper.java
index 2f60299..9ead264 100644
--- a/ard-system/src/main/java/com/ard/data/model/mapper/ArdModelMapper.java
+++ b/ard-system/src/main/java/com/ard/data/model/mapper/ArdModelMapper.java
@@ -1,6 +1,7 @@
 package com.ard.data.model.mapper;
 
 import com.ard.data.model.domain.ArdModel;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -59,4 +60,13 @@
      * @return 结果
      */
     public int deleteArdModelByIds(String[] ids);
+
+    /**
+     * 校验模型管理信息名称是否唯一
+     *
+     * @param modelName 名称
+     * @return 结果
+     */
+    public ArdModel checkArdModelNameUnique(@Param("modelName") String modelName);
+
 }
diff --git a/ard-system/src/main/java/com/ard/data/model/service/IArdModelService.java b/ard-system/src/main/java/com/ard/data/model/service/IArdModelService.java
index 12fb816..66776cc 100644
--- a/ard-system/src/main/java/com/ard/data/model/service/IArdModelService.java
+++ b/ard-system/src/main/java/com/ard/data/model/service/IArdModelService.java
@@ -59,4 +59,13 @@
      * @return 结果
      */
     public int deleteArdModelById(String id);
+
+    /**
+     * 校验模型管理信息名称是否唯一
+     *
+     * @param ardModel 模型管理
+     * @return 结果
+     */
+    public String checkArdModelNameUnique(ArdModel ardModel);
+
 }
diff --git a/ard-system/src/main/java/com/ard/data/model/service/impl/ArdModelServiceImpl.java b/ard-system/src/main/java/com/ard/data/model/service/impl/ArdModelServiceImpl.java
index 8b3a1a5..b000909 100644
--- a/ard-system/src/main/java/com/ard/data/model/service/impl/ArdModelServiceImpl.java
+++ b/ard-system/src/main/java/com/ard/data/model/service/impl/ArdModelServiceImpl.java
@@ -2,6 +2,7 @@
 
 import java.util.List;
 
+import com.ard.common.constant.DeviceConstants;
 import com.ard.common.utils.uuid.IdUtils;
 import com.ard.data.model.domain.ArdModel;
 import com.ard.data.model.mapper.ArdModelMapper;
@@ -87,4 +88,24 @@
     public int deleteArdModelById(String id) {
         return ardModelMapper.deleteArdModelById(id);
     }
+
+    /**
+     * 校验模型管理信息名称是否唯一
+     *
+     * @param ardModel 模型管理
+     * @return 结果
+     */
+    @Override
+    public String checkArdModelNameUnique(ArdModel ardModel) {
+        String id = ardModel.getId();
+        String modelName = ardModel.getModelName();
+
+        ArdModel model = ardModelMapper.checkArdModelNameUnique(modelName);
+
+        if (model != null && (id == null || !id.equals(model.getId()))) {
+            return DeviceConstants.NOT_UNIQUE;
+        }
+        return DeviceConstants.UNIQUE;
+    }
+
 }
diff --git a/ard-system/src/main/java/com/ard/data/noflyzone/mapper/ArdNoFlyZoneMapper.java b/ard-system/src/main/java/com/ard/data/noflyzone/mapper/ArdNoFlyZoneMapper.java
index 5b59628..1419745 100644
--- a/ard-system/src/main/java/com/ard/data/noflyzone/mapper/ArdNoFlyZoneMapper.java
+++ b/ard-system/src/main/java/com/ard/data/noflyzone/mapper/ArdNoFlyZoneMapper.java
@@ -2,6 +2,7 @@
 
 import java.util.List;
 import com.ard.data.noflyzone.domain.ArdNoFlyZone;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * 禁飞区管理Mapper接口
@@ -58,4 +59,12 @@
      * @return 结果
      */
     public int deleteArdNoFlyZoneByIds(String[] ids);
+
+    /**
+     * 校验禁飞区名称是否唯一
+     *
+     * @param name 禁飞区名称
+     * @return 禁飞区信息
+     */
+    public ArdNoFlyZone checkArdNoFlyZoneNameUnique(@Param("name") String name);
 }
diff --git a/ard-system/src/main/java/com/ard/data/noflyzone/service/IArdNoFlyZoneService.java b/ard-system/src/main/java/com/ard/data/noflyzone/service/IArdNoFlyZoneService.java
index 72ecbd5..e7ace4d 100644
--- a/ard-system/src/main/java/com/ard/data/noflyzone/service/IArdNoFlyZoneService.java
+++ b/ard-system/src/main/java/com/ard/data/noflyzone/service/IArdNoFlyZoneService.java
@@ -58,4 +58,12 @@
      * @return 结果
      */
     public int deleteArdNoFlyZoneById(String id);
+
+    /**
+     * 校验禁飞区名称是否唯一
+     *
+     * @param ardNoFlyZone 禁飞区实体
+     * @return 结果
+     */
+    public String checkArdNoFlyZoneNameUnique(ArdNoFlyZone ardNoFlyZone);
 }
diff --git a/ard-system/src/main/java/com/ard/data/noflyzone/service/impl/ArdNoFlyZoneServiceImpl.java b/ard-system/src/main/java/com/ard/data/noflyzone/service/impl/ArdNoFlyZoneServiceImpl.java
index 56e2c48..d4949eb 100644
--- a/ard-system/src/main/java/com/ard/data/noflyzone/service/impl/ArdNoFlyZoneServiceImpl.java
+++ b/ard-system/src/main/java/com/ard/data/noflyzone/service/impl/ArdNoFlyZoneServiceImpl.java
@@ -1,8 +1,11 @@
 package com.ard.data.noflyzone.service.impl;
 
 import java.util.List;
+
+import com.ard.common.constant.DeviceConstants;
 import com.ard.common.utils.DateUtils;
 import com.ard.common.utils.SecurityUtils;
+import com.ard.common.utils.StringUtils;
 import com.ard.common.utils.uuid.IdUtils;
 import com.ard.data.noflyzone.service.IArdNoFlyZoneService;
 import org.springframework.stereotype.Service;
@@ -14,49 +17,45 @@
 
 /**
  * 禁飞区管理Service业务层处理
- * 
+ *
  * @author ard
  * @date 2025-10-14
  */
 @Service
-public class ArdNoFlyZoneServiceImpl implements IArdNoFlyZoneService
-{
+public class ArdNoFlyZoneServiceImpl implements IArdNoFlyZoneService {
     @Resource
     private ArdNoFlyZoneMapper ardNoFlyZoneMapper;
 
     /**
      * 查询禁飞区管理
-     * 
+     *
      * @param id 禁飞区管理主键
      * @return 禁飞区管理
      */
     @Override
-    public ArdNoFlyZone selectArdNoFlyZoneById(String id)
-    {
+    public ArdNoFlyZone selectArdNoFlyZoneById(String id) {
         return ardNoFlyZoneMapper.selectArdNoFlyZoneById(id);
     }
 
     /**
      * 查询禁飞区管理列表
-     * 
+     *
      * @param ardNoFlyZone 禁飞区管理
      * @return 禁飞区管理
      */
     @Override
-    public List<ArdNoFlyZone> selectArdNoFlyZoneList(ArdNoFlyZone ardNoFlyZone)
-    {
+    public List<ArdNoFlyZone> selectArdNoFlyZoneList(ArdNoFlyZone ardNoFlyZone) {
         return ardNoFlyZoneMapper.selectArdNoFlyZoneList(ardNoFlyZone);
     }
 
     /**
      * 新增禁飞区管理
-     * 
+     *
      * @param ardNoFlyZone 禁飞区管理
      * @return 结果
      */
     @Override
-    public int insertArdNoFlyZone(ArdNoFlyZone ardNoFlyZone)
-    {
+    public int insertArdNoFlyZone(ArdNoFlyZone ardNoFlyZone) {
         ardNoFlyZone.setId(IdUtils.simpleUUID());
         ardNoFlyZone.setCreateTime(DateUtils.getNowDate());
         ardNoFlyZone.setCreateBy(SecurityUtils.getUsername());
@@ -65,13 +64,12 @@
 
     /**
      * 修改禁飞区管理
-     * 
+     *
      * @param ardNoFlyZone 禁飞区管理
      * @return 结果
      */
     @Override
-    public int updateArdNoFlyZone(ArdNoFlyZone ardNoFlyZone)
-    {
+    public int updateArdNoFlyZone(ArdNoFlyZone ardNoFlyZone) {
         ardNoFlyZone.setUpdateBy(SecurityUtils.getUsername());
         ardNoFlyZone.setUpdateTime(DateUtils.getNowDate());
         return ardNoFlyZoneMapper.updateArdNoFlyZone(ardNoFlyZone);
@@ -79,27 +77,42 @@
 
     /**
      * 批量删除禁飞区管理
-     * 
+     *
      * @param ids 需要删除的禁飞区管理主键
      * @return 结果
      */
     @Override
-    public int deleteArdNoFlyZoneByIds(String[] ids)
-    {
+    public int deleteArdNoFlyZoneByIds(String[] ids) {
         return ardNoFlyZoneMapper.deleteArdNoFlyZoneByIds(ids);
     }
 
     /**
      * 删除禁飞区管理信息
-     * 
+     *
      * @param id 禁飞区管理主键
      * @return 结果
      */
     @Override
-    public int deleteArdNoFlyZoneById(String id)
-    {
+    public int deleteArdNoFlyZoneById(String id) {
         return ardNoFlyZoneMapper.deleteArdNoFlyZoneById(id);
     }
 
+    /**
+     * 校验禁飞区名称是否唯一
+     *
+     * @param ardNoFlyZone 禁飞区实体
+     * @return 结果
+     */
+    @Override
+    public String checkArdNoFlyZoneNameUnique(ArdNoFlyZone ardNoFlyZone) {
+        String id = ardNoFlyZone.getId();
+        String name = ardNoFlyZone.getName();
 
+        ArdNoFlyZone info = ardNoFlyZoneMapper.checkArdNoFlyZoneNameUnique(name);
+
+        if (info != null && (id == null || !id.equals(info.getId()))) {
+            return DeviceConstants.NOT_UNIQUE;
+        }
+        return DeviceConstants.UNIQUE;
+    }
 }
diff --git a/ard-system/src/main/java/com/ard/data/tile/mapper/ArdTileMapper.java b/ard-system/src/main/java/com/ard/data/tile/mapper/ArdTileMapper.java
index 13edb58..656bfd6 100644
--- a/ard-system/src/main/java/com/ard/data/tile/mapper/ArdTileMapper.java
+++ b/ard-system/src/main/java/com/ard/data/tile/mapper/ArdTileMapper.java
@@ -1,6 +1,7 @@
 package com.ard.data.tile.mapper;
 
 import com.ard.data.tile.domain.ArdTile;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -59,4 +60,13 @@
      * @return 结果
      */
     public int deleteArdTileByIds(String[] ids);
+
+    /**
+     * 校验tile管理信息名称是否唯一
+     *
+     * @param tilesName 名称
+     * @return 结果
+     */
+    public ArdTile checkArdTileNameUnique(@Param("tilesName") String tilesName);
+
 }
diff --git a/ard-system/src/main/java/com/ard/data/tile/service/IArdTileService.java b/ard-system/src/main/java/com/ard/data/tile/service/IArdTileService.java
index 1a7b6ce..b08ba82 100644
--- a/ard-system/src/main/java/com/ard/data/tile/service/IArdTileService.java
+++ b/ard-system/src/main/java/com/ard/data/tile/service/IArdTileService.java
@@ -59,4 +59,13 @@
      * @return 结果
      */
     public int deleteArdTileById(String id);
+
+    /**
+     * 校验tile管理信息名称是否唯一
+     *
+     * @param ardTile tile管理
+     * @return 结果
+     */
+    public String checkArdTileNameUnique(ArdTile ardTile);
+
 }
diff --git a/ard-system/src/main/java/com/ard/data/tile/service/impl/ArdTileServiceImpl.java b/ard-system/src/main/java/com/ard/data/tile/service/impl/ArdTileServiceImpl.java
index c068545..8014252 100644
--- a/ard-system/src/main/java/com/ard/data/tile/service/impl/ArdTileServiceImpl.java
+++ b/ard-system/src/main/java/com/ard/data/tile/service/impl/ArdTileServiceImpl.java
@@ -2,6 +2,7 @@
 
 import java.util.List;
 
+import com.ard.common.constant.DeviceConstants;
 import com.ard.common.utils.uuid.IdUtils;
 import com.ard.data.tile.domain.ArdTile;
 import com.ard.data.tile.mapper.ArdTileMapper;
@@ -87,4 +88,24 @@
     public int deleteArdTileById(String id) {
         return ardTileMapper.deleteArdTileById(id);
     }
+
+    /**
+     * 校验tile管理信息名称是否唯一
+     *
+     * @param ardTile tile管理
+     * @return 结果
+     */
+    @Override
+    public String checkArdTileNameUnique(ArdTile ardTile) {
+        String id = ardTile.getId();
+        String tilesName = ardTile.getTilesName();
+
+        ArdTile tile = ardTileMapper.checkArdTileNameUnique(tilesName);
+
+        if (tile != null && (id == null || !id.equals(tile.getId()))) {
+            return DeviceConstants.NOT_UNIQUE;
+        }
+        return DeviceConstants.UNIQUE;
+    }
+
 }
diff --git a/ard-system/src/main/resources/mapper/data/ArdMapMapper.xml b/ard-system/src/main/resources/mapper/data/ArdMapMapper.xml
index a5ecaac..11bc45f 100644
--- a/ard-system/src/main/resources/mapper/data/ArdMapMapper.xml
+++ b/ard-system/src/main/resources/mapper/data/ArdMapMapper.xml
@@ -85,4 +85,9 @@
             #{id}
         </foreach>
     </delete>
+
+    <select id="checkArdMapNameUnique" resultMap="ArdMapResult">
+        select * from ard_map where map_name = #{mapName} limit 1
+    </select>
+
 </mapper>
\ No newline at end of file
diff --git a/ard-system/src/main/resources/mapper/data/ArdModelMapper.xml b/ard-system/src/main/resources/mapper/data/ArdModelMapper.xml
index a014f0e..8d84bee 100644
--- a/ard-system/src/main/resources/mapper/data/ArdModelMapper.xml
+++ b/ard-system/src/main/resources/mapper/data/ArdModelMapper.xml
@@ -87,4 +87,9 @@
             #{id}
         </foreach>
     </delete>
+
+    <select id="checkArdModelNameUnique" parameterType="String" resultMap="ArdModelResult">
+        select * from ard_model where model_name = #{modelName} limit 1
+    </select>
+
 </mapper>
\ No newline at end of file
diff --git a/ard-system/src/main/resources/mapper/data/ArdNoFlyZoneMapper.xml b/ard-system/src/main/resources/mapper/data/ArdNoFlyZoneMapper.xml
index cf49160..28ffec0 100644
--- a/ard-system/src/main/resources/mapper/data/ArdNoFlyZoneMapper.xml
+++ b/ard-system/src/main/resources/mapper/data/ArdNoFlyZoneMapper.xml
@@ -83,4 +83,9 @@
             #{id}
         </foreach>
     </delete>
+
+    <select id="checkArdNoFlyZoneNameUnique" resultMap="ArdNoFlyZoneResult">
+        select * from ard_no_fly_zone where name = #{name} limit 1
+    </select>
+
 </mapper>
\ No newline at end of file
diff --git a/ard-system/src/main/resources/mapper/data/ArdTileMapper.xml b/ard-system/src/main/resources/mapper/data/ArdTileMapper.xml
index 2ed3409..d0fce45 100644
--- a/ard-system/src/main/resources/mapper/data/ArdTileMapper.xml
+++ b/ard-system/src/main/resources/mapper/data/ArdTileMapper.xml
@@ -104,4 +104,9 @@
             #{id}
         </foreach>
     </delete>
+
+    <select id="checkArdTileNameUnique" parameterType="String" resultMap="ArdTileResult">
+        select * from ard_tile where tiles_name = #{tilesName} limit 1
+    </select>
+
 </mapper>
\ No newline at end of file
diff --git a/ard-vtdu/src/main/java/com/ard/vtdu/service/impl/ArdVtduServiceImpl.java b/ard-vtdu/src/main/java/com/ard/vtdu/service/impl/ArdVtduServiceImpl.java
index a0c5f7b..806b065 100644
--- a/ard-vtdu/src/main/java/com/ard/vtdu/service/impl/ArdVtduServiceImpl.java
+++ b/ard-vtdu/src/main/java/com/ard/vtdu/service/impl/ArdVtduServiceImpl.java
@@ -38,14 +38,6 @@
         mediaService.clearAll();
     }
 
-    @Scheduled(cron = "0/5 * * * * ?")
-    public void syncMediamtx() {
-        log.debug("定时同步MediaMTX任务");
-        List<String> nameList = mediaService.getNameList();
-        List<ArdVtdu> ardVtduList = selectArdVtduList(new ArdVtdu());
-        asyncMediaMTX(ardVtduList, nameList);
-    }
-
     /**
      * 查询流媒体管理
      *
diff --git a/ard-work/src/main/java/com/ard/work/domain/entity/ArdMedia.java b/ard-work/src/main/java/com/ard/work/domain/entity/ArdMedia.java
deleted file mode 100644
index f5b97d5..0000000
--- a/ard-work/src/main/java/com/ard/work/domain/entity/ArdMedia.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.ard.work.domain.entity;
-
-import com.ard.common.core.domain.BaseEntity;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import lombok.*;
-
-import java.util.Date;
-
-@EqualsAndHashCode(callSuper = true)
-@Data
-@Builder
-@AllArgsConstructor
-@NoArgsConstructor
-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;          // 源地址
-    private Long   photoId;            // 图片ID
-    private Double longitude;          // 经度
-    private Double latitude;           // 纬度
-    private Double height;             // 高度
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date captureTime;          // 抓拍时间
-    private String thumbnailUrl;       // 视频缩略图URL
-}
diff --git a/ard-work/src/main/java/com/ard/work/domain/entity/ArdMediaPic.java b/ard-work/src/main/java/com/ard/work/domain/entity/ArdMediaPic.java
new file mode 100644
index 0000000..d415301
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/domain/entity/ArdMediaPic.java
@@ -0,0 +1,28 @@
+package com.ard.work.domain.entity;
+
+import com.ard.common.core.domain.BaseEntity;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.*;
+
+import java.util.Date;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ArdMediaPic extends BaseEntity {
+    private Long id;
+    private Integer taskId;
+    private Long fileSize;
+    private String fileMd5;
+    private String mediaUrl;
+    private String sourceUrl;
+    private Double longitude;
+    private Double latitude;
+    private Double height;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date captureTime;
+    //同步状态 0待处理 1处理中 2成功 3失败
+    private Integer syncStatus;
+}
diff --git a/ard-work/src/main/java/com/ard/work/domain/entity/ArdMediaVideo.java b/ard-work/src/main/java/com/ard/work/domain/entity/ArdMediaVideo.java
new file mode 100644
index 0000000..f6d7220
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/domain/entity/ArdMediaVideo.java
@@ -0,0 +1,30 @@
+package com.ard.work.domain.entity;
+
+
+import com.ard.common.core.domain.BaseEntity;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.*;
+
+import java.util.Date;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ArdMediaVideo extends BaseEntity {
+    private Long id;
+    private String name;
+    private Long fileSize;
+    private String mediaUrl;
+    private String sourceUrl;
+    private String thumbnailUrl;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date startTime;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date endTime;
+    private Integer spanSeconds;
+    private Integer taskId;
+    //同步状态 0待处理 1处理中 2成功 3失败
+    private Integer syncStatus;
+}
diff --git a/ard-work/src/main/java/com/ard/work/mapper/ArdMediaMapper.java b/ard-work/src/main/java/com/ard/work/mapper/ArdMediaMapper.java
deleted file mode 100644
index 5a45bf6..0000000
--- a/ard-work/src/main/java/com/ard/work/mapper/ArdMediaMapper.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.ard.work.mapper;
-
-import com.ard.work.domain.entity.ArdMedia;
-import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Param;
-
-import java.util.List;
-
-
-public interface ArdMediaMapper {
-
-    /**
-     * 查询媒体数据列表
-     *
-     * @param ardMedia 媒体数据
-     * @return 媒体数据集合
-     */
-    List<ArdMedia> selectArdMedia(ArdMedia ardMedia);  // 动态查询
-
-    List<ArdMedia> selectArdMediaByTaskId(Integer taskId);  // 根据任务ID查询
-
-    List<ArdMedia> selectArdMediaByTaskIdAndType(@Param("taskId") Integer taskId, @Param("mediaType") Integer mediaType);  //
-
-    /**
-     * 插入媒体数据记录
-     *
-     * @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);  // 批量删除记录
-}
diff --git a/ard-work/src/main/java/com/ard/work/mapper/ArdMediaPicMapper.java b/ard-work/src/main/java/com/ard/work/mapper/ArdMediaPicMapper.java
new file mode 100644
index 0000000..161b5bb
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/mapper/ArdMediaPicMapper.java
@@ -0,0 +1,60 @@
+package com.ard.work.mapper;
+
+import com.ard.work.domain.entity.ArdMediaPic;
+import com.ard.work.domain.entity.ArdMediaVideo;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface ArdMediaPicMapper {
+
+    /**
+     * 查询图片列表(条件查询)
+     */
+    List<ArdMediaPic> selectPicList(ArdMediaPic query);
+
+    /**
+     * 查询待处理图片(sync_status = 0)
+     */
+    List<ArdMediaPic> selectPendingPics(@Param("limit") int limit);
+    /**
+     * 根据任务ID查询图片
+     */
+    List<ArdMediaPic> selectPicByTaskId(@Param("taskId") Integer taskId);
+
+    /**
+     * 新增图片
+     */
+    int insertPic(ArdMediaPic pic);
+
+    /**
+     * 更新图片
+     */
+    int updatePic(ArdMediaPic pic);
+    /**
+     * 原子抢占:0 → 1
+     */
+    int markProcessing(@Param("id") Long id);
+
+    /**
+     * 更新同步状态
+     * 0 = 待处理
+     * 1 = 处理中
+     * 2 = 已完成
+     */
+    int updateSyncStatus(@Param("id") Long id, @Param("status") Integer status);
+
+    /**
+     * 根据ID删除图片
+     */
+    int deletePicById(@Param("id") String id);
+
+    /**
+     * 根据任务ID删除图片
+     */
+    int deletePicByTaskId(@Param("taskId") Integer taskId);
+
+
+}
diff --git a/ard-work/src/main/java/com/ard/work/mapper/ArdMediaVideoMapper.java b/ard-work/src/main/java/com/ard/work/mapper/ArdMediaVideoMapper.java
new file mode 100644
index 0000000..bb465eb
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/work/mapper/ArdMediaVideoMapper.java
@@ -0,0 +1,59 @@
+package com.ard.work.mapper;
+
+import com.ard.work.domain.entity.ArdMediaVideo;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface ArdMediaVideoMapper {
+
+    /**
+     * 查询视频列表(条件查询)
+     */
+    List<ArdMediaVideo> selectArdMediaVideoList(ArdMediaVideo query);
+
+    /**
+     * 查询待处理视频(sync_status = 0)
+     */
+    List<ArdMediaVideo> selectPendingVideos(@Param("limit") int limit);
+
+    /**
+     * 原子抢占:0 → 1
+     */
+    int markProcessing(@Param("id") Long id);
+
+    /**
+     * 更新同步状态
+     * 0 = 待处理
+     * 1 = 处理中
+     * 2 = 已完成
+     */
+    int updateSyncStatus(@Param("id") Long id, @Param("status") Integer status);
+
+    /**
+     * 根据任务ID查询视频
+     */
+    List<ArdMediaVideo> selectArdMediaVideoByTaskId(@Param("taskId") Integer taskId);
+
+    /**
+     * 新增视频
+     */
+    int insertArdMediaVideo(ArdMediaVideo video);
+
+    /**
+     * 更新视频
+     */
+    int updateArdMediaVideo(ArdMediaVideo video);
+
+    /**
+     * 根据ID删除视频
+     */
+    int deleteArdMediaVideoById(@Param("id") String id);
+
+    /**
+     * 根据任务ID删除视频
+     */
+    int deleteArdMediaVideoByTaskId(@Param("taskId") Integer taskId);
+}
diff --git a/ard-work/src/main/java/com/ard/work/service/IArdDroneInfoService.java b/ard-work/src/main/java/com/ard/work/service/IArdDroneInfoService.java
index 907d94e..5d84897 100644
--- a/ard-work/src/main/java/com/ard/work/service/IArdDroneInfoService.java
+++ b/ard-work/src/main/java/com/ard/work/service/IArdDroneInfoService.java
@@ -77,7 +77,7 @@
      * @param deptId 部门ID
      * @return 结果
      */
-    public int insertArdDroneLs(String[] boxSn,Long deptId);
+    public int insertArdDroneLs(String[] boxSn,Long deptId,String serverId);
 
     /**
      * 修改无人机状态
diff --git a/ard-work/src/main/java/com/ard/work/service/IArdMediaService.java b/ard-work/src/main/java/com/ard/work/service/IArdMediaService.java
index 356ed7a..8356b0c 100644
--- a/ard-work/src/main/java/com/ard/work/service/IArdMediaService.java
+++ b/ard-work/src/main/java/com/ard/work/service/IArdMediaService.java
@@ -1,7 +1,8 @@
 package com.ard.work.service;
 
-import com.ard.work.domain.entity.ArdMedia;
-
+import com.ard.work.domain.entity.ArdFlightTask;
+import com.ard.work.domain.entity.ArdMediaPic;
+import com.ard.work.domain.entity.ArdMediaVideo;
 import java.util.List;
 
 /**
@@ -10,11 +11,12 @@
  * Date: 2025年11月18日13:15
  **/
 public interface IArdMediaService {
-    /**
-     * 查询媒体数据列表
-     *
-     * @param ardMedia 媒体数据
-     * @return 媒体数据集合
-     */
-    public List<ArdMedia> selectArdMediaList(ArdMedia ardMedia);
+
+    List<ArdMediaPic> selectArdMediaPicList(ArdMediaPic ardMediaPic);
+    List<ArdMediaVideo> selectArdMediaVideoList(ArdMediaVideo ardMediaVideo);
+    void insertPhotoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn);
+    void insertVideoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn);
+    void syncPicToDatabase(ArdFlightTask flightTask, String serverId, String boxSn);
+    void syncVideoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn);
+
 }
diff --git a/ard-work/src/main/java/com/ard/work/service/IArdServerInfoService.java b/ard-work/src/main/java/com/ard/work/service/IArdServerInfoService.java
index 2e25e77..78b1125 100644
--- a/ard-work/src/main/java/com/ard/work/service/IArdServerInfoService.java
+++ b/ard-work/src/main/java/com/ard/work/service/IArdServerInfoService.java
@@ -28,8 +28,6 @@
      */
     public List<ArdServerInfo> selectArdServerInfoList(ArdServerInfo ardServerInfo);
 
-    public List<ArdServerInfo> selectArdServerInfoListNo(ArdServerInfo ardServerInfo);
-
     /**
      * 新增服务器管理
      * 
diff --git a/ard-work/src/main/java/com/ard/work/service/ITyService.java b/ard-work/src/main/java/com/ard/work/service/ITyService.java
index 18db889..d21a331 100644
--- a/ard-work/src/main/java/com/ard/work/service/ITyService.java
+++ b/ard-work/src/main/java/com/ard/work/service/ITyService.java
@@ -36,7 +36,7 @@
      *
      * @return
      */
-    AccessTokenReceiver getAccessToken(String ServerId);
+    AccessTokenReceiver getAccessToken(String serverId);
 
     /**
      * 获取全部服务器云盒列表
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/ArdDroneInfoServiceImpl.java b/ard-work/src/main/java/com/ard/work/service/impl/ArdDroneInfoServiceImpl.java
index 053cfa6..7064f3f 100644
--- a/ard-work/src/main/java/com/ard/work/service/impl/ArdDroneInfoServiceImpl.java
+++ b/ard-work/src/main/java/com/ard/work/service/impl/ArdDroneInfoServiceImpl.java
@@ -164,7 +164,7 @@
      * @return 成功插入的记录数
      */
     @Override
-    public int insertArdDroneLs(String[] boxSn, Long deptId) {
+    public int insertArdDroneLs(String[] boxSn,Long deptId,String serverId) {
         // 参数校验
         if (boxSn == null || boxSn.length == 0) {
             log.warn("传入的盒子编码为空");
@@ -176,10 +176,12 @@
         try {
             // 1. 检查已存在的无人机盒子
             List<ArdDroneInfo> existingDrones = getExistingDrones(boxSn);
+            // 1.1 新增时不能添加重复的boxSn
+            checkDuplicateBox(existingDrones, boxSn);
             // 2. 授权数量验证
             validateAuthorization(existingDrones.size(), boxSn.length);
             // 3. 获取并组装新的无人机信息
-            List<ArdDroneInfo> newDrones = assembleNewDrones(boxSn, deptId);
+            List<ArdDroneInfo> newDrones = assembleNewDrones(boxSn,deptId,serverId);
             // 4. 批量插入
             return batchInsertDrones(newDrones);
         } catch (Exception e) {
@@ -204,6 +206,29 @@
                 .map(ArdDroneInfoMapper::selectArdDroneByBoxSn)
                 .filter(Objects::nonNull)
                 .collect(Collectors.toList());
+    }
+
+    /**
+     * 检查是否有重复的盒子编码
+     */
+    private void checkDuplicateBox(List<ArdDroneInfo> existingDrone,String[] boxSn) {
+        Set<String> existingBoxSnSet = new HashSet<>();
+        for (ArdDroneInfo droneInfo : existingDrone) {
+            existingBoxSnSet.add(droneInfo.getBoxSn());
+        }
+
+        // 检查是否有重复的盒子编码
+        List<String> duplicateBoxSns = new ArrayList<>();
+        for (String sn : boxSn) {
+            if (existingBoxSnSet.contains(sn)) {
+                duplicateBoxSns.add(sn);
+            }
+        }
+
+        if (!duplicateBoxSns.isEmpty()) {
+            log.warn("存在重复的无人机盒子编码: {}", duplicateBoxSns);
+            throw new ServiceException("以下无人机盒子编码已存在: " + String.join(", ", duplicateBoxSns));
+        }
     }
 
     /**
@@ -236,19 +261,19 @@
     /**
      * 组装新的无人机信息
      */
-    private List<ArdDroneInfo> assembleNewDrones(String[] boxSn, Long deptId) {
+    private List<ArdDroneInfo> assembleNewDrones(String[] boxSn,Long deptId,String serverId) {
         List<BoxInfoReceiver> allBoxes = tyService.getBoxList();
         Set<String> targetBoxSnSet = new HashSet<>(Arrays.asList(boxSn));
         return allBoxes.stream()
                 .filter(box -> targetBoxSnSet.contains(box.getBoxSn()))
-                .map(box -> createDroneInfo(box, deptId))
+                .map(box -> createDroneInfo(box, deptId, serverId))
                 .collect(Collectors.toList());
     }
 
     /**
      * 创建无人机信息对象
      */
-    private ArdDroneInfo createDroneInfo(BoxInfoReceiver box, Long deptId) {
+    private ArdDroneInfo createDroneInfo(BoxInfoReceiver box, Long deptId ,String serverId) {
         ArdDroneInfo drone = new ArdDroneInfo();
         drone.setId(IdUtils.simpleUUID());
         drone.setBoxSn(box.getBoxSn());
@@ -259,21 +284,13 @@
         drone.setVideoPort(box.getVideoPort());
         drone.setLastTaskId(box.getLastTaskId());
         drone.setDeptId(deptId);
-
-        // 设置创建信息和更新信息
-        setCreateAndUpdateInfo(drone);
-
+        drone.setCreateTime(new Date());
+        drone.setUpdateTime(new Date());
+        drone.setServerId(serverId);
+        drone.setBoxVersion(box.getBoxVersion());
         return drone;
     }
 
-    /**
-     * 设置创建和更新信息
-     */
-    private void setCreateAndUpdateInfo(ArdDroneInfo drone) {
-        Date currentTime = new Date();
-        drone.setCreateTime(currentTime);
-        drone.setUpdateTime(currentTime);
-    }
 
     /**
      * 批量插入无人机信息
@@ -345,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;
-    }
 
 }
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskPartServiceImpl.java b/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskPartServiceImpl.java
index 0917724..da66e03 100644
--- a/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskPartServiceImpl.java
+++ b/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskPartServiceImpl.java
@@ -1,6 +1,7 @@
 package com.ard.work.service.impl;
 
 import cn.hutool.core.util.SerializeUtil;
+import com.ard.common.utils.DateUtils;
 import com.ard.work.domain.cache.UavStateCache;
 import com.ard.work.domain.cache.UavTelemetryDataCache;
 import com.ard.work.domain.dto.UavFlightStateDTO;
@@ -44,22 +45,22 @@
 
     /**
      * 定时任务:每秒读取一次设备状态数据
-     * 注意:这里模拟读取状态,实际应该调用设备接口或缓存
      */
     @Scheduled(fixedRate = 1000)
     public void readTelemetryTask() {
         long now = System.currentTimeMillis();
         UavTelemetryDataCache.forEach((boxSn, telemetryData) -> {
-
             // 1️⃣ 从状态缓存里取当前状态
             UavState uavState = UavStateCache.get(boxSn);
             if (uavState == null) {
+                log.warn("状态不存在,不记录");
                 return; // 状态不存在,不记录
             }
 
             // 2️⃣ 使用遥测自身时间戳判断是否还活着
             long dataTs = telemetryData.getTimestamp(); // 设备时间 / 服务器接收时间
             if (now - dataTs > 3000) {
+                log.warn("超过 3 秒没有新遥测 → 认为已离线或冻结,不记录:{}", new Date(dataTs));
                 return; // 超过 3 秒没有新遥测 → 认为已离线或冻结,不记录
             }
 
@@ -71,18 +72,13 @@
                 flushRemaining(boxSn);
             }
             lastFlightStatusMap.put(boxSn, currentStatus);
-
+            log.debug("设备 {} 当前状态为:{}", boxSn, currentStatus);
             // 只有飞行中才添加缓存
             if (currentStatus == 2) {
+                log.warn("设备 {} 当前状态为飞行中,添加遥测数据到缓存", boxSn);
                 UavFlightStateDTO dto = new UavFlightStateDTO(uavState, telemetryData, dataTs);
                 addTelemetryData(dto);
             }
-            UavFlightStateDTO dto = new UavFlightStateDTO(
-                    uavState,
-                    telemetryData,
-                    dataTs
-            );
-            addTelemetryData(dto);
         });
     }
 
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 4ab7d19..ff1016d 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,47 +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.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.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
@@ -53,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;
+
     /**
      * 查询飞行架次列表
      *
@@ -80,7 +53,7 @@
     /**
      * 同步所有服务的飞行历史
      */
-    @Scheduled(fixedDelay = 10000)
+    @Scheduled(fixedDelay = 5000)
     public void syncAllFlightHistory() {
         List<String> serverIds = ardServerInfoService.getServerIdsList();
         for (String serverId : serverIds) {
@@ -97,10 +70,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();
 
@@ -121,7 +102,11 @@
         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());
+            }
             // 创建FlightTask实体对象并填充数据
             ArdFlightTask flightTask = ArdFlightTask.builder()
                     .taskId(boxHistoryReceiver.getTaskId())
@@ -134,7 +119,7 @@
                     .videoPath(boxHistoryReceiver.getVideoPath())
                     .serverId(serverId)
                     .startTime(DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, boxHistoryReceiver.getStartTime()))
-                    .endTime(DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, boxHistoryReceiver.getEndTime()))
+                    .endTime(endTime)
                     .flightMileage(boxHistoryReceiver.getFlightMileage())
                     .flightDuration(boxHistoryReceiver.getFlightDuration())
                     .aveSpeed(boxHistoryReceiver.getAveSpeed())
@@ -152,35 +137,37 @@
             // 插入新记录
             int rows = ardFlightTaskMapper.insertArdFlightTask(flightTask);
             if (rows > 0) {
-                // 删除旧的媒体记录,防止重复插入
-                ardMediaMapper.deleteArdMediaByTaskId(flightTask.getTaskId());
-
                 // 插入视频
-                insertVideoToDatabase(flightTask, boxHistoryReceiver.getVideoPathList());
-
+                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, boxHistoryReceiver.getVideoPathList());
-            // 增量更新图片
-            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;
 
         // endTime 更新(重要!!!)
-        Date newEnd = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, remote.getEndTime());
-        if (newEnd != null && (local.getEndTime() == null || !local.getEndTime().equals(newEnd))) {
-            local.setEndTime(newEnd);
-            needUpdate = true;
+        if (remote.getEndTime() != null) {
+            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;
+            }
         }
 
         // photoCount 更新
@@ -199,386 +186,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 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 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);
-        List<BoxPhotosReceiver> photoList = getBoxPhotos(serverId, boxPhotosDTO);
-        if (photoList != null && !photoList.isEmpty()) {
-            addPhotos(flightTask, photoList);
-        }
-    }
-
-    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 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 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;
         }
     }
 
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/ArdMediaServiceImpl.java b/ard-work/src/main/java/com/ard/work/service/impl/ArdMediaServiceImpl.java
index 2fe5482..8b84da8 100644
--- a/ard-work/src/main/java/com/ard/work/service/impl/ArdMediaServiceImpl.java
+++ b/ard-work/src/main/java/com/ard/work/service/impl/ArdMediaServiceImpl.java
@@ -1,31 +1,280 @@
 package com.ard.work.service.impl;
 
-import com.ard.work.domain.entity.ArdMedia;
-import com.ard.work.mapper.ArdMediaMapper;
+import com.ard.common.utils.DateUtils;
+import com.ard.common.utils.StringUtils;
+import com.ard.common.utils.file.MimeTypeUtils;
+import com.ard.oss.service.impl.OssServiceContext;
+import com.ard.work.component.forest.TyClient;
+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.ArdMediaPic;
+import com.ard.work.domain.entity.ArdMediaVideo;
+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.ArdMediaPicMapper;
+import com.ard.work.mapper.ArdMediaVideoMapper;
 import com.ard.work.service.IArdMediaService;
+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.Service;
 
 import javax.annotation.Resource;
-import java.util.List;
+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.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /**
  * Description:
  * Author: lsy
  * Date: 2025年11月18日13:17
  **/
+@Slf4j
 @Service
 public class ArdMediaServiceImpl implements IArdMediaService {
-    @Resource
-    private ArdMediaMapper ardMediaMapper;
 
-    /**
-     * 查询媒体数据列表
-     *
-     * @param ardMedia 媒体数据
-     * @return 媒体数据集合
-     */
+    @Resource
+    private TyClient tyClient;
+    @Resource
+    private ArdMediaPicMapper ardMediaPicMapper;
+    @Resource
+    private ArdMediaVideoMapper ardMediaVideoMapper;
+
+    @Resource
+    private ArdFlightTaskMapper ardFlightTaskMapper;
+
+
+    // 调用接口获取架次图片
+    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();
+    }
+
     @Override
-    public List<ArdMedia> selectArdMediaList(ArdMedia ardMedia) {
-        return ardMediaMapper.selectArdMedia(ardMedia);
+    public List<ArdMediaPic> selectArdMediaPicList(ArdMediaPic ardMediaPic) {
+        return ardMediaPicMapper.selectPicList(ardMediaPic);
+    }
+
+    @Override
+    public List<ArdMediaVideo> selectArdMediaVideoList(ArdMediaVideo ardMediaVideo) {
+        return ardMediaVideoMapper.selectArdMediaVideoList(ardMediaVideo);
+    }
+
+    @Override
+    public void insertPhotoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn) {
+        log.warn("开始获取图片信息");
+        // 查询图片
+        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);
+        }
+    }
+
+    @Override
+    public void insertVideoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn) {
+        log.warn("开始获取视频信息");
+        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);
+        }
+    }
+
+    @Override
+    public void syncPicToDatabase(ArdFlightTask flightTask, String serverId, String boxSn) {
+
+        // 1️⃣ 查第三方图片列表
+        BoxPhotosDTO dto = BoxPhotosDTO.builder()
+                .taskId(flightTask.getTaskId())
+                .boxSn(boxSn)
+                .pageIndex(1)
+                .pageSize(100)
+                .build();
+
+        TyHttpCommonReceiver<List<BoxPhotosReceiver>> resp = tyClient.getBoxPhotos(serverId, dto);
+
+        List<BoxPhotosReceiver> photoList = resp.getContent();
+        if (photoList == null || photoList.isEmpty()) {
+            return;
+        }
+
+        // 2️⃣ 本地图片转 Map(用“业务唯一键”)
+        List<ArdMediaPic> existingList = ardMediaPicMapper.selectPicByTaskId(flightTask.getTaskId());
+
+        Map<Long, ArdMediaPic> localPicMap = existingList.stream()
+                .filter(p -> p.getId() != null)
+                .collect(Collectors.toMap(
+                        ArdMediaPic::getId,   // ✅ 远端 photoId
+                        p -> p,
+                        (a, b) -> a
+                ));
+
+
+        // 3️⃣ 逐条处理(新增 or 补全)
+        for (BoxPhotosReceiver remote : photoList) {
+
+            String imgUrl = remote.getImgUrl();
+            if (StringUtils.isBlank(imgUrl)) {
+                continue; // 第三方字段还没准备好
+            }
+            ArdMediaPic local = localPicMap.get(remote.getPhotoId());
+            if (local == null) {
+                // ✅ 新图片 → 插入
+                addPhotos(flightTask, List.of(remote));
+                continue;
+            }
+
+            // ✅ 已存在 → 补字段
+            boolean needUpdate = false;
+
+            if (local.getCaptureTime() == null && remote.getTimeStamp() != null) {
+                local.setCaptureTime(DateUtils.parseDate(remote.getTimeStamp()));
+                needUpdate = true;
+            }
+            if (local.getFileSize() == 0 && remote.getFileSize() != 0) {
+                local.setFileSize(remote.getFileSize());
+                needUpdate = true;
+            }
+            if ("0".equals(local.getFileMd5())&& !"0".equals(remote.getFileMd5())) {
+                local.setFileMd5(remote.getFileMd5());
+                needUpdate = true;
+            }
+            if (needUpdate) {
+                local.setUpdateTime(new Date());
+                ardMediaPicMapper.updatePic(local);
+            }
+        }
+    }
+
+    @Override
+    public void syncVideoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn) {
+
+        // 1️⃣ 拉取远端视频列表
+        BoxVideosDTO dto = BoxVideosDTO.builder()
+                .taskId(flightTask.getTaskId())
+                .boxSn(boxSn)
+                .pageIndex(1)
+                .pageSize(100)
+                .build();
+        TyHttpCommonReceiver<List<BoxVideosReceiver>> resp = tyClient.getBoxVideos(serverId, dto);
+        List<BoxVideosReceiver> videoList = resp.getContent();
+        if (videoList == null || videoList.isEmpty()) {
+            return;
+        }
+
+        // 2️⃣ 本地视频转 Map(videoId 为唯一键)
+        List<ArdMediaVideo> existingVideos = ardMediaVideoMapper.selectArdMediaVideoByTaskId(flightTask.getTaskId());
+        Map<Long, ArdMediaVideo> localVideoMap = existingVideos.stream()
+                .filter(v -> v.getId() != null)
+                .collect(Collectors.toMap(
+                        ArdMediaVideo::getId,  // 远端 videoId
+                        v -> v,
+                        (a, b) -> a
+                ));
+
+        // 3️⃣ 遍历远端视频,逐条处理
+        for (BoxVideosReceiver remote : videoList) {
+
+            ArdMediaVideo local = localVideoMap.get(remote.getId());
+            if (local == null) {
+                // 新视频 → 立即插入
+                addVideos(flightTask, List.of(remote));
+                localVideoMap.put(remote.getId(), new ArdMediaVideo()); // 占位
+                flightTask.setVideoCount(flightTask.getVideoCount() + 1);
+                ardFlightTaskMapper.updateArdFlightTask(flightTask);
+                continue;
+            }
+
+            // 已存在 → 补字段
+            boolean needUpdate = false;
+            if (local.getName() == null && remote.getName() != null) {
+                local.setName(remote.getName());
+                needUpdate = true;
+            }
+            if (local.getStartTime() == null && remote.getStartTime() != null) {
+                local.setStartTime(DateUtils.parseDate(remote.getStartTime()));
+                needUpdate = true;
+            }
+            if (local.getEndTime() == null && remote.getEndTime() != null) {
+                local.setEndTime(DateUtils.parseDate(remote.getEndTime()));
+                needUpdate = true;
+            }
+            if (local.getSpanSeconds() == null && remote.getSpanSeconds() != null) {
+                local.setSpanSeconds(Integer.parseInt(remote.getSpanSeconds()));
+                needUpdate = true;
+            }
+            if (needUpdate) {
+                local.setUpdateTime(new Date());
+                ardMediaVideoMapper.updateArdMediaVideo(local);
+            }
+        }
+    }
+
+    private void addPhotos(ArdFlightTask flightTask, List<BoxPhotosReceiver> photoList) {
+        log.warn("开始添加图片");
+        for (BoxPhotosReceiver photo : photoList) {
+            ArdMediaPic ardMedia = ArdMediaPic.builder()
+                    .id(photo.getPhotoId())
+                    .taskId(flightTask.getTaskId())
+                    .fileSize(photo.getFileSize())
+                    .fileMd5(photo.getFileMd5())
+                    .sourceUrl(photo.getImgUrl())
+                    .longitude(photo.getLng())
+                    .latitude(photo.getLat())
+                    .height(photo.getHeight())
+                    .captureTime(DateUtils.parseDate(photo.getTimeStamp()))
+                    .build();
+            ardMedia.setCreateTime(new Date());
+            ardMediaPicMapper.insertPic(ardMedia);
+        }
+    }
+
+    private void addVideos(ArdFlightTask flightTask, List<BoxVideosReceiver> videoList) {
+        for (BoxVideosReceiver video : videoList) {
+            ArdMediaVideo ardMedia = ArdMediaVideo.builder()
+                    .id(video.getId())
+                    .name(video.getName())
+                    .taskId(flightTask.getTaskId())
+//                    .fileSize(getFileSizeByUrl(video.getImgUrl()))
+                    .sourceUrl(video.getImgUrl())
+                    .startTime(DateUtils.parseDate(video.getStartTime()))
+                    .endTime(DateUtils.parseDate(video.getEndTime()))
+                    .spanSeconds(Integer.parseInt(video.getSpanSeconds()))
+                    .build();
+            ardMedia.setCreateTime(new Date());
+            ardMediaVideoMapper.insertArdMediaVideo(ardMedia);
+        }
     }
 }
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/ArdServerInfoServiceImpl.java b/ard-work/src/main/java/com/ard/work/service/impl/ArdServerInfoServiceImpl.java
index ad2e534..025eee6 100644
--- a/ard-work/src/main/java/com/ard/work/service/impl/ArdServerInfoServiceImpl.java
+++ b/ard-work/src/main/java/com/ard/work/service/impl/ArdServerInfoServiceImpl.java
@@ -5,14 +5,11 @@
 import com.ard.work.component.forest.TyClient;
 import com.ard.work.component.netty.TyTcpClientManager;
 import com.ard.work.domain.dto.TokenDTO;
-import com.ard.work.domain.entity.ArdDroneInfo;
 import com.ard.work.domain.entity.ArdServerInfo;
 import com.ard.work.domain.receiver.AccessTokenReceiver;
 import com.ard.work.domain.receiver.TyHttpCommonReceiver;
-import com.ard.work.mapper.ArdDroneInfoMapper;
 import com.ard.work.mapper.ArdServerInfoMapper;
 import com.ard.work.service.IArdServerInfoService;
-import com.dtflys.forest.exceptions.ForestRuntimeException;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.util.DigestUtils;
@@ -40,9 +37,6 @@
     @Resource
     private TyTcpClientManager tyTcpClientManager;
 
-    @Resource
-    private ArdDroneInfoMapper ArdDroneInfoMapper;
-
     /**
      * 查询服务器管理
      *
@@ -63,24 +57,6 @@
     @Override
     @DataScope(deptAlias = "d", userAlias = "u")
     public List<ArdServerInfo> selectArdServerInfoList(ArdServerInfo ardServerInfo) {
-        return ardServerInfoMapper.selectArdServerInfoList(ardServerInfo);
-    }
-
-    @Override
-    @DataScope(deptAlias = "d", userAlias = "u")
-    public List<ArdServerInfo> selectArdServerInfoListNo(ArdServerInfo ardServerInfo) {
-
-//        List<ArdDroneInfo> ardDroneInfos = ArdDroneInfoMapper.selectArdDroneList(new ArdDroneInfo());
-//        for (ArdDroneInfo ardDroneInfo : ardDroneInfos) {
-//            ardDroneInfo.getBoxSn();
-//        }
-//        List<ArdServerInfo> servers = ardServerInfoMapper.selectArdServerInfoList(ardServerInfo);
-//        for (ArdServerInfo server : servers) {
-//            for (ArdDroneInfo ardDroneInfo : server.getArdDroneInfoList()) {
-//                ardDroneInfo.getBoxSn();
-//            }
-//        }
-
         return ardServerInfoMapper.selectArdServerInfoList(ardServerInfo);
     }
 
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java b/ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java
index c6a0d66..ed5d4bb 100644
--- a/ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java
+++ b/ard-work/src/main/java/com/ard/work/service/impl/TyServiceImpl.java
@@ -606,6 +606,10 @@
         if (ardDroneInfo == null) {
             throw new ControlException("未找到对应的飞行器信息,boxSn:{}" + boxSn);
         }
+        //打点之前先停止打点
+        FlyPointControlCancelCmd flyPointControlCancelCmd = new FlyPointControlCancelCmd(FlyPointControlCancelDTO.builder().boxSn(boxSn).build());
+        this.sendMessage(ardDroneInfo.getServerId(), flyPointControlCancelCmd);
+
         FlyPointControlCmd flyPointControlCmd = new FlyPointControlCmd(flyPointControlDTO);
         this.sendMessage(ardDroneInfo.getServerId(), flyPointControlCmd);
         return true;
@@ -974,7 +978,6 @@
                 // 处理无人机遥测消息
                 UavTelemetryDataCmd uavTelemetryDataCmd = (UavTelemetryDataCmd) tyCommand;
                 TelemetryData telemetryData = uavTelemetryDataCmd.getTelemetryData();
-                telemetryData.setTimestamp(System.currentTimeMillis());//由于六合提供的状态时间戳不准,先以接收状态作为时间戳
                 UavTelemetryDataCache.put(telemetryData);
                 //logPrint("无人机遥测", uavTelemetryDataCmd.getTelemetryData().getBoxSn());
                 //WebSocketUtil.sendMessageAll(uavTelemetryDataCmd.getTelemetryData());
diff --git a/ard-work/src/main/resources/mapper/ArdFlightTaskMapper.xml b/ard-work/src/main/resources/mapper/ArdFlightTaskMapper.xml
index a231ccf..906d113 100644
--- a/ard-work/src/main/resources/mapper/ArdFlightTaskMapper.xml
+++ b/ard-work/src/main/resources/mapper/ArdFlightTaskMapper.xml
@@ -146,13 +146,13 @@
         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
diff --git a/ard-work/src/main/resources/mapper/ArdMediaMapper.xml b/ard-work/src/main/resources/mapper/ArdMediaMapper.xml
deleted file mode 100644
index 2548d11..0000000
--- a/ard-work/src/main/resources/mapper/ArdMediaMapper.xml
+++ /dev/null
@@ -1,202 +0,0 @@
-<?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="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="photoId" column="photo_id"/>
-        <result property="longitude" column="longitude"/>
-        <result property="latitude" column="latitude"/>
-        <result property="height" column="height"/>
-        <result property="thumbnailUrl" column="thumbnail_url"/>
-        <result property="captureTime" column="capture_time"/>
-        <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,thumbnail_url,
-        photo_id, longitude, latitude, height, capture_time, create_time
-        FROM ard_media
-        <where>
-            <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>
-            <if test="fileMd5 != null">AND file_md5 = #{fileMd5}</if>
-            <if test="mediaUrl != null">AND media_url = #{mediaUrl}</if>
-            <if test="sourceUrl != null">AND source_url = #{sourceUrl}</if>
-            <if test="thumbnailUrl != null">AND thumbnail_url = #{thumbnailUrl}</if>
-            <if test="photoId != null">AND photo_id = #{photoId}</if>
-            <if test="longitude != null">AND longitude = #{longitude}</if>
-            <if test="latitude != null">AND latitude = #{latitude}</if>
-            <if test="height != null">AND height = #{height}</if>
-            <if test="captureTime != null">AND capture_time = #{captureTime}</if>
-            <if test="createTime != null">AND create_time = #{createTime}</if>
-        </where>
-        ORDER BY
-            CASE
-                WHEN media_type = 0 THEN capture_time
-                ELSE create_time
-            END DESC
-    </select>
-
-
-    <!-- 根据任务ID查询媒体 -->
-    <select id="selectArdMediaByTaskId" resultMap="ArdMediaResult">
-        SELECT id,
-               task_id,
-               media_type,
-               file_size,
-               file_md5,
-               media_url,
-               source_url,
-               thumbnail_url,
-               photo_id,
-               longitude,
-               latitude,
-               height,
-               capture_time,
-               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,
-               thumbnail_url,
-               photo_id,
-               longitude,
-               latitude,
-               height,
-               capture_time,
-               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
-        <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="thumbnailUrl != null">thumbnail_url,</if>
-            <if test="photoId != null">photo_id,</if>
-            <if test="longitude != null">longitude,</if>
-            <if test="latitude != null">latitude,</if>
-            <if test="height != null">height,</if>
-            <if test="captureTime != null">capture_time,</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="thumbnailUrl != null">#{thumbnailUrl},</if>
-            <if test="photoId != null">#{photoId},</if>
-            <if test="longitude != null">#{longitude},</if>
-            <if test="latitude != null">#{latitude},</if>
-            <if test="height != null">#{height},</if>
-            <if test="captureTime != null">#{captureTime},</if>
-            <if test="createTime != null">#{createTime},</if>
-        </trim>
-    </insert>
-
-    <!-- 批量插入媒体记录 -->
-    <insert id="insertArdMediaList" parameterType="java.util.List">
-        INSERT INTO ard_media(
-        task_id,
-        media_type,
-        file_size,
-        file_md5,
-        media_url,
-        source_url,
-        thumbnail_url,
-        photo_id,
-        longitude,
-        latitude,
-        height,
-        capture_time,
-        create_time
-        )
-        VALUES
-        <foreach collection="list" item="item" separator=",">
-            (
-            #{item.taskId},
-            #{item.mediaType},
-            #{item.fileSize},
-            #{item.fileMd5},
-            #{item.mediaUrl},
-            #{item.sourceUrl},
-            #{item.thumbnailUrl},
-            #{item.photoId},
-            #{item.longitude},
-            #{item.latitude},
-            #{item.height},
-            #{item.captureTime},
-            #{item.createTime}
-            )
-        </foreach>
-    </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>
diff --git a/ard-work/src/main/resources/mapper/ArdMediaPicMapper.xml b/ard-work/src/main/resources/mapper/ArdMediaPicMapper.xml
new file mode 100644
index 0000000..986ade0
--- /dev/null
+++ b/ard-work/src/main/resources/mapper/ArdMediaPicMapper.xml
@@ -0,0 +1,115 @@
+<?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.ArdMediaPicMapper">
+
+    <resultMap id="ArdMediaPicResult" type="ArdMediaPic">
+        <result property="id" column="id"/>
+        <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="longitude" column="longitude"/>
+        <result property="latitude" column="latitude"/>
+        <result property="height" column="height"/>
+        <result property="taskId" column="task_id"/>
+        <result property="captureTime" column="capture_time"/>
+        <result property="createTime" column="create_time"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="syncStatus" column="sync_status"/>
+    </resultMap>
+
+    <!-- 查询图片列表 -->
+    <select id="selectPicList" resultMap="ArdMediaPicResult">
+        SELECT *
+        FROM ard_media_pic
+        <where>
+            <if test="taskId != null">AND task_id = #{taskId}</if>
+            <if test="syncStatus != null">AND sync_status = #{syncStatus}</if>
+        </where>
+        ORDER BY capture_time DESC
+    </select>
+    <!-- 查询待处理的图片 -->
+    <select id="selectPendingPics" resultMap="ArdMediaPicResult">
+        SELECT *
+        FROM ard_media_pic
+        WHERE sync_status = 0
+        ORDER BY id LIMIT #{limit}
+    </select>
+
+    <!-- 根据任务ID查询图片 -->
+    <select id="selectPicByTaskId" resultMap="ArdMediaPicResult">
+        SELECT * FROM ard_media_pic WHERE task_id = #{taskId}
+        ORDER BY capture_time DESC
+    </select>
+
+    <!-- 插入图片 -->
+    <insert id="insertPic" parameterType="ArdMediaPic">
+        INSERT INTO ard_media_pic (
+            id, task_id, file_size, file_md5,
+            media_url, source_url,
+            longitude, latitude, height,
+            capture_time, create_time,sync_status
+        ) VALUES (
+                     #{id}, #{taskId}, #{fileSize}, #{fileMd5},
+                     #{mediaUrl}, #{sourceUrl},
+                     #{longitude}, #{latitude}, #{height},
+                     #{captureTime}, #{createTime},0
+                 )
+    </insert>
+
+    <!-- 更新图片(按需补全字段) -->
+    <update id="updatePic" parameterType="ArdMediaPic">
+        UPDATE ard_media_pic
+        <set>
+            <if test="mediaUrl != null">
+                media_url = #{mediaUrl},
+            </if>
+            <if test="fileSize != null">
+                file_size = #{fileSize},
+            </if>
+            <if test="fileMd5 != null">
+                file_md5 = #{fileMd5},
+            </if>
+            <if test="captureTime != null">
+                capture_time = #{captureTime},
+            </if>
+            <if test="longitude != null">
+                longitude = #{longitude},
+            </if>
+            <if test="latitude != null">
+                latitude = #{latitude},
+            </if>
+            <if test="height != null">
+                height = #{height},
+            </if>
+            <if test="syncStatus != null">
+                sync_status = #{syncStatus},
+            </if>
+            update_time = #{updateTime}
+        </set>
+        WHERE id = #{id}
+    </update>
+
+    <!-- 标记为处理中 -->
+    <update id="markProcessing">
+        UPDATE ard_media_pic SET sync_status = 1 WHERE id = #{id} AND sync_status = 0
+    </update>
+    <!-- 更新同步状态 -->
+    <update id="updateSyncStatus">
+        UPDATE ard_media_pic
+        SET sync_status = #{status},update_time = NOW()
+        WHERE id = #{id}
+    </update>
+
+    <delete id="deletePicById">
+        DELETE FROM ard_media_pic WHERE id = #{id}
+    </delete>
+
+    <delete id="deletePicByTaskId">
+        DELETE FROM ard_media_pic WHERE task_id = #{taskId}
+    </delete>
+
+</mapper>
diff --git a/ard-work/src/main/resources/mapper/ArdMediaVideoMapper.xml b/ard-work/src/main/resources/mapper/ArdMediaVideoMapper.xml
new file mode 100644
index 0000000..a19d361
--- /dev/null
+++ b/ard-work/src/main/resources/mapper/ArdMediaVideoMapper.xml
@@ -0,0 +1,108 @@
+<?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.ArdMediaVideoMapper">
+
+    <!-- 结果映射 -->
+    <resultMap id="ArdMediaVideoResult" type="ArdMediaVideo">
+        <id property="id" column="id"/>
+        <result property="name" column="name"/>
+        <result property="fileSize" column="file_size"/>
+        <result property="mediaUrl" column="media_url"/>
+        <result property="sourceUrl" column="source_url"/>
+        <result property="thumbnailUrl" column="thumbnail_url"/>
+        <result property="startTime" column="start_time"/>
+        <result property="endTime" column="end_time"/>
+        <result property="spanSeconds" column="span_seconds"/>
+        <result property="taskId" column="task_id"/>
+        <result property="createTime" column="create_time"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="syncStatus" column="sync_status"/>
+    </resultMap>
+
+    <!-- 查询视频列表 -->
+    <select id="selectArdMediaVideoList" resultMap="ArdMediaVideoResult">
+        SELECT
+        id, name, file_size, media_url, source_url, thumbnail_url,
+        start_time, end_time, span_seconds, task_id,
+        create_time, update_time,sync_status
+        FROM ard_media_video
+        <where>
+            <if test="taskId != null">AND task_id = #{taskId}</if>
+            <if test="name != null and name != ''">AND name LIKE CONCAT('%', #{name}, '%')</if>
+            <if test="startTime != null">AND start_time &gt;= #{startTime}</if>
+            <if test="endTime != null">AND end_time &lt;= #{endTime}</if>
+            <if test="syncStatus != null">AND sync_status = #{syncStatus}</if>
+        </where>
+        ORDER BY start_time DESC
+    </select>
+
+    <!-- 查询待处理的视频 -->
+    <select id="selectPendingVideos" resultMap="ArdMediaVideoResult">
+        SELECT *
+        FROM ard_media_video
+        WHERE sync_status = 0
+        ORDER BY id LIMIT #{limit}
+    </select>
+
+    <!-- 根据任务ID查询 -->
+    <select id="selectArdMediaVideoByTaskId" resultMap="ArdMediaVideoResult">
+        SELECT *
+        FROM ard_media_video
+        WHERE task_id = #{taskId}
+        ORDER BY start_time DESC
+    </select>
+
+    <!-- 新增视频 -->
+    <insert id="insertArdMediaVideo" parameterType="com.ard.work.domain.entity.ArdMediaVideo">
+        INSERT INTO ard_media_video (id, name, file_size, media_url, source_url, thumbnail_url,
+                                     start_time, end_time, span_seconds, task_id,
+                                     create_time, update_time, sync_status)
+        VALUES (#{id}, #{name}, #{fileSize}, #{mediaUrl}, #{sourceUrl}, #{thumbnailUrl},
+                #{startTime}, #{endTime}, #{spanSeconds}, #{taskId},
+                #{createTime}, #{updateTime}, 0)
+    </insert>
+
+    <!-- 更新视频 -->
+    <update id="updateArdMediaVideo">
+        UPDATE ard_media_video
+        <set>
+            <if test="name != null">name = #{name},</if>
+            <if test="fileSize != null">file_size = #{fileSize},</if>
+            <if test="mediaUrl != null">media_url = #{mediaUrl},</if>
+            <if test="sourceUrl != null">source_url = #{sourceUrl},</if>
+            <if test="thumbnailUrl != null">thumbnail_url = #{thumbnailUrl},</if>
+            <if test="startTime != null">start_time = #{startTime},</if>
+            <if test="endTime != null">end_time = #{endTime},</if>
+            <if test="spanSeconds != null">span_seconds = #{spanSeconds},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="syncStatus != null">sync_status = #{syncStatus},</if>
+        </set>
+        WHERE id = #{id}
+    </update>
+    <!-- 标记为处理中 -->
+    <update id="markProcessing">
+        UPDATE ard_media_video SET sync_status = 1 WHERE id = #{id} AND sync_status = 0
+    </update>
+    <!-- 更新同步状态 -->
+    <update id="updateSyncStatus">
+        UPDATE ard_media_video
+        SET sync_status = #{status},update_time = NOW()
+        WHERE id = #{id}
+    </update>
+    <!-- 删除 -->
+    <delete id="deleteArdMediaVideoById">
+        DELETE
+        FROM ard_media_video
+        WHERE id = #{id}
+    </delete>
+
+    <delete id="deleteArdMediaVideoByTaskId">
+        DELETE
+        FROM ard_media_video
+        WHERE task_id = #{taskId}
+    </delete>
+
+</mapper>

--
Gitblit v1.9.3