From bb508127e9454e8b39ac7d431765b6de0ac74c35 Mon Sep 17 00:00:00 2001
From: liusuyi <1951119284@qq.com>
Date: Sat, 06 Sep 2025 15:39:02 +0800
Subject: [PATCH] 优化

---
 ard-ui/src/main.js                                                                            |    6 
 ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdWaylineDTO.java                   |   77 +
 ard-work/src/main/java/com/ard/tianyu/domain/entity/UavWayline.java                           |   80 +
 ard-work/src/main/java/com/ard/tianyu/util/GeoTools.java                                      |   98 ++
 ard-work/src/main/java/com/ard/tianyu/service/IKmzFileService.java                            |   13 
 ard-ui/package.json                                                                           |   10 
 pom.xml                                                                                       |    8 
 ard-work/src/main/java/com/ard/tianyu/config/OssConfiguration.java                            |   92 +
 ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdGimbalDTO.java                    |   27 
 ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/WaylineFileDTO.java                  |   43 
 ard-work/src/main/resources/mapper/UavWaylineMapper.xml                                       |  161 +++
 ard-work/src/main/java/com/ard/tianyu/config/GeoserverConfig.java                             |   14 
 ard-work/src/main/java/com/ard/tianyu/config/WaylinePlaningConfig.java                        |   18 
 ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdPositionDTO.java                  |   30 
 ard-work/src/main/java/com/ard/tianyu/service/impl/UavWaylineServiceImpl.java                 |  278 +++++
 ard-work/src/main/java/com/ard/tianyu/controller/UavWayLineController.java                    |    2 
 ard-work/src/main/java/com/ard/tianyu/service/impl/KmzFileServiceImpl.java                    |  517 ++++++++++
 ard-work/src/main/java/com/ard/tianyu/service/impl/GeoToolsServiceImpl.java                   |  158 +++
 ard-work/src/main/java/com/ard/tianyu/controller/wayline/terrain/TerrainFollowingPlanner.java |  222 ++++
 ard-work/src/main/java/com/ard/tianyu/domain/enums/WaylineEnum.java                           |   76 +
 ard-work/src/main/java/com/ard/tianyu/util/Vertex.java                                        |   29 
 ard-work/src/main/java/com/ard/tianyu/component/minio/config/MinioConfig.java                 |   44 
 ard-admin/src/main/resources/application.yml                                                  |    4 
 ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdActionDTO.java                    |   53 +
 ard-work/src/main/java/com/ard/tianyu/util/CameraUtil.java                                    |    6 
 ard-work/src/main/java/com/ard/tianyu/controller/wayline/terrain/GeoUtils.java                |   62 +
 ard-work/pom.xml                                                                              |   57 +
 ard-work/src/main/java/com/ard/tianyu/component/websocket/handler/TyMessageHandler.java       |    6 
 ard-work/src/main/java/com/ard/tianyu/service/IGeoToolsService.java                           |   16 
 ard-ui/vue.config.js                                                                          |   82 +
 ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdUavPositionDTO.java               |   65 +
 ard-admin/src/main/resources/application-dev.yml                                              |   30 
 ard-work/src/main/java/com/ard/tianyu/controller/wayline/UavWaylineController.java            |  114 ++
 ard-work/src/main/java/com/ard/tianyu/mapper/UavWaylineMapper.java                            |   71 +
 ard-work/src/main/java/com/ard/tianyu/service/IUavWaylineService.java                         |   71 +
 ard-work/src/main/java/com/ard/tianyu/component/minio/utils/MinioUtil.java                    |  210 ++++
 36 files changed, 2,820 insertions(+), 30 deletions(-)

diff --git a/ard-admin/src/main/resources/application-dev.yml b/ard-admin/src/main/resources/application-dev.yml
index 4bff9d9..9889498 100644
--- a/ard-admin/src/main/resources/application-dev.yml
+++ b/ard-admin/src/main/resources/application-dev.yml
@@ -37,14 +37,36 @@
 # 天宇服务器配置
 tianyu:
  # 天宇http服务器地址
- endpoint: 59.110.237.99:8060
+ endpoint: 192.168.1.97:8060
  accesskeyid: wuwuwu
  accesskeysecret: 9c9f139f63b343b0a9ef42e05ed29b0b
  # 天宇tcp服务器地址
- tcpserver: 59.110.237.99
- tcpport: 8125
+ tcpserver: 192.168.1.97
+ tcpport: 8126
 
+# 地图资源配置
+geoserver:
+  deviceCoordinateType: WGS84
+  tifPath: H:\map\hlj.tif
+wayline:
+  #可理解为100米大小占图像的比例
+  targetRatio: 0.5
+  #最大航段距离
+  maxLength: 2000
+  #最小航段距离
+  minLength: 1
+  #随飞随动分段长度
+  PeerAtLength: 10
+  #航线KMZ临时存放路径
+  waylineSavePath: /home/ard/myworkspace/uav_server/mainserver/waylines
+
+# MinIO
+minio:
+  url: http://192.168.1.15:9001
+  accessKey: admin
+  secretKey: xzx12345
+  bucketName: kmz
 #流媒体配置
 mediamtx:
   # 流媒体服务器地址
-  host: 192.168.1.27
\ No newline at end of file
+  host: 192.168.1.15
\ No newline at end of file
diff --git a/ard-admin/src/main/resources/application.yml b/ard-admin/src/main/resources/application.yml
index 4347a12..2d96918 100644
--- a/ard-admin/src/main/resources/application.yml
+++ b/ard-admin/src/main/resources/application.yml
@@ -111,13 +111,13 @@
   # redis 配置
   redis:
     # 地址
-    host: 127.0.0.1
+    host: 192.168.1.15
     # 端口,默认为6379
     port: 6379
     # 数据库索引
     database: 0
     # 密码
-    password: xzx12345
+    password:
     # 连接超时时间
     timeout: 10s
     lettuce:
diff --git a/ard-ui/package.json b/ard-ui/package.json
index 2a091d2..f1c7380 100644
--- a/ard-ui/package.json
+++ b/ard-ui/package.json
@@ -6,10 +6,16 @@
   "license": "MIT",
   "scripts": {
     "dev": "vue-cli-service serve",
-    "build:prod": "vue-cli-service build",
+    "build:prod": "vue-cli-service build --mode production",
     "build:stage": "vue-cli-service build --mode staging",
+    "build:analyze": "vue-cli-service build --report",
+    "build:test": "vue-cli-service build --mode test",
     "preview": "node build/index.js --preview",
-    "lint": "eslint --ext .js,.vue src"
+    "lint": "eslint --ext .js,.vue src",
+    "lint:fix": "eslint --ext .js,.vue src --fix",
+    "clean": "rimraf dist",
+    "serve:prod": "npm run build:prod && npm run preview",
+    "serve:stage": "npm run build:stage && npm run preview"
   },
   "husky": {
     "hooks": {
diff --git a/ard-ui/src/main.js b/ard-ui/src/main.js
index 13c6cf2..baa78af 100644
--- a/ard-ui/src/main.js
+++ b/ard-ui/src/main.js
@@ -37,6 +37,10 @@
 import VueMeta from 'vue-meta'
 // 字典数据组件
 import DictData from '@/components/DictData'
+// 性能监控工具
+import performanceMonitor from '@/utils/performance'
+// 缓存管理器
+import cacheManager from '@/utils/cache'
 
 // 全局方法挂载
 Vue.prototype.getDicts = getDicts
@@ -48,6 +52,8 @@
 Vue.prototype.selectDictLabels = selectDictLabels
 Vue.prototype.download = download
 Vue.prototype.handleTree = handleTree
+Vue.prototype.performanceMonitor = performanceMonitor
+Vue.prototype.cacheManager = cacheManager
 
 // 全局组件挂载
 Vue.component('DictTag', DictTag)
diff --git a/ard-ui/vue.config.js b/ard-ui/vue.config.js
index be17c26..c27e37d 100644
--- a/ard-ui/vue.config.js
+++ b/ard-ui/vue.config.js
@@ -1,14 +1,13 @@
 'use strict'
 const path = require('path')
+const CompressionPlugin = require('compression-webpack-plugin')
+const TerserPlugin = require('terser-webpack-plugin')
 
 function resolve(dir) {
   return path.join(__dirname, dir)
 }
 
-const CompressionPlugin = require('compression-webpack-plugin')
-
 const name = process.env.VUE_APP_TITLE || '无人机后台管理系统' // 网页标题
-
 const port = process.env.port || process.env.npm_config_port || 80 // 端口
 
 // vue.config.js 配置说明
@@ -27,12 +26,23 @@
   lintOnSave: process.env.NODE_ENV === 'development',
   // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
   productionSourceMap: false,
+  // 转译依赖
   transpileDependencies: ['quill'],
   // webpack-dev-server 相关配置
   devServer: {
     host: '0.0.0.0',
     port: port,
     open: true,
+    // 添加热更新配置
+    hot: true,
+    // 添加客户端日志级别
+    client: {
+      logging: 'warn',
+      overlay: {
+        errors: true,
+        warnings: false,
+      },
+    },
     proxy: {
       // detail: https://cli.vuejs.org/config/#devserver-proxy
       [process.env.VUE_APP_BASE_API]: {
@@ -40,34 +50,66 @@
         changeOrigin: true,
         pathRewrite: {
           ['^' + process.env.VUE_APP_BASE_API]: ''
-        }
+        },
+        // 添加代理超时配置
+        timeout: 30000,
+        // 添加代理日志
+        logLevel: 'debug'
       }
     },
-    disableHostCheck: true
+    // 移除已废弃的配置
+    // disableHostCheck: true,
+    // 使用新的配置
+    allowedHosts: 'all'
   },
   css: {
     loaderOptions: {
       sass: {
         sassOptions: { outputStyle: "expanded" }
       }
-    }
+    },
+    // 添加CSS提取配置
+    extract: process.env.NODE_ENV === 'production'
   },
   configureWebpack: {
     name: name,
     resolve: {
       alias: {
         '@': resolve('src')
-      }
+      },
+      // 添加文件扩展名解析
+      extensions: ['.js', '.vue', '.json']
+    },
+    // 添加性能提示配置
+    performance: {
+      hints: process.env.NODE_ENV === 'production' ? 'warning' : false,
+      maxEntrypointSize: 512000,
+      maxAssetSize: 512000
+    },
+    // 添加优化配置
+    optimization: {
+      minimizer: [
+        new TerserPlugin({
+          terserOptions: {
+            compress: {
+              drop_console: process.env.NODE_ENV === 'production',
+              drop_debugger: process.env.NODE_ENV === 'production'
+            }
+          }
+        })
+      ]
     },
     plugins: [
       // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
       new CompressionPlugin({
         cache: false,                                  // 不启用文件缓存
-        test: /\.(js|css|html|jpe?g|png|gif|svg)?$/i,  // 压缩文件格式
+        test: /\.(js|css|html|jpe?g|png|gif|svg|woff2?|eot|ttf|otf)?$/i,  // 压缩文件格式
         filename: '[path][base].gz[query]',            // 压缩后的文件名
         algorithm: 'gzip',                             // 使用gzip压缩
         minRatio: 0.8,                                 // 压缩比例,小于 80% 的文件不会被压缩
-        deleteOriginalAssets: false                    // 压缩后删除原文件
+        deleteOriginalAssets: false,                   // 压缩后删除原文件
+        threshold: 10240,                              // 只有大于该值的资源会被处理
+        minSize: 0                                     // 只有大于该值的资源会被处理
       })
     ],
   },
@@ -91,6 +133,13 @@
         symbolId: 'icon-[name]'
       })
       .end()
+
+    // 添加图片压缩配置
+    config.module
+      .rule('images')
+      .use('url-loader')
+      .loader('url-loader')
+      .tap(options => Object.assign(options, { limit: 10240 }))
 
     config.when(process.env.NODE_ENV !== 'development', config => {
           config
@@ -116,6 +165,11 @@
                 test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm
                 priority: 20 // the weight needs to be larger than libs and app or it will be packaged into libs or app
               },
+              echarts: {
+                name: 'chunk-echarts', // split echarts into a single package
+                test: /[\\/]node_modules[\\/]_?echarts(.*)/,
+                priority: 15
+              },
               commons: {
                 name: 'chunk-commons',
                 test: resolve('src/components'), // can customize your rules
@@ -127,5 +181,15 @@
           })
           config.optimization.runtimeChunk('single')
     })
+
+    // 添加开发环境优化
+    config.when(process.env.NODE_ENV === 'development', config => {
+      config.devtool('cheap-module-eval-source-map')
+    })
+
+    // 添加生产环境优化
+    config.when(process.env.NODE_ENV === 'production', config => {
+      config.devtool('source-map')
+    })
   }
 }
diff --git a/ard-work/pom.xml b/ard-work/pom.xml
index 63e9ebb..5b14053 100644
--- a/ard-work/pom.xml
+++ b/ard-work/pom.xml
@@ -14,7 +14,10 @@
     <description>
         主业务入口
     </description>
-
+    <properties>
+        <minio.version>8.3.7</minio.version>
+        <dom4j.version>2.1.3</dom4j.version>
+    </properties>
     <dependencies>
 
         <!-- 通用工具-->
@@ -69,6 +72,56 @@
             <groupId>org.aspectj</groupId>
             <artifactId>aspectjweaver</artifactId>
         </dependency>
+        <!--geo-->
+        <dependency>
+            <groupId>org.geotools</groupId>
+            <artifactId>gt-geotiff</artifactId>
+            <version>28.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.geotools</groupId>
+            <artifactId>gt-main</artifactId>
+            <version>28.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.geotools</groupId>
+            <artifactId>gt-referencing</artifactId>
+            <version>28.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.geotools</groupId>
+            <artifactId>gt-epsg-hsql</artifactId>
+            <version>28.2</version>
+        </dependency>
+        <!--minio-->
+        <dependency>
+            <groupId>io.minio</groupId>
+            <artifactId>minio</artifactId>
+            <version>${minio.version}</version>
+        </dependency>
+        <!--DOM4j-->
+        <dependency>
+            <groupId>org.dom4j</groupId>
+            <artifactId>dom4j</artifactId>
+            <version>${dom4j.version}</version>
+        </dependency>
+        <!--okhttp3-->
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+        </dependency>
     </dependencies>
-
+    <repositories>
+        <repository>
+            <id>osgeo</id>
+            <name>OSGeo Release Repository</name>
+            <url>https://repo.osgeo.org/repository/release/</url>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+        </repository>
+    </repositories>
 </project>
\ No newline at end of file
diff --git a/ard-work/src/main/java/com/ard/tianyu/component/minio/config/MinioConfig.java b/ard-work/src/main/java/com/ard/tianyu/component/minio/config/MinioConfig.java
new file mode 100644
index 0000000..8eda37b
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/component/minio/config/MinioConfig.java
@@ -0,0 +1,44 @@
+package com.ard.tianyu.component.minio.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import io.minio.MinioClient;
+
+/**
+ * Minio 配置信息
+ *
+ * @author ruoyi
+ */
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "minio")
+public class MinioConfig {
+
+    /**
+     * 服务地址
+     */
+    private String url;
+
+    /**
+     * 用户名
+     */
+    private String accessKey;
+
+    /**
+     * 密码
+     */
+    private String secretKey;
+
+    /**
+     * 存储桶名称
+     */
+    private String bucketName;
+
+
+    @Bean
+    public MinioClient getMinioClient() {
+        return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build();
+    }
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/component/minio/utils/MinioUtil.java b/ard-work/src/main/java/com/ard/tianyu/component/minio/utils/MinioUtil.java
new file mode 100644
index 0000000..a6c0ed6
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/component/minio/utils/MinioUtil.java
@@ -0,0 +1,210 @@
+package com.ard.tianyu.component.minio.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.ard.common.utils.ServletUtils;
+import com.ard.common.utils.spring.SpringUtils;
+import io.minio.*;
+import org.springframework.web.multipart.MultipartFile;
+import io.minio.http.Method;
+
+/**
+ * Minio 文件存储工具类
+ *
+ * @author ruoyi
+ */
+public class MinioUtil {
+    /**
+     * 判断桶是否存在
+     *
+     * @param bucketName 桶名称
+     * @return 如果桶存在返回 true,否则返回 false
+     * @throws IOException
+     */
+    public static boolean bucketExists(String bucketName) throws IOException {
+        MinioClient minioClient = SpringUtils.getBean(MinioClient.class);
+        try {
+            return minioClient.bucketExists(io.minio.BucketExistsArgs.builder().bucket(bucketName).build());
+        } catch (Exception e) {
+            throw new IOException("Error checking if bucket exists", e);
+        }
+    }
+
+    // 创建桶
+    public static void createBucket(String bucketName) throws IOException {
+        MinioClient minioClient = SpringUtils.getBean(MinioClient.class);
+        try {
+            minioClient.makeBucket(io.minio.MakeBucketArgs.builder().bucket(bucketName).build());
+            setBucketPolicy(bucketName);
+        } catch (Exception e) {
+            throw new IOException("Error creating bucket", e);
+        }
+    }
+
+
+    /**
+     * 设置桶策略
+     * 刘苏义
+     * 2023/9/20 10:01:39
+     */
+    public static boolean setBucketPolicy(String bucket) throws Exception {
+        MinioClient minioClient = SpringUtils.getBean(MinioClient.class);
+        try {
+            //设置策略
+            String sb = "{\"Version\":\"2012-10-17\"," +
+                    "\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":" +
+                    "{\"AWS\":[\"*\"]},\"Action\":[\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"," +
+                    "\"s3:GetBucketLocation\"],\"Resource\":[\"arn:aws:s3:::" + bucket +
+                    "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:PutObject\",\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\"],\"Resource\":[\"arn:aws:s3:::" +
+                    bucket + "/*\"]}]}";
+            SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder()
+                    .bucket(bucket)
+                    .config(sb)
+                    .build();
+            minioClient.setBucketPolicy(setBucketPolicyArgs);
+            return true;
+        } catch (Exception ex) {
+            throw new Exception("设置桶策略异常:" + ex.getMessage());
+        }
+    }
+
+
+    /**
+     * 上传文件
+     *
+     * @param bucketName 桶名称
+     * @param fileName
+     * @throws IOException
+     */
+    public static String uploadFile(String bucketName, String fileName, MultipartFile multipartFile) throws IOException {
+        String url = "";
+        MinioClient minioClient = SpringUtils.getBean(MinioClient.class);
+        try (InputStream inputStream = multipartFile.getInputStream()) {
+            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(inputStream,
+                    multipartFile.getSize(), -1).contentType(multipartFile.getContentType()).build());
+            url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(fileName).method(Method.GET).build());
+            url = url.substring(0, url.indexOf('?'));
+            return ServletUtils.urlDecode(url);
+        } catch (Exception e) {
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 上传本地文件
+     *
+     * @param bucketName    桶名称
+     * @param fileName      存储到 Minio 的文件名
+     * @param localFilePath 本地文件绝对路径
+     * @return 访问 URL
+     * @throws IOException
+     */
+    public static String uploadLocalFile(String bucketName, String fileName, String localFilePath) throws IOException {
+        MinioClient minioClient = SpringUtils.getBean(MinioClient.class);
+        try (InputStream inputStream = new FileInputStream(localFilePath)) {
+            File file = new File(localFilePath);
+            minioClient.putObject(
+                    PutObjectArgs.builder()
+                            .bucket(bucketName)
+                            .object(fileName)
+                            .stream(inputStream, file.length(), -1)
+                            .contentType("application/octet-stream") // 可根据需求自行修改
+                            .build()
+            );
+            String url = minioClient.getPresignedObjectUrl(
+                    GetPresignedObjectUrlArgs.builder()
+                            .bucket(bucketName)
+                            .object(fileName)
+                            .method(Method.GET)
+                            .build()
+            );
+            return ServletUtils.urlDecode(url.substring(0, url.indexOf('?')));
+        } catch (Exception e) {
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+
+    public static String uploadInputStream(String bucketName, String fileName, InputStream inputStream, long size,
+                                           String contentType) throws IOException {
+        MinioClient minioClient = SpringUtils.getBean(MinioClient.class);
+        if (!bucketExists(bucketName)) {
+            createBucket(bucketName);
+        }
+        try {
+            minioClient.putObject(
+                    PutObjectArgs.builder()
+                            .bucket(bucketName)
+                            .object(fileName)
+                            .stream(inputStream, size, -1)
+                            .contentType(contentType)
+                            .build()
+            );
+
+            String url = minioClient.getPresignedObjectUrl(
+                    GetPresignedObjectUrlArgs.builder()
+                            .bucket(bucketName)
+                            .object(fileName)
+                            .method(Method.GET)
+                            .build()
+            );
+            url = url.substring(0, url.indexOf('?'));
+            return ServletUtils.urlDecode(url);
+        } catch (Exception e) {
+            throw new IOException(e.getMessage(), e);
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException ignore) {
+                }
+            }
+        }
+    }
+
+    /**
+     * 判断文件是否存在
+     */
+    public static boolean exists(String bucketName, String fileName) throws IOException {
+        MinioClient minioClient = SpringUtils.getBean(MinioClient.class);
+        if (!bucketExists(bucketName)) {
+            createBucket(bucketName);
+        }
+        try {
+            minioClient.statObject(
+                    io.minio.StatObjectArgs.builder()
+                            .bucket(bucketName)
+                            .object(fileName)
+                            .build()
+            );
+            return true;
+        } catch (io.minio.errors.ErrorResponseException e) {
+            if ("NoSuchKey".equals(e.errorResponse().code()) || "NoSuchObject".equals(e.errorResponse().code())) {
+                return false;
+            }
+            throw new IOException(e.getMessage(), e);
+        } catch (Exception e) {
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 删除文件
+     */
+    public static void delete(String bucketName, String fileName) throws IOException {
+        MinioClient minioClient = SpringUtils.getBean(MinioClient.class);
+        try {
+            minioClient.removeObject(
+                    io.minio.RemoveObjectArgs.builder()
+                            .bucket(bucketName)
+                            .object(fileName)
+                            .build()
+            );
+        } catch (Exception e) {
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/component/websocket/handler/TyMessageHandler.java b/ard-work/src/main/java/com/ard/tianyu/component/websocket/handler/TyMessageHandler.java
index bfb4cfa..c9b158f 100644
--- a/ard-work/src/main/java/com/ard/tianyu/component/websocket/handler/TyMessageHandler.java
+++ b/ard-work/src/main/java/com/ard/tianyu/component/websocket/handler/TyMessageHandler.java
@@ -122,7 +122,7 @@
             Boolean subscribe = uavStateMessage.getSubscribe();
             if (subscribe) {
                 // 执行订阅操作
-                uavStateManager.subscribe(boxSn, userId, 2);
+                uavStateManager.subscribe(boxSn, userId, 1);
             } else {
                 // 执行退订操作
                 uavStateManager.unsubscribe(boxSn, userId);
@@ -153,7 +153,7 @@
             Boolean subscribe = uavTelemetryMessage.getSubscribe();
             if (subscribe) {
                 // 执行订阅操作
-                uavTelemetryManager.subscribe(boxSn, userId, 2);
+                uavTelemetryManager.subscribe(boxSn, userId, 1);
             } else {
                 // 执行退订操作
                 uavTelemetryManager.unsubscribe(boxSn, userId);
@@ -239,7 +239,7 @@
             Boolean subscribe = uavControlMessage.getSubscribe();
             if (subscribe) {
                 // 执行订阅操作
-                uavAllManager.subscribe(boxSn, userId, 2);
+                uavAllManager.subscribe(boxSn, userId, 1);
             } else {
                 // 执行退订操作
                 uavAllManager.unsubscribe(boxSn, userId);
diff --git a/ard-work/src/main/java/com/ard/tianyu/config/GeoserverConfig.java b/ard-work/src/main/java/com/ard/tianyu/config/GeoserverConfig.java
new file mode 100644
index 0000000..409a28e
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/config/GeoserverConfig.java
@@ -0,0 +1,14 @@
+package com.ard.tianyu.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "geoserver")
+public class GeoserverConfig {
+    private String deviceCoordinateType;
+    private String tifPath;//用于仿地飞行的地形文件
+
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/config/OssConfiguration.java b/ard-work/src/main/java/com/ard/tianyu/config/OssConfiguration.java
new file mode 100644
index 0000000..5a0678a
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/config/OssConfiguration.java
@@ -0,0 +1,92 @@
+package com.ard.tianyu.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author sean
+ * @version 0.2
+ * @date 2021/12/9
+ */
+@ConfigurationProperties(prefix = "oss")
+@Component
+public class OssConfiguration {
+
+    /**
+     * @see com.dji.ard.component.oss.model.enums.OssTypeEnum
+     */
+    public static String provider;
+
+    /**
+     * Whether to use the object storage service.
+     */
+    public static boolean enable;
+
+    /**
+     * The protocol needs to be included at the beginning of the address.
+     */
+    public static String endpoint;
+
+    public static String accessKey;
+
+    public static String secretKey;
+
+    public static String region;
+
+    public static Long expire;
+
+    public static String roleSessionName;
+
+    public static String roleArn;
+
+    public static String bucket;
+
+    public static String objectDirPrefix;
+
+    public void setProvider(String provider) {
+        OssConfiguration.provider = provider;
+    }
+
+    public void setEnable(boolean enable) {
+        OssConfiguration.enable = enable;
+    }
+
+    public void setEndpoint(String endpoint) {
+        OssConfiguration.endpoint = endpoint;
+    }
+
+    public void setAccessKey(String accessKey) {
+        OssConfiguration.accessKey = accessKey;
+    }
+
+    public void setSecretKey(String secretKey) {
+        OssConfiguration.secretKey = secretKey;
+    }
+
+    public void setRegion(String region) {
+        OssConfiguration.region = region;
+    }
+
+    public void setExpire(Long expire) {
+        OssConfiguration.expire = expire;
+    }
+
+    public void setRoleSessionName(String roleSessionName) {
+        OssConfiguration.roleSessionName = roleSessionName;
+    }
+
+    public void setRoleArn(String roleArn) {
+        OssConfiguration.roleArn = roleArn;
+    }
+
+    public void setBucket(String bucket) {
+        OssConfiguration.bucket = bucket;
+    }
+
+    public void setObjectDirPrefix(String objectDirPrefix) {
+        OssConfiguration.objectDirPrefix = objectDirPrefix;
+    }
+}
+
+
+
diff --git a/ard-work/src/main/java/com/ard/tianyu/config/WaylinePlaningConfig.java b/ard-work/src/main/java/com/ard/tianyu/config/WaylinePlaningConfig.java
new file mode 100644
index 0000000..fa897e1
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/config/WaylinePlaningConfig.java
@@ -0,0 +1,18 @@
+package com.ard.tianyu.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "wayline")
+public class WaylinePlaningConfig {
+
+    private Float targetRatio;
+    private Integer maxLength;
+    private Integer minLength;
+    private Integer PeerAtLength;
+    private String waylineSavePath;
+
+}
\ No newline at end of file
diff --git a/ard-work/src/main/java/com/ard/tianyu/controller/UavWayLineController.java b/ard-work/src/main/java/com/ard/tianyu/controller/UavWayLineController.java
index e4200d9..b2d19eb 100644
--- a/ard-work/src/main/java/com/ard/tianyu/controller/UavWayLineController.java
+++ b/ard-work/src/main/java/com/ard/tianyu/controller/UavWayLineController.java
@@ -19,7 +19,7 @@
  * @author liusuyi
  * @date 2024年10月21日13:52
  **/
-@Api(tags = "无人机航线飞行接口")
+@Api(tags = "无人机航线操作接口")
 @RequestMapping("/uav/wayline")
 @RestController
 public class UavWayLineController {
diff --git a/ard-work/src/main/java/com/ard/tianyu/controller/wayline/UavWaylineController.java b/ard-work/src/main/java/com/ard/tianyu/controller/wayline/UavWaylineController.java
new file mode 100644
index 0000000..6dddc4f
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/controller/wayline/UavWaylineController.java
@@ -0,0 +1,114 @@
+package com.ard.tianyu.controller.wayline;
+
+import com.ard.tianyu.domain.dto.wayline.ArdWaylineDTO;
+import com.ard.tianyu.domain.entity.UavWayline;
+import com.ard.tianyu.service.IUavWaylineService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import java.util.List;
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import com.ard.common.annotation.Log;
+import com.ard.common.core.controller.BaseController;
+import com.ard.common.core.domain.AjaxResult;
+import com.ard.common.enums.BusinessType;
+import com.ard.common.utils.poi.ExcelUtil;
+import com.ard.common.core.page.TableDataInfo;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 航线Controller
+ * 
+ * @author ruoyi
+ * @date 2025-06-27
+ */
+@Api(tags = "无人机航线管理接口")
+@RestController
+@RequestMapping("/uav/manage/wayline")
+public class UavWaylineController extends BaseController
+{
+    @Resource
+    private IUavWaylineService uavWaylineService;
+
+    /**
+     * 查询航线列表
+     */
+    @ApiOperation("查询航线列表")
+    @PreAuthorize("@ss.hasPermi('uav:wayline:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(UavWayline uavWayline)
+    {
+        startPage();
+        List<UavWayline> list = uavWaylineService.selectUavWaylineList(uavWayline);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出航线列表
+     */
+    @PreAuthorize("@ss.hasPermi('uav:wayline:export')")
+    @Log(title = "航线", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, UavWayline uavWayline)
+    {
+        List<UavWayline> list = uavWaylineService.selectUavWaylineList(uavWayline);
+        ExcelUtil<UavWayline> util = new ExcelUtil<UavWayline>(UavWayline.class);
+        util.exportExcel(response, list, "航线数据");
+    }
+
+    /**
+     * 获取航线详细信息
+     */
+    @ApiOperation("获取航线详细信息")
+    @PreAuthorize("@ss.hasPermi('uav:wayline:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") String id)
+    {
+        return success(uavWaylineService.selectUavWaylineById(id));
+    }
+
+    /**
+     * 新增航线
+     */
+    @ApiOperation("新增航线")
+    @PreAuthorize("@ss.hasPermi('uav:wayline:add')")
+    @Log(title = "航线", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody ArdWaylineDTO ardWaylineDTO)
+    {
+        if (!uavWaylineService.checkWaylineNameUnique(ardWaylineDTO))
+        {
+            return error("新增航线'" + ardWaylineDTO.getWaylineName() + "'失败,航线已存在");
+        }
+        return toAjax(uavWaylineService.insertUavWayline(ardWaylineDTO));
+    }
+
+    /**
+     * 修改航线
+     */
+    @ApiOperation("修改航线")
+    @PreAuthorize("@ss.hasPermi('uav:wayline:edit')")
+    @Log(title = "航线", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody ArdWaylineDTO ardWaylineDTO)
+    {
+        if (!uavWaylineService.checkWaylineNameUnique(ardWaylineDTO))
+        {
+            return error("修改航线'" + ardWaylineDTO.getWaylineName() + "'失败,航线已存在");
+        }
+        return toAjax(uavWaylineService.updateUavWayline(ardWaylineDTO));
+    }
+
+    /**
+     * 删除航线
+     */
+    @ApiOperation("删除航线")
+    @PreAuthorize("@ss.hasPermi('uav:wayline:remove')")
+    @Log(title = "航线", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable String[] ids)
+    {
+        return toAjax(uavWaylineService.deleteUavWaylineByIds(ids));
+    }
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/controller/wayline/terrain/GeoUtils.java b/ard-work/src/main/java/com/ard/tianyu/controller/wayline/terrain/GeoUtils.java
new file mode 100644
index 0000000..8c03c43
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/controller/wayline/terrain/GeoUtils.java
@@ -0,0 +1,62 @@
+package com.ard.tianyu.controller.wayline.terrain;
+
+import com.ard.tianyu.config.GeoserverConfig;
+import lombok.extern.slf4j.Slf4j;
+import org.geotools.coverage.grid.GridCoverage2D;
+import org.geotools.coverage.processing.Operations;
+import org.geotools.gce.geotiff.GeoTiffReader;
+import org.geotools.geometry.DirectPosition2D;
+import org.geotools.referencing.CRS;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.springframework.stereotype.Component;
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.io.File;
+import java.io.IOException;
+
+@Slf4j
+@Component
+public class GeoUtils {
+
+    @Resource
+    private GeoserverConfig geoserverConfig;
+    private GridCoverage2D coverage;
+
+    /**
+     * 加载地形文件
+     *
+     * @author lsy
+     * @date 2025/6/18
+     */
+    @PostConstruct
+    public void initTif() {
+        try {
+            File file = new File(geoserverConfig.getTifPath());
+            GeoTiffReader reader = new GeoTiffReader(file);
+            GridCoverage2D rawCoverage = reader.read(null);
+            CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4326");
+            coverage = (GridCoverage2D) Operations.DEFAULT.resample(rawCoverage, targetCRS);
+        } catch (Exception e) {
+            log.error("加载tif地形文件异常:{}", e.getMessage());
+        }
+    }
+
+    /**
+     * 获取地形高度
+     *
+     * @param lon 经度
+     * @param lat 纬度
+     * @return 海拔高度
+     * @author lsy
+     * @date 2025/6/18
+     */
+
+    public double getHeight(double lon, double lat) throws IOException {
+        DirectPosition pos = new DirectPosition2D(null, lat, lon);
+        double[] result = coverage.evaluate(pos, new double[1]);
+        System.out.println("Elevation at (" + lon + ", " + lat + "): " + result[0]);
+        return result[0];
+    }
+
+}
\ No newline at end of file
diff --git a/ard-work/src/main/java/com/ard/tianyu/controller/wayline/terrain/TerrainFollowingPlanner.java b/ard-work/src/main/java/com/ard/tianyu/controller/wayline/terrain/TerrainFollowingPlanner.java
new file mode 100644
index 0000000..2703a0f
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/controller/wayline/terrain/TerrainFollowingPlanner.java
@@ -0,0 +1,222 @@
+package com.ard.tianyu.controller.wayline.terrain;
+
+import com.ard.tianyu.domain.dto.wayline.ArdActionDTO;
+import com.ard.tianyu.domain.dto.wayline.ArdPositionDTO;
+import com.ard.tianyu.util.GeoTools;
+import com.ard.tianyu.util.Vertex;
+import com.ard.tianyu.domain.dto.wayline.ArdUavPositionDTO;
+import com.ard.tianyu.domain.dto.wayline.ArdWaylineDTO;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.stereotype.Component;
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 仿地飞行规划器
+ *
+ * @author lsy
+ * @date 2025/6/18
+ */
+@Component
+public class TerrainFollowingPlanner {
+    @Resource
+    private GeoUtils geoUtils;
+
+    private final double intervalMeter = 100.0; // 插值距离
+    private final double smoothThreshold = 3.0; // 平滑阈值
+    private static final ObjectMapper mapper = new ObjectMapper();
+
+    // 算法主函数
+    public List<ArdUavPositionDTO> interpolateWithTerrain(ArdWaylineDTO ardWaylineDTO) throws Exception {
+        // 0. 验证源航线数据
+        validateWaylineData(ardWaylineDTO);
+
+        // 1. 先进行纯几何插值(不考虑高度)
+        List<ArdUavPositionDTO> interpolatedPoints = pureInterpolation(ardWaylineDTO.getUavPositions());
+
+        // 2. 然后统一处理地形高度
+        return adjustAltitudeForTerrain(interpolatedPoints, ardWaylineDTO.getGlobalFlyHeight());
+    }
+
+    // 第一阶段:纯几何插值
+    private List<ArdUavPositionDTO> pureInterpolation(List<ArdUavPositionDTO> originalPoints) {
+        List<ArdUavPositionDTO> result = new ArrayList<>();
+
+        for (int i = 0; i < originalPoints.size() - 1; i++) {
+            ArdUavPositionDTO start = originalPoints.get(i);
+            ArdUavPositionDTO end = originalPoints.get(i + 1);
+
+            // 添加原始起点
+            result.add(deepCopy(start, ArdUavPositionDTO.class));
+
+            // 插值中间点
+            result.addAll(interpolateBetween(start, end));
+        }
+
+        // 添加最后一个点
+        result.add(deepCopy(originalPoints.get(originalPoints.size() - 1), ArdUavPositionDTO.class));
+
+        return result;
+    }
+
+    // 第二阶段:地形高度调整
+    private List<ArdUavPositionDTO> adjustAltitudeForTerrain(List<ArdUavPositionDTO> points, double flyHeight) throws IOException {
+
+        double homeTerrainHeight = 0; // 记录起飞点地形高度作为基准海拔
+        double lastFlightAltitude = flyHeight; // 记录上一个飞行高度
+
+        for (int i = 0; i < points.size(); i++) {
+            ArdUavPositionDTO point = points.get(i);
+            double currentTerrainHeight = geoUtils.getHeight(
+                    point.getUavPosition().getLongitude(),
+                    point.getUavPosition().getLatitude()
+            );
+            if (i == 0) {
+                homeTerrainHeight = currentTerrainHeight;
+            }
+            // 计算相对于起飞点的地形高度差
+            double terrainHeightDiff = currentTerrainHeight - homeTerrainHeight;
+
+            // 理论目标高度 = 设定高度 + 地形高度变化量
+            double targetAltitude = flyHeight + terrainHeightDiff;
+
+            // 平滑处理:如果高度变化不大,沿用上一个飞行高度
+            if (i > 0 && Math.abs(targetAltitude - lastFlightAltitude) < smoothThreshold) {
+                targetAltitude = lastFlightAltitude;
+            }
+
+            // 设置高度
+            point.getUavPosition().setAltitude(targetAltitude);
+            updateActionAltitude(point, currentTerrainHeight);
+
+            lastFlightAltitude = targetAltitude; // 更新最后飞行高度
+        }
+
+        renumberWaypoints(points);
+        return points;
+    }
+
+    // 辅助方法:两点间插值
+    private List<ArdUavPositionDTO> interpolateBetween(ArdUavPositionDTO start, ArdUavPositionDTO end) {
+        List<ArdUavPositionDTO> interpolated = new ArrayList<>();
+        // 计算两点间的距离
+        Vertex startV=new Vertex( start.getUavPosition().getLatitude(), start.getUavPosition().getLongitude());
+        Vertex endV=new Vertex( end.getUavPosition().getLatitude(), end.getUavPosition().getLongitude());
+        double distance = GeoTools.DistanceSphere(startV,endV);
+        // 如果距离小于插值间隔,则直接返回
+        if (distance <= intervalMeter) return interpolated;
+        // 计算插值步数
+        int steps = (int) (distance / intervalMeter);
+        for (int i = 1; i < steps; i++) {
+            double ratio = i * intervalMeter / distance;
+            double lat = interpolate(
+                    start.getUavPosition().getLatitude(),
+                    end.getUavPosition().getLatitude(),
+                    ratio
+            );
+            double lon = interpolate(
+                    start.getUavPosition().getLongitude(),
+                    end.getUavPosition().getLongitude(),
+                    ratio
+            );
+
+            ArdUavPositionDTO newPoint = deepCopy(start, ArdUavPositionDTO.class);
+            newPoint.setType(1);
+            newPoint.getUavPosition().setLatitude(lat);
+            newPoint.getUavPosition().setLongitude(lon);
+            interpolated.add(newPoint);
+        }
+
+        return interpolated;
+    }
+
+    /**
+     * 更新航点动作中的地面高度
+     *
+     * @param point         航点对象
+     * @param terrainHeight 当前地形高度
+     */
+    private void updateActionAltitude(ArdUavPositionDTO point, double terrainHeight) {
+        if (point.getActionGroup() == null || point.getActionGroup().isEmpty()) {
+            return;
+        }
+
+        for (ArdActionDTO action : point.getActionGroup()) {
+            // 如果已有地面位置,则更新高度
+            if (action.getGroundPosition() != null) {
+                action.getGroundPosition().setLongitude(point.getUavPosition().getLongitude());
+                action.getGroundPosition().setLatitude(point.getUavPosition().getLatitude());
+                action.getGroundPosition().setAltitude(terrainHeight);
+            }
+            // 如果没有地面位置,则创建新的地面位置
+            else {
+                action.setGroundPosition(ArdPositionDTO.builder()
+                        .longitude(point.getUavPosition().getLongitude())
+                        .latitude(point.getUavPosition().getLatitude())
+                        .altitude(terrainHeight)
+                        .build());
+            }
+        }
+    }
+
+    /**
+     * 重新编号航点和动作
+     *
+     * @param points 航点列表
+     */
+    private void renumberWaypoints(List<ArdUavPositionDTO> points) {
+        // 航点重新编号
+        for (int i = 0; i < points.size(); i++) {
+            ArdUavPositionDTO point = points.get(i);
+            point.setIndex(i + 1); // 通常从1开始编号
+
+            // 动作重新编号
+            if (point.getActionGroup() != null && !point.getActionGroup().isEmpty()) {
+                int actionIndex = 1;
+                for (ArdActionDTO action : point.getActionGroup()) {
+                    action.setActionIndex(actionIndex++);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 校验航线数据有效性
+     *
+     * @param ardWaylineDTO 航线数据
+     * @throws IllegalArgumentException 如果数据无效
+     */
+    private void validateWaylineData(ArdWaylineDTO ardWaylineDTO) {
+        if (ardWaylineDTO == null) {
+            throw new IllegalArgumentException("航线数据不能为null");
+        }
+        if (ardWaylineDTO.getUavPositions() == null || ardWaylineDTO.getUavPositions().size() < 2) {
+            throw new IllegalArgumentException("航点数量必须大于等于2");
+        }
+        if (ardWaylineDTO.getGlobalFlyHeight() <= 0) {
+            throw new IllegalArgumentException("全局飞行高度必须大于0");
+        }
+    }
+
+    // 通用深拷贝
+    public static <T> T deepCopy(T original, Class<T> clazz) {
+        if (original == null) {
+            return null;
+        }
+        try {
+            byte[] bytes = mapper.writeValueAsBytes(original);
+            return mapper.readValue(bytes, clazz);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    // 线性插值
+    public double interpolate(double start, double end, double ratio) {
+        return start + (end - start) * ratio;
+    }
+}
\ No newline at end of file
diff --git a/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdActionDTO.java b/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdActionDTO.java
new file mode 100644
index 0000000..50bee04
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdActionDTO.java
@@ -0,0 +1,53 @@
+package com.ard.tianyu.domain.dto.wayline;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
+public class ArdActionDTO {
+
+    @JsonProperty("actionIndex")
+    public Integer actionIndex;
+
+    @JsonProperty("actionType")
+    public Integer actionType;
+
+    @JsonProperty("actionParameter")
+    public Integer actionParameter;
+
+    @JsonProperty("waitTime")
+    public Integer waitTime;
+
+    @JsonProperty("gimbalMode")
+    @Builder.Default
+    public Integer gimbalMode = 0;
+
+    @JsonProperty("actionGimbal")
+    public ArdGimbalDTO actionGimbal;
+
+    @JsonProperty("zoomFactor")
+    public Integer zoomFactor;
+
+    @Builder.Default
+    @JsonProperty("fieldAngle")
+    public Float fieldAngle = 66.6F;
+
+    @JsonProperty("groundPosition")
+    public ArdPositionDTO groundPosition;
+
+    //地面视场范围
+    @JsonProperty("groundViewRange")
+    private List<ArdPositionDTO> groundViewRange;
+
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdGimbalDTO.java b/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdGimbalDTO.java
new file mode 100644
index 0000000..4c06dfe
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdGimbalDTO.java
@@ -0,0 +1,27 @@
+package com.ard.tianyu.domain.dto.wayline;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
+public class ArdGimbalDTO {
+
+    @JsonProperty("pitch")
+    public Double pitch;
+
+    @JsonProperty("yaw")
+    public Double yaw;
+
+    @JsonProperty("roll")
+    public Double roll;
+
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdPositionDTO.java b/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdPositionDTO.java
new file mode 100644
index 0000000..23b4fb4
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdPositionDTO.java
@@ -0,0 +1,30 @@
+package com.ard.tianyu.domain.dto.wayline;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ArdPositionDTO {
+
+    /**
+     * 经度
+     */
+    private Double longitude;
+
+    /**
+     * 纬度
+     */
+    private Double latitude;
+
+    /**
+     * 高度
+     */
+    @Builder.Default
+    private Double altitude = 0.0;
+
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdUavPositionDTO.java b/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdUavPositionDTO.java
new file mode 100644
index 0000000..696fc08
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdUavPositionDTO.java
@@ -0,0 +1,65 @@
+package com.ard.tianyu.domain.dto.wayline;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ArdUavPositionDTO {
+    /**
+     * 索引
+     */
+    private Integer Index;
+
+    /**
+     * 位置类型
+     * 0-用户输入
+     * 1-算法生成
+     */
+    @Builder.Default
+    private Integer Type = 0;
+
+    /**
+     * 飞行路径模式
+     * 1-直线飞行(过点且到点停)
+     * 2-协调转弯(提前转弯不过点)
+     * 3-曲线飞行(到点停)
+     * 4-曲线飞行(到点不停)
+     */
+    @JsonProperty("flightPathMode")
+    @Builder.Default
+    private Integer flightPathMode = 1;
+
+    /**
+     * 偏航模式
+     */
+    @JsonProperty("headingMode")
+    @Builder.Default
+    private Integer headingMode = 0;
+
+    /**
+     * 点飞行速度
+     */
+    @JsonProperty("pointFlySpeed")
+    private Float pointFlySpeed;
+
+    /**
+     * 无人机位置
+     */
+    @JsonProperty("uavPosition")
+    private ArdPositionDTO uavPosition;
+
+    /**
+     * 动作组
+     */
+    @JsonProperty("actionGroup")
+    private List<ArdActionDTO> actionGroup;
+
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdWaylineDTO.java b/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdWaylineDTO.java
new file mode 100644
index 0000000..7ac2492
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/ArdWaylineDTO.java
@@ -0,0 +1,77 @@
+package com.ard.tianyu.domain.dto.wayline;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import java.util.List;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ArdWaylineDTO {
+
+    /**
+     * 航线id
+     */
+    public String waylineId;
+
+    /**
+     * 航线名称
+     */
+    public String waylineName;
+
+    /**
+     * 航线类型
+     */
+    public Integer waylineType;
+
+    /**
+     * 无人机型号
+     */
+    private String uavModel;
+
+    /**
+     * 负载型号
+     */
+    private String payloadModel;
+
+    /**
+     * 航线飞行速度
+     */
+    public Float globalFlySpeed;
+
+    /**
+     * 航线飞行高度
+     */
+    public Float globalFlyHeight;
+
+    /**
+     * 航线长度
+     */
+    public Double waylineLength;
+
+    /**
+     * 用户航点数量
+     */
+    public Integer waylineUserPointCount;
+
+    /**
+     * 航点总数量
+     */
+    public Integer waylineAllPointCount;
+
+    /**
+     * 航线飞行时间
+     */
+    public Double waylineBaseFlyTime;
+
+    /**
+     * 航线航点集合
+     */
+    public List<ArdUavPositionDTO> uavPositions;
+
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/WaylineFileDTO.java b/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/WaylineFileDTO.java
new file mode 100644
index 0000000..ed4a9de
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/domain/dto/wayline/WaylineFileDTO.java
@@ -0,0 +1,43 @@
+package com.ard.tianyu.domain.dto.wayline;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * @author sean
+ * @version 0.3
+ * @date 2021/12/22
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class WaylineFileDTO {
+
+    private String name;
+
+    @JsonProperty("id")
+    private String waylineId;
+
+    private String droneModelKey;
+
+    private String sign;
+
+    private List<String> payloadModelKeys;
+
+    private Boolean favorited;
+
+    private List<Integer> templateTypes;
+
+    private String objectKey;
+
+    @JsonProperty("user_name")
+    private String username;
+
+    private Long updateTime;
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/domain/entity/UavWayline.java b/ard-work/src/main/java/com/ard/tianyu/domain/entity/UavWayline.java
new file mode 100644
index 0000000..e67ca07
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/domain/entity/UavWayline.java
@@ -0,0 +1,80 @@
+package com.ard.tianyu.domain.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ard.common.annotation.Excel;
+import com.ard.common.core.domain.BaseEntity;
+
+/**
+ * 航线对象 uav_wayline
+ * 
+ * @author ruoyi
+ * @date 2025-06-27
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class UavWayline extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 主键 */
+    private String id;
+
+    /** 航线名称 */
+    @Excel(name = "航线名称")
+    private String name;
+
+    /** 航线类型 */
+    @Excel(name = "航线类型")
+    private Integer type;
+
+    /** 无人机型号 */
+    @Excel(name = "无人机型号")
+    private String uavModel;
+
+    /** 负载型号 */
+    @Excel(name = "负载型号")
+    private String payloadModel;
+
+    /** 全局速度 */
+    @Excel(name = "全局速度")
+    private Float globalSpeed;
+
+    /** 全局高度 */
+    @Excel(name = "全局高度")
+    private Float globalHeight;
+
+    /** 用户航点数量 */
+    @Excel(name = "用户航点数量")
+    private Integer userWaypointCount;
+
+    /** 规划航点数量 */
+    @Excel(name = "规划航点数量")
+    private Integer planWaypointCount;
+
+    /** 原始航线数据 */
+    @Excel(name = "航线数据")
+    private byte[] srcWaylineData;
+
+    /** 规划航线数据 */
+    @Excel(name = "航线数据")
+    private byte[] planWaylineData;
+
+    /** kmz地址 */
+    @Excel(name = "kmz地址")
+    private String kmzUrl;
+
+    /** 用户ID */
+    @Excel(name = "用户ID")
+    private Long userId;
+
+    /** 部门ID */
+    @Excel(name = "部门ID")
+    private Long deptId;
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/domain/enums/WaylineEnum.java b/ard-work/src/main/java/com/ard/tianyu/domain/enums/WaylineEnum.java
new file mode 100644
index 0000000..c3e84b3
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/domain/enums/WaylineEnum.java
@@ -0,0 +1,76 @@
+package com.ard.tianyu.domain.enums;
+
+public enum WaylineEnum {
+
+
+    //航向
+    coordinateTurn(1,"coordinateTurn"),
+
+    toPointAndStopWithDiscontinuityCurvature(2,"toPointAndStopWithDiscontinuityCurvature"),
+
+    toPointAndStopWithContinuityCurvature(3,"toPointAndStopWithContinuityCurvature"),
+
+    toPointAndPassWithContinuityCurvature(4,"toPointAndPassWithContinuityCurvature"),
+
+    UNKNOWN(-1, "unknown");
+
+    private int val;
+
+    private String desc;
+
+    WaylineEnum(int val, String desc) {
+        this.val = val;
+        this.desc = desc;
+    }
+
+    public int getVal() {
+        return val;
+    }
+
+    public static String getDesc(int val) {
+
+
+        if (coordinateTurn.val == val) {
+            return coordinateTurn.desc;
+        }
+
+        if (toPointAndStopWithDiscontinuityCurvature.val == val) {
+            return toPointAndStopWithDiscontinuityCurvature.desc;
+        }
+
+        if (toPointAndStopWithContinuityCurvature.val == val) {
+            return toPointAndStopWithContinuityCurvature.desc;
+        }
+
+        if (toPointAndPassWithContinuityCurvature.val == val) {
+            return toPointAndPassWithContinuityCurvature.desc;
+        }
+
+
+
+        return UNKNOWN.desc;
+    }
+
+    public static int getVal(String desc) {
+
+        if (coordinateTurn.desc.equals(desc)) {
+            return coordinateTurn.val;
+        }
+
+        if (toPointAndStopWithDiscontinuityCurvature.desc.equals(desc)) {
+            return toPointAndStopWithDiscontinuityCurvature.val;
+        }
+
+        if (toPointAndStopWithContinuityCurvature.desc.equals(desc)) {
+            return toPointAndStopWithContinuityCurvature.val;
+        }
+
+        if (toPointAndPassWithContinuityCurvature.desc.equals(desc)) {
+            return toPointAndPassWithContinuityCurvature.val;
+        }
+
+
+        return UNKNOWN.val;
+    }
+
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/mapper/UavWaylineMapper.java b/ard-work/src/main/java/com/ard/tianyu/mapper/UavWaylineMapper.java
new file mode 100644
index 0000000..9f89dd3
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/mapper/UavWaylineMapper.java
@@ -0,0 +1,71 @@
+package com.ard.tianyu.mapper;
+
+import com.ard.common.core.domain.entity.SysUser;
+import com.ard.tianyu.domain.entity.UavWayline;
+
+import java.util.List;
+
+/**
+ * 航线Mapper接口
+ * 
+ * @author ruoyi
+ * @date 2025-06-27
+ */
+public interface UavWaylineMapper 
+{
+    /**
+     * 查询航线
+     * 
+     * @param id 航线主键
+     * @return 航线
+     */
+    public UavWayline selectUavWaylineById(String id);
+
+    /**
+     * 查询航线列表
+     * 
+     * @param uavWayline 航线
+     * @return 航线集合
+     */
+    public List<UavWayline> selectUavWaylineList(UavWayline uavWayline);
+
+    /**
+     * 新增航线
+     * 
+     * @param uavWayline 航线
+     * @return 结果
+     */
+    public int insertUavWayline(UavWayline uavWayline);
+
+    /**
+     * 修改航线
+     * 
+     * @param uavWayline 航线
+     * @return 结果
+     */
+    public int updateUavWayline(UavWayline uavWayline);
+
+    /**
+     * 删除航线
+     * 
+     * @param id 航线主键
+     * @return 结果
+     */
+    public int deleteUavWaylineById(String id);
+
+    /**
+     * 批量删除航线
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteUavWaylineByIds(String[] ids);
+
+    /**
+     * 校验航线是否唯一
+     *
+     * @param name 航线名称
+     * @return 结果
+     */
+    public UavWayline checkUavWaylineNameUnique(String name);
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/service/IGeoToolsService.java b/ard-work/src/main/java/com/ard/tianyu/service/IGeoToolsService.java
new file mode 100644
index 0000000..b008e41
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/service/IGeoToolsService.java
@@ -0,0 +1,16 @@
+package com.ard.tianyu.service;
+
+import com.ard.tianyu.domain.dto.wayline.ArdPositionDTO;
+import com.ard.tianyu.util.Vertex;
+import java.util.List;
+
+public interface IGeoToolsService {
+
+
+    Vertex gcj02ToTargetCoordinate(Vertex gcjVertex);
+
+    Vertex targetCoordinateToGcj02(Vertex targetVertex);
+
+    List<ArdPositionDTO> calConvexHull(List<ArdPositionDTO> polygonPoints);
+
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/service/IKmzFileService.java b/ard-work/src/main/java/com/ard/tianyu/service/IKmzFileService.java
new file mode 100644
index 0000000..7b77a7a
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/service/IKmzFileService.java
@@ -0,0 +1,13 @@
+package com.ard.tianyu.service;
+
+import com.ard.tianyu.domain.dto.wayline.ArdWaylineDTO;
+
+
+/**
+ * @author sean
+ * @version 0.3
+ * @date 2021/12/22
+ */
+public interface IKmzFileService {
+    void outputKmz(ArdWaylineDTO ardWaylineDTO);
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/service/IUavWaylineService.java b/ard-work/src/main/java/com/ard/tianyu/service/IUavWaylineService.java
new file mode 100644
index 0000000..49c0df5
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/service/IUavWaylineService.java
@@ -0,0 +1,71 @@
+package com.ard.tianyu.service;
+
+import java.util.List;
+
+import com.ard.tianyu.domain.dto.wayline.ArdWaylineDTO;
+import com.ard.tianyu.domain.entity.UavWayline;
+
+/**
+ * 航线Service接口
+ * 
+ * @author ruoyi
+ * @date 2025-06-27
+ */
+public interface IUavWaylineService 
+{
+    /**
+     * 查询航线
+     * 
+     * @param id 航线主键
+     * @return 航线
+     */
+    public ArdWaylineDTO selectUavWaylineById(String id);
+
+    /**
+     * 查询航线列表
+     * 
+     * @param uavWayline 航线
+     * @return 航线集合
+     */
+    public List<UavWayline> selectUavWaylineList(UavWayline uavWayline);
+
+    /**
+     * 新增航线
+     * 
+     * @param ardWaylineDTO 航线
+     * @return 结果
+     */
+    public int insertUavWayline(ArdWaylineDTO ardWaylineDTO);
+
+    /**
+     * 修改航线
+     * 
+     * @param ardWaylineDTO 航线
+     * @return 结果
+     */
+    public int updateUavWayline(ArdWaylineDTO ardWaylineDTO);
+
+    /**
+     * 批量删除航线
+     * 
+     * @param ids 需要删除的航线主键集合
+     * @return 结果
+     */
+    public int deleteUavWaylineByIds(String[] ids);
+
+    /**
+     * 删除航线信息
+     * 
+     * @param id 航线主键
+     * @return 结果
+     */
+    public int deleteUavWaylineById(String id);
+
+    /**
+     * 校验航线是否唯一
+     *
+     * @param ardWaylineDTO 航线信息
+     * @return 结果
+     */
+    public boolean checkWaylineNameUnique(ArdWaylineDTO ardWaylineDTO);
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/service/impl/GeoToolsServiceImpl.java b/ard-work/src/main/java/com/ard/tianyu/service/impl/GeoToolsServiceImpl.java
new file mode 100644
index 0000000..bc2445c
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/service/impl/GeoToolsServiceImpl.java
@@ -0,0 +1,158 @@
+package com.ard.tianyu.service.impl;
+
+
+import com.ard.tianyu.config.GeoserverConfig;
+import com.ard.tianyu.domain.dto.wayline.ArdPositionDTO;
+import com.ard.tianyu.service.IGeoToolsService;
+import com.ard.tianyu.util.GeoTools;
+import com.ard.tianyu.util.Vertex;
+import lombok.extern.slf4j.Slf4j;
+import org.geotools.data.DefaultTransaction;
+import org.geotools.data.Transaction;
+import org.geotools.data.collection.ListFeatureCollection;
+import org.geotools.data.simple.SimpleFeatureCollection;
+import org.geotools.data.simple.SimpleFeatureSource;
+import org.geotools.data.simple.SimpleFeatureStore;
+import org.geotools.feature.simple.SimpleFeatureBuilder;
+import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
+import org.geotools.geometry.jts.JTSFactoryFinder;
+import org.geotools.referencing.crs.DefaultGeographicCRS;
+import org.locationtech.jts.geom.*;
+import org.locationtech.jts.operation.buffer.BufferParameters;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+@Service
+@Slf4j
+public class GeoToolsServiceImpl implements IGeoToolsService {
+
+    @Autowired
+    private GeoserverConfig geoserverConfig;
+
+    private static Double distance = 1500 * 180 / Math.PI / 6371000;
+    private static GeometryFactory geoFactory = new GeometryFactory();
+
+
+    //坐标类型转换
+    public Vertex gcj02ToTargetCoordinate(Vertex gcjVertex){
+        Vertex newVertex = new Vertex(0,0);
+        if(geoserverConfig.getDeviceCoordinateType().equals("WGS84")){
+            newVertex = GeoTools.GCJ02toWGS84(new Vertex(gcjVertex.x, gcjVertex.y));
+        }else {
+            newVertex = new Vertex(gcjVertex.x, gcjVertex.y);
+        }
+        return newVertex;
+    }
+
+    public Vertex targetCoordinateToGcj02(Vertex targetVertex){
+        Vertex newVertex = new Vertex(0,0);
+        if(geoserverConfig.getDeviceCoordinateType().equals("WGS84")){
+            newVertex = GeoTools.WGS84toGCJ02(new Vertex(targetVertex.x, targetVertex.y));
+        }else {
+            newVertex = new Vertex(targetVertex.x, targetVertex.y);
+        }
+        return newVertex;
+    }
+
+    public List<ArdPositionDTO> calConvexHull(List<ArdPositionDTO> polygonPoints){
+        //缓冲距离
+        List<ArdPositionDTO> convexHullPoints = new ArrayList<>();
+        try{
+            //用户定义区域
+            Integer pCount = polygonPoints.size();
+            //首位最后一个点
+            Coordinate[] uPoints = new Coordinate[pCount+1];
+            int i = 0;
+            for(i=0;i<pCount;i++){
+                uPoints[i] = new Coordinate(polygonPoints.get(i).getLongitude(),polygonPoints.get(i).getLatitude());
+            }
+            //将第一个点重新加入到最后,形成闭环
+            uPoints[i] = new Coordinate(polygonPoints.get(0).getLongitude(),polygonPoints.get(0).getLatitude());
+            //多边形点的个数必须大于等于3
+            if(uPoints.length >= 3){
+                //生成集合要素
+                Geometry oneGeometry = geoFactory.createPolygon(uPoints);
+                //做凸包
+                Geometry geoConvexHull = oneGeometry.convexHull();
+                Coordinate[] points = geoConvexHull.getCoordinates();
+                for (Coordinate coordinate : points) {
+                    convexHullPoints.add(ArdPositionDTO.builder().longitude(coordinate.getX())
+                            .latitude(coordinate.getY()).build());
+                }
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return convexHullPoints;
+    }
+
+
+    private double meterToDegree(double threshold){
+        return threshold * 180 / Math.PI / 6371000;
+    }
+
+    public void deleteFile(File filePath) {
+        //判断路径是否存在
+        if(filePath.exists()) {
+            //判断是否是目录
+            if (filePath.isDirectory()) {
+                //将目录逐层拆分成数组元素
+                File[] files = filePath.listFiles();
+                for (File f : files) {
+                    //递归调用,由最底层往上逐层删除
+                    deleteFile(f);
+                }
+            }
+            //如果是文件,直接删除
+            filePath.delete();
+        }
+    }
+
+    public void zipShapeFile(String shpPath){
+        try{
+            File shpFile = new File(shpPath);
+            String shpRoot = shpFile.getParentFile().getPath(),
+                    _shpName = shpFile.getName(),
+                    shpName = _shpName.substring(0, _shpName.lastIndexOf("."));
+
+            String zipPath = shpRoot+File.separator+shpName+".zip";
+            File zipFile = new File(zipPath);
+            InputStream input = null;
+            ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
+            // zip的名称为
+            zipOut.setComment(shpName);
+            String[] shpFiles = new String[]{
+                    shpRoot+File.separator+shpName+".dbf",
+                    shpRoot+File.separator+shpName+".prj",
+                    shpRoot+File.separator+shpName+".shp",
+                    shpRoot+File.separator+shpName+".shx",
+            };
+            for(int i=0;i<shpFiles.length;i++){
+                File _file = new File(shpFiles[i]);
+                input = new FileInputStream(_file);
+                zipOut.putNextEntry(new ZipEntry(_file.getName()));
+                int temp = 0;
+                while ((temp = input.read()) != -1) {
+                    zipOut.write(temp);
+                }
+                input.close();
+            }
+            zipOut.close();
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+    }
+
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/service/impl/KmzFileServiceImpl.java b/ard-work/src/main/java/com/ard/tianyu/service/impl/KmzFileServiceImpl.java
new file mode 100644
index 0000000..9f60018
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/service/impl/KmzFileServiceImpl.java
@@ -0,0 +1,517 @@
+package com.ard.tianyu.service.impl;
+
+import com.ard.tianyu.component.minio.config.MinioConfig;
+import com.ard.tianyu.component.minio.utils.MinioUtil;
+import com.ard.tianyu.config.WaylinePlaningConfig;
+import com.ard.tianyu.domain.dto.wayline.*;
+import com.ard.tianyu.domain.entity.UavWayline;
+import com.ard.tianyu.domain.enums.WaylineEnum;
+import com.ard.tianyu.mapper.UavWaylineMapper;
+import com.ard.tianyu.service.IGeoToolsService;
+import com.ard.tianyu.service.IKmzFileService;
+import com.ard.tianyu.util.Vertex;
+import lombok.extern.slf4j.Slf4j;
+import org.dom4j.*;
+import org.dom4j.io.OutputFormat;
+import org.dom4j.io.XMLWriter;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import javax.annotation.Resource;
+import java.io.*;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * @author liusuyi
+ * @version 3.0
+ * @date 2025/06/27
+ */
+@Service
+@Slf4j
+@Transactional
+public class KmzFileServiceImpl implements IKmzFileService {
+
+    @Resource
+    private MinioConfig minioConfig;
+
+    @Resource
+    private IGeoToolsService geoToolsService;
+
+    @Resource
+    private WaylinePlaningConfig waylinePlaningConfig;
+
+    @Resource
+    private UavWaylineMapper uavWaylineMapper;
+
+    private Document initWayline(Boolean isTemplate) {
+        //创建Document对象
+        Document document = DocumentHelper.createDocument();
+        document.setXMLEncoding("UTF-8");
+
+        // 定义命名空间
+        String KML_NAMESPACE = "http://www.opengis.net/kml/2.2";
+        String WPML_NAMESPACE = "http://www.dji.com/wpmz/1.0.6";
+
+        //创建kml节点
+        QName kmlQName = QName.get("kml", Namespace.get("", KML_NAMESPACE));
+        Element kml = document.addElement(kmlQName);
+        //添加命名空间
+        kml.addNamespace("wpml", WPML_NAMESPACE);
+        //生成Document节点
+        Element wpml = kml.addElement("Document", KML_NAMESPACE);
+        //是否为模板
+        if (isTemplate) {
+            // Step 1: Implement File Creation Information
+            //wpml.addElement("wpml:author").addText(user);
+            wpml.addElement("wpml:createTime").addText(
+                    String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()));
+            wpml.addElement("wpml:updateTime").addText(
+                    String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()));
+        }
+        //返回
+        return document;
+    }
+
+    //产生kmz总体过程
+    private void gntTemplateKml(ArdWaylineDTO ardWaylineDTO, String savePath) {
+        //初始化文件头
+        Document reader = this.initWayline(true);
+        //Document元素
+        Element document = reader.getRootElement().element("Document");
+        //添加missionConfig信息
+        this.addMissionConfig(document, ardWaylineDTO);
+        //添加folder信息
+        Element folder = this.addFolder(document, ardWaylineDTO);
+        //2设置航点参数
+        List<ArdUavPositionDTO> uavPositions = ardWaylineDTO.getUavPositions();
+        //循环添加placemark
+        Integer actionGroupId = 0;
+        for (int i = 0; i < uavPositions.size(); i++) {
+            ArdUavPositionDTO onePosition = uavPositions.get(i);
+            this.addPlaceMark(folder, i, actionGroupId, onePosition);
+            if (onePosition.getActionGroup().size() > 0) {
+                actionGroupId += 1;
+            }
+        }
+        //打印输出结果
+        //log.info(reader.asXML());
+        //输出
+        String tmpKml = savePath + File.separator + "wpmz" + File.separator + "template.kml";
+        this.outputXml(tmpKml, reader);
+    }
+
+    public void gntWaylineWpml(ArdWaylineDTO ardWaylineDTO, String savePath) {
+        //初始化文件头
+        Document reader = this.initWayline(false);
+        //Document元素
+        Element document = reader.getRootElement().element("Document");
+        //添加missionConfig信息
+        this.addMissionConfig(document, ardWaylineDTO);
+        //添加folder信息
+        Element folder = this.addFolderWayline(document, ardWaylineDTO);
+        //2设置航点参数
+        //循环添加placemark
+        Integer actionGroupId = 0;
+        for (int i = 0; i < ardWaylineDTO.getUavPositions().size(); i++) {
+            ArdUavPositionDTO onePosition = ardWaylineDTO.getUavPositions().get(i);
+            this.addPlaceMarkWayline(folder, i, actionGroupId, onePosition, ardWaylineDTO);
+            if (onePosition.getActionGroup().size() > 0) {
+                actionGroupId += 1;
+            }
+        }
+        //打印输出结果
+        //log.info(reader.asXML());
+        //输出
+        String waylineWpml = savePath + File.separator + "wpmz" + File.separator + "waylines.wpml";
+        this.outputXml(waylineWpml, reader);
+    }
+
+    //missionConfig信息
+    private void addMissionConfig(Element document, ArdWaylineDTO ardWaylineDTO) {
+        //创建wpml:missionConfig元素
+        Element missionConfig = document.addElement("wpml:missionConfig");
+        //创建子元素
+        missionConfig.addElement("wpml:flyToWaylineMode").addText("safely");
+        missionConfig.addElement("wpml:finishAction").addText("goHome");
+        missionConfig.addElement("wpml:exitOnRCLost").addText("goContinue");
+        missionConfig.addElement("wpml:executeRCLostAction").addText("goBack");
+        missionConfig.addElement("wpml:takeOffSecurityHeight").addText("30");
+        //与第一个点重合
+        ArdPositionDTO firstPoint = ardWaylineDTO.getUavPositions().get(0).getUavPosition();
+        String refPoint = String.format("%s,%s,%s", firstPoint.getLatitude(), firstPoint.getLongitude()
+                , firstPoint.getAltitude());
+        missionConfig.addElement("wpml:takeOffRefPoint").addText(refPoint);
+        missionConfig.addElement("wpml:globalTransitionalSpeed").addText(String.valueOf(ardWaylineDTO.getGlobalFlySpeed()));
+        missionConfig.addElement("wpml:globalRTHHeight").addText(String.valueOf(ardWaylineDTO.getGlobalFlyHeight()));
+        //添加droneInfo
+        Element droneInfo = missionConfig.addElement("wpml:droneInfo");
+        droneInfo.addElement("wpml:droneEnumValue").addText("89");
+        droneInfo.addElement("wpml:droneSubEnumValue").addText("1");
+        //添加payloadInfo
+        Element payloadInfo = missionConfig.addElement("wpml:payloadInfo");
+        payloadInfo.addElement("wpml:payloadEnumValue").addText("43");
+        payloadInfo.addElement("wpml:payloadSubEnumValue").addText("0");
+        payloadInfo.addElement("wpml:payloadPositionIndex").addText("0");
+    }
+
+    //folder信息
+    private Element addFolder(Element document, ArdWaylineDTO ardWaylineDTO) {
+        //全局信息
+        Element folder = document.addElement("Folder");
+        folder.addElement("wpml:templateType").addText("waypoint");
+        folder.addElement("wpml:templateId").addText("0");
+        //坐标系统
+        Element waylineCoordinateSysParam = folder.addElement("wpml:waylineCoordinateSysParam");
+        waylineCoordinateSysParam.addElement("wpml:coordinateMode").addText("WGS84");
+        waylineCoordinateSysParam.addElement("wpml:heightMode").addText("relativeToStartPoint");
+        //
+        folder.addElement("wpml:autoFlightSpeed").addText(String.valueOf(ardWaylineDTO.getGlobalFlySpeed()));
+        folder.addElement("wpml:globalWaypointTurnMode").addText("toPointAndStopWithDiscontinuityCurvature");
+        folder.addElement("wpml:globalUseStraightLine").addText("1");
+        //全局航向角模式
+        Element globalWaypointHeadingParam = folder.addElement("wpml:globalWaypointHeadingParam");
+        globalWaypointHeadingParam.addElement("wpml:waypointHeadingMode").addText("followWayline");
+        globalWaypointHeadingParam.addElement("wpml:waypointHeadingYawPathMode").addText("followBadArc");
+        //
+        folder.addElement("wpml:gimbalPitchMode").addText("manual");//manual , usePointSetting
+        folder.addElement("wpml:globalHeight").addText(String.valueOf(ardWaylineDTO.getGlobalFlyHeight()));
+        //
+        return folder;
+    }
+
+    private Element addFolderWayline(Element document, ArdWaylineDTO ardWaylineDTO) {
+        //全局信息
+        Element folder = document.addElement("Folder");
+        folder.addElement("wpml:templateId").addText("0");
+        folder.addElement("wpml:executeHeightMode").addText("relativeToStartPoint");
+        folder.addElement("wpml:waylineId").addText("0");
+        folder.addElement("wpml:autoFlightSpeed").addText(String.valueOf(ardWaylineDTO.getGlobalFlySpeed()));
+        return folder;
+    }
+
+    //placemark信息
+    private void addPlaceMark(Element folder, Integer index, Integer actionGroupId, ArdUavPositionDTO onePostion) {
+        //添加placemark
+        Element placemark = folder.addElement("Placemark");
+        //转换坐标
+        //航点坐标位置转换
+        Double tLng = 0.0;
+        Double tLat = 0.0;
+        //转换坐标,将目标坐标转化为高德坐标
+        if (onePostion.getUavPosition().getLongitude() != null &&
+                onePostion.getUavPosition().getLatitude() != null) {
+            Vertex newVertex = geoToolsService.gcj02ToTargetCoordinate(
+                    new Vertex(onePostion.getUavPosition().getLongitude(),
+                            onePostion.getUavPosition().getLatitude()));
+            tLng = newVertex.x;
+            tLat = newVertex.y;
+        }
+        Element point = placemark.addElement("Point");
+        point.addElement("coordinates").addText(tLng + "," + tLat);
+        //添加一般元素
+        placemark.addElement("wpml:index").addText(String.valueOf(index));
+        placemark.addElement("wpml:ellipsoidHeight").addText(String.valueOf(onePostion.getUavPosition().getAltitude()));
+        placemark.addElement("wpml:height").addText(String.valueOf(onePostion.getUavPosition().getAltitude()));
+        //全局信息
+        placemark.addElement("wpml:useGlobalHeight").addText("1");
+        placemark.addElement("wpml:useGlobalSpeed").addText("1");
+        placemark.addElement("wpml:useGlobalHeadingParam").addText("1");
+        placemark.addElement("wpml:useGlobalTurnParam").addText("1");
+        placemark.addElement("wpml:useStraightLine").addText("1");
+        //添加actionGroup
+        if (onePostion.getActionGroup().size() > 0) {
+            Element actionGroup = placemark.addElement("wpml:actionGroup");
+            //添加动作组基础信息
+            actionGroup.addElement("wpml:actionGroupId").addText(String.valueOf(actionGroupId));
+            actionGroup.addElement("wpml:actionGroupStartIndex").addText(String.valueOf(index));
+            actionGroup.addElement("wpml:actionGroupEndIndex").addText(String.valueOf(index));
+            actionGroup.addElement("wpml:actionGroupMode").addText("sequence");
+            Element actionTrigger = actionGroup.addElement("wpml:actionTrigger");
+            actionTrigger.addElement("wpml:actionTriggerType").addText("reachPoint");
+            //定义action
+            Integer actionId = 0;
+            for (int j = 0; j < onePostion.getActionGroup().size(); j++) {
+                //逐个添加action
+                Integer actionCount = this.addAction(actionGroup, actionId, onePostion.getActionGroup().get(j));
+                actionId += actionCount;
+            }
+        }
+    }
+
+    //placemark信息
+    private void addPlaceMarkWayline(Element folder, Integer index, Integer actionGroupId,
+                                     ArdUavPositionDTO onePostion, ArdWaylineDTO ardWaylineDTO) {
+        //添加placemark
+        Element placemark = folder.addElement("Placemark");
+        //转换坐标
+        //航点坐标位置转换
+        Double tLng = 0.0;
+        Double tLat = 0.0;
+        //转换坐标,将目标坐标转化为高德坐标
+        if (onePostion.getUavPosition().getLongitude() != null &&
+                onePostion.getUavPosition().getLatitude() != null) {
+            Vertex newVertex = geoToolsService.gcj02ToTargetCoordinate(
+                    new Vertex(onePostion.getUavPosition().getLongitude(),
+                            onePostion.getUavPosition().getLatitude()));
+            tLng = newVertex.x;
+            tLat = newVertex.y;
+        }
+        Element point = placemark.addElement("Point");
+        point.addElement("coordinates").addText(tLng + "," + tLat);
+        //添加一般元素
+        placemark.addElement("wpml:index").addText(String.valueOf(index));
+        placemark.addElement("wpml:executeHeight").addText(String.valueOf(onePostion.getUavPosition().getAltitude()));
+        placemark.addElement("wpml:waypointSpeed").addText(String.valueOf(ardWaylineDTO.getGlobalFlySpeed()));
+        Element waypointHeadingParam = placemark.addElement("wpml:waypointHeadingParam");
+        waypointHeadingParam.addElement("wpml:waypointHeadingMode").addText("followWayline");
+        Element waypointTurnParam = placemark.addElement("wpml:waypointTurnParam");
+        waypointTurnParam.addElement("wpml:waypointTurnMode").addText(WaylineEnum.getDesc(onePostion.getFlightPathMode()));
+        waypointTurnParam.addElement("wpml:waypointTurnDampingDist").addText("0");
+        //添加actionGroup
+        if (onePostion.getActionGroup().size() > 0) {
+            Element actionGroup = placemark.addElement("wpml:actionGroup");
+            //添加动作组基础信息
+            actionGroup.addElement("wpml:actionGroupId").addText(String.valueOf(actionGroupId));
+            actionGroup.addElement("wpml:actionGroupStartIndex").addText(String.valueOf(index));
+            actionGroup.addElement("wpml:actionGroupEndIndex").addText(String.valueOf(index));
+            actionGroup.addElement("wpml:actionGroupMode").addText("sequence");
+            Element actionTrigger = actionGroup.addElement("wpml:actionTrigger");
+            actionTrigger.addElement("wpml:actionTriggerType").addText("reachPoint");
+            //定义action
+            Integer actionId = 0;
+            for (int j = 0; j < onePostion.getActionGroup().size(); j++) {
+                //逐个添加action
+                Integer actionCount = this.addAction(actionGroup, actionId, onePostion.getActionGroup().get(j));
+                actionId += actionCount;
+            }
+        }
+    }
+    //action
+    private Integer addAction(Element actionGroup, Integer index, ArdActionDTO actionDTO) {
+        Integer actionCount = 2;
+        //变焦动作
+        Element actionGimbal = actionGroup.addElement("wpml:action");
+        actionGimbal.addElement("wpml:actionId").addText(String.valueOf(index));
+        actionGimbal.addElement("wpml:actionActuatorFunc").addText("gimbalRotate");
+        Element param = actionGimbal.addElement("wpml:actionActuatorFuncParam");
+        param.addElement("wpml:payloadPositionIndex").addText(String.valueOf(0));
+        param.addElement("wpml:gimbalHeadingYawBase").addText("north");
+        param.addElement("wpml:gimbalRotateMode").addText("absoluteAngle");
+        param.addElement("wpml:gimbalPitchRotateEnable").addText(String.valueOf(1));
+        param.addElement("wpml:gimbalPitchRotateAngle").addText(String.valueOf(actionDTO.getActionGimbal().getPitch()));
+        param.addElement("wpml:gimbalRollRotateEnable").addText(String.valueOf(0));
+        param.addElement("wpml:gimbalRollRotateAngle").addText(String.valueOf(0));
+        param.addElement("wpml:gimbalYawRotateEnable").addText(String.valueOf(1));
+        param.addElement("wpml:gimbalYawRotateAngle").addText(String.valueOf(actionDTO.getActionGimbal().getYaw()));
+        param.addElement("wpml:gimbalRotateTimeEnable").addText(String.valueOf(0));
+        param.addElement("wpml:gimbalRotateTime").addText(String.valueOf(0));
+        //动作类型
+        switch (actionDTO.actionType) {
+            case 0://拍照
+                Element action0 = actionGroup.addElement("wpml:action");
+                action0.addElement("wpml:actionId").addText(String.valueOf(index + 1));
+                action0.addElement("wpml:actionActuatorFunc").addText("takePhoto");
+                Element param0 = action0.addElement("wpml:actionActuatorFuncParam");
+                param0.addElement("wpml:fileSuffix").addText("point");
+                param0.addElement("wpml:payloadPositionIndex").addText(String.valueOf(0));
+                break;
+            case 1://录像,拆解为3个动作
+                //添加开始录像动作
+                Element action11 = actionGroup.addElement("wpml:action");
+                action11.addElement("wpml:actionId").addText(String.valueOf(index + 1));
+                action11.addElement("wpml:actionActuatorFunc").addText("startRecord");
+                Element param11 = action11.addElement("wpml:actionActuatorFuncParam");
+                param11.addElement("wpml:fileSuffix").addText("point");
+                param11.addElement("wpml:payloadPositionIndex").addText(String.valueOf(0));
+                //添加新的悬停动作
+                Element action12 = actionGroup.addElement("wpml:action");
+                action12.addElement("wpml:actionId").addText(String.valueOf(index + 2));
+                action12.addElement("wpml:actionActuatorFunc").addText("hover");
+                Element param12 = action12.addElement("wpml:actionActuatorFuncParam");
+                param12.addElement("wpml:hoverTime").addText(String.valueOf(actionDTO.getActionParameter()));
+                //添加结束动作
+                Element action13 = actionGroup.addElement("wpml:action");
+                action13.addElement("wpml:actionId").addText(String.valueOf(index + 3));
+                action13.addElement("wpml:actionActuatorFunc").addText("stopRecord");
+                Element param13 = action13.addElement("wpml:actionActuatorFuncParam");
+                param13.addElement("wpml:payloadPositionIndex").addText(String.valueOf(0));
+                actionCount = 5;
+                break;
+            case 2://悬停 ,协议是3,前端发送为2
+                Element action2 = actionGroup.addElement("wpml:action");
+                action2.addElement("wpml:actionId").addText(String.valueOf(index + 1));
+                action2.addElement("wpml:actionActuatorFunc").addText("hover");
+                Element param2 = action2.addElement("wpml:actionActuatorFuncParam");
+                param2.addElement("wpml:hoverTime").addText(String.valueOf(actionDTO.getWaitTime()));
+                break;
+            default:
+                actionCount = 0;
+                log.info("未知的动作类型{}!", actionDTO.actionType);
+                break;
+        }
+        return actionCount;
+    }
+
+    //输出模板kml
+    private void outputXml(String vpath, Document doc) {
+        try {
+            OutputFormat format = OutputFormat.createPrettyPrint();
+            format.setEncoding("UTF-8");  //FileWriter改为FileOutputStream解决乱码问题
+            XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(vpath), format);
+            xmlWriter.write(doc);
+            xmlWriter.close();
+        } catch (Exception e) {
+            log.error("输出航线文件失败:{}", vpath);
+            e.printStackTrace();
+        }
+    }
+
+    //创建路径
+    private String createSavePath(String waylineName) {
+        //创建航线名称路径
+        String pathStr = waylinePlaningConfig.getWaylineSavePath() + File.separator + waylineName;
+        File savePath = new File(pathStr);
+        if (!savePath.exists()) {
+            savePath.mkdirs();
+        } else {
+            deleteFile(savePath);
+            savePath.mkdirs();
+        }
+        //创建wpmz
+        File wpmz = new File(pathStr + File.separator + "wpmz");
+        wpmz.mkdir();
+        //创建res
+        File res = new File(pathStr + File.separator + "wpmz" + File.separator + "res");
+        res.mkdir();
+        return pathStr;
+    }
+
+    private void deleteFile(File filePath) {
+        //判断路径是否存在
+        if (filePath.exists()) {
+            //判断是否是目录
+            if (filePath.isDirectory()) {
+                //将目录逐层拆分成数组元素
+                File[] files = filePath.listFiles();
+                for (File f : files) {
+                    //递归调用,由最底层往上逐层删除
+                    deleteFile(f);
+                }
+            }
+            //如果是文件,直接删除
+            filePath.delete();
+        }
+    }
+
+    //输出MKZ过程
+    @Override
+    @Async
+    public void outputKmz(ArdWaylineDTO ardWaylineDTO) {
+        try {
+            //创建目录
+            String savePath = this.createSavePath(ardWaylineDTO.getWaylineName());
+            //输出模板kml
+            this.gntTemplateKml(ardWaylineDTO, savePath);
+            //输出航线wpml
+            this.gntWaylineWpml(ardWaylineDTO, savePath);
+            //压缩输出成kmz
+            String fileName = ardWaylineDTO.getWaylineName() + ".kmz";
+            String kmzPath = waylinePlaningConfig.getWaylineSavePath() + File.separator + fileName;
+            //输出kmz文件,是否成功
+            Boolean isSuccess = this.toKmz(kmzPath, savePath + File.separator + "wpmz",
+                    true);
+            if (isSuccess) {
+                //上传kmz文件到对象数据库
+                InputStream inputStream = new FileInputStream(kmzPath);
+                //构建记录
+                String objectKey = fileName;
+                //若已经存在,则先删除,再添加
+                if (MinioUtil.exists(minioConfig.getBucketName(), objectKey))
+                    MinioUtil.delete(minioConfig.getBucketName(), objectKey);
+                String kmzUrl = MinioUtil.uploadInputStream(minioConfig.getBucketName(), objectKey, inputStream,
+                        inputStream.available(), "application/octet-stream");
+                // 更新航线kmz地址
+                UavWayline uavWayline =  UavWayline.builder()
+                        .id(ardWaylineDTO.waylineId)
+                        .kmzUrl(kmzUrl)
+                        .build();
+                uavWaylineMapper.updateUavWayline(uavWayline);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.info("生成kmz文件失败!");
+        }
+    }
+
+    private Boolean toKmz(String zipFileName, String sourceFileName, boolean KeepDirStructure) {
+        Boolean result = true;
+        long start = System.currentTimeMillis();//开始
+        ZipOutputStream zos = null;
+        try {
+            FileOutputStream fileOutputStream = new FileOutputStream(zipFileName);
+            zos = new ZipOutputStream(fileOutputStream);
+            File sourceFile = new File(sourceFileName);
+            compress(sourceFile, zos, sourceFile.getName(), KeepDirStructure);
+            long end = System.currentTimeMillis();//结束
+            System.out.println("压缩完成,耗时:" + (end - start) + " 毫秒");
+        } catch (Exception e) {
+            result = false;
+            e.printStackTrace();
+        } finally {
+            if (zos != null) {
+                try {
+                    zos.close();
+                } catch (IOException e) {
+                    e.getStackTrace();
+                }
+            }
+        }
+        return result;
+    }
+
+    private void compress(File sourceFile, ZipOutputStream zos, String name,
+                          boolean KeepDirStructure) throws Exception {
+        byte[] buf = new byte[2 * 1024];
+        if (sourceFile.isFile()) {
+            // 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
+            zos.putNextEntry(new ZipEntry(name));
+            // copy文件到zip输出流中
+            int len;
+            FileInputStream in = new FileInputStream(sourceFile);
+            while ((len = in.read(buf)) != -1) {
+                zos.write(buf, 0, len);
+            }
+            // Complete the entry
+            zos.closeEntry();
+            in.close();
+        } else {
+            File[] listFiles = sourceFile.listFiles();
+            if (listFiles == null || listFiles.length == 0) {
+                // 需要保留原来的文件结构时,需要对空文件夹进行处理
+                if (KeepDirStructure) {
+                    // 空文件夹的处理
+                    zos.putNextEntry(new ZipEntry(name + "/"));
+                    // 没有文件,不需要文件的copy
+                    zos.closeEntry();
+                }
+            } else {
+                for (File file : listFiles) {
+                    // 判断是否需要保留原来的文件结构
+                    if (KeepDirStructure) {
+                        // 注意:file.getName()前面需要带上父文件夹的名字加一斜杠,
+                        // 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了
+                        compress(file, zos, name + "/" + file.getName(), KeepDirStructure);
+                    } else {
+                        compress(file, zos, file.getName(), KeepDirStructure);
+                    }
+
+                }
+            }
+        }
+    }
+
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/service/impl/UavWaylineServiceImpl.java b/ard-work/src/main/java/com/ard/tianyu/service/impl/UavWaylineServiceImpl.java
new file mode 100644
index 0000000..eaa88b8
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/service/impl/UavWaylineServiceImpl.java
@@ -0,0 +1,278 @@
+package com.ard.tianyu.service.impl;
+
+import com.ard.common.constant.UserConstants;
+import com.ard.common.core.domain.entity.SysUser;
+import com.ard.common.utils.DateUtils;
+import com.ard.common.utils.StringUtils;
+import com.ard.common.utils.uuid.IdUtils;
+import com.ard.tianyu.controller.wayline.terrain.TerrainFollowingPlanner;
+import com.ard.tianyu.domain.dto.wayline.ArdUavPositionDTO;
+import com.ard.tianyu.domain.dto.wayline.ArdWaylineDTO;
+import com.ard.tianyu.domain.entity.UavWayline;
+import com.ard.tianyu.mapper.UavWaylineMapper;
+import com.ard.tianyu.service.IUavWaylineService;
+import com.ard.tianyu.service.IKmzFileService;
+import com.ard.tianyu.util.GeoTools;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 航线Service业务层处理
+ *
+ * @author ruoyi
+ * @date 2025-06-27
+ */
+@Slf4j
+@Service
+public class UavWaylineServiceImpl implements IUavWaylineService {
+    ObjectMapper mapper = new ObjectMapper();
+
+    @Resource
+    private UavWaylineMapper uavWaylineMapper;
+    @Resource
+    private IKmzFileService waylineFileService;
+    @Resource
+    private TerrainFollowingPlanner terrainFollowingPlanner;
+
+    /**
+     * 查询航线
+     *
+     * @param id 航线主键
+     * @return 航线
+     */
+    @Override
+    public ArdWaylineDTO selectUavWaylineById(String id) {
+        UavWayline uavWayline = uavWaylineMapper.selectUavWaylineById(id);
+        return EntityConverterTowaylineDTO(uavWayline);
+    }
+
+    /**
+     * 查询航线列表
+     *
+     * @param uavWayline 航线
+     * @return 航线
+     */
+    @Override
+    public List<UavWayline> selectUavWaylineList(UavWayline uavWayline) {
+        return uavWaylineMapper.selectUavWaylineList(uavWayline);
+    }
+
+    /**
+     * 新增航线
+     *
+     * @param ardWaylineDTO 航线
+     * @return 结果
+     */
+    @Override
+    public int insertUavWayline(ArdWaylineDTO ardWaylineDTO) {
+        UavWayline uavWayline = waylineDTOConverterToEntity(ardWaylineDTO);
+        uavWayline.setCreateTime(DateUtils.getNowDate());
+        int row = uavWaylineMapper.insertUavWayline(uavWayline);
+        if (row > 0) {
+            waylineFileService.outputKmz(ardWaylineDTO);
+        }
+        return row;
+    }
+
+    /**
+     * 修改航线
+     *
+     * @param ardWaylineDTO 航线
+     * @return 结果
+     */
+    @Override
+    public int updateUavWayline(ArdWaylineDTO ardWaylineDTO) {
+        UavWayline uavWayline = waylineDTOConverterToEntity(ardWaylineDTO);
+        uavWayline.setUpdateTime(DateUtils.getNowDate());
+        int row = uavWaylineMapper.updateUavWayline(uavWayline);
+        if (row > 0) {
+            waylineFileService.outputKmz(ardWaylineDTO);
+        }
+        return row;
+    }
+
+    /**
+     * 批量删除航线
+     *
+     * @param ids 需要删除的航线主键
+     * @return 结果
+     */
+    @Override
+    public int deleteUavWaylineByIds(String[] ids) {
+        return uavWaylineMapper.deleteUavWaylineByIds(ids);
+    }
+
+    /**
+     * 删除航线信息
+     *
+     * @param id 航线主键
+     * @return 结果
+     */
+    @Override
+    public int deleteUavWaylineById(String id) {
+        return uavWaylineMapper.deleteUavWaylineById(id);
+    }
+
+    /**
+     * 校验航线是否唯一
+     *
+     * @param ardWaylineDTO 航线信息
+     * @return 结果
+     */
+    @Override
+    public boolean checkWaylineNameUnique(ArdWaylineDTO ardWaylineDTO)
+    {
+        String waylineId = ardWaylineDTO.getWaylineId();
+        UavWayline info = uavWaylineMapper.checkUavWaylineNameUnique(ardWaylineDTO.getWaylineName());
+        if (StringUtils.isNotNull(info) && !info.getId().equals(waylineId))
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    // DTO转换为实体类
+    private UavWayline waylineDTOConverterToEntity(ArdWaylineDTO ardWaylineDTO) {
+        UavWayline uavWayline = UavWayline.builder()
+                .id(Optional.ofNullable(ardWaylineDTO.getWaylineId()).orElse(IdUtils.simpleUUID()))
+                .name(ardWaylineDTO.getWaylineName())
+                .type(ardWaylineDTO.getWaylineType())
+                .uavModel(ardWaylineDTO.getUavModel())
+                .payloadModel(ardWaylineDTO.getPayloadModel())
+                .userWaypointCount(ardWaylineDTO.waylineUserPointCount)
+                .globalSpeed(ardWaylineDTO.getGlobalFlySpeed())
+                .globalHeight(ardWaylineDTO.getGlobalFlyHeight())
+                .build();
+        generateWaylineBytes(ardWaylineDTO, uavWayline);
+        return uavWayline;
+    }
+
+    // 实体类转换DTO
+    private ArdWaylineDTO EntityConverterTowaylineDTO(UavWayline uavWayline) {
+        try {
+            ArdWaylineDTO ardWaylineDTO = ArdWaylineDTO.builder()
+                    .waylineId(uavWayline.getId())
+                    .waylineName(uavWayline.getName())
+                    .waylineType(uavWayline.getType())
+                    .uavModel(uavWayline.getUavModel())
+                    .payloadModel(uavWayline.getPayloadModel())
+                    .waylineUserPointCount(uavWayline.getUserWaypointCount())
+                    .waylineAllPointCount(uavWayline.getPlanWaypointCount())
+                    .globalFlySpeed(uavWayline.getGlobalSpeed())
+                    .globalFlyHeight(uavWayline.getGlobalHeight())
+                    .build();
+            byte[] planWaylineBytes = uavWayline.getPlanWaylineData();
+            ardWaylineDTO = mapper.readValue(planWaylineBytes, ArdWaylineDTO.class);
+
+            //计算长度
+            double length = calculateWaylineLength(ardWaylineDTO);
+            ardWaylineDTO.setWaylineLength(length);
+            //计算飞行时间
+            double flyTime = calculateFlyTime(ardWaylineDTO);
+            ardWaylineDTO.setWaylineBaseFlyTime(flyTime);
+            return ardWaylineDTO;
+        } catch (IOException e) {
+            throw new RuntimeException("恢复航线对象错误!" + e.getMessage());
+        }
+    }
+
+    // 生成航线字节数组
+    private void generateWaylineBytes(ArdWaylineDTO ardWaylineDTO, UavWayline uavWayline) {
+        try {
+            ardWaylineDTO.setWaylineId(uavWayline.getId());
+            //二进制航线编码,保存原始航线数据
+            byte[] waylineBytes = mapper.writeValueAsBytes(ardWaylineDTO);
+            uavWayline.setSrcWaylineData(waylineBytes);
+            if (ardWaylineDTO.getWaylineType() == 1) {
+                log.info("面向逐点的航线规划!");
+                byte[] newWaylineBytes = mapper.writeValueAsBytes(ardWaylineDTO);
+                uavWayline.setPlanWaylineData(newWaylineBytes);
+                uavWayline.setPlanWaypointCount(ardWaylineDTO.getWaylineUserPointCount());
+            } else if (ardWaylineDTO.getWaylineType() == 3) {
+                log.info("面向仿地的航线规划!");
+                //仿地飞行算法
+                ArdWaylineDTO newWayline = terrainFollowingWayline(ardWaylineDTO);
+                byte[] newWaylineBytes = mapper.writeValueAsBytes(newWayline);
+                uavWayline.setPlanWaylineData(newWaylineBytes);
+                uavWayline.setPlanWaypointCount(newWayline.getWaylineUserPointCount());
+            } else {
+                log.error("航线设置错误:{}", ardWaylineDTO.getWaylineType());
+                throw new RuntimeException("航线类型错误" + ardWaylineDTO.getWaylineType());
+            }
+        } catch (Exception e) {
+            log.error("航线规划失败:{}", e.getMessage());
+            throw new RuntimeException("航线规划失败!" + e.getMessage());
+        }
+    }
+
+    //调用仿地航线算法
+    private ArdWaylineDTO terrainFollowingWayline(ArdWaylineDTO ardWaylineDTO) throws Exception {
+
+        ArdWaylineDTO newWaylineDTO = deepCopyWayline(ardWaylineDTO);
+        //使用仿地飞行规划器
+        List<ArdUavPositionDTO> ardUavPositionDTOS = terrainFollowingPlanner.interpolateWithTerrain(ardWaylineDTO);
+        newWaylineDTO.setWaylineAllPointCount(ardUavPositionDTOS.size());//总航点数量
+        long userPointCount = ardUavPositionDTOS.stream()
+                .filter(p -> p.getType() == 0)
+                .count();
+        newWaylineDTO.setWaylineUserPointCount((int) userPointCount);//用户航点数量
+        newWaylineDTO.setUavPositions(ardUavPositionDTOS);
+        // 规划成功,返回结果
+        return newWaylineDTO;
+
+    }
+
+    private ArdWaylineDTO deepCopyWayline(ArdWaylineDTO ardWaylineDTO) {
+
+        ArdWaylineDTO newWaylineDTO = null;
+        try {
+            byte[] waylineBytes = mapper.writeValueAsBytes(ardWaylineDTO);
+
+            newWaylineDTO = mapper.readValue(waylineBytes, ArdWaylineDTO.class);
+
+        } catch (JsonProcessingException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return newWaylineDTO;
+    }
+    
+    //计算航线长度
+    public double calculateWaylineLength( ArdWaylineDTO ardWaylineDTO) {
+        List<ArdUavPositionDTO> uavPositions = ardWaylineDTO.getUavPositions();
+        if (uavPositions == null || uavPositions.size() < 2) {
+            return 0.0;
+        }
+
+        double totalLength = 0.0;
+        for (int i = 1; i < uavPositions.size(); i++) {
+            ArdUavPositionDTO prev = uavPositions.get(i - 1);
+            ArdUavPositionDTO curr = uavPositions.get(i);
+            totalLength += GeoTools.calculateDistance(
+                    prev.getUavPosition().getLatitude(), prev.getUavPosition().getLongitude(),
+                    curr.getUavPosition().getLatitude(), curr.getUavPosition().getLongitude()
+            );
+        }
+        return totalLength;
+    }
+    //计算飞行时间
+    public double calculateFlyTime(ArdWaylineDTO ardWaylineDTO) {
+        double totalLength = calculateWaylineLength(ardWaylineDTO); // 单位:米
+        Float globalSpeed = ardWaylineDTO.getGlobalFlySpeed(); // 单位:米/秒
+
+        if (globalSpeed == null || globalSpeed <= 0) {
+            throw new IllegalArgumentException("全局速度无效,必须大于0");
+        }
+        return totalLength / globalSpeed; // 返回时间,单位:秒
+    }
+
+
+}
diff --git a/ard-work/src/main/java/com/ard/tianyu/util/CameraUtil.java b/ard-work/src/main/java/com/ard/tianyu/util/CameraUtil.java
index f30ce8e..32362ce 100644
--- a/ard-work/src/main/java/com/ard/tianyu/util/CameraUtil.java
+++ b/ard-work/src/main/java/com/ard/tianyu/util/CameraUtil.java
@@ -66,10 +66,4 @@
         }
         return zoomFactor;
     }
-
-
-    public static void main(String[] args) {
-        Float fieldAngleFromZoom = getFieldAngleFromZoom(1, 3F);
-        System.out.println(fieldAngleFromZoom);
-    }
 }
\ No newline at end of file
diff --git a/ard-work/src/main/java/com/ard/tianyu/util/GeoTools.java b/ard-work/src/main/java/com/ard/tianyu/util/GeoTools.java
new file mode 100644
index 0000000..a407484
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/util/GeoTools.java
@@ -0,0 +1,98 @@
+package com.ard.tianyu.util;
+
+public class GeoTools {
+	public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
+		final double R = 6371000; // 地球半径,单位:米
+
+		double radLat1 = Math.toRadians(lat1);
+		double radLat2 = Math.toRadians(lat2);
+		double deltaLat = radLat2 - radLat1;
+		double deltaLon = Math.toRadians(lon2 - lon1);
+
+		double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
+				Math.cos(radLat1) * Math.cos(radLat2) *
+						Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
+
+		double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+
+		return R * c;
+	}
+
+	// 经纬度坐标球面距离计算
+	public static double DistanceSphere(Vertex v1, Vertex v2) {
+		double radLat1 = Math.toRadians(v1.y);
+		double radLat2 = Math.toRadians(v2.y);
+		double a = Math.abs(radLat1 - radLat2);
+		double b = Math.abs(Math.toRadians(v1.x) - Math.toRadians(v2.x));
+		double h = Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1)
+				* Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2);
+		double earthRadius = 6371000;
+        return 2 * earthRadius * Math.asin(Math.sqrt(h));
+	}
+	/*
+	 *  高德坐标系转wgs84
+	 */
+	public static Vertex GCJ02toWGS84(Vertex v_gcj) {
+		Vertex temp = WGS84toGCJ02(v_gcj);
+		double lng_wgs = v_gcj.x * 2 - temp.x;
+		double lat_wgs = v_gcj.y * 2 - temp.y;
+
+		return new Vertex(lng_wgs, lat_wgs);
+	}
+	/*
+	 *  wgs84转高德坐标系
+	 */
+	public static Vertex WGS84toGCJ02(Vertex v_wgs) {
+		// 一些常数
+		double a = 6378245.0;  // 长半轴
+		double ee = 0.00669342162296594323;  // 扁率
+		double lng = v_wgs.x;
+		double lat = v_wgs.y;
+
+		//判断是否在国内
+		if (OutOfChina(v_wgs))
+			return v_wgs;
+		double dlat = transformlat(lng - 105.0, lat - 35.0);
+		double dlng = transformlng(lng - 105.0, lat - 35.0);
+		double radlat = lat / 180.0 * Math.PI;
+		double magic = Math.sin(radlat);
+		magic = 1 - ee * magic * magic;
+		double sqrtmagic = Math.sqrt(magic);
+		dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * Math.PI);
+		dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * Math.PI);
+
+		return new Vertex(lng + dlng, lat + dlat);
+	}
+	/*
+	 * 判断点(经纬度)是否在国内,不在国内不做偏移
+	 */
+	public static boolean OutOfChina(Vertex v) {
+		if (v.x < 72.004 || v.x > 137.8347)
+			return true;
+		if (v.y < 0.8293 || v.y > 55.8271)
+			return true;
+		return false;
+	}
+	public static double transformlat(double lng, double lat) {
+		double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat +
+				0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
+		ret += (20.0 * Math.sin(6.0 * lng * Math.PI) + 20.0 *
+				Math.sin(2.0 * lng * Math.PI)) * 2.0 / 3.0;
+		ret += (20.0 * Math.sin(lat * Math.PI) + 40.0 *
+				Math.sin(lat / 3.0 * Math.PI)) * 2.0 / 3.0;
+		ret += (160.0 * Math.sin(lat / 12.0 * Math.PI) + 320 *
+				Math.sin(lat * Math.PI / 30.0)) * 2.0 / 3.0;
+		return ret;
+	}
+	public static double transformlng(double lng, double lat) {
+		double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng +
+				0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
+		ret += (20.0 * Math.sin(6.0 * lng * Math.PI) + 20.0 *
+				Math.sin(2.0 * lng * Math.PI)) * 2.0 / 3.0;
+		ret += (20.0 * Math.sin(lng * Math.PI) + 40.0 *
+				Math.sin(lng / 3.0 * Math.PI)) * 2.0 / 3.0;
+		ret += (150.0 * Math.sin(lng / 12.0 * Math.PI) + 300.0 *
+				Math.sin(lng / 30.0 * Math.PI)) * 2.0 / 3.0;
+		return ret;
+	}
+}
\ No newline at end of file
diff --git a/ard-work/src/main/java/com/ard/tianyu/util/Vertex.java b/ard-work/src/main/java/com/ard/tianyu/util/Vertex.java
new file mode 100644
index 0000000..6af8ec2
--- /dev/null
+++ b/ard-work/src/main/java/com/ard/tianyu/util/Vertex.java
@@ -0,0 +1,29 @@
+package com.ard.tianyu.util;
+
+public class Vertex {
+	public double x;
+	public double y;
+
+	public Vertex() {
+
+	}
+
+	public Vertex(double x, double y) {
+		this.x = x;
+		this.y = y;
+	}
+
+	// 判断这个点是不是和另外一个点重合(允许极小的误差,算作同一个点
+	public boolean IsSame(Vertex anotherV) {
+		double almostZero = 0.00000001;
+		if ((Math.abs(x - anotherV.x) < almostZero) && (Math.abs(y - anotherV.y) < almostZero))
+			return true;
+		else
+			return false;
+	}
+
+	// 直线距离
+	public double Distance(Vertex anotherV) {
+		return Math.sqrt(Math.pow(x - anotherV.x, 2) + Math.pow(y - anotherV.y, 2));
+	}
+}
\ No newline at end of file
diff --git a/ard-work/src/main/resources/mapper/UavWaylineMapper.xml b/ard-work/src/main/resources/mapper/UavWaylineMapper.xml
new file mode 100644
index 0000000..2fab33d
--- /dev/null
+++ b/ard-work/src/main/resources/mapper/UavWaylineMapper.xml
@@ -0,0 +1,161 @@
+<?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.tianyu.mapper.UavWaylineMapper">
+    
+    <resultMap type="UavWayline" id="UavWaylineResult">
+        <result property="id"    column="id"    />
+        <result property="name"    column="name"    />
+        <result property="type"    column="type"    />
+        <result property="uavModel"    column="uav_model"    />
+        <result property="payloadModel"    column="payload_model"    />
+        <result property="globalSpeed"    column="global_speed"    />
+        <result property="globalHeight"    column="global_height"    />
+        <result property="userWaypointCount"    column="user_waypoint_count"    />
+        <result property="planWaypointCount"    column="plan_waypoint_count"    />
+        <result property="srcWaylineData"    column="src_wayline_data"    />
+        <result property="planWaylineData"    column="plan_wayline_data"    />
+        <result property="kmzUrl"    column="kmz_url"    />
+        <result property="userId"    column="user_id"    />
+        <result property="deptId"    column="dept_id"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="remark"    column="remark"    />
+    </resultMap>
+
+    <sql id="selectUavWaylineVo">
+        select id,
+               name,
+               type,
+               uav_model,
+               payload_model,
+               global_speed,
+               global_height,
+               user_waypoint_count,
+               plan_waypoint_count,
+               src_wayline_data,
+               plan_wayline_data,
+               kmz_url,
+               user_id,
+               dept_id,
+               create_by,
+               create_time,
+               update_by,
+               update_time,
+               remark
+        from uav_wayline
+    </sql>
+
+    <select id="selectUavWaylineList" parameterType="UavWayline" resultMap="UavWaylineResult">
+        <include refid="selectUavWaylineVo"/>
+        <where>  
+            <if test="name != null  and name != ''"> and name like concat('%', #{name}, '%')</if>
+            <if test="type != null  and type != ''"> and type = #{type}</if>
+            <if test="uavModel != null  and uavModel != ''"> and uav_model = #{uavModel}</if>
+            <if test="payloadModel != null  and payloadModel != ''"> and payload_model = #{payloadModel}</if>
+            <if test="globalSpeed != null "> and global_speed = #{globalSpeed}</if>
+            <if test="globalHeight != null "> and global_height = #{globalHeight}</if>
+            <if test="userWaypointCount != null "> and user_waypoint_count = #{userWaypointCount}</if>
+            <if test="planWaypointCount != null "> and plan_waypoint_count = #{planWaypointCount}</if>
+            <if test="srcWaylineData != null"> and src_wayline_data =#{srcWaylineData}</if>
+            <if test="planWaylineData != null"> and plan_wayline_data = #{planWaylineData}</if>
+            <if test="kmzUrl != null  and kmzUrl != ''"> and kmz_url = #{kmzUrl}</if>
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="deptId != null "> and dept_id = #{deptId}</if>
+        </where>
+    </select>
+    
+    <select id="selectUavWaylineById" parameterType="String" resultMap="UavWaylineResult">
+        <include refid="selectUavWaylineVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertUavWayline" parameterType="UavWayline">
+        insert into uav_wayline
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="name != null">name,</if>
+            <if test="type != null">type,</if>
+            <if test="uavModel != null">uav_model,</if>
+            <if test="payloadModel != null">payload_model,</if>
+            <if test="globalSpeed != null">global_speed,</if>
+            <if test="globalHeight != null">global_height,</if>
+            <if test="userWaypointCount != null">user_waypoint_count,</if>
+            <if test="planWaypointCount != null">plan_waypoint_count,</if>
+            <if test="srcWaylineData != null">src_wayline_data,</if>
+            <if test="planWaylineData != null">plan_wayline_data,</if>
+            <if test="kmzUrl != null">kmz_url,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="deptId != null">dept_id,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="remark != null">remark,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="name != null">#{name},</if>
+            <if test="type != null">#{type},</if>
+            <if test="uavModel != null">#{uavModel},</if>
+            <if test="payloadModel != null">#{payloadModel},</if>
+            <if test="globalSpeed != null">#{globalSpeed},</if>
+            <if test="globalHeight != null">#{globalHeight},</if>
+            <if test="userWaypointCount != null">#{userWaypointCount},</if>
+            <if test="planWaypointCount != null">#{planWaypointCount},</if>
+            <if test="srcWaylineData != null">#{srcWaylineData},</if>
+            <if test="planWaylineData != null">#{planWaylineData},</if>
+            <if test="kmzUrl != null">#{kmzUrl},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="deptId != null">#{deptId},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="remark != null">#{remark},</if>
+         </trim>
+    </insert>
+
+    <update id="updateUavWayline" parameterType="UavWayline">
+        update uav_wayline
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="name != null">name = #{name},</if>
+            <if test="type != null">type = #{type},</if>
+            <if test="uavModel != null">uav_model = #{uavModel},</if>
+            <if test="payloadModel != null">payload_model = #{payloadModel},</if>
+            <if test="globalSpeed != null">global_speed = #{globalSpeed},</if>
+            <if test="globalHeight != null">global_height = #{globalHeight},</if>
+            <if test="userWaypointCount != null ">  user_waypoint_count = #{userWaypointCount}</if>
+            <if test="planWaypointCount != null ">  plan_waypoint_count = #{planWaypointCount}</if>
+            <if test="srcWaylineData != null ">  src_wayline_data =#{srcWaylineData}</if>
+            <if test="planWaylineData != null ">  plan_wayline_data = #{planWaylineData}</if>
+            <if test="kmzUrl != null">kmz_url = #{kmzUrl},</if>
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="deptId != null">dept_id = #{deptId},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="remark != null">remark = #{remark},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteUavWaylineById" parameterType="String">
+        delete from uav_wayline where id = #{id}
+    </delete>
+
+    <delete id="deleteUavWaylineByIds" parameterType="String">
+        delete from uav_wayline where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <select id="checkUavWaylineNameUnique" parameterType="String" resultMap="UavWaylineResult">
+        select id, name from uav_wayline where name = #{name}  limit 1
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 3cf3f23..81e6962 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,6 +31,7 @@
         <velocity.version>2.3</velocity.version>
         <jwt.version>0.9.1</jwt.version>
         <knife4j.version>3.0.3</knife4j.version>
+        <okhttp.version>4.8.1</okhttp.version>
     </properties>
 
     <!-- 依赖声明 -->
@@ -182,7 +183,12 @@
                 <artifactId>ard-common</artifactId>
                 <version>${ard.version}</version>
             </dependency>
-
+            <!-- okhttp3-->
+            <dependency>
+                <groupId>com.squareup.okhttp3</groupId>
+                <artifactId>okhttp</artifactId>
+                <version>${okhttp.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 

--
Gitblit v1.9.3